diff --git a/.eslintignore b/.eslintignore index 0df415b13..75b5f6575 100644 --- a/.eslintignore +++ b/.eslintignore @@ -8,3 +8,4 @@ dist # ignoring scenario dir because it contains deprecated saddle code and will be removed scenario typechain +tmp diff --git a/.gitignore b/.gitignore index b8e94c4fc..da81dbf90 100644 --- a/.gitignore +++ b/.gitignore @@ -64,3 +64,6 @@ deployments/localhost # OSX .DS_Store + +tmp/* +.vscode diff --git a/.prettierignore b/.prettierignore index 055069d87..37b1fe65a 100644 --- a/.prettierignore +++ b/.prettierignore @@ -10,3 +10,4 @@ scenario typechain contracts/oracle CHANGELOG.md +tmp diff --git a/contracts/Lens/SpokePoolLens.sol b/contracts/Lens/SpokePoolLens.sol new file mode 100644 index 000000000..e4e54e177 --- /dev/null +++ b/contracts/Lens/SpokePoolLens.sol @@ -0,0 +1,713 @@ +// SPDX-License-Identifier: BSD-3-Clause +pragma solidity 0.8.25; + +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import { IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; +import { ResilientOracleInterface } from "@venusprotocol/oracle/contracts/interfaces/OracleInterface.sol"; +import { TimeManagerV8 } from "@venusprotocol/solidity-utilities/contracts/TimeManagerV8.sol"; + +import { ExponentialNoError } from "../ExponentialNoError.sol"; +import { VToken } from "../VToken.sol"; +import { Action, ComptrollerInterface, ComptrollerViewInterface } from "../ComptrollerInterface.sol"; +import { PoolRegistryInterface } from "../Pool/PoolRegistryInterface.sol"; +import { PoolRegistry } from "../Pool/PoolRegistry.sol"; +import { RewardsDistributor } from "../Rewards/RewardsDistributor.sol"; +import { SpokeComptrollerViewInterface } from "../Spoke/SpokeComptrollerInterface.sol"; + +/** + * @title SpokePoolLens + * @author Venus + * @notice Reads pool and market state specific to a spoke pool + * + * @dev Spoke pools expose additional state that is not included in the shared PoolLens data, including the + * deviation-bounded oracle, the supply and liquidation allowlists, liquidation thresholds and liquidation incentives. + * Those reads are named `spoke*` or `getSpokePool*` and return spoke-shaped structs. + * + * The rest of the surface mirrors `PoolLens` under the same names and struct shapes, so that reading a spoke pool + * takes one address rather than two. Those reads go through getters both kinds of pool share. + * + * Access-controlled call permissions are deliberately not reported: `AccessControlManager` derives the role from its + * own caller, so asked from a lens it answers `false` for an account that can in fact call. + * + * This contract holds no state and is versioned by redeployment. + */ +contract SpokePoolLens is ExponentialNoError, TimeManagerV8 { + /** + * @dev Mirrors `PoolLens.PoolData` with spoke-pool-specific fields. + */ + struct SpokePoolData { + string name; + address creator; + address comptroller; + uint256 blockPosted; + uint256 timestampPosted; + string category; + string logoURL; + string description; + address priceOracle; + uint256 closeFactor; + /// @notice The pool-wide liquidation incentive, scaled by 1e18 + uint256 poolLiquidationIncentiveMantissa; + uint256 minLiquidatableCollateral; + /// @notice Oracle used to bound collateral prices for borrowing and redeeming. Zero until governance sets it, + /// and while it is zero both actions revert + address deviationBoundedOracle; + /// @notice Whether liquidation is restricted to allowlisted accounts + bool liquidationAllowlistEnabled; + SpokeVTokenMetadata[] vTokens; + } + + /** + * @dev Mirrors `PoolLens.VTokenMetadata` with spoke-pool-specific fields. + */ + struct SpokeVTokenMetadata { + address vToken; + uint256 exchangeRateCurrent; + uint256 supplyRatePerBlockOrTimestamp; + uint256 borrowRatePerBlockOrTimestamp; + uint256 reserveFactorMantissa; + uint256 supplyCaps; + uint256 borrowCaps; + uint256 totalBorrows; + uint256 totalReserves; + uint256 totalSupply; + uint256 totalCash; + bool isListed; + uint256 collateralFactorMantissa; + /// @notice The collateral weight above which a position becomes liquidatable, scaled by 1e18 + uint256 liquidationThresholdMantissa; + address underlyingAssetAddress; + uint256 vTokenDecimals; + uint256 underlyingDecimals; + uint256 pausedActions; + /// @notice The liquidation incentive effective for this market, scaled by 1e18 + uint256 effectiveLiquidationIncentiveMantissa; + /// @notice The market-specific liquidation incentive, or zero when using the pool-wide value + uint256 ownLiquidationIncentiveMantissa; + /// @notice Whether supply is restricted to allowlisted accounts + bool supplyAllowlistEnabled; + /// @notice Whether the market allows full liquidation without a shortfall + bool forcedLiquidationEnabled; + } + + /** + * @dev Returns both the market's allowlist status and whether the account is allowlisted. + */ + struct SpokeSupplyPermission { + address vToken; + /// @notice Whether supply is restricted to allowlisted accounts + bool allowlistEnabled; + /// @notice Whether the account is allowlisted for the market + bool accountAllowlisted; + } + + /** + * @dev Struct for VTokenBalance. + */ + struct VTokenBalances { + address vToken; + uint256 balanceOf; + uint256 borrowBalanceCurrent; + uint256 balanceOfUnderlying; + uint256 tokenBalance; + uint256 tokenAllowance; + } + + /** + * @dev Struct for underlyingPrice of VToken. + */ + struct VTokenUnderlyingPrice { + address vToken; + uint256 underlyingPrice; + } + + /** + * @dev Struct with pending reward info for a market. + */ + struct PendingReward { + address vTokenAddress; + uint256 amount; + } + + /** + * @dev Struct with reward distribution totals for a single reward token and distributor. + */ + struct RewardSummary { + address distributorAddress; + address rewardTokenAddress; + uint256 totalRewards; + PendingReward[] pendingRewards; + } + + /** + * @dev Struct used in RewardDistributor to save last updated market state. + */ + struct RewardTokenState { + // The market's last updated rewardTokenBorrowIndex or rewardTokenSupplyIndex + uint224 index; + // The block number or timestamp the index was last updated at + uint256 blockOrTimestamp; + // The block number or timestamp at which to stop rewards + uint256 lastRewardingBlockOrTimestamp; + } + + /** + * @dev Struct with bad debt of a market denominated + */ + struct BadDebt { + address vTokenAddress; + uint256 badDebtUsd; + } + + /** + * @dev Struct with bad debt total denominated in usd for a pool and an array of BadDebt structs for each market + */ + struct BadDebtSummary { + address comptroller; + uint256 totalBadDebtUsd; + BadDebt[] badDebts; + } + + /** + * @param timeBased_ A boolean indicating whether the contract is based on time or block + * @param blocksPerYear_ The number of blocks per year + * @custom:oz-upgrades-unsafe-allow constructor + */ + constructor(bool timeBased_, uint256 blocksPerYear_) TimeManagerV8(timeBased_, blocksPerYear_) {} + + /** + * @notice Queries the user's supply/borrow balances in vTokens + * @param vTokens The list of vToken addresses + * @param account The user Account + * @return A list of structs containing balances data + */ + function vTokenBalancesAll(VToken[] calldata vTokens, address account) external returns (VTokenBalances[] memory) { + uint256 vTokenCount = vTokens.length; + VTokenBalances[] memory res = new VTokenBalances[](vTokenCount); + for (uint256 i; i < vTokenCount; ++i) { + res[i] = vTokenBalances(vTokens[i], account); + } + return res; + } + + /** + * @notice Queries every pool and market in a spoke pool registry + * @dev Not intended to be called in a transaction due to its gas cost. The registry must contain only spoke pools. + * @param poolRegistryAddress The registry to enumerate + * @return The data of every pool in the registry + */ + function getAllSpokePools(address poolRegistryAddress) external view returns (SpokePoolData[] memory) { + PoolRegistry.VenusPool[] memory pools = PoolRegistryInterface(poolRegistryAddress).getAllPools(); + uint256 poolLength = pools.length; + + SpokePoolData[] memory poolDataItems = new SpokePoolData[](poolLength); + + for (uint256 i; i < poolLength; ++i) { + poolDataItems[i] = getSpokePoolData(poolRegistryAddress, pools[i]); + } + + return poolDataItems; + } + + /** + * @notice Queries a spoke pool by its comptroller address + * @param poolRegistryAddress The registry containing the pool + * @param comptroller The pool's comptroller + * @return The pool's data + */ + function getSpokePoolByComptroller( + address poolRegistryAddress, + address comptroller + ) external view returns (SpokePoolData memory) { + PoolRegistryInterface poolRegistry = PoolRegistryInterface(poolRegistryAddress); + return getSpokePoolData(poolRegistryAddress, poolRegistry.getPoolByComptroller(comptroller)); + } + + /** + * @notice Returns whether an account may supply to each specified market + * @dev The account checked is the one receiving the minted vTokens, which can differ from the payer when using + * `mintBehalf`. + * @param comptroller The spoke pool's comptroller + * @param vTokens The markets to test + * @param account The account to test + * @return One entry per market, in the order given + */ + function spokeSupplyPermissions( + address comptroller, + VToken[] memory vTokens, + address account + ) external view returns (SpokeSupplyPermission[] memory) { + SpokeComptrollerViewInterface spoke = SpokeComptrollerViewInterface(comptroller); + uint256 len = vTokens.length; + + SpokeSupplyPermission[] memory permissions = new SpokeSupplyPermission[](len); + + for (uint256 i; i < len; ++i) { + address vToken = address(vTokens[i]); + permissions[i] = SpokeSupplyPermission({ + vToken: vToken, + allowlistEnabled: spoke.isSupplyAllowlistEnabled(vToken), + accountAllowlisted: spoke.isAllowedSupplier(vToken, account) + }); + } + + return permissions; + } + + /** + * @notice Returns whether an account may seize collateral in a spoke pool + * @param comptroller The spoke pool's comptroller + * @param liquidator The account to test + * @return allowlistEnabled Whether liquidation is restricted to allowlisted accounts + * @return accountAllowlisted Whether the account is allowlisted + */ + function spokeLiquidationPermission( + address comptroller, + address liquidator + ) external view returns (bool allowlistEnabled, bool accountAllowlisted) { + SpokeComptrollerViewInterface spoke = SpokeComptrollerViewInterface(comptroller); + return (spoke.isLiquidationAllowlistEnabled(), spoke.isAllowedLiquidator(liquidator)); + } + + /** + * @notice Returns the price data for the underlying assets of the specified vTokens + * @param vTokens The list of vToken addresses + * @return An array containing the price data for each asset + */ + function vTokenUnderlyingPriceAll( + VToken[] calldata vTokens + ) external view returns (VTokenUnderlyingPrice[] memory) { + uint256 vTokenCount = vTokens.length; + VTokenUnderlyingPrice[] memory res = new VTokenUnderlyingPrice[](vTokenCount); + for (uint256 i; i < vTokenCount; ++i) { + res[i] = vTokenUnderlyingPrice(vTokens[i]); + } + return res; + } + + /** + * @notice Returns the pending rewards for a user for a given pool. + * @param account The user account. + * @param comptrollerAddress address + * @return Pending rewards array + */ + function getPendingRewards( + address account, + address comptrollerAddress + ) external view returns (RewardSummary[] memory) { + VToken[] memory markets = ComptrollerInterface(comptrollerAddress).getAllMarkets(); + RewardsDistributor[] memory rewardsDistributors = ComptrollerViewInterface(comptrollerAddress) + .getRewardDistributors(); + RewardSummary[] memory rewardSummary = new RewardSummary[](rewardsDistributors.length); + for (uint256 i; i < rewardsDistributors.length; ++i) { + RewardSummary memory reward; + reward.distributorAddress = address(rewardsDistributors[i]); + reward.rewardTokenAddress = address(rewardsDistributors[i].rewardToken()); + reward.totalRewards = rewardsDistributors[i].rewardTokenAccrued(account); + reward.pendingRewards = _calculateNotDistributedAwards(account, markets, rewardsDistributors[i]); + rewardSummary[i] = reward; + } + return rewardSummary; + } + + /** + * @notice Returns a summary of a pool's bad debt broken down by market + * + * @param comptrollerAddress Address of the comptroller + * + * @return badDebtSummary A struct with comptroller address, total bad debut denominated in usd, and + * a break down of bad debt by market + */ + function getPoolBadDebt(address comptrollerAddress) external view returns (BadDebtSummary memory) { + uint256 totalBadDebtUsd; + + // Get every market in the pool + ComptrollerViewInterface comptroller = ComptrollerViewInterface(comptrollerAddress); + VToken[] memory markets = comptroller.getAllMarkets(); + ResilientOracleInterface priceOracle = comptroller.oracle(); + + BadDebt[] memory badDebts = new BadDebt[](markets.length); + + BadDebtSummary memory badDebtSummary; + badDebtSummary.comptroller = comptrollerAddress; + badDebtSummary.badDebts = badDebts; + + // // Calculate the bad debt is USD per market + for (uint256 i; i < markets.length; ++i) { + BadDebt memory badDebt; + badDebt.vTokenAddress = address(markets[i]); + badDebt.badDebtUsd = + (VToken(address(markets[i])).badDebt() * priceOracle.getUnderlyingPrice(address(markets[i]))) / + EXP_SCALE; + badDebtSummary.badDebts[i] = badDebt; + totalBadDebtUsd = totalBadDebtUsd + badDebt.badDebtUsd; + } + + badDebtSummary.totalBadDebtUsd = totalBadDebtUsd; + + return badDebtSummary; + } + + /** + * @notice Returns vToken holding the specified underlying asset in the specified pool + * @param poolRegistryAddress The address of the PoolRegistry contract + * @param comptroller The pool comptroller + * @param asset The underlyingAsset of VToken + * @return Address of the vToken + */ + function getVTokenForAsset( + address poolRegistryAddress, + address comptroller, + address asset + ) external view returns (address) { + PoolRegistryInterface poolRegistryInterface = PoolRegistryInterface(poolRegistryAddress); + return poolRegistryInterface.getVTokenForAsset(comptroller, asset); + } + + /** + * @notice Returns all pools that support the specified underlying asset + * @param poolRegistryAddress The address of the PoolRegistry contract + * @param asset The underlying asset of vToken + * @return A list of Comptroller contracts + */ + function getPoolsSupportedByAsset( + address poolRegistryAddress, + address asset + ) external view returns (address[] memory) { + PoolRegistryInterface poolRegistryInterface = PoolRegistryInterface(poolRegistryAddress); + return poolRegistryInterface.getPoolsSupportedByAsset(asset); + } + + /** + * @notice Queries the user's supply/borrow balances in the specified vToken + * @param vToken vToken address + * @param account The user Account + * @return A struct containing the balances data + */ + function vTokenBalances(VToken vToken, address account) public returns (VTokenBalances memory) { + uint256 balanceOf = vToken.balanceOf(account); + uint256 borrowBalanceCurrent = vToken.borrowBalanceCurrent(account); + uint256 balanceOfUnderlying = vToken.balanceOfUnderlying(account); + uint256 tokenBalance; + uint256 tokenAllowance; + + IERC20 underlying = IERC20(vToken.underlying()); + tokenBalance = underlying.balanceOf(account); + tokenAllowance = underlying.allowance(account, address(vToken)); + + return + VTokenBalances({ + vToken: address(vToken), + balanceOf: balanceOf, + borrowBalanceCurrent: borrowBalanceCurrent, + balanceOfUnderlying: balanceOfUnderlying, + tokenBalance: tokenBalance, + tokenAllowance: tokenAllowance + }); + } + + /** + * @notice Queries a spoke pool from its registry entry + * @param poolRegistryAddress The registry containing the pool + * @param venusPool The pool's registry entry + * @return The pool's data, including its markets + */ + function getSpokePoolData( + address poolRegistryAddress, + PoolRegistry.VenusPool memory venusPool + ) public view returns (SpokePoolData memory) { + address comptroller = venusPool.comptroller; + + SpokeVTokenMetadata[] memory vTokenMetadataItems = spokeVTokenMetadataAll( + ComptrollerInterface(comptroller).getAllMarkets() + ); + + PoolRegistry.VenusPoolMetaData memory metaData = PoolRegistryInterface(poolRegistryAddress) + .getVenusPoolMetadata(comptroller); + + ComptrollerViewInterface pooledView = ComptrollerViewInterface(comptroller); + SpokeComptrollerViewInterface spokeView = SpokeComptrollerViewInterface(comptroller); + + SpokePoolData memory poolData; + poolData.name = venusPool.name; + poolData.creator = venusPool.creator; + poolData.comptroller = comptroller; + poolData.blockPosted = venusPool.blockPosted; + poolData.timestampPosted = venusPool.timestampPosted; + poolData.category = metaData.category; + poolData.logoURL = metaData.logoURL; + poolData.description = metaData.description; + poolData.priceOracle = address(pooledView.oracle()); + poolData.closeFactor = pooledView.closeFactorMantissa(); + // This call is made from the lens, so the comptroller returns the pool-wide incentive. + poolData.poolLiquidationIncentiveMantissa = pooledView.liquidationIncentiveMantissa(); + poolData.minLiquidatableCollateral = pooledView.minLiquidatableCollateral(); + poolData.deviationBoundedOracle = address(spokeView.deviationBoundedOracle()); + poolData.liquidationAllowlistEnabled = spokeView.isLiquidationAllowlistEnabled(); + poolData.vTokens = vTokenMetadataItems; + + return poolData; + } + + /** + * @notice Queries the metadata of every given market of a spoke pool + * @param vTokens The markets to read + * @return One entry per market, in the order given + */ + function spokeVTokenMetadataAll(VToken[] memory vTokens) public view returns (SpokeVTokenMetadata[] memory) { + uint256 len = vTokens.length; + + SpokeVTokenMetadata[] memory metadataItems = new SpokeVTokenMetadata[](len); + + for (uint256 i; i < len; ++i) { + metadataItems[i] = spokeVTokenMetadata(vTokens[i]); + } + + return metadataItems; + } + + /** + * @notice Queries the metadata of one market of a spoke pool + * @param vToken The market to read + * @return The market's metadata + */ + function spokeVTokenMetadata(VToken vToken) public view returns (SpokeVTokenMetadata memory) { + address vTokenAddress = address(vToken); + address comptroller = address(vToken.comptroller()); + + ComptrollerViewInterface pooledView = ComptrollerViewInterface(comptroller); + SpokeComptrollerViewInterface spokeView = SpokeComptrollerViewInterface(comptroller); + + // Read through the spoke interface, which declares all three returns. `ComptrollerViewInterface` declares + // the first two, so reading it there drops the liquidation threshold with no error. + (bool isListed, uint256 collateralFactorMantissa, uint256 liquidationThresholdMantissa) = spokeView.markets( + vTokenAddress + ); + + address underlying = vToken.underlying(); + + return + SpokeVTokenMetadata({ + vToken: vTokenAddress, + exchangeRateCurrent: vToken.exchangeRateStored(), + // Zero for an empty market, as `PoolLens` reports: the rate model divides by the market's supply. + supplyRatePerBlockOrTimestamp: vToken.totalSupply() > 0 ? vToken.supplyRatePerBlock() : 0, + borrowRatePerBlockOrTimestamp: vToken.borrowRatePerBlock(), + reserveFactorMantissa: vToken.reserveFactorMantissa(), + supplyCaps: pooledView.supplyCaps(vTokenAddress), + borrowCaps: pooledView.borrowCaps(vTokenAddress), + totalBorrows: vToken.totalBorrows(), + totalReserves: vToken.totalReserves(), + totalSupply: vToken.totalSupply(), + totalCash: vToken.getCash(), + isListed: isListed, + collateralFactorMantissa: collateralFactorMantissa, + liquidationThresholdMantissa: liquidationThresholdMantissa, + underlyingAssetAddress: underlying, + vTokenDecimals: vToken.decimals(), + underlyingDecimals: IERC20Metadata(underlying).decimals(), + pausedActions: _pausedActions(comptroller, vTokenAddress), + effectiveLiquidationIncentiveMantissa: spokeView.effectiveLiquidationIncentive(vTokenAddress), + ownLiquidationIncentiveMantissa: spokeView.liquidationIncentives(vTokenAddress), + supplyAllowlistEnabled: spokeView.isSupplyAllowlistEnabled(vTokenAddress), + forcedLiquidationEnabled: spokeView.isForcedLiquidationEnabled(vTokenAddress) + }); + } + + /** + * @notice Returns the price data for the underlying asset of the specified vToken + * @param vToken vToken address + * @return The price data for each asset + */ + function vTokenUnderlyingPrice(VToken vToken) public view returns (VTokenUnderlyingPrice memory) { + ComptrollerViewInterface comptroller = ComptrollerViewInterface(address(vToken.comptroller())); + ResilientOracleInterface priceOracle = comptroller.oracle(); + + return + VTokenUnderlyingPrice({ + vToken: address(vToken), + underlyingPrice: priceOracle.getUnderlyingPrice(address(vToken)) + }); + } + + function _calculateNotDistributedAwards( + address account, + VToken[] memory markets, + RewardsDistributor rewardsDistributor + ) internal view returns (PendingReward[] memory) { + PendingReward[] memory pendingRewards = new PendingReward[](markets.length); + + for (uint256 i; i < markets.length; ++i) { + // Market borrow and supply state we will modify update in-memory, in order to not modify storage + RewardTokenState memory borrowState; + RewardTokenState memory supplyState; + + if (isTimeBased) { + ( + borrowState.index, + borrowState.blockOrTimestamp, + borrowState.lastRewardingBlockOrTimestamp + ) = rewardsDistributor.rewardTokenBorrowStateTimeBased(address(markets[i])); + ( + supplyState.index, + supplyState.blockOrTimestamp, + supplyState.lastRewardingBlockOrTimestamp + ) = rewardsDistributor.rewardTokenSupplyStateTimeBased(address(markets[i])); + } else { + ( + borrowState.index, + borrowState.blockOrTimestamp, + borrowState.lastRewardingBlockOrTimestamp + ) = rewardsDistributor.rewardTokenBorrowState(address(markets[i])); + ( + supplyState.index, + supplyState.blockOrTimestamp, + supplyState.lastRewardingBlockOrTimestamp + ) = rewardsDistributor.rewardTokenSupplyState(address(markets[i])); + } + + Exp memory marketBorrowIndex = Exp({ mantissa: markets[i].borrowIndex() }); + + // Update market supply and borrow index in-memory + updateMarketBorrowIndex(address(markets[i]), rewardsDistributor, borrowState, marketBorrowIndex); + updateMarketSupplyIndex(address(markets[i]), rewardsDistributor, supplyState); + + // Calculate pending rewards + uint256 borrowReward = calculateBorrowerReward( + address(markets[i]), + rewardsDistributor, + account, + borrowState, + marketBorrowIndex + ); + uint256 supplyReward = calculateSupplierReward( + address(markets[i]), + rewardsDistributor, + account, + supplyState + ); + + PendingReward memory pendingReward; + pendingReward.vTokenAddress = address(markets[i]); + pendingReward.amount = borrowReward + supplyReward; + pendingRewards[i] = pendingReward; + } + return pendingRewards; + } + + function updateMarketBorrowIndex( + address vToken, + RewardsDistributor rewardsDistributor, + RewardTokenState memory borrowState, + Exp memory marketBorrowIndex + ) internal view { + uint256 borrowSpeed = rewardsDistributor.rewardTokenBorrowSpeeds(vToken); + uint256 blockNumberOrTimestamp = getBlockNumberOrTimestamp(); + + if ( + borrowState.lastRewardingBlockOrTimestamp > 0 && + blockNumberOrTimestamp > borrowState.lastRewardingBlockOrTimestamp + ) { + blockNumberOrTimestamp = borrowState.lastRewardingBlockOrTimestamp; + } + + uint256 deltaBlocksOrTimestamp = sub_(blockNumberOrTimestamp, borrowState.blockOrTimestamp); + if (deltaBlocksOrTimestamp > 0 && borrowSpeed > 0) { + // Remove the total earned interest rate since the opening of the market from total borrows + uint256 borrowAmount = div_(VToken(vToken).totalBorrows(), marketBorrowIndex); + uint256 tokensAccrued = mul_(deltaBlocksOrTimestamp, borrowSpeed); + Double memory ratio = borrowAmount > 0 ? fraction(tokensAccrued, borrowAmount) : Double({ mantissa: 0 }); + Double memory index = add_(Double({ mantissa: borrowState.index }), ratio); + borrowState.index = safe224(index.mantissa, "new index overflows"); + borrowState.blockOrTimestamp = blockNumberOrTimestamp; + } else if (deltaBlocksOrTimestamp > 0) { + borrowState.blockOrTimestamp = blockNumberOrTimestamp; + } + } + + function updateMarketSupplyIndex( + address vToken, + RewardsDistributor rewardsDistributor, + RewardTokenState memory supplyState + ) internal view { + uint256 supplySpeed = rewardsDistributor.rewardTokenSupplySpeeds(vToken); + uint256 blockNumberOrTimestamp = getBlockNumberOrTimestamp(); + + if ( + supplyState.lastRewardingBlockOrTimestamp > 0 && + blockNumberOrTimestamp > supplyState.lastRewardingBlockOrTimestamp + ) { + blockNumberOrTimestamp = supplyState.lastRewardingBlockOrTimestamp; + } + + uint256 deltaBlocksOrTimestamp = sub_(blockNumberOrTimestamp, supplyState.blockOrTimestamp); + if (deltaBlocksOrTimestamp > 0 && supplySpeed > 0) { + uint256 supplyTokens = VToken(vToken).totalSupply(); + uint256 tokensAccrued = mul_(deltaBlocksOrTimestamp, supplySpeed); + Double memory ratio = supplyTokens > 0 ? fraction(tokensAccrued, supplyTokens) : Double({ mantissa: 0 }); + Double memory index = add_(Double({ mantissa: supplyState.index }), ratio); + supplyState.index = safe224(index.mantissa, "new index overflows"); + supplyState.blockOrTimestamp = blockNumberOrTimestamp; + } else if (deltaBlocksOrTimestamp > 0) { + supplyState.blockOrTimestamp = blockNumberOrTimestamp; + } + } + + function calculateBorrowerReward( + address vToken, + RewardsDistributor rewardsDistributor, + address borrower, + RewardTokenState memory borrowState, + Exp memory marketBorrowIndex + ) internal view returns (uint256) { + Double memory borrowIndex = Double({ mantissa: borrowState.index }); + Double memory borrowerIndex = Double({ + mantissa: rewardsDistributor.rewardTokenBorrowerIndex(vToken, borrower) + }); + if (borrowerIndex.mantissa == 0 && borrowIndex.mantissa >= rewardsDistributor.INITIAL_INDEX()) { + // Covers the case where users borrowed tokens before the market's borrow state index was set + borrowerIndex.mantissa = rewardsDistributor.INITIAL_INDEX(); + } + Double memory deltaIndex = sub_(borrowIndex, borrowerIndex); + uint256 borrowerAmount = div_(VToken(vToken).borrowBalanceStored(borrower), marketBorrowIndex); + uint256 borrowerDelta = mul_(borrowerAmount, deltaIndex); + return borrowerDelta; + } + + function calculateSupplierReward( + address vToken, + RewardsDistributor rewardsDistributor, + address supplier, + RewardTokenState memory supplyState + ) internal view returns (uint256) { + Double memory supplyIndex = Double({ mantissa: supplyState.index }); + Double memory supplierIndex = Double({ + mantissa: rewardsDistributor.rewardTokenSupplierIndex(vToken, supplier) + }); + if (supplierIndex.mantissa == 0 && supplyIndex.mantissa >= rewardsDistributor.INITIAL_INDEX()) { + // Covers the case where users supplied tokens before the market's supply state index was set + supplierIndex.mantissa = rewardsDistributor.INITIAL_INDEX(); + } + Double memory deltaIndex = sub_(supplyIndex, supplierIndex); + uint256 supplierTokens = VToken(vToken).balanceOf(supplier); + uint256 supplierDelta = mul_(supplierTokens, deltaIndex); + return supplierDelta; + } + + /** + * @dev Encodes paused actions using the same bit positions as `PoolLens`. + * @param comptroller The market's comptroller + * @param vToken The market to read + * @return A bitmask of the paused actions + */ + function _pausedActions(address comptroller, address vToken) private view returns (uint256) { + uint256 pausedActions; + + for (uint8 i; i <= uint8(type(Action).max); ++i) { + uint256 paused = ComptrollerInterface(comptroller).actionPaused(vToken, Action(i)) ? 1 : 0; + pausedActions |= paused << i; + } + + return pausedActions; + } +} diff --git a/contracts/Spoke/SpokeComptroller.sol b/contracts/Spoke/SpokeComptroller.sol new file mode 100644 index 000000000..c25ddbc07 --- /dev/null +++ b/contracts/Spoke/SpokeComptroller.sol @@ -0,0 +1,1952 @@ +// SPDX-License-Identifier: BSD-3-Clause +pragma solidity 0.8.25; + +import { Ownable2StepUpgradeable } from "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol"; +import { ResilientOracleInterface } from "@venusprotocol/oracle/contracts/interfaces/OracleInterface.sol"; +import { IDeviationBoundedOracle } from "@venusprotocol/oracle/contracts/interfaces/IDeviationBoundedOracle.sol"; +import { AccessControlledV8 } from "@venusprotocol/governance-contracts/contracts/Governance/AccessControlledV8.sol"; + +import { Action } from "../ComptrollerInterface.sol"; +import { SpokeComptrollerInterface } from "./SpokeComptrollerInterface.sol"; +import { SpokeComptrollerStorage } from "./SpokeComptrollerStorage.sol"; +import { ExponentialNoError } from "../ExponentialNoError.sol"; +import { VToken } from "../VToken.sol"; +import { RewardsDistributor } from "../Rewards/RewardsDistributor.sol"; +import { MaxLoopsLimitHelper } from "../MaxLoopsLimitHelper.sol"; +import { ensureNonzeroAddress } from "../lib/validators.sol"; + +/// @notice Which per-market risk parameter weights an account's collateral in a liquidity snapshot. Internal to the +/// implementation: no event, error or external function exposes it, so it is deliberately not part of +/// `SpokeComptrollerInterface`. +enum WeightFunction { + USE_COLLATERAL_FACTOR, + USE_LIQUIDATION_THRESHOLD +} + +/** + * @title SpokeComptroller + * @author Venus + * @notice The `SpokeComptroller` provides checks for all minting, redeeming, transferring, borrowing, repaying, + * liquidating and seizing done by the `vToken` contract. It is the comptroller of a single pool and checks those + * interactions across every market in it: when a user interacts with a market by one of these actions, the market + * calls a corresponding hook here, which either allows or reverts the transaction. These hooks also update supply and + * borrow rewards as they are called. The comptroller holds the logic for assessing liquidity snapshots of an account + * via the collateral factor and liquidation threshold. This check determines the collateral needed for a borrow, as + * well as how much of a borrow may be liquidated. A user may borrow a portion of their collateral with the maximum + * amount determined by the market's collateral factor, applied to a collateral value the deviation-bounded oracle + * caps against the asset's recent price window. However, if their borrowed amount exceeds an amount calculated using + * the market's corresponding liquidation threshold, the borrow is eligible for liquidation. Liquidations themselves + * are priced at spot. + * + * The `SpokeComptroller` also includes two functions `liquidateAccount()` and `healAccount()`, which are meant to + * handle accounts that do not exceed the `minLiquidatableCollateral` for the `SpokeComptroller`: + * + * - `healAccount()`: This function is called to seize all of a given user's collateral, requiring the `msg.sender` + * repay a certain percentage of the debt calculated by `maxClearableDebt/borrows`, where `maxClearableDebt` is the + * sum over the user's collateral markets of `collateralValue/liquidationIncentive`, each market taken at its own + * incentive. The function can only be called if the calculated percentage does not exceed 100%, because otherwise no + * `badDebt` would be created and `liquidateAccount()` should be used instead. The difference in the actual amount of + * debt and debt paid off is recorded as `badDebt` for each market, which can then be auctioned off for the risk + * reserves of the pool. + * - `liquidateAccount()`: This function can only be called if the collateral seized will cover all borrows of an + * account, as well as the liquidation incentive of each collateral market, which is the same condition stated as + * `borrows < maxClearableDebt`. Otherwise, the pool will incur bad debt, in which case the function `healAccount()` + * should be used instead. This function skips the logic verifying that the repay amount does not exceed the close + * factor. + * + * The two conditions are complements, so every account below `minLiquidatableCollateral` is served by exactly one of + * them. + * + * @dev Fork of `Comptroller` (`contracts/Comptroller.sol`). It is a separate implementation because `Comptroller` is + * the one every other pool in this repo shares, here and on other chains, so policy that applies only to a spoke pool + * does not belong in it: bounded collateral pricing, the supply and liquidation allowlists, and per-market liquidation + * incentives. Nothing keeps the two in sync automatically: a change to `Comptroller` has to be reviewed and + * re-applied here by hand, and `diff contracts/Comptroller.sol contracts/Spoke/SpokeComptroller.sol` shows what this + * fork currently changes. + */ +contract SpokeComptroller is + Ownable2StepUpgradeable, + AccessControlledV8, + SpokeComptrollerStorage, + SpokeComptrollerInterface, + ExponentialNoError, + MaxLoopsLimitHelper +{ + // PoolRegistry, immutable to save on gas + /// @custom:oz-upgrades-unsafe-allow state-variable-immutable + address public immutable poolRegistry; + + /// @param poolRegistry_ Pool registry address + /// @custom:oz-upgrades-unsafe-allow constructor + /// @custom:error ZeroAddressNotAllowed is thrown when pool registry address is zero + constructor(address poolRegistry_) { + ensureNonzeroAddress(poolRegistry_); + + poolRegistry = poolRegistry_; + _disableInitializers(); + } + + /** + * @param loopLimit Limit for the loops can iterate to avoid the DOS + * @param accessControlManager Access control manager contract address + */ + function initialize(uint256 loopLimit, address accessControlManager) external initializer { + __Ownable2Step_init(); + __AccessControlled_init_unchained(accessControlManager); + + _setMaxLoopsLimit(loopLimit); + } + + /** + * @notice Add assets to be included in account liquidity calculation; enabling them to be used as collateral + * @param vTokens The list of addresses of the vToken markets to be enabled + * @return errors An array of NO_ERROR for compatibility with Venus core tooling + * @custom:event MarketEntered is emitted for each market on success + * @custom:error ActionPaused error is thrown if entering any of the markets is paused + * @custom:error MarketNotListed error is thrown if any of the markets is not listed + * @custom:access Not restricted + */ + function enterMarkets(address[] memory vTokens) external override returns (uint256[] memory) { + uint256 len = vTokens.length; + + uint256[] memory results = new uint256[](len); + for (uint256 i; i < len; ++i) { + VToken vToken = VToken(vTokens[i]); + + _addToMarket(vToken, msg.sender); + results[i] = NO_ERROR; + } + + return results; + } + + /** + * @notice Add an asset to another account's liquidity calculation, enabling it as collateral for that account + * @dev `enterMarkets` reads `msg.sender`, so a router supplying through `VToken.mintBehalf` would enter itself + * rather than the supplier. This enters the supplier, so both steps fit in one user transaction. Grant the + * permission only to a router that passes its own caller as `account`. + * @param vToken The address of the vToken market to be enabled + * @param account The account to enable the market for + * @custom:event MarketEntered is emitted on success + * @custom:error ActionPaused error is thrown if entering the market is paused + * @custom:error MarketNotListed error is thrown if the market is not listed + * @custom:error ZeroAddressNotAllowed is thrown when the account address is zero + * @custom:access Controlled by AccessControlManager + */ + function enterMarketBehalf(address vToken, address account) external { + _checkAccessAllowed("enterMarketBehalf(address,address)"); + ensureNonzeroAddress(account); + + _addToMarket(VToken(vToken), account); + } + + /** + * @notice Unlist a market by setting isListed to false + * @dev Checks if all actions are paused, borrow/supply caps is set to 0 and collateral factor is to 0. + * @param market The address of the market (token) to unlist + * @return uint256 Always NO_ERROR for compatibility with Venus core tooling + * @custom:event MarketUnlisted is emitted on success + * @custom:error MarketNotListed error is thrown when the market is not listed + * @custom:error BorrowActionNotPaused error is thrown if borrow action is not paused + * @custom:error MintActionNotPaused error is thrown if mint action is not paused + * @custom:error RedeemActionNotPaused error is thrown if redeem action is not paused + * @custom:error RepayActionNotPaused error is thrown if repay action is not paused + * @custom:error EnterMarketActionNotPaused error is thrown if enter market action is not paused + * @custom:error LiquidateActionNotPaused error is thrown if liquidate action is not paused + * @custom:error BorrowCapIsNotZero error is thrown if borrow cap is not zero + * @custom:error SupplyCapIsNotZero error is thrown if supply cap is not zero + * @custom:error CollateralFactorIsNotZero error is thrown if collateral factor is not zero + */ + function unlistMarket(address market) external returns (uint256) { + _checkAccessAllowed("unlistMarket(address)"); + + Market storage _market = markets[market]; + + if (!_market.isListed) { + revert MarketNotListed(market); + } + + if (!actionPaused(market, Action.BORROW)) { + revert BorrowActionNotPaused(); + } + + if (!actionPaused(market, Action.MINT)) { + revert MintActionNotPaused(); + } + + if (!actionPaused(market, Action.REDEEM)) { + revert RedeemActionNotPaused(); + } + + if (!actionPaused(market, Action.REPAY)) { + revert RepayActionNotPaused(); + } + + if (!actionPaused(market, Action.SEIZE)) { + revert SeizeActionNotPaused(); + } + + if (!actionPaused(market, Action.ENTER_MARKET)) { + revert EnterMarketActionNotPaused(); + } + + if (!actionPaused(market, Action.LIQUIDATE)) { + revert LiquidateActionNotPaused(); + } + + if (!actionPaused(market, Action.TRANSFER)) { + revert TransferActionNotPaused(); + } + + if (!actionPaused(market, Action.EXIT_MARKET)) { + revert ExitMarketActionNotPaused(); + } + + if (borrowCaps[market] != 0) { + revert BorrowCapIsNotZero(); + } + + if (supplyCaps[market] != 0) { + revert SupplyCapIsNotZero(); + } + + if (_market.collateralFactorMantissa != 0) { + revert CollateralFactorIsNotZero(); + } + + _market.isListed = false; + emit MarketUnlisted(market); + + return NO_ERROR; + } + + /** + * @notice Grants or revokes the borrowing or redeeming delegate rights to / from an account + * If allowed, the delegate will be able to borrow funds on behalf of the sender + * Upon a delegated borrow, the delegate will receive the funds, and the borrower + * will see the debt on their account + * Upon a delegated redeem, the delegate will receive the redeemed amount and the approver + * will see a deduction in his vToken balance + * @param delegate The address to update the rights for + * @param approved Whether to grant (true) or revoke (false) the borrowing or redeeming rights + * @custom:event DelegateUpdated emits on success + * @custom:error ZeroAddressNotAllowed is thrown when delegate address is zero + * @custom:error DelegationStatusUnchanged is thrown if approval status is already set to the requested value + * @custom:access Not restricted + */ + function updateDelegate(address delegate, bool approved) external { + ensureNonzeroAddress(delegate); + if (approvedDelegates[msg.sender][delegate] == approved) { + revert DelegationStatusUnchanged(); + } + + approvedDelegates[msg.sender][delegate] = approved; + emit DelegateUpdated(msg.sender, delegate, approved); + } + + /** + * @notice Removes asset from sender's account liquidity calculation; disabling them as collateral + * @dev Sender must not have an outstanding borrow balance in the asset, + * or be providing necessary collateral for an outstanding borrow. + * @param vTokenAddress The address of the asset to be removed + * @return error Always NO_ERROR for compatibility with Venus core tooling + * @custom:event MarketExited is emitted on success + * @custom:error ActionPaused error is thrown if exiting the market is paused + * @custom:error NonzeroBorrowBalance error is thrown if the user has an outstanding borrow in this market + * @custom:error MarketNotListed error is thrown when the market is not listed + * @custom:error InsufficientLiquidity error is thrown if exiting the market would lead to user's insolvency + * @custom:error SnapshotError is thrown if some vToken fails to return the account's supply and borrows + * @custom:error PriceError is thrown if the oracle returns an incorrect price for some asset + * @custom:access Not restricted + */ + function exitMarket(address vTokenAddress) external override returns (uint256) { + _checkActionPauseState(vTokenAddress, Action.EXIT_MARKET); + VToken vToken = VToken(vTokenAddress); + /* Get sender tokensHeld and amountOwed underlying from the vToken */ + (uint256 tokensHeld, uint256 amountOwed, ) = _safeGetAccountSnapshot(vToken, msg.sender); + + /* Fail if the sender has a borrow balance */ + if (amountOwed != 0) { + revert NonzeroBorrowBalance(); + } + + /* Fail if the sender is not permitted to redeem all of their tokens */ + _checkRedeemAllowed(vTokenAddress, msg.sender, tokensHeld); + + Market storage marketToExit = markets[address(vToken)]; + + /* Return true if the sender is not already ‘in’ the market */ + if (!marketToExit.accountMembership[msg.sender]) { + return NO_ERROR; + } + + /* Set vToken account membership to false */ + delete marketToExit.accountMembership[msg.sender]; + + /* Delete vToken from the account’s list of assets */ + // load into memory for faster iteration + VToken[] memory userAssetList = accountAssets[msg.sender]; + uint256 len = userAssetList.length; + + uint256 assetIndex = len; + for (uint256 i; i < len; ++i) { + if (userAssetList[i] == vToken) { + assetIndex = i; + break; + } + } + + // We *must* have found the asset in the list or our redundant data structure is broken + assert(assetIndex < len); + + // copy last item in list to location of item to be removed, reduce length by 1 + VToken[] storage storedList = accountAssets[msg.sender]; + storedList[assetIndex] = storedList[storedList.length - 1]; + storedList.pop(); + + emit MarketExited(vToken, msg.sender); + + return NO_ERROR; + } + + /*** Policy Hooks ***/ + + /** + * @notice Checks if the account should be allowed to mint tokens in the given market + * @param vToken The market to verify the mint against + * @param minter The account which would get the minted tokens + * @param mintAmount The amount of underlying being supplied to the market in exchange for tokens + * @custom:error ActionPaused error is thrown if supplying to this market is paused + * @custom:error MarketNotListed error is thrown when the market is not listed + * @custom:error SupplyNotAllowed error is thrown if the market's supply allowlist is enabled and the minter is + * not on it + * @custom:error SupplyCapExceeded error is thrown if the total supply exceeds the cap after minting + * @custom:access Not restricted + */ + function preMintHook(address vToken, address minter, uint256 mintAmount) external override { + _checkActionPauseState(vToken, Action.MINT); + + if (!markets[vToken].isListed) { + revert MarketNotListed(address(vToken)); + } + + // `minter` is the account credited with the newly minted vTokens, not necessarily the account paying for + // them: `mintBehalf` lets a third party fund a mint attributed to someone else. Metering the recipient is + // what bounds the market's supply, so the payer is deliberately not checked. + if (isSupplyAllowlistEnabled[vToken] && !isAllowedSupplier[vToken][minter]) { + revert SupplyNotAllowed(vToken, minter); + } + + uint256 supplyCap = supplyCaps[vToken]; + // Skipping the cap check for uncapped coins to save some gas + if (supplyCap != type(uint256).max) { + uint256 vTokenSupply = VToken(vToken).totalSupply(); + Exp memory exchangeRate = Exp({ mantissa: VToken(vToken).exchangeRateStored() }); + uint256 nextTotalSupply = mul_ScalarTruncateAddUInt(exchangeRate, vTokenSupply, mintAmount); + if (nextTotalSupply > supplyCap) { + revert SupplyCapExceeded(vToken, supplyCap); + } + } + + // Keep the flywheel moving + uint256 rewardDistributorsCount = rewardsDistributors.length; + + for (uint256 i; i < rewardDistributorsCount; ++i) { + RewardsDistributor rewardsDistributor = rewardsDistributors[i]; + rewardsDistributor.updateRewardTokenSupplyIndex(vToken); + rewardsDistributor.distributeSupplierRewardToken(vToken, minter); + } + } + + /** + * @notice Checks if the account should be allowed to redeem tokens in the given market + * @param vToken The market to verify the redeem against + * @param redeemer The account which would redeem the tokens + * @param redeemTokens The number of vTokens to exchange for the underlying asset in the market + * @custom:error ActionPaused error is thrown if withdrawals are paused in this market + * @custom:error MarketNotListed error is thrown when the market is not listed + * @custom:error InsufficientLiquidity error is thrown if the withdrawal would lead to user's insolvency + * @custom:error SnapshotError is thrown if some vToken fails to return the account's supply and borrows + * @custom:error PriceError is thrown if the oracle returns an incorrect price for some asset + * @custom:access Not restricted + */ + function preRedeemHook(address vToken, address redeemer, uint256 redeemTokens) external override { + _checkActionPauseState(vToken, Action.REDEEM); + + _checkRedeemAllowed(vToken, redeemer, redeemTokens); + + // Keep the flywheel moving + uint256 rewardDistributorsCount = rewardsDistributors.length; + + for (uint256 i; i < rewardDistributorsCount; ++i) { + RewardsDistributor rewardsDistributor = rewardsDistributors[i]; + rewardsDistributor.updateRewardTokenSupplyIndex(vToken); + rewardsDistributor.distributeSupplierRewardToken(vToken, redeemer); + } + } + + /** + * @notice Checks if the account should be allowed to borrow the underlying asset of the given market + * @param vToken The market to verify the borrow against + * @param borrower The account which would borrow the asset + * @param borrowAmount The amount of underlying the account would borrow + * @custom:error ActionPaused error is thrown if borrowing is paused in this market + * @custom:error MarketNotListed error is thrown when the market is not listed + * @custom:error InsufficientLiquidity error is thrown if there is not enough collateral to borrow + * @custom:error BorrowCapExceeded is thrown if the borrow cap will be exceeded should this borrow succeed + * @custom:error SnapshotError is thrown if some vToken fails to return the account's supply and borrows + * @custom:error PriceError is thrown if the oracle returns an incorrect price for some asset + * @custom:access Not restricted if vToken is enabled as collateral, otherwise only vToken + */ + /// disable-eslint + function preBorrowHook(address vToken, address borrower, uint256 borrowAmount) external override { + _checkActionPauseState(vToken, Action.BORROW); + + if (!markets[vToken].isListed) { + revert MarketNotListed(address(vToken)); + } + + if (!markets[vToken].accountMembership[borrower]) { + // only vTokens may call borrowAllowed if borrower not in market + _checkSenderIs(vToken); + + // attempt to add borrower to the market or revert + _addToMarket(VToken(msg.sender), borrower); + } + + // Update the prices of tokens. Resolved once here, after the membership change above, and reused for both + // updates. + VToken[] memory borrowerAssets = getAssetsIn(borrower); + _updatePrices(borrowerAssets); + _updateProtectionStates(borrowerAssets); + + if (oracle.getUnderlyingPrice(vToken) == 0) { + revert PriceError(address(vToken)); + } + + uint256 borrowCap = borrowCaps[vToken]; + // Skipping the cap check for uncapped coins to save some gas + if (borrowCap != type(uint256).max) { + uint256 totalBorrows = VToken(vToken).totalBorrows(); + uint256 badDebt = VToken(vToken).badDebt(); + uint256 nextTotalBorrows = totalBorrows + borrowAmount + badDebt; + if (nextTotalBorrows > borrowCap) { + revert BorrowCapExceeded(vToken, borrowCap); + } + } + + AccountLiquiditySnapshot memory snapshot = _getHypotheticalLiquiditySnapshot( + borrower, + VToken(vToken), + 0, + borrowAmount, + WeightFunction.USE_COLLATERAL_FACTOR + ); + + if (snapshot.shortfall > 0) { + revert InsufficientLiquidity(); + } + + Exp memory borrowIndex = Exp({ mantissa: VToken(vToken).borrowIndex() }); + + // Keep the flywheel moving + uint256 rewardDistributorsCount = rewardsDistributors.length; + + for (uint256 i; i < rewardDistributorsCount; ++i) { + RewardsDistributor rewardsDistributor = rewardsDistributors[i]; + rewardsDistributor.updateRewardTokenBorrowIndex(vToken, borrowIndex); + rewardsDistributor.distributeBorrowerRewardToken(vToken, borrower, borrowIndex); + } + } + + /** + * @notice Checks if the account should be allowed to repay a borrow in the given market + * @param vToken The market to verify the repay against + * @param borrower The account which would borrowed the asset + * @custom:error ActionPaused error is thrown if repayments are paused in this market + * @custom:error MarketNotListed error is thrown when the market is not listed + * @custom:access Not restricted + */ + function preRepayHook(address vToken, address borrower) external override { + _checkActionPauseState(vToken, Action.REPAY); + + oracle.updatePrice(vToken); + + if (!markets[vToken].isListed) { + revert MarketNotListed(address(vToken)); + } + + // Keep the flywheel moving + uint256 rewardDistributorsCount = rewardsDistributors.length; + + for (uint256 i; i < rewardDistributorsCount; ++i) { + Exp memory borrowIndex = Exp({ mantissa: VToken(vToken).borrowIndex() }); + RewardsDistributor rewardsDistributor = rewardsDistributors[i]; + rewardsDistributor.updateRewardTokenBorrowIndex(vToken, borrowIndex); + rewardsDistributor.distributeBorrowerRewardToken(vToken, borrower, borrowIndex); + } + } + + /** + * @notice Checks if the liquidation should be allowed to occur + * @param vTokenBorrowed Asset which was borrowed by the borrower + * @param vTokenCollateral Asset which was used as collateral and will be seized + * @param borrower The address of the borrower + * @param repayAmount The amount of underlying being repaid + * @param skipLiquidityCheck Allows the borrow to be liquidated regardless of the account liquidity + * @custom:error ActionPaused error is thrown if liquidations are paused in this market + * @custom:error MarketNotListed error is thrown if either collateral or borrowed token is not listed + * @custom:error TooMuchRepay error is thrown if the liquidator is trying to repay more than allowed by close factor + * @custom:error MinimalCollateralViolated is thrown if the users' total collateral is lower than the threshold for non-batch liquidations + * @custom:error InsufficientShortfall is thrown when trying to liquidate a healthy account + * @custom:error SnapshotError is thrown if some vToken fails to return the account's supply and borrows + * @custom:error PriceError is thrown if the oracle returns an incorrect price for some asset + */ + function preLiquidateHook( + address vTokenBorrowed, + address vTokenCollateral, + address borrower, + uint256 repayAmount, + bool skipLiquidityCheck + ) external override { + // Pause Action.LIQUIDATE on BORROWED TOKEN to prevent liquidating it. + // If we want to pause liquidating to vTokenCollateral, we should pause + // Action.SEIZE on it + _checkActionPauseState(vTokenBorrowed, Action.LIQUIDATE); + + // Update the prices of tokens + updatePrices(borrower); + + if (!markets[vTokenBorrowed].isListed) { + revert MarketNotListed(address(vTokenBorrowed)); + } + if (!markets[vTokenCollateral].isListed) { + revert MarketNotListed(address(vTokenCollateral)); + } + + uint256 borrowBalance = VToken(vTokenBorrowed).borrowBalanceStored(borrower); + + /* Allow accounts to be liquidated if it is a forced liquidation */ + if (skipLiquidityCheck || isForcedLiquidationEnabled[vTokenBorrowed]) { + if (repayAmount > borrowBalance) { + revert TooMuchRepay(); + } + return; + } + + /* The borrower must have shortfall and collateral > threshold in order to be liquidatable */ + AccountLiquiditySnapshot memory snapshot = _getCurrentLiquiditySnapshot( + borrower, + WeightFunction.USE_LIQUIDATION_THRESHOLD + ); + + if (snapshot.totalCollateral <= minLiquidatableCollateral) { + /* The liquidator should use either liquidateAccount or healAccount */ + revert MinimalCollateralViolated(minLiquidatableCollateral, snapshot.totalCollateral); + } + + if (snapshot.shortfall == 0) { + revert InsufficientShortfall(); + } + + /* The liquidator may not repay more than what is allowed by the closeFactor */ + uint256 maxClose = mul_ScalarTruncate(Exp({ mantissa: closeFactorMantissa }), borrowBalance); + if (repayAmount > maxClose) { + revert TooMuchRepay(); + } + } + + /** + * @notice Checks if the seizing of assets should be allowed to occur + * @param vTokenCollateral Asset which was used as collateral and will be seized + * @param seizerContract Contract that tries to seize the asset (either borrowed vToken or Comptroller) + * @param liquidator The address repaying the borrow and seizing the collateral + * @param borrower The address of the borrower + * @custom:error ActionPaused error is thrown if seizing this type of collateral is paused + * @custom:error MarketNotListed error is thrown if either collateral or borrowed token is not listed + * @custom:error ComptrollerMismatch error is when seizer contract or seized asset belong to different pools + * @custom:error LiquidationNotAllowed is thrown if the liquidation allowlist is enabled and the liquidator is not + * on it + * @custom:access Not restricted while the liquidation allowlist is disabled, otherwise the liquidator has to be + * on it + */ + function preSeizeHook( + address vTokenCollateral, + address seizerContract, + address liquidator, + address borrower + ) external override { + // Pause Action.SEIZE on COLLATERAL to prevent seizing it. + // If we want to pause liquidating vTokenBorrowed, we should pause + // Action.LIQUIDATE on it + _checkActionPauseState(vTokenCollateral, Action.SEIZE); + + Market storage market = markets[vTokenCollateral]; + + if (!market.isListed) { + revert MarketNotListed(vTokenCollateral); + } + + if (seizerContract == address(this)) { + // If Comptroller is the seizer, just check if collateral's comptroller + // is equal to the current address + if (address(VToken(vTokenCollateral).comptroller()) != address(this)) { + revert ComptrollerMismatch(); + } + } else { + // If the seizer is not the Comptroller, check that the seizer is a + // listed market, and that the markets' comptrollers match + if (!markets[seizerContract].isListed) { + revert MarketNotListed(seizerContract); + } + if (VToken(vTokenCollateral).comptroller() != VToken(seizerContract).comptroller()) { + revert ComptrollerMismatch(); + } + } + + if (!market.accountMembership[borrower]) { + revert MarketNotCollateral(vTokenCollateral, borrower); + } + + // Every seizure reaches this hook with the account that receives the collateral, whichever entry point it + // came from, so this is the check that actually enforces the allowlist. + _checkLiquidatorAllowlisted(liquidator); + + // Keep the flywheel moving + uint256 rewardDistributorsCount = rewardsDistributors.length; + + for (uint256 i; i < rewardDistributorsCount; ++i) { + RewardsDistributor rewardsDistributor = rewardsDistributors[i]; + rewardsDistributor.updateRewardTokenSupplyIndex(vTokenCollateral); + rewardsDistributor.distributeSupplierRewardToken(vTokenCollateral, borrower); + rewardsDistributor.distributeSupplierRewardToken(vTokenCollateral, liquidator); + } + } + + /** + * @notice Checks if the account should be allowed to transfer tokens in the given market + * @param vToken The market to verify the transfer against + * @param src The account which sources the tokens + * @param dst The account which receives the tokens + * @param transferTokens The number of vTokens to transfer + * @custom:error ActionPaused error is thrown if withdrawals are paused in this market + * @custom:error MarketNotListed error is thrown when the market is not listed + * @custom:error InsufficientLiquidity error is thrown if the withdrawal would lead to user's insolvency + * @custom:error SnapshotError is thrown if some vToken fails to return the account's supply and borrows + * @custom:error PriceError is thrown if the oracle returns an incorrect price for some asset + * @custom:access Not restricted + */ + function preTransferHook(address vToken, address src, address dst, uint256 transferTokens) external override { + _checkActionPauseState(vToken, Action.TRANSFER); + + // Currently the only consideration is whether or not + // the src is allowed to redeem this many tokens + _checkRedeemAllowed(vToken, src, transferTokens); + + // Keep the flywheel moving + uint256 rewardDistributorsCount = rewardsDistributors.length; + + for (uint256 i; i < rewardDistributorsCount; ++i) { + RewardsDistributor rewardsDistributor = rewardsDistributors[i]; + rewardsDistributor.updateRewardTokenSupplyIndex(vToken); + rewardsDistributor.distributeSupplierRewardToken(vToken, src); + rewardsDistributor.distributeSupplierRewardToken(vToken, dst); + } + } + + /*** Post-action Hooks ***/ + + // The vToken calls one of the seven functions below as the last step of every successful mint, redeem, borrow, + // repayment, liquidation, seizure and transfer. All of them are no-ops here, and none of them can be dropped: + // `ComptrollerInterface` declares all seven, so omitting one leaves this contract abstract, and the vToken calls + // each of them with a plain external call against a comptroller that has no fallback, so a missing function would + // revert the operation it belongs to. They are unrestricted, which is harmless because they do nothing, and their + // parameters are unnamed because the bodies are empty. + // solhint-disable no-empty-blocks + + /// @notice Called by the vToken once a mint has succeeded. No-op, see the note above. + function mintVerify(address, address, uint256, uint256) external {} + + /// @notice Called by the vToken once a redeem has succeeded. No-op, see the note above. + function redeemVerify(address, address, uint256, uint256) external {} + + /// @notice Called by the vToken once a borrow has succeeded. No-op, see the note above. + function borrowVerify(address, address, uint256) external {} + + /// @notice Called by the vToken once a repayment has succeeded. No-op, see the note above. + function repayBorrowVerify(address, address, address, uint256, uint256) external {} + + /// @notice Called by the vToken once a liquidation has succeeded. No-op, see the note above. + function liquidateBorrowVerify(address, address, address, address, uint256, uint256) external {} + + /// @notice Called by the vToken once a seizure has succeeded. No-op, see the note above. + function seizeVerify(address, address, address, address, uint256) external {} + + /// @notice Called by the vToken once a transfer has succeeded. No-op, see the note above. + function transferVerify(address, address, address, uint256) external {} + + // solhint-enable no-empty-blocks + + /*** Pool-level operations ***/ + + /** + * @notice Seizes all the remaining collateral, makes msg.sender repay the existing + * borrows, and treats the rest of the debt as bad debt (for each market). + * The sender has to repay a certain percentage of the debt, computed as `maxClearableDebt / borrows`: see the + * note on `AccountLiquiditySnapshot.maxClearableDebt`. + * @param user account to heal + * @custom:error LiquidationNotAllowed is thrown if the liquidation allowlist is enabled and the caller is not on it + * @custom:error CollateralExceedsThreshold error is thrown when the collateral is too big for healing + * @custom:error CollateralCoversDebt is thrown when the collateral can clear the whole debt, which leaves nothing + * to heal + * @custom:error SnapshotError is thrown if some vToken fails to return the account's supply and borrows + * @custom:error PriceError is thrown if the oracle returns an incorrect price for some asset + * @custom:access Not restricted while the liquidation allowlist is disabled, otherwise restricted to the accounts + * on it + */ + function healAccount(address user) external { + // Checked here as well as in `preSeizeHook`, because a borrower holding no vTokens at all takes the branch + // below that only calls `healBorrow`, which reaches no hook carrying the caller. Without this the whole + // remaining principal could be moved into bad debt by anyone, at no cost. + _checkLiquidatorAllowlisted(msg.sender); + + VToken[] memory userAssets = getAssetsIn(user); + uint256 userAssetsCount = userAssets.length; + + address liquidator = msg.sender; + + // We need all user's markets to be fresh for the computations to be correct + _refreshMarkets(userAssets); + + AccountLiquiditySnapshot memory snapshot = _getCurrentLiquiditySnapshot( + user, + WeightFunction.USE_LIQUIDATION_THRESHOLD + ); + + if (snapshot.totalCollateral > minLiquidatableCollateral) { + revert CollateralExceedsThreshold(minLiquidatableCollateral, snapshot.totalCollateral); + } + + if (snapshot.shortfall == 0) { + revert InsufficientShortfall(); + } + + // The collateral covers the whole debt at every market's own incentive, so healing would forgive nothing and + // the account should go through `liquidateAccount` instead. + if (snapshot.maxClearableDebt > snapshot.borrows) { + revert CollateralCoversDebt(snapshot.borrows, snapshot.maxClearableDebt); + } + + // percentage = maxClearableDebt / borrows. One blended share applies to every borrow, so what the caller + // pays in total is the sum over the collateral markets of each market's value at its own liquidation + // incentive. The discount is exact in aggregate; it is not attributed per piece of collateral. + Exp memory percentage = div_(Exp({ mantissa: snapshot.maxClearableDebt }), Exp({ mantissa: snapshot.borrows })); + + for (uint256 i; i < userAssetsCount; ++i) { + VToken market = userAssets[i]; + + (uint256 tokens, uint256 borrowBalance, ) = _safeGetAccountSnapshot(market, user); + uint256 repaymentAmount = mul_ScalarTruncate(percentage, borrowBalance); + + // Seize the entire collateral + if (tokens != 0) { + market.seize(liquidator, user, tokens); + } + // Repay a certain percentage of the borrow, forgive the rest + if (borrowBalance != 0) { + market.healBorrow(liquidator, user, repaymentAmount); + } + } + } + + /** + * @notice Liquidates all borrows of the borrower. Callable only if the collateral is less than + * a predefined threshold, and the account collateral can be seized to cover all borrows. If + * the collateral is higher than the threshold, use regular liquidations. If the collateral is + * below the threshold, and the account is insolvent, use healAccount. + * @param borrower the borrower address + * @param orders an array of liquidation orders + * @custom:error LiquidationNotAllowed is thrown by `preSeizeHook`, while seizing for the first order, if the + * liquidation allowlist is enabled and the caller is not on it + * @custom:error CollateralExceedsThreshold error is thrown when the collateral is too big for a batch liquidation + * @custom:error DebtExceedsClearableAmount is thrown when the collateral cannot clear the whole debt, which + * means the account has to go through `healAccount` + * @custom:error NonzeroBorrowBalanceAfterLiquidation is thrown if the orders do not clear every borrow + * @custom:error SnapshotError is thrown if some vToken fails to return the account's supply and borrows + * @custom:error PriceError is thrown if the oracle returns an incorrect price for some asset + * @custom:access Not restricted while the liquidation allowlist is disabled, otherwise restricted to the accounts + * on it + */ + function liquidateAccount(address borrower, LiquidationOrder[] calldata orders) external { + // No entry check on the liquidation allowlist here, unlike `healAccount`: every order ends in a seizure and + // `preSeizeHook` carries the caller, so the allowlist is enforced there. + + VToken[] memory borrowerAssets = getAssetsIn(borrower); + + // We need all borrower's markets to be fresh for the computations to be correct. The orders below run with + // the liquidity check skipped, so this snapshot is the only thing standing between a solvent account and a + // full liquidation, and it decides on the same state the orders will execute against. + _refreshMarkets(borrowerAssets); + + AccountLiquiditySnapshot memory snapshot = _getCurrentLiquiditySnapshot( + borrower, + WeightFunction.USE_LIQUIDATION_THRESHOLD + ); + + if (snapshot.totalCollateral > minLiquidatableCollateral) { + // You should use the regular vToken.liquidateBorrow(...) call + revert CollateralExceedsThreshold(minLiquidatableCollateral, snapshot.totalCollateral); + } + + if (snapshot.borrows >= snapshot.maxClearableDebt) { + // There is not enough collateral to seize. Use healAccount to repay some part of the borrow + // and record bad debt. + revert DebtExceedsClearableAmount(snapshot.borrows, snapshot.maxClearableDebt); + } + + if (snapshot.shortfall == 0) { + revert InsufficientShortfall(); + } + + uint256 ordersCount = orders.length; + + _ensureMaxLoops(ordersCount / 2); + + for (uint256 i; i < ordersCount; ++i) { + if (!markets[address(orders[i].vTokenBorrowed)].isListed) { + revert MarketNotListed(address(orders[i].vTokenBorrowed)); + } + if (!markets[address(orders[i].vTokenCollateral)].isListed) { + revert MarketNotListed(address(orders[i].vTokenCollateral)); + } + + LiquidationOrder calldata order = orders[i]; + order.vTokenBorrowed.forceLiquidateBorrow( + msg.sender, + borrower, + order.repayAmount, + order.vTokenCollateral, + true + ); + } + + uint256 marketsCount = borrowerAssets.length; + + for (uint256 i; i < marketsCount; ++i) { + (, uint256 borrowBalance, ) = _safeGetAccountSnapshot(borrowerAssets[i], borrower); + if (borrowBalance != 0) { + revert NonzeroBorrowBalanceAfterLiquidation(); + } + } + } + + /** + * @notice Sets the closeFactor to use when liquidating borrows + * @param newCloseFactorMantissa New close factor, scaled by 1e18 + * @custom:event Emits NewCloseFactor on success + * @custom:error InvalidCloseFactor is thrown if the new close factor is outside the allowed bounds + * @custom:access Controlled by AccessControlManager + */ + function setCloseFactor(uint256 newCloseFactorMantissa) external { + _checkAccessAllowed("setCloseFactor(uint256)"); + if (newCloseFactorMantissa > MAX_CLOSE_FACTOR_MANTISSA) { + revert InvalidCloseFactor(); + } + + if (newCloseFactorMantissa < MIN_CLOSE_FACTOR_MANTISSA) { + revert InvalidCloseFactor(); + } + + uint256 oldCloseFactorMantissa = closeFactorMantissa; + closeFactorMantissa = newCloseFactorMantissa; + emit NewCloseFactor(oldCloseFactorMantissa, newCloseFactorMantissa); + } + + /** + * @notice Sets the collateralFactor for a market + * @dev This function is restricted by the AccessControlManager + * @param vToken The market to set the factor on + * @param newCollateralFactorMantissa The new collateral factor, scaled by 1e18 + * @param newLiquidationThresholdMantissa The new liquidation threshold, scaled by 1e18 + * @custom:event Emits NewCollateralFactor when collateral factor is updated + * and NewLiquidationThreshold when liquidation threshold is updated + * @custom:error MarketNotListed error is thrown when the market is not listed + * @custom:error InvalidCollateralFactor error is thrown when collateral factor is too high + * @custom:error InvalidLiquidationThreshold error is thrown when liquidation threshold is lower than collateral factor + * @custom:error PriceError is thrown when the oracle returns an invalid price for the asset + * @custom:access Controlled by AccessControlManager + */ + function setCollateralFactor( + VToken vToken, + uint256 newCollateralFactorMantissa, + uint256 newLiquidationThresholdMantissa + ) external { + _checkAccessAllowed("setCollateralFactor(address,uint256,uint256)"); + + // Verify market is listed + Market storage market = markets[address(vToken)]; + if (!market.isListed) { + revert MarketNotListed(address(vToken)); + } + + // Check collateral factor <= 0.9 + if (newCollateralFactorMantissa > MAX_COLLATERAL_FACTOR_MANTISSA) { + revert InvalidCollateralFactor(); + } + + // Ensure that liquidation threshold <= 1 + if (newLiquidationThresholdMantissa > MANTISSA_ONE) { + revert InvalidLiquidationThreshold(); + } + + // Ensure that liquidation threshold >= CF + if (newLiquidationThresholdMantissa < newCollateralFactorMantissa) { + revert InvalidLiquidationThreshold(); + } + + // If collateral factor != 0, fail if price == 0 + if (newCollateralFactorMantissa != 0 && oracle.getUnderlyingPrice(address(vToken)) == 0) { + revert PriceError(address(vToken)); + } + + uint256 oldCollateralFactorMantissa = market.collateralFactorMantissa; + if (newCollateralFactorMantissa != oldCollateralFactorMantissa) { + market.collateralFactorMantissa = newCollateralFactorMantissa; + emit NewCollateralFactor(vToken, oldCollateralFactorMantissa, newCollateralFactorMantissa); + } + + uint256 oldLiquidationThresholdMantissa = market.liquidationThresholdMantissa; + if (newLiquidationThresholdMantissa != oldLiquidationThresholdMantissa) { + market.liquidationThresholdMantissa = newLiquidationThresholdMantissa; + emit NewLiquidationThreshold(vToken, oldLiquidationThresholdMantissa, newLiquidationThresholdMantissa); + } + } + + /** + * @notice Sets the liquidation incentive applied to any market that has no incentive of its own + * @dev This function is restricted by the AccessControlManager + * @dev `PoolRegistry.addPool` calls this while registering the pool, so the value clears the floor below by the + * time any market can be listed. + * + * This value has to stay at or above `1e18 + protocolSeizeShareMantissa` of every market that has no incentive of + * its own, or a liquidator of that market's collateral receives less than the debt it repaid. The shares of those + * markets are only readable market by market, so what is enforced here is the floor for a market at the default + * share: `MIN_POOL_LIQUIDATION_INCENTIVE_MANTISSA`. That covers a freshly listed market, which is the case + * nothing else was watching, and it is where this fork parts with upstream `Comptroller` - which stops at 1e18 + * and would let a pool be registered one that pays every default-share market's liquidator less than it repaid. + * A market whose share is raised above the default still needs an incentive of its own; + * `setMarketLiquidationIncentive` bounds that from one side and `VToken.setProtocolSeizeShare` from the other. + * @param newLiquidationIncentiveMantissa New liquidationIncentive scaled by 1e18 + * @custom:event Emits NewLiquidationIncentive on success + * @custom:error InvalidLiquidationIncentive is thrown if the new incentive is below + * `MIN_POOL_LIQUIDATION_INCENTIVE_MANTISSA` + * @custom:access Controlled by AccessControlManager + */ + function setLiquidationIncentive(uint256 newLiquidationIncentiveMantissa) external { + _checkAccessAllowed("setLiquidationIncentive(uint256)"); + + // Upstream `Comptroller` stops at 1e18 and rejects with a revert string. Raised to the floor a default-share + // market needs, and reduced to the custom error the per-market setter uses, so that the same condition + // reports the same way from both. + if (newLiquidationIncentiveMantissa < MIN_POOL_LIQUIDATION_INCENTIVE_MANTISSA) { + revert InvalidLiquidationIncentive(); + } + + // Save current value for use in log + uint256 oldLiquidationIncentiveMantissa = _poolLiquidationIncentiveMantissa; + + // Set liquidation incentive to new incentive + _poolLiquidationIncentiveMantissa = newLiquidationIncentiveMantissa; + + // Emit event with old incentive, new incentive + emit NewLiquidationIncentive(oldLiquidationIncentiveMantissa, newLiquidationIncentiveMantissa); + } + + /** + * @notice Add the market to the markets mapping and set it as listed + * @dev Only callable by the PoolRegistry + * @param vToken The address of the market (token) to list + * @custom:error MarketAlreadyListed is thrown if the market is already listed in this pool + * @custom:error InvalidVToken is thrown if the market does not identify itself as a VToken + * @custom:access Only PoolRegistry + */ + function supportMarket(VToken vToken) external { + _checkSenderIs(poolRegistry); + + if (markets[address(vToken)].isListed) { + revert MarketAlreadyListed(address(vToken)); + } + + // Sanity check to make sure its really a VToken + if (!vToken.isVToken()) { + revert InvalidVToken(); + } + + Market storage newMarket = markets[address(vToken)]; + newMarket.isListed = true; + newMarket.collateralFactorMantissa = 0; + newMarket.liquidationThresholdMantissa = 0; + + _addMarket(address(vToken)); + + uint256 rewardDistributorsCount = rewardsDistributors.length; + + for (uint256 i; i < rewardDistributorsCount; ++i) { + rewardsDistributors[i].initializeMarket(address(vToken)); + } + + emit MarketSupported(vToken); + } + + /** + * @notice Set the given borrow caps for the given vToken markets. Borrowing that brings total borrows to or above borrow cap will revert. + * @dev This function is restricted by the AccessControlManager + * @dev A borrow cap of type(uint256).max corresponds to unlimited borrowing. + * @dev Borrow caps smaller than the current total borrows are accepted. This way, new borrows will not be allowed + until the total borrows amount goes below the new borrow cap + * @param vTokens The addresses of the markets (tokens) to change the borrow caps for + * @param newBorrowCaps The new borrow cap values in underlying to be set. A value of type(uint256).max corresponds to unlimited borrowing. + * @custom:error InvalidArrayLength is thrown if the arrays are empty or their lengths do not match + * @custom:access Controlled by AccessControlManager + */ + function setMarketBorrowCaps(VToken[] calldata vTokens, uint256[] calldata newBorrowCaps) external { + _checkAccessAllowed("setMarketBorrowCaps(address[],uint256[])"); + + uint256 numMarkets = vTokens.length; + uint256 numBorrowCaps = newBorrowCaps.length; + + if (numMarkets == 0 || numMarkets != numBorrowCaps) { + revert InvalidArrayLength(); + } + + _ensureMaxLoops(numMarkets); + + for (uint256 i; i < numMarkets; ++i) { + borrowCaps[address(vTokens[i])] = newBorrowCaps[i]; + emit NewBorrowCap(vTokens[i], newBorrowCaps[i]); + } + } + + /** + * @notice Set the given supply caps for the given vToken markets. Supply that brings total Supply to or above supply cap will revert. + * @dev This function is restricted by the AccessControlManager + * @dev A supply cap of type(uint256).max corresponds to unlimited supply. + * @dev Supply caps smaller than the current total supplies are accepted. This way, new supplies will not be allowed + until the total supplies amount goes below the new supply cap + * @param vTokens The addresses of the markets (tokens) to change the supply caps for + * @param newSupplyCaps The new supply cap values in underlying to be set. A value of type(uint256).max corresponds to unlimited supply. + * @custom:error InvalidArrayLength is thrown if the arrays are empty or their lengths do not match + * @custom:access Controlled by AccessControlManager + */ + function setMarketSupplyCaps(VToken[] calldata vTokens, uint256[] calldata newSupplyCaps) external { + _checkAccessAllowed("setMarketSupplyCaps(address[],uint256[])"); + uint256 vTokensCount = vTokens.length; + + if (vTokensCount == 0 || vTokensCount != newSupplyCaps.length) { + revert InvalidArrayLength(); + } + + _ensureMaxLoops(vTokensCount); + + for (uint256 i; i < vTokensCount; ++i) { + supplyCaps[address(vTokens[i])] = newSupplyCaps[i]; + emit NewSupplyCap(vTokens[i], newSupplyCaps[i]); + } + } + + /** + * @notice Pause/unpause specified actions + * @dev This function is restricted by the AccessControlManager + * @param marketsList Markets to pause/unpause the actions on + * @param actionsList List of action ids to pause/unpause + * @param paused The new paused state (true=paused, false=unpaused) + * @custom:error MarketNotListed is thrown if any of the markets is not listed + * @custom:access Controlled by AccessControlManager + */ + function setActionsPaused(VToken[] calldata marketsList, Action[] calldata actionsList, bool paused) external { + _checkAccessAllowed("setActionsPaused(address[],uint256[],bool)"); + + uint256 marketsCount = marketsList.length; + uint256 actionsCount = actionsList.length; + + _ensureMaxLoops(marketsCount * actionsCount); + + for (uint256 marketIdx; marketIdx < marketsCount; ++marketIdx) { + for (uint256 actionIdx; actionIdx < actionsCount; ++actionIdx) { + _setActionPaused(address(marketsList[marketIdx]), actionsList[actionIdx], paused); + } + } + } + + /** + * @notice Set the given collateral threshold for non-batch liquidations. Regular liquidations + * will fail if the collateral amount is less than this threshold. Liquidators should use batch + * operations like liquidateAccount or healAccount. + * @dev This function is restricted by the AccessControlManager + * @param newMinLiquidatableCollateral The new min liquidatable collateral (in USD). + * @custom:access Controlled by AccessControlManager + */ + function setMinLiquidatableCollateral(uint256 newMinLiquidatableCollateral) external { + _checkAccessAllowed("setMinLiquidatableCollateral(uint256)"); + + uint256 oldMinLiquidatableCollateral = minLiquidatableCollateral; + minLiquidatableCollateral = newMinLiquidatableCollateral; + emit NewMinLiquidatableCollateral(oldMinLiquidatableCollateral, newMinLiquidatableCollateral); + } + + /** + * @notice Add a new RewardsDistributor and initialize it with all markets. We can add several RewardsDistributor + * contracts with the same rewardToken, and there could be overlaping among them considering the last reward slot (block or second) + * @dev Only callable by the admin + * @param _rewardsDistributor Address of the RewardDistributor contract to add + * @custom:access Only Governance + * @custom:event Emits NewRewardsDistributor with distributor address + * @custom:error RewardsDistributorAlreadyExists is thrown if this pool already has the given distributor + */ + function addRewardsDistributor(RewardsDistributor _rewardsDistributor) external onlyOwner { + if (rewardsDistributorExists[address(_rewardsDistributor)]) { + revert RewardsDistributorAlreadyExists(); + } + + uint256 rewardsDistributorsLen = rewardsDistributors.length; + _ensureMaxLoops(rewardsDistributorsLen + 1); + + rewardsDistributors.push(_rewardsDistributor); + rewardsDistributorExists[address(_rewardsDistributor)] = true; + + uint256 marketsCount = allMarkets.length; + + for (uint256 i; i < marketsCount; ++i) { + _rewardsDistributor.initializeMarket(address(allMarkets[i])); + } + + emit NewRewardsDistributor(address(_rewardsDistributor), address(_rewardsDistributor.rewardToken())); + } + + /** + * @notice Sets a new price oracle for the Comptroller + * @dev Only callable by the admin + * @param newOracle Address of the new price oracle to set + * @custom:event Emits NewPriceOracle on success + * @custom:error ZeroAddressNotAllowed is thrown when the new oracle address is zero + */ + function setPriceOracle(ResilientOracleInterface newOracle) external onlyOwner { + ensureNonzeroAddress(address(newOracle)); + + ResilientOracleInterface oldOracle = oracle; + oracle = newOracle; + emit NewPriceOracle(oldOracle, newOracle); + } + + /** + * @notice Sets a new deviation-bounded oracle for the Comptroller + * @dev Only callable by the admin. Nothing falls back to spot when this is unset: both of the calls into the zero + * address revert, so borrow and redeem fail closed rather than running unbounded, and this has to be set before + * the pool serves either action. `_updateProtectionStates` is the one that fails first and it is the stronger of + * the two guards, because solc emits an `extcodesize` existence check ahead of a call whose return data it does + * not decode, which is exactly that call. `_safeGetUnderlyingPrices` gets no such check and instead relies on + * the ABI decoder rejecting the empty return data. + * @param newBoundedOracle Address of the new deviation-bounded oracle to set + * @custom:event Emits NewDeviationBoundedOracle on success + * @custom:error ZeroAddressNotAllowed is thrown when the new oracle address is zero + */ + function setDeviationBoundedOracle(IDeviationBoundedOracle newBoundedOracle) external onlyOwner { + ensureNonzeroAddress(address(newBoundedOracle)); + + IDeviationBoundedOracle oldBoundedOracle = deviationBoundedOracle; + deviationBoundedOracle = newBoundedOracle; + emit NewDeviationBoundedOracle(oldBoundedOracle, newBoundedOracle); + } + + /** + * @notice Set the for loop iteration limit to avoid DOS + * @param limit Limit for the max loops can execute at a time + */ + function setMaxLoopsLimit(uint256 limit) external onlyOwner { + _setMaxLoopsLimit(limit); + } + + /** + * @notice Enables forced liquidations for a market. If forced liquidation is enabled, + * borrows in the market may be liquidated regardless of the account liquidity + * @param vTokenBorrowed Borrowed vToken + * @param enable Whether to enable forced liquidations + */ + function setForcedLiquidation(address vTokenBorrowed, bool enable) external { + _checkAccessAllowed("setForcedLiquidation(address,bool)"); + ensureNonzeroAddress(vTokenBorrowed); + + if (!markets[vTokenBorrowed].isListed) { + revert MarketNotListed(vTokenBorrowed); + } + + isForcedLiquidationEnabled[vTokenBorrowed] = enable; + emit IsForcedLiquidationEnabledUpdated(vTokenBorrowed, enable); + } + + /** + * @notice Sets the discount a liquidator receives on the collateral it seizes from a single market + * @dev This function is restricted by the AccessControlManager + * @dev Keyed on the collateral market, since that is what the discount prices. Setting it steers liquidators + * toward one collateral over another, and it feeds the routing between `liquidateAccount` and `healAccount` + * through `AccountLiquiditySnapshot.maxClearableDebt`. + * + * There is no way back to "unset" once a value is stored: `0` is the sentinel that means the pool-wide discount + * applies, and it is rejected here so that a mistaken zero cannot silently move a market back onto the pool-wide + * value. Pass that value explicitly to get the same effect. + * @param vToken The collateral market to set the incentive for + * @param newLiquidationIncentiveMantissa New incentive for this market, scaled by 1e18, at least + * 1e18 + the market's `protocolSeizeShareMantissa` + * @custom:event Emits NewMarketLiquidationIncentive on success + * @custom:error MarketNotListed is thrown if the market is not listed + * @custom:error InvalidLiquidationIncentive is thrown if the new incentive would leave the liquidator with less + * collateral than the debt it repaid + * @custom:access Controlled by AccessControlManager + */ + function setMarketLiquidationIncentive(address vToken, uint256 newLiquidationIncentiveMantissa) external { + _checkAccessAllowed("setMarketLiquidationIncentive(address,uint256)"); + + // Checked before reading from `vToken` below, so this never calls out to an arbitrary address + if (!markets[vToken].isListed) { + revert MarketNotListed(vToken); + } + + // The incentive is a multiplier on the debt repaid, and `VToken._seize` hands the protocol + // `protocolSeizeShareMantissa` of that debt out of the seized collateral, so an incentive below + // `1e18 + protocolSeizeShareMantissa` pays the liquidator less collateral than it repaid and no one liquidates + // this collateral. The 1e18 floor falls out of the same bound, since the seize share is never negative. + // `VToken.setProtocolSeizeShare` holds the bound from the other side: it reads the incentive back through + // `liquidationIncentiveMantissa()`, which answers for the calling market. + if (newLiquidationIncentiveMantissa < MANTISSA_ONE + VToken(vToken).protocolSeizeShareMantissa()) { + revert InvalidLiquidationIncentive(); + } + + uint256 oldLiquidationIncentiveMantissa = liquidationIncentives[vToken]; + liquidationIncentives[vToken] = newLiquidationIncentiveMantissa; + emit NewMarketLiquidationIncentive(vToken, oldLiquidationIncentiveMantissa, newLiquidationIncentiveMantissa); + } + + /** + * @notice Restricts supplying to a market to the accounts on its supply allowlist, or lifts the restriction + * @dev Enforced in `preMintHook`, so only supply is metered. Redeeming is never restricted, and an account + * removed from the allowlist keeps the position it already holds and can still exit. Enabling it on a market that + * is already serving supply cuts off every account that is not on the list, the seed supplier included. + * @param vToken The market to change the setting for + * @param enabled Whether the market should accept supply only from allowlisted accounts + * @custom:event Emits SupplyAllowlistEnabledUpdated on success + * @custom:error MarketNotListed is thrown if the market is not listed + * @custom:access Controlled by AccessControlManager + */ + function setSupplyAllowlistEnabled(address vToken, bool enabled) external { + _checkAccessAllowed("setSupplyAllowlistEnabled(address,bool)"); + + if (!markets[vToken].isListed) { + revert MarketNotListed(vToken); + } + + isSupplyAllowlistEnabled[vToken] = enabled; + emit SupplyAllowlistEnabledUpdated(vToken, enabled); + } + + /** + * @notice Adds an account to a market's supply allowlist or removes it + * @dev Takes effect only while the market's supply allowlist is enabled. Setting an account to the value it + * already holds is not an error, so a governance action that overlaps an earlier one still executes. + * @param vToken The market whose allowlist to update + * @param supplier The account to add or remove + * @param allowed Whether the account should be allowed to supply + * @custom:event Emits AllowedSupplierUpdated on success + * @custom:error MarketNotListed is thrown if the market is not listed + * @custom:error ZeroAddressNotAllowed is thrown if the account is the zero address + * @custom:access Controlled by AccessControlManager + */ + function setAllowedSupplier(address vToken, address supplier, bool allowed) external { + _checkAccessAllowed("setAllowedSupplier(address,address,bool)"); + ensureNonzeroAddress(supplier); + + if (!markets[vToken].isListed) { + revert MarketNotListed(vToken); + } + + isAllowedSupplier[vToken][supplier] = allowed; + emit AllowedSupplierUpdated(vToken, supplier, allowed); + } + + /** + * @notice Restricts seizing collateral in this pool to the accounts on the liquidation allowlist, or lifts the + * restriction + * @dev Pool-wide rather than per market, for the reason given on `isLiquidationAllowlistEnabled`. Enabling this + * also restricts `healAccount`, so any keeper relied on to record bad debt has to be allowlisted too. + * @param enabled Whether seizing collateral should be restricted to allowlisted accounts + * @custom:event Emits LiquidationAllowlistEnabledUpdated on success + * @custom:access Controlled by AccessControlManager + */ + function setLiquidationAllowlistEnabled(bool enabled) external { + _checkAccessAllowed("setLiquidationAllowlistEnabled(bool)"); + + isLiquidationAllowlistEnabled = enabled; + emit LiquidationAllowlistEnabledUpdated(enabled); + } + + /** + * @notice Adds an account to the pool's liquidation allowlist or removes it + * @dev Takes effect only while the liquidation allowlist is enabled. + * @param liquidator The account to add or remove + * @param allowed Whether the account should be allowed to seize collateral + * @custom:event Emits AllowedLiquidatorUpdated on success + * @custom:error ZeroAddressNotAllowed is thrown if the account is the zero address + * @custom:access Controlled by AccessControlManager + */ + function setAllowedLiquidator(address liquidator, bool allowed) external { + _checkAccessAllowed("setAllowedLiquidator(address,bool)"); + ensureNonzeroAddress(liquidator); + + isAllowedLiquidator[liquidator] = allowed; + emit AllowedLiquidatorUpdated(liquidator, allowed); + } + + /** + * @notice Determine the current account liquidity with respect to liquidation threshold requirements + * @dev The interface of this function is intentionally kept compatible with Compound and Venus Core + * @param account The account get liquidity for + * @return error Always NO_ERROR for compatibility with Venus core tooling + * @return liquidity Account liquidity in excess of liquidation threshold requirements, + * @return shortfall Account shortfall below liquidation threshold requirements + */ + function getAccountLiquidity( + address account + ) external view returns (uint256 error, uint256 liquidity, uint256 shortfall) { + AccountLiquiditySnapshot memory snapshot = _getCurrentLiquiditySnapshot( + account, + WeightFunction.USE_LIQUIDATION_THRESHOLD + ); + return (NO_ERROR, snapshot.liquidity, snapshot.shortfall); + } + + /** + * @notice Determine the current account liquidity with respect to collateral requirements + * @dev The interface of this function is intentionally kept compatible with Compound and Venus Core + * @param account The account get liquidity for + * @return error Always NO_ERROR for compatibility with Venus core tooling + * @return liquidity Account liquidity in excess of collateral requirements, + * @return shortfall Account shortfall below collateral requirements + */ + function getBorrowingPower( + address account + ) external view returns (uint256 error, uint256 liquidity, uint256 shortfall) { + AccountLiquiditySnapshot memory snapshot = _getCurrentLiquiditySnapshot( + account, + WeightFunction.USE_COLLATERAL_FACTOR + ); + return (NO_ERROR, snapshot.liquidity, snapshot.shortfall); + } + + /** + * @notice Determine what the account liquidity would be if the given amounts were redeemed/borrowed + * @dev The interface of this function is intentionally kept compatible with Compound and Venus Core + * @param vTokenModify The market to hypothetically redeem/borrow in + * @param account The account to determine liquidity for + * @param redeemTokens The number of tokens to hypothetically redeem + * @param borrowAmount The amount of underlying to hypothetically borrow + * @return error Always NO_ERROR for compatibility with Venus core tooling + * @return liquidity Hypothetical account liquidity in excess of collateral requirements, + * @return shortfall Hypothetical account shortfall below collateral requirements + */ + function getHypotheticalAccountLiquidity( + address account, + address vTokenModify, + uint256 redeemTokens, + uint256 borrowAmount + ) external view returns (uint256 error, uint256 liquidity, uint256 shortfall) { + AccountLiquiditySnapshot memory snapshot = _getHypotheticalLiquiditySnapshot( + account, + VToken(vTokenModify), + redeemTokens, + borrowAmount, + WeightFunction.USE_COLLATERAL_FACTOR + ); + return (NO_ERROR, snapshot.liquidity, snapshot.shortfall); + } + + /** + * @notice Return all of the markets + * @dev The automatic getter may be used to access an individual market. + * @return markets The list of market addresses + */ + function getAllMarkets() external view override returns (VToken[] memory) { + return allMarkets; + } + + /** + * @notice Check if a market is marked as listed (active) + * @param vToken vToken Address for the market to check + * @return listed True if listed otherwise false + */ + function isMarketListed(VToken vToken) external view returns (bool) { + return markets[address(vToken)].isListed; + } + + /** + * @notice Returns the discount a liquidator receives on the collateral it seizes from the calling market + * @dev Answers for `msg.sender` instead of taking the market as an argument, because this is the getter + * `ComptrollerViewInterface` declares and `VToken` calls on itself: `_seize` divides the protocol seize share by + * it, and `setProtocolSeizeShare` bounds that share against it. Both have to see the discount that prices the + * calling market's own collateral, or the protocol takes more than its configured share of the repaid debt and the + * liquidator is paid less than the market's configured discount. + * + * Any caller that is not a market of this pool, a lens or a liquidation bot, reads the pool-wide discount. Ask + * about a specific market through `effectiveLiquidationIncentive`. + * @return The discount that applies to the caller, scaled by 1e18 + */ + function liquidationIncentiveMantissa() external view returns (uint256) { + return _liquidationIncentive(msg.sender); + } + + /** + * @notice Returns the discount a liquidator receives on the collateral it seizes from a market + * @param vToken The collateral market to read the discount of + * @return The market's own discount if it has one, otherwise the pool-wide discount, scaled by 1e18 + */ + function effectiveLiquidationIncentive(address vToken) external view returns (uint256) { + return _liquidationIncentive(vToken); + } + + /*** Assets You Are In ***/ + + /** + * @notice Returns whether the given account is entered in a given market + * @param account The address of the account to check + * @param vToken The vToken to check + * @return True if the account is in the market specified, otherwise false. + */ + function checkMembership(address account, VToken vToken) external view returns (bool) { + return markets[address(vToken)].accountMembership[account]; + } + + /** + * @notice Calculate number of tokens of collateral asset to seize given an underlying amount + * @dev Used in liquidation (called in vToken.liquidateBorrowFresh) + * @param vTokenBorrowed The address of the borrowed vToken + * @param vTokenCollateral The address of the collateral vToken + * @param actualRepayAmount The amount of vTokenBorrowed underlying to convert into vTokenCollateral tokens + * @return error Always NO_ERROR for compatibility with Venus core tooling + * @return tokensToSeize Number of vTokenCollateral tokens to be seized in a liquidation + * @custom:error PriceError if the oracle returns an invalid price + */ + function liquidateCalculateSeizeTokens( + address vTokenBorrowed, + address vTokenCollateral, + uint256 actualRepayAmount + ) external view override returns (uint256 error, uint256 tokensToSeize) { + /* Read oracle prices for borrowed and collateral markets */ + uint256 priceBorrowedMantissa = _safeGetUnderlyingPrice(VToken(vTokenBorrowed)); + uint256 priceCollateralMantissa = _safeGetUnderlyingPrice(VToken(vTokenCollateral)); + + /* + * Get the exchange rate and calculate the number of collateral tokens to seize, where + * `liquidationIncentive` is the collateral market's own discount if it has one and the pool-wide default + * otherwise, the same value `VToken._seize` reads back through `liquidationIncentiveMantissa()`: + * seizeAmount = actualRepayAmount * liquidationIncentive * priceBorrowed / priceCollateral + * seizeTokens = seizeAmount / exchangeRate + * = actualRepayAmount * (liquidationIncentive * priceBorrowed) / (priceCollateral * exchangeRate) + */ + uint256 exchangeRateMantissa = VToken(vTokenCollateral).exchangeRateStored(); // Note: reverts on error + uint256 seizeTokens; + Exp memory numerator; + Exp memory denominator; + Exp memory ratio; + + numerator = mul_( + Exp({ mantissa: _liquidationIncentive(vTokenCollateral) }), + Exp({ mantissa: priceBorrowedMantissa }) + ); + denominator = mul_(Exp({ mantissa: priceCollateralMantissa }), Exp({ mantissa: exchangeRateMantissa })); + ratio = div_(numerator, denominator); + + seizeTokens = mul_ScalarTruncate(ratio, actualRepayAmount); + + return (NO_ERROR, seizeTokens); + } + + /** + * @notice Returns reward speed given a vToken + * @param vToken The vToken to get the reward speeds for + * @return rewardSpeeds Array of total supply and borrow speeds and reward token for all reward distributors + */ + function getRewardsByMarket(address vToken) external view returns (RewardSpeeds[] memory rewardSpeeds) { + uint256 rewardsDistributorsLength = rewardsDistributors.length; + rewardSpeeds = new RewardSpeeds[](rewardsDistributorsLength); + for (uint256 i; i < rewardsDistributorsLength; ++i) { + RewardsDistributor rewardsDistributor = rewardsDistributors[i]; + address rewardToken = address(rewardsDistributor.rewardToken()); + rewardSpeeds[i] = RewardSpeeds({ + rewardToken: rewardToken, + supplySpeed: rewardsDistributor.rewardTokenSupplySpeeds(vToken), + borrowSpeed: rewardsDistributor.rewardTokenBorrowSpeeds(vToken) + }); + } + return rewardSpeeds; + } + + /** + * @notice Return all reward distributors for this pool + * @return Array of RewardDistributor addresses + */ + function getRewardDistributors() external view returns (RewardsDistributor[] memory) { + return rewardsDistributors; + } + + /** + * @notice A marker method that returns true for a valid Comptroller contract + * @return Always true + */ + function isComptroller() external pure override returns (bool) { + return true; + } + + /** + * @notice Update the prices of all the tokens associated with the provided account + * @param account Address of the account to get associated tokens with + */ + function updatePrices(address account) public { + _updatePrices(getAssetsIn(account)); + } + + /** + * @notice Checks if a certain action is paused on a market + * @param market vToken address + * @param action Action to check + * @return paused True if the action is paused otherwise false + */ + function actionPaused(address market, Action action) public view returns (bool) { + return _actionPaused[market][action]; + } + + /** + * @notice Returns the assets an account has entered + * @param account The address of the account to pull assets for + * @return A list with the assets the account has entered + */ + function getAssetsIn(address account) public view returns (VToken[] memory) { + uint256 len; + VToken[] memory _accountAssets = accountAssets[account]; + uint256 _accountAssetsLength = _accountAssets.length; + + VToken[] memory assetsIn = new VToken[](_accountAssetsLength); + + for (uint256 i; i < _accountAssetsLength; ++i) { + Market storage market = markets[address(_accountAssets[i])]; + if (market.isListed) { + assetsIn[len] = _accountAssets[i]; + ++len; + } + } + + assembly { + mstore(assetsIn, len) + } + + return assetsIn; + } + + /** + * @notice Add the market to the borrower's "assets in" for liquidity calculations + * @param vToken The market to enter + * @param borrower The address of the account to modify + */ + function _addToMarket(VToken vToken, address borrower) internal { + _checkActionPauseState(address(vToken), Action.ENTER_MARKET); + Market storage marketToJoin = markets[address(vToken)]; + + if (!marketToJoin.isListed) { + revert MarketNotListed(address(vToken)); + } + + if (marketToJoin.accountMembership[borrower]) { + // already joined + return; + } + + // survived the gauntlet, add to list + // NOTE: we store these somewhat redundantly as a significant optimization + // this avoids having to iterate through the list for the most common use cases + // that is, only when we need to perform liquidity checks + // and not whenever we want to check if an account is in a particular market + marketToJoin.accountMembership[borrower] = true; + accountAssets[borrower].push(vToken); + + emit MarketEntered(vToken, borrower); + } + + /** + * @notice Internal function to validate that a market hasn't already been added + * and if it hasn't adds it + * @param vToken The market to support + */ + function _addMarket(address vToken) internal { + uint256 marketsCount = allMarkets.length; + + for (uint256 i; i < marketsCount; ++i) { + if (allMarkets[i] == VToken(vToken)) { + revert MarketAlreadyListed(vToken); + } + } + allMarkets.push(VToken(vToken)); + marketsCount = allMarkets.length; + _ensureMaxLoops(marketsCount); + } + + /** + * @dev Pause/unpause an action on a market + * @param market Market to pause/unpause the action on + * @param action Action id to pause/unpause + * @param paused The new paused state (true=paused, false=unpaused) + */ + function _setActionPaused(address market, Action action, bool paused) internal { + if (!markets[market].isListed) { + revert MarketNotListed(market); + } + _actionPaused[market][action] = paused; + emit ActionPausedMarket(VToken(market), action, paused); + } + + /** + * @dev Accrues interest and pushes an oracle update for each of the given markets, so that a snapshot taken + * afterwards values the account on live balances and prices. Used by the two batch liquidation entry points, + * which decide on their snapshot and then execute with the per-order liquidity check skipped. + * @param vTokens The markets to refresh, as returned by `getAssetsIn` + */ + function _refreshMarkets(VToken[] memory vTokens) internal { + uint256 vTokensCount = vTokens.length; + + for (uint256 i; i < vTokensCount; ++i) { + vTokens[i].accrueInterest(); + } + + _updatePrices(vTokens); + } + + /** + * @dev Pushes an oracle update for each of the given markets. Takes the resolved market list rather than an + * account, because the two callers that need protection state as well would otherwise walk `getAssetsIn` twice + * for the same account in the same call. + * @param vTokens The markets to update, as returned by `getAssetsIn` + */ + function _updatePrices(VToken[] memory vTokens) internal { + uint256 vTokensCount = vTokens.length; + + ResilientOracleInterface oracle_ = oracle; + + for (uint256 i; i < vTokensCount; ++i) { + oracle_.updatePrice(address(vTokens[i])); + } + } + + /** + * @dev Persists the deviation-bounded oracle's price window and protection state for each of the given markets. + * Runs before the borrow and redeem liquidity checks, the ones weighted by the collateral factor, so that a + * deviating print latches protection and starts its cooldown instead of evaporating once the price returns to + * the window. The liquidation-threshold paths never call this, matching how they read spot prices: see + * `_safeGetUnderlyingPrices`. + * @param vTokens The markets to update, as returned by `getAssetsIn` + */ + function _updateProtectionStates(VToken[] memory vTokens) internal { + uint256 vTokensCount = vTokens.length; + + IDeviationBoundedOracle boundedOracle = deviationBoundedOracle; + + for (uint256 i; i < vTokensCount; ++i) { + boundedOracle.updateProtectionState(address(vTokens[i])); + } + } + + /** + * @dev Internal function to check that vTokens can be safely redeemed for the underlying asset. + * @param vToken Address of the vTokens to redeem + * @param redeemer Account redeeming the tokens + * @param redeemTokens The number of tokens to redeem + */ + function _checkRedeemAllowed(address vToken, address redeemer, uint256 redeemTokens) internal { + Market storage market = markets[vToken]; + + if (!market.isListed) { + revert MarketNotListed(address(vToken)); + } + + /* If the redeemer is not 'in' the market, then we can bypass the liquidity check */ + if (!market.accountMembership[redeemer]) { + return; + } + + // Update the prices of tokens + VToken[] memory redeemerAssets = getAssetsIn(redeemer); + _updatePrices(redeemerAssets); + _updateProtectionStates(redeemerAssets); + + /* Otherwise, perform a hypothetical liquidity check to guard against shortfall */ + AccountLiquiditySnapshot memory snapshot = _getHypotheticalLiquiditySnapshot( + redeemer, + VToken(vToken), + redeemTokens, + 0, + WeightFunction.USE_COLLATERAL_FACTOR + ); + if (snapshot.shortfall > 0) { + revert InsufficientLiquidity(); + } + } + + /** + * @notice Get the total collateral, weighted collateral, borrow balance, liquidity, shortfall + * @param account The account to get the snapshot for + * @param weighting Which per-market risk parameter weights the collateral, either the collateral factor or + * the liquidation threshold + * @dev Note that we calculate the exchangeRateStored for each collateral vToken using stored data, + * without calculating accumulated interest. + * @return snapshot Account liquidity snapshot + */ + function _getCurrentLiquiditySnapshot( + address account, + WeightFunction weighting + ) internal view returns (AccountLiquiditySnapshot memory snapshot) { + return _getHypotheticalLiquiditySnapshot(account, VToken(address(0)), 0, 0, weighting); + } + + /** + * @notice Determine what the supply/borrow balances would be if the given amounts were redeemed/borrowed + * @param vTokenModify The market to hypothetically redeem/borrow in + * @param account The account to determine liquidity for + * @param redeemTokens The number of tokens to hypothetically redeem + * @param borrowAmount The amount of underlying to hypothetically borrow + * @param weighting Which per-market risk parameter weights the collateral, either the collateral factor or + * the liquidation threshold + * @dev Note that we calculate the exchangeRateStored for each collateral vToken using stored data, + * without calculating accumulated interest. + * @return snapshot Account liquidity snapshot + */ + function _getHypotheticalLiquiditySnapshot( + address account, + VToken vTokenModify, + uint256 redeemTokens, + uint256 borrowAmount, + WeightFunction weighting + ) internal view returns (AccountLiquiditySnapshot memory snapshot) { + // For each asset the account is in + VToken[] memory assets = getAssetsIn(account); + uint256 assetsCount = assets.length; + + for (uint256 i; i < assetsCount; ++i) { + VToken asset = assets[i]; + (Exp memory weightedVTokenPrice, Exp memory debtPrice) = _accumulateMarketPosition( + snapshot, + asset, + account, + weighting + ); + + // Calculate effects of interacting with vTokenModify + if (asset == vTokenModify) { + // redeem effect + // effects += tokensToDenom * redeemTokens + snapshot.effects = mul_ScalarTruncateAddUInt(weightedVTokenPrice, redeemTokens, snapshot.effects); + + // borrow effect + // effects += debtPrice * borrowAmount + snapshot.effects = mul_ScalarTruncateAddUInt(debtPrice, borrowAmount, snapshot.effects); + } + } + + uint256 borrowPlusEffects = snapshot.borrows + snapshot.effects; + // These are safe, as the underflow condition is checked first + unchecked { + if (snapshot.weightedCollateral > borrowPlusEffects) { + snapshot.liquidity = snapshot.weightedCollateral - borrowPlusEffects; + snapshot.shortfall = 0; + } else { + snapshot.liquidity = 0; + snapshot.shortfall = borrowPlusEffects - snapshot.weightedCollateral; + } + } + + return snapshot; + } + + /** + * @dev Adds one market's collateral and debt to an account's liquidity snapshot, and returns the two prices the + * caller needs to apply a hypothetical redeem or borrow in that market. Split out of + * `_getHypotheticalLiquiditySnapshot` because holding this market's prices and the loop's own variables in one + * frame exceeds the stack the legacy code generator can address, and this repo does not enable `viaIR`. + * `snapshot` is a memory reference, so the accumulated fields are updated in place. + * @param snapshot The snapshot to accumulate into + * @param asset The market to value + * @param account The account whose position in the market is being valued + * @param weighting Which per-market risk parameter weights the collateral + * @return weightedVTokenPrice Value of one vToken after the risk weight, which prices a hypothetical redeem + * @return debtPrice Price valuing the market's debt, which prices a hypothetical borrow + */ + function _accumulateMarketPosition( + AccountLiquiditySnapshot memory snapshot, + VToken asset, + address account, + WeightFunction weighting + ) internal view returns (Exp memory weightedVTokenPrice, Exp memory debtPrice) { + // Read the balances and exchange rate from the vToken + (uint256 vTokenBalance, uint256 borrowBalance, uint256 exchangeRateMantissa) = _safeGetAccountSnapshot( + asset, + account + ); + + // Get the normalized prices that value this market's collateral and debt + Exp memory collateralPrice; + (collateralPrice, debtPrice) = _safeGetUnderlyingPrices(asset, weighting); + + // Pre-compute conversion factors from vTokens -> usd + Exp memory vTokenPrice = mul_(Exp({ mantissa: exchangeRateMantissa }), collateralPrice); + weightedVTokenPrice = mul_(_getWeightingFactor(asset, weighting), vTokenPrice); + + // weightedCollateral += weightedVTokenPrice * vTokenBalance + snapshot.weightedCollateral = mul_ScalarTruncateAddUInt( + weightedVTokenPrice, + vTokenBalance, + snapshot.weightedCollateral + ); + + // totalCollateral += vTokenPrice * vTokenBalance + snapshot.totalCollateral = mul_ScalarTruncateAddUInt(vTokenPrice, vTokenBalance, snapshot.totalCollateral); + + // maxClearableDebt += (vTokenPrice * vTokenBalance) / liquidationIncentive, at this market's own incentive. + // Only the liquidation-threshold weighting gives this field a meaning and only that weighting's callers read + // it, so a collateral-factor snapshot would read the incentive and divide only to discard the result: see the + // note on `AccountLiquiditySnapshot.maxClearableDebt`. Also skipped when the account holds none of this + // market, since the term would be zero and a borrower is a member of every market it borrows from, including + // ones it holds no collateral in. The repeated product costs nothing, the optimizer shares it with the line + // above. + if (weighting == WeightFunction.USE_LIQUIDATION_THRESHOLD && vTokenBalance != 0) { + snapshot.maxClearableDebt += div_( + mul_ScalarTruncate(vTokenPrice, vTokenBalance), + Exp({ mantissa: _liquidationIncentive(address(asset)) }) + ); + } + + // borrows += debtPrice * borrowBalance + snapshot.borrows = mul_ScalarTruncateAddUInt(debtPrice, borrowBalance, snapshot.borrows); + } + + /** + * @dev Retrieves price from oracle for an asset and checks it is nonzero + * @param asset Address for asset to query price + * @return Underlying price + */ + function _safeGetUnderlyingPrice(VToken asset) internal view returns (uint256) { + uint256 oraclePriceMantissa = oracle.getUnderlyingPrice(address(asset)); + if (oraclePriceMantissa == 0) { + revert PriceError(address(asset)); + } + return oraclePriceMantissa; + } + + /** + * @dev Retrieves the two prices that value a market's collateral and its debt, and checks they are nonzero. + * Under the collateral factor both come from `deviationBoundedOracle`: while protection is active for the asset + * it values collateral at the low end of the asset's recent price window and debt at the high end, and it returns + * spot on both legs otherwise, including for an asset it holds no configuration for. A deviating print can + * therefore only ever shrink an account's borrowing capacity, never inflate it. Under the liquidation threshold + * both legs are spot, because those snapshots route an unhealthy account between `liquidateAccount` and + * `healAccount` and set how much of its debt healing repays, which has to track the live price. + * @param asset Address for asset to query prices for + * @param weighting Which risk parameter weights the position being valued + * @return collateralPrice Price valuing the collateral held in the market + * @return debtPrice Price valuing the debt owed to the market + */ + function _safeGetUnderlyingPrices( + VToken asset, + WeightFunction weighting + ) internal view returns (Exp memory collateralPrice, Exp memory debtPrice) { + if (weighting == WeightFunction.USE_LIQUIDATION_THRESHOLD) { + uint256 spotPriceMantissa = _safeGetUnderlyingPrice(asset); + return (Exp({ mantissa: spotPriceMantissa }), Exp({ mantissa: spotPriceMantissa })); + } + + (uint256 collateralPriceMantissa, uint256 debtPriceMantissa) = deviationBoundedOracle.getBoundedPricesView( + address(asset) + ); + if (collateralPriceMantissa == 0 || debtPriceMantissa == 0) { + revert PriceError(address(asset)); + } + return (Exp({ mantissa: collateralPriceMantissa }), Exp({ mantissa: debtPriceMantissa })); + } + + /** + * @dev Returns the risk parameter that weights a market's collateral in a liquidity snapshot + * @param asset Address for asset whose parameter to read + * @param weighting Which of the two parameters to read + * @return The market's collateral factor or liquidation threshold, as an exponential + */ + function _getWeightingFactor(VToken asset, WeightFunction weighting) internal view returns (Exp memory) { + Market storage market = markets[address(asset)]; + return + Exp({ + mantissa: weighting == WeightFunction.USE_COLLATERAL_FACTOR + ? market.collateralFactorMantissa + : market.liquidationThresholdMantissa + }); + } + + /** + * @dev Returns the liquidation incentive that applies when a market's collateral is seized + * @param vTokenCollateral Market whose collateral would be seized + * @return The market's own incentive, or the pool-wide one if the market has none. Never zero for a listed + * market, so callers may divide by it: see the note on `liquidationIncentives`. An unlisted market still reads + * back whatever incentive it was last given, which no seizure can reach, since every path that divides by this + * is gated on the market being listed. + */ + function _liquidationIncentive(address vTokenCollateral) internal view returns (uint256) { + uint256 incentive = liquidationIncentives[vTokenCollateral]; + return incentive != 0 ? incentive : _poolLiquidationIncentiveMantissa; + } + + /** + * @dev Returns supply and borrow balances of user in vToken, reverts on failure + * @param vToken Market to query + * @param user Account address + * @return vTokenBalance Balance of vTokens, the same as vToken.balanceOf(user) + * @return borrowBalance Borrowed amount, including the interest + * @return exchangeRateMantissa Stored exchange rate + */ + function _safeGetAccountSnapshot( + VToken vToken, + address user + ) internal view returns (uint256 vTokenBalance, uint256 borrowBalance, uint256 exchangeRateMantissa) { + uint256 err; + (err, vTokenBalance, borrowBalance, exchangeRateMantissa) = vToken.getAccountSnapshot(user); + if (err != 0) { + revert SnapshotError(address(vToken), user); + } + return (vTokenBalance, borrowBalance, exchangeRateMantissa); + } + + /// @notice Reverts if the call is not from expectedSender + /// @param expectedSender Expected transaction sender + function _checkSenderIs(address expectedSender) internal view { + if (msg.sender != expectedSender) { + revert UnexpectedSender(expectedSender, msg.sender); + } + } + + /// @notice Reverts if a certain action is paused on a market + /// @param market Market to check + /// @param action Action to check + function _checkActionPauseState(address market, Action action) private view { + if (actionPaused(market, action)) { + revert ActionPaused(market, action); + } + } + + /// @notice Reverts if the liquidation allowlist is enabled and the given account is not on it + /// @param liquidator Account that would receive the seized collateral + function _checkLiquidatorAllowlisted(address liquidator) private view { + if (isLiquidationAllowlistEnabled && !isAllowedLiquidator[liquidator]) { + revert LiquidationNotAllowed(liquidator); + } + } +} diff --git a/contracts/Spoke/SpokeComptrollerInterface.sol b/contracts/Spoke/SpokeComptrollerInterface.sol new file mode 100644 index 000000000..5cc213eff --- /dev/null +++ b/contracts/Spoke/SpokeComptrollerInterface.sol @@ -0,0 +1,366 @@ +// SPDX-License-Identifier: BSD-3-Clause +pragma solidity ^0.8.25; + +import { ResilientOracleInterface } from "@venusprotocol/oracle/contracts/interfaces/OracleInterface.sol"; +import { IDeviationBoundedOracle } from "@venusprotocol/oracle/contracts/interfaces/IDeviationBoundedOracle.sol"; + +import { ComptrollerInterface, Action } from "../ComptrollerInterface.sol"; +import { VToken } from "../VToken.sol"; + +/** + * @title SpokeComptrollerInterface + * @author Venus + * @notice Interface implemented by the `SpokeComptroller` contract. It declares the events and errors that make up the + * contract's observable surface, so integrators and off-chain consumers can decode them without depending on the + * implementation. + * @dev The getters this fork adds are declared separately, in `SpokeComptrollerViewInterface` below. They cannot live + * here: `SpokeComptroller` implements this interface, and it serves those getters from public variables declared in + * `SpokeComptrollerStorage`, which is a sibling base rather than a derived one. Solidity will not let a public + * variable in one base satisfy a function declared in another, so declaring them here would force + * `SpokeComptrollerStorage` to inherit this interface and mark six variables `override` - noise in the file that has + * to be diffed by hand against `ComptrollerStorage`, for no gain over a standalone view interface. + */ +interface SpokeComptrollerInterface is ComptrollerInterface { + /// @notice Emitted when an account enters a market + event MarketEntered(VToken indexed vToken, address indexed account); + + /// @notice Emitted when an account exits a market + event MarketExited(VToken indexed vToken, address indexed account); + + /// @notice Emitted when close factor is changed by admin + event NewCloseFactor(uint256 oldCloseFactorMantissa, uint256 newCloseFactorMantissa); + + /// @notice Emitted when a collateral factor is changed by admin + event NewCollateralFactor(VToken vToken, uint256 oldCollateralFactorMantissa, uint256 newCollateralFactorMantissa); + + /// @notice Emitted when liquidation threshold is changed by admin + event NewLiquidationThreshold( + VToken vToken, + uint256 oldLiquidationThresholdMantissa, + uint256 newLiquidationThresholdMantissa + ); + + /// @notice Emitted when liquidation incentive is changed by admin + event NewLiquidationIncentive(uint256 oldLiquidationIncentiveMantissa, uint256 newLiquidationIncentiveMantissa); + + /// @notice Emitted when the liquidation incentive of a single market is changed by admin + event NewMarketLiquidationIncentive( + address indexed vToken, + uint256 oldLiquidationIncentiveMantissa, + uint256 newLiquidationIncentiveMantissa + ); + + /// @notice Emitted when price oracle is changed + event NewPriceOracle(ResilientOracleInterface oldPriceOracle, ResilientOracleInterface newPriceOracle); + + /// @notice Emitted when the deviation-bounded oracle is changed + event NewDeviationBoundedOracle(IDeviationBoundedOracle oldBoundedOracle, IDeviationBoundedOracle newBoundedOracle); + + /// @notice Emitted when an action is paused on a market + event ActionPausedMarket(VToken vToken, Action action, bool pauseState); + + /// @notice Emitted when borrow cap for a vToken is changed + event NewBorrowCap(VToken indexed vToken, uint256 newBorrowCap); + + /// @notice Emitted when the collateral threshold (in USD) for non-batch liquidations is changed + event NewMinLiquidatableCollateral(uint256 oldMinLiquidatableCollateral, uint256 newMinLiquidatableCollateral); + + /// @notice Emitted when supply cap for a vToken is changed + event NewSupplyCap(VToken indexed vToken, uint256 newSupplyCap); + + /// @notice Emitted when a rewards distributor is added + event NewRewardsDistributor(address indexed rewardsDistributor, address indexed rewardToken); + + /// @notice Emitted when a market is supported + event MarketSupported(VToken vToken); + + /// @notice Emitted when forced liquidation is enabled or disabled for a market + event IsForcedLiquidationEnabledUpdated(address indexed vToken, bool enable); + + /// @notice Emitted when a market is unlisted + event MarketUnlisted(address indexed vToken); + + /// @notice Emitted when the borrowing or redeeming delegate rights are updated for an account + event DelegateUpdated(address indexed approver, address indexed delegate, bool approved); + + /// @notice Emitted when a market's supply allowlist is enabled or disabled + event SupplyAllowlistEnabledUpdated(address indexed vToken, bool enabled); + + /// @notice Emitted when an account is added to or removed from a market's supply allowlist + event AllowedSupplierUpdated(address indexed vToken, address indexed supplier, bool allowed); + + /// @notice Emitted when the pool's liquidation allowlist is enabled or disabled + event LiquidationAllowlistEnabledUpdated(bool enabled); + + /// @notice Emitted when an account is added to or removed from the pool's liquidation allowlist + event AllowedLiquidatorUpdated(address indexed liquidator, bool allowed); + + /// @notice Thrown when the close factor is outside the bounds set by `MIN_CLOSE_FACTOR_MANTISSA` and + /// `MAX_CLOSE_FACTOR_MANTISSA` + error InvalidCloseFactor(); + + /// @notice Thrown when collateral factor exceeds the upper bound + error InvalidCollateralFactor(); + + /// @notice Thrown when liquidation threshold exceeds the collateral factor + error InvalidLiquidationThreshold(); + + /// @notice Thrown when a liquidation incentive is low enough that a liquidator would seize less value than it + /// repaid: below `1e18 + protocolSeizeShareMantissa` for a single market, below + /// `MIN_POOL_LIQUIDATION_INCENTIVE_MANTISSA` for the pool-wide fallback + error InvalidLiquidationIncentive(); + + /// @notice Thrown when the action is only available to specific sender, but the real sender was different + error UnexpectedSender(address expectedSender, address actualSender); + + /// @notice Thrown when the oracle returns an invalid price for some asset + error PriceError(address vToken); + + /// @notice Thrown if VToken unexpectedly returned a nonzero error code while trying to get account snapshot + error SnapshotError(address vToken, address user); + + /// @notice Thrown when the market is not listed + error MarketNotListed(address market); + + /// @notice Thrown when a market has an unexpected comptroller + error ComptrollerMismatch(); + + /// @notice Thrown when user is not member of market + error MarketNotCollateral(address vToken, address user); + + /// @notice Thrown when borrow action is not paused + error BorrowActionNotPaused(); + + /// @notice Thrown when mint action is not paused + error MintActionNotPaused(); + + /// @notice Thrown when redeem action is not paused + error RedeemActionNotPaused(); + + /// @notice Thrown when repay action is not paused + error RepayActionNotPaused(); + + /// @notice Thrown when seize action is not paused + error SeizeActionNotPaused(); + + /// @notice Thrown when exit market action is not paused + error ExitMarketActionNotPaused(); + + /// @notice Thrown when transfer action is not paused + error TransferActionNotPaused(); + + /// @notice Thrown when enter market action is not paused + error EnterMarketActionNotPaused(); + + /// @notice Thrown when liquidate action is not paused + error LiquidateActionNotPaused(); + + /// @notice Thrown when borrow cap is not zero + error BorrowCapIsNotZero(); + + /// @notice Thrown when supply cap is not zero + error SupplyCapIsNotZero(); + + /// @notice Thrown when collateral factor is not zero + error CollateralFactorIsNotZero(); + + /** + * @notice Thrown during the liquidation if user's total collateral amount is lower than + * a predefined threshold. In this case only batch liquidations (either liquidateAccount + * or healAccount) are available. + */ + error MinimalCollateralViolated(uint256 expectedGreaterThan, uint256 actual); + + /** + * @notice Thrown by the batch operations when the account's total collateral is above + * `minLiquidatableCollateral`, which means it is large enough for a regular `VToken.liquidateBorrow` + * @dev Same name, arguments and meaning as the shared `Comptroller`, so a decoder written against either one + * reads this correctly. + */ + error CollateralExceedsThreshold(uint256 expectedLessThanOrEqualTo, uint256 actual); + + /// @notice Thrown by `healAccount` when the collateral can clear the whole debt at each market's own liquidation + /// incentive, so healing would forgive nothing and `liquidateAccount` has to be used instead + error CollateralCoversDebt(uint256 borrows, uint256 maxClearableDebt); + + /// @notice Thrown by `liquidateAccount` when the debt is too large for the collateral to clear, so `healAccount` + /// has to be used instead. Reports the debt and the largest debt the collateral could have cleared. + /// @dev Deliberately not the shared `Comptroller`'s `InsufficientCollateral`: both arguments here are debt values + /// rather than collateral values, and reusing that name would leave the two versions sharing one selector. + error DebtExceedsClearableAmount(uint256 borrows, uint256 maxClearableDebt); + + /// @notice Thrown when the account doesn't have enough liquidity to redeem or borrow + error InsufficientLiquidity(); + + /// @notice Thrown when trying to liquidate a healthy account + error InsufficientShortfall(); + + /// @notice Thrown if the liquidation allowlist is enabled and the account liquidating is not on it + error LiquidationNotAllowed(address liquidator); + + /// @notice Thrown when trying to repay more than allowed by close factor + error TooMuchRepay(); + + /// @notice Thrown if the user is trying to exit a market in which they have an outstanding debt + error NonzeroBorrowBalance(); + + /// @notice Thrown if a debt remains in any of the borrower's markets once every liquidation order has been + /// executed, which means the orders passed to `liquidateAccount` did not cover the whole position + error NonzeroBorrowBalanceAfterLiquidation(); + + /// @notice Thrown when trying to perform an action that is paused + error ActionPaused(address market, Action action); + + /// @notice Thrown when trying to add a market that is already listed + error MarketAlreadyListed(address market); + + /// @notice Thrown when the market being listed does not identify itself as a VToken + error InvalidVToken(); + + /// @notice Thrown when an array argument is empty, or when two array arguments have different lengths + error InvalidArrayLength(); + + /// @notice Thrown if the supply cap is exceeded + error SupplyCapExceeded(address market, uint256 cap); + + /// @notice Thrown if the account being credited with the minted vTokens is not on the market's supply allowlist + error SupplyNotAllowed(address market, address supplier); + + /// @notice Thrown if the borrow cap is exceeded + error BorrowCapExceeded(address market, uint256 cap); + + /// @notice Thrown if delegate approval status is already set to the requested value + error DelegationStatusUnchanged(); + + /// @notice Thrown when adding a rewards distributor that this pool already has + error RewardsDistributorAlreadyExists(); +} + +/** + * @title SpokeComptrollerViewInterface + * @author Venus + * @notice The getters `SpokeComptroller` adds on top of the pooled `Comptroller`, for integrators that read this pool + * rather than implement it - the Liquidity Hub's spoke adapter, lenses, keepers and VIP tooling. + * @dev Standalone and not implemented by `SpokeComptroller`, matching how `ComptrollerViewInterface` exposes the + * shared pool's getters. + * + * Scope is the supply side: what an integrator that funds this pool has to read before and while it supplies, plus + * the two allowlists and the per-market discount that only exist on this fork. `supplyCaps` and `actionPaused` are + * repeated from `ComptrollerViewInterface` and `ComptrollerInterface` rather than inherited, so that a consumer of a + * spoke pool needs one import and not three. `actionPaused` also has to be repeated on its own terms: the shared + * declaration types its second argument as the `Action` enum, and a consumer that does not want this repo's enum in + * its build needs the ABI-equivalent `uint8` form. The two share a selector, so they cannot both be declared in one + * inheritance chain - which is the reason this interface inherits nothing. + * + * `markets` is repeated for a different reason: the shared declaration returns two of the three words the getter + * actually produces, so a consumer reading it there silently loses `liquidationThresholdMantissa`. The form declared + * here returns all three. + * + * Anything else the fork shares with the pooled `Comptroller` - `borrowCaps`, `oracle`, `closeFactorMantissa`, + * `minLiquidatableCollateral` - is read through `ComptrollerViewInterface` as usual. + */ +interface SpokeComptrollerViewInterface { + /** + * @notice The listing state and both collateral weights of a market + * @dev The auto-generated getter over the `markets` mapping. It returns three words, one per non-mapping member + * of `Market`. `ComptrollerViewInterface` declares only the first two, so a caller reading the getter through + * that declaration drops `liquidationThresholdMantissa` without any error: the extra word is simply trailing + * return data the decoder ignores. Read it here to get all three. + * @param vToken The market to query + * @return isListed True once the market has been added to the pool + * @return collateralFactorMantissa The most an account may borrow against this collateral, scaled by 1e18 + * @return liquidationThresholdMantissa The weighting past which the position becomes liquidatable, scaled by 1e18 + */ + function markets( + address vToken + ) external view returns (bool isListed, uint256 collateralFactorMantissa, uint256 liquidationThresholdMantissa); + + /** + * @notice Whether a market may be liquidated while the borrower is still above the liquidation threshold + * @dev Read in `preLiquidateHook`. While this is set the close factor no longer bounds the repayment, so a + * position in the market can be closed in full in one call. + * @param vToken The market to read the setting of + * @return enabled True if this market allows liquidation without a shortfall + */ + function isForcedLiquidationEnabled(address vToken) external view returns (bool enabled); + + /** + * @notice The most underlying a market will hold before it stops accepting supply + * @dev `type(uint256).max` disables the check. Zero is a real cap of zero rather than an "unset" sentinel: + * `preMintHook` compares `nextTotalSupply > supplyCap`, so a market left at zero rejects every mint. + * @param vToken The market to query + * @return cap The market's supply cap, in underlying units + */ + function supplyCaps(address vToken) external view returns (uint256 cap); + + /** + * @notice Whether a market accepts supply only from the accounts on its supply allowlist + * @dev Enforced in `preMintHook` alone. Redeeming is never restricted, and a market that has never had this + * enabled accepts supply from anyone. + * @param vToken The market to read the setting of + * @return enabled True if the market's supply allowlist is armed + */ + function isSupplyAllowlistEnabled(address vToken) external view returns (bool enabled); + + /** + * @notice Whether an account may supply to a market while that market's supply allowlist is enabled + * @dev The account this meters is the one CREDITED with the newly minted vTokens, not the one paying for them: + * `mintBehalf` lets a third party fund a mint attributed to someone else, and metering the recipient is what + * bounds the market's supply. + * @param vToken The market whose allowlist to read + * @param supplier The account to test + * @return allowed True if `supplier` may be credited with this market's vTokens + */ + function isAllowedSupplier(address vToken, address supplier) external view returns (bool allowed); + + /** + * @notice Whether seizing collateral in this pool is restricted to the accounts on the liquidation allowlist + * @dev Pool-wide rather than per market, because `healAccount` seizes across every market the borrower is in and + * so cannot attribute a seizure to a single one of them. + * @return enabled True if the pool's liquidation allowlist is armed + */ + function isLiquidationAllowlistEnabled() external view returns (bool enabled); + + /** + * @notice Whether an account may seize collateral in this pool while the liquidation allowlist is enabled + * @param liquidator The account to test + * @return allowed True if `liquidator` may seize collateral in this pool + */ + function isAllowedLiquidator(address liquidator) external view returns (bool allowed); + + /** + * @notice The discount a market has of its own, or zero when it takes the pool-wide one + * @dev Zero is the "unset" sentinel rather than a real discount. Read `effectiveLiquidationIncentive` to get the + * value that actually applies to a market. + * @param vToken The collateral market to read the discount of + * @return incentiveMantissa The market's own discount scaled by 1e18, or zero if it has none + */ + function liquidationIncentives(address vToken) external view returns (uint256 incentiveMantissa); + + /** + * @notice The discount that applies to a market: its own if it has one, otherwise the pool-wide value + * @param vToken The collateral market to read the discount of + * @return incentiveMantissa The discount that prices this market's collateral, scaled by 1e18 + */ + function effectiveLiquidationIncentive(address vToken) external view returns (uint256 incentiveMantissa); + + /** + * @notice The oracle that bounds an asset's price against a recent window + * @dev Read only where the collateral factor weights a position. The liquidation-threshold paths stay on the + * `oracle`, because they route liquidations. Zero until governance sets it, and while it is zero borrowing and + * redeeming fail closed. + * @return boundedOracle The deviation-bounded oracle this pool prices borrowing capacity through + */ + function deviationBoundedOracle() external view returns (IDeviationBoundedOracle boundedOracle); + + /** + * @notice Whether an action is paused on a market + * @dev Typed as `uint8` rather than `Action` so that a consumer can read this without importing the enum. The + * encoding is identical; the deployed ordering is + * `{ MINT, REDEEM, BORROW, REPAY, SEIZE, LIQUIDATE, TRANSFER, ENTER_MARKET, EXIT_MARKET }`. + * @param market The market to query + * @param action The action, as its position in `Action` + * @return paused True if the action is paused on this market + */ + function actionPaused(address market, uint8 action) external view returns (bool paused); +} diff --git a/contracts/Spoke/SpokeComptrollerStorage.sol b/contracts/Spoke/SpokeComptrollerStorage.sol new file mode 100644 index 000000000..713a72b94 --- /dev/null +++ b/contracts/Spoke/SpokeComptrollerStorage.sol @@ -0,0 +1,188 @@ +// SPDX-License-Identifier: BSD-3-Clause +pragma solidity 0.8.25; + +import { ResilientOracleInterface } from "@venusprotocol/oracle/contracts/interfaces/OracleInterface.sol"; +import { IDeviationBoundedOracle } from "@venusprotocol/oracle/contracts/interfaces/IDeviationBoundedOracle.sol"; + +import { VToken } from "../VToken.sol"; +import { RewardsDistributor } from "../Rewards/RewardsDistributor.sol"; +import { Action } from "../ComptrollerInterface.sol"; + +/** + * @title SpokeComptrollerStorage + * @author Venus + * @notice Storage layout for the `SpokeComptroller` contract. + * @dev Fork of `ComptrollerStorage` (`contracts/ComptrollerStorage.sol`), kept separate so the spoke layout and the + * `AccountLiquiditySnapshot` struct can change without touching the shared implementation. Re-synced by hand when + * `ComptrollerStorage` changes, like the implementation it accompanies. + */ +contract SpokeComptrollerStorage { + struct LiquidationOrder { + VToken vTokenCollateral; + VToken vTokenBorrowed; + uint256 repayAmount; + } + + /// @dev `totalCollateral` and `maxClearableDebt` are only meaningful under + /// `WeightFunction.USE_LIQUIDATION_THRESHOLD`, which is the weighting every caller that reads them passes. Under + /// the collateral factor `totalCollateral` is derived from the deviation-bounded collateral price rather than spot, + /// and `maxClearableDebt` is not accumulated at all, so it stays at zero. A new caller on that weighting must not + /// start reading either one without deciding what it wants first: a zero `maxClearableDebt` puts `healAccount` on a + /// repayment percentage of zero, which forgives the whole position as bad debt. + struct AccountLiquiditySnapshot { + uint256 totalCollateral; + uint256 weightedCollateral; + uint256 borrows; + uint256 effects; + uint256 liquidity; + uint256 shortfall; + // The largest borrow value the account's collateral can clear without leaving bad debt behind, computed as + // the sum over the account's collateral markets of `collateralValue / liquidationIncentive`, each market at + // its own incentive. Used to route an under-threshold account between `liquidateAccount` and `healAccount`. + uint256 maxClearableDebt; + } + + struct RewardSpeeds { + address rewardToken; + uint256 supplySpeed; + uint256 borrowSpeed; + } + + struct Market { + // Whether or not this market is listed + bool isListed; + // Multiplier representing the most one can borrow against their collateral in this market. + // For instance, 0.9 to allow borrowing 90% of collateral value. + // Must be between 0 and 1, and stored as a mantissa. + uint256 collateralFactorMantissa; + // Multiplier representing the collateralization after which the borrow is eligible + // for liquidation. For instance, 0.8 liquidate when the borrow is 80% of collateral + // value. Must be between 0 and collateral factor, stored as a mantissa. + uint256 liquidationThresholdMantissa; + // Per-market mapping of "accounts in this asset" + mapping(address => bool) accountMembership; + } + + /** + * @notice Oracle which gives the price of any given asset + */ + ResilientOracleInterface public oracle; + + /** + * @notice Multiplier used to calculate the maximum repayAmount when liquidating a borrow + */ + uint256 public closeFactorMantissa; + + /** + * @notice Multiplier representing the discount on collateral that a liquidator receives, applied to every market + * that has no discount of its own + * @dev Internal rather than public, because the `liquidationIncentiveMantissa()` getter has to resolve the + * caller's market before answering: `VToken` reads that getter on itself and needs the discount that prices its + * own collateral, not the pool-wide default. See `SpokeComptroller.liquidationIncentiveMantissa`. + */ + uint256 internal _poolLiquidationIncentiveMantissa; + + /** + * @notice Per-account mapping of "assets you are in" + */ + mapping(address => VToken[]) public accountAssets; + + /** + * @notice Official mapping of vTokens -> Market metadata + * @dev Used e.g. to determine if a market is supported + */ + mapping(address => Market) public markets; + + /// @notice A list of all markets + VToken[] public allMarkets; + + /// @notice Borrow caps enforced by borrowAllowed for each vToken address. Defaults to zero which restricts borrowing. + mapping(address => uint256) public borrowCaps; + + /// @notice Minimal collateral required for regular (non-batch) liquidations + uint256 public minLiquidatableCollateral; + + /// @notice Supply caps enforced by mintAllowed for each vToken address. Defaults to zero which corresponds to minting not allowed + mapping(address => uint256) public supplyCaps; + + /// @notice True if a certain action is paused on a certain market + mapping(address => mapping(Action => bool)) internal _actionPaused; + + // List of Reward Distributors added + RewardsDistributor[] internal rewardsDistributors; + + // Used to check if rewards distributor is added + mapping(address => bool) internal rewardsDistributorExists; + + /// @notice Flag indicating whether forced liquidation enabled for a market + mapping(address => bool) public isForcedLiquidationEnabled; + + uint256 internal constant NO_ERROR = 0; + + // closeFactorMantissa must be strictly greater than this value + uint256 internal constant MIN_CLOSE_FACTOR_MANTISSA = 0.05e18; // 0.05 + + // closeFactorMantissa must not exceed this value + uint256 internal constant MAX_CLOSE_FACTOR_MANTISSA = 0.9e18; // 0.9 + + // No collateralFactorMantissa may exceed this value + uint256 internal constant MAX_COLLATERAL_FACTOR_MANTISSA = 0.95e18; // 0.95 + + // No pool-wide liquidation incentive may fall below this value. It is `1e18 + VToken`'s + // `DEFAULT_PROTOCOL_SEIZE_SHARE_MANTISSA`, restated here because that constant is internal to `VToken` and a + // market's share is only readable once the market exists. A newly listed market carries the default share and no + // incentive of its own, so this floor is what keeps its first liquidation from paying the liquidator less than it + // repaid. It is a backstop for that case, not a full guarantee: a market whose share is later raised above the + // default needs an incentive of its own, which `setMarketLiquidationIncentive` and `VToken.setProtocolSeizeShare` + // bound from both sides. + uint256 internal constant MIN_POOL_LIQUIDATION_INCENTIVE_MANTISSA = 1.05e18; // 1e18 + 0.05e18 + + /// @notice Whether the delegate is allowed to borrow or redeem on behalf of the user + //mapping(address user => mapping (address delegate => bool approved)) public approvedDelegates; + mapping(address => mapping(address => bool)) public approvedDelegates; + + /// @notice Whether a market accepts supply only from the accounts on its supply allowlist. Keyed by market, and + /// disabled by default, so a newly listed market accepts supply from anyone. + mapping(address => bool) public isSupplyAllowlistEnabled; + + /// @notice The accounts a market accepts supply from while its supply allowlist is enabled. Keyed by market, + /// then by account. + mapping(address => mapping(address => bool)) public isAllowedSupplier; + + /// @notice Whether seizing collateral in this pool is restricted to the accounts on the liquidation allowlist. + /// Disabled by default. + /// @dev Pool-wide rather than per market, because `healAccount` seizes across every market the borrower is in and + /// so cannot attribute a seizure to a single one of them. + bool public isLiquidationAllowlistEnabled; + + /// @notice The accounts allowed to seize collateral in this pool while the liquidation allowlist is enabled + mapping(address => bool) public isAllowedLiquidator; + + /// @notice Per-market discount a liquidator receives on the collateral it seizes, scaled by 1e18. Keyed by the + /// collateral market, since that is what the discount prices. + /// @dev Zero means no market value has been set, in which case `_poolLiquidationIncentiveMantissa` applies. That + /// pool-wide value is always at least 1e18 for a listed market: `PoolRegistry.addMarket` refuses to add one to an + /// unregistered pool, and registering a pool goes through `setLiquidationIncentive`, which enforces the floor. + mapping(address => uint256) public liquidationIncentives; + + /// @notice Oracle that bounds an asset's price against a recent window, so a deviating print cannot inflate + /// borrowing capacity. Read only where the collateral factor weights the position; the liquidation-threshold + /// paths stay on `oracle`, because they route liquidations. + IDeviationBoundedOracle public deviationBoundedOracle; + + /** + * @dev This empty reserved space is put in place to allow future versions to add new + * variables without shifting down storage in the inheritance chain. + * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps + * The size is derived from the 47 slots `ComptrollerStorage` reserves: plus one for the Prime token slot, which + * is unused here and is returned to the gap rather than left as a hole, minus the six slots the allowlists, the + * per-market liquidation incentives and the deviation-bounded oracle above take. This contract therefore occupies + * the same number of slots as the one it was forked from. + * + * That is not layout compatibility. Reclaiming the Prime slot moved every variable declared after it up by one, + * so `approvedDelegates` here sits in the slot `ComptrollerStorage` gives to `prime`, and the two layouts cannot + * be swapped under a live pool in either direction. They do not have to be: a spoke pool upgrades through its own + * beacon. `tests/hardhat/Spoke/storageLayout.ts` pins the slot list, the divergence and this gap size. + */ + uint256[42] private __gap; +} diff --git a/contracts/test/HubSpoke/AdapterSpokeV1.sol b/contracts/test/HubSpoke/AdapterSpokeV1.sol new file mode 100644 index 000000000..e134c5e84 --- /dev/null +++ b/contracts/test/HubSpoke/AdapterSpokeV1.sol @@ -0,0 +1,459 @@ +// SPDX-License-Identifier: BSD-3-Clause +pragma solidity 0.8.25; + +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; + +import { IResourceAdapter } from "./interfaces/IResourceAdapter.sol"; +import { IVTokenIsolated } from "./interfaces/external/IVTokenIsolated.sol"; +import { ISpokeComptroller } from "./interfaces/external/ISpokeComptroller.sol"; + +/** + * @title AdapterSpokeV1 + * @author Venus + * @notice Stateless adapter for the liquidity side of a hub-funded spoke pool — an isolated-pools + * `VToken` governed by a `SpokeComptroller`. ONE deployment serves every YieldGroup that + * registers a spoke market; there is no per-(YieldGroup, market) instance, no proxy, no + * clone factory. + * @dev Dispatch model: mutating functions (`deposit`, `withdraw`) MUST be invoked via DELEGATECALL + * from a YieldGroup. They execute in the YieldGroup's storage context, so vTokens are credited + * to / burned from the YieldGroup, not this adapter. View functions are invoked via normal + * CALL / STATICCALL with an explicit `holder` argument. + * + * Storage-safety invariants enforced by this contract: + * 1. Zero `internal`/`private`/`public` state variables are declared. Only `immutable` values + * are used. Immutables live in bytecode, not storage slots, so they're delegatecall-safe. + * 2. No inline assembly performs `sstore`. + * 3. All external calls go to the supplied `resource` (vToken), its `underlying()`, or its + * `comptroller()`. No arbitrary user-supplied call target. + * 4. {onlyDelegateCall} reverts when a mutating function is invoked directly on the adapter, + * preventing accidents that would orphan vTokens here. + * + * **Why this is not `AdapterCoreV1`.** The two speak nearly the same selectors and disagree on + * every number that matters. Four differences, each verified against `isolated-pools`: + * + * - **NAV excludes `badDebt`.** The market's exchange rate keeps `badDebt` in its numerator, so + * it does not fall when `healAccount` writes a loss off; the loss surfaces only as redemptions + * failing for want of cash. Valuing at that rate would report unrecoverable value, and inside + * the Hub the loss would then land by exit order — early LPs out at the pre-loss share price, + * the last one absorbing everything. {totalAssets} therefore values the position off the + * components with `badDebt` excluded, which marks every LP down at the same instant. The mark + * is pro-rata (`balance / totalSupply`), so it stays correct whether or not the Hub is the + * market's only supplier. A Shortfall auction that later recovers the debt raises cash and + * lowers `badDebt` by the same amount, so the mark recovers on its own. + * - **Liquidity is cash NET of reserves**, floored to what a whole number of vTokens is worth + * and then checked against the market's own redeem arithmetic, so a certified amount is + * always one the market will actually settle. + * - **No exit fee to gross up.** Isolated pools have no `treasuryPercent`; their cut is the + * reserve factor, already netted out of the exchange rate. There is nothing unmodeled to + * reject at registration, so {validateRegistration} guards the supply allowlist instead. + * - **The supply-cap sentinels are inverted** (see {ISpokeComptroller-supplyCaps}). + * + * **The supply allowlist and caller identity.** A spoke market can restrict minting to + * allowlisted accounts, and the account it checks is the one CREDITED with the vTokens. Under + * delegatecall that is the YieldGroup. {maxDeposit} and {validateRegistration} are the two + * `IResourceAdapter` members that take no `holder`, and both are invoked by the YieldGroup as a + * plain call — so `msg.sender` IS the prospective supplier, and reading the allowlist against it + * is exact rather than a convention. This matters operationally: a YieldGroup whose grant is + * revoked reports zero room and is routed around, instead of advertising capacity that every + * deposit then reverts on. + * + * Fee-on-transfer underlyings are unsupported, matching the Hub. The market itself tolerates + * them (it mints against the measured delta) but {deposit} reports the requested amount, so the + * YieldGroup's accounting would drift. + */ +contract AdapterSpokeV1 is IResourceAdapter { + using SafeERC20 for IERC20; + + // ============================== State (immutable only) ==================== + + /// @notice This adapter's own address, captured at deployment. Used by {onlyDelegateCall} to + /// detect direct invocation. Immutables live in bytecode, not storage. + address private immutable _ADAPTER_SELF; + + // ============================== State (constants) ========================= + + /// @notice Fixed-point scale matching Compound's mantissa (`1e18`). + uint256 public constant EXP_SCALE = 1e18; + + /// @notice Deposit room reported for a market whose supply cap is the isolated-pools "uncapped" + /// sentinel (`type(uint256).max`). + /// @dev Finite on purpose. `YieldGroupBase.maxDeposit()` SUMS the room of every queued resource + /// in checked arithmetic, so reporting `type(uint256).max` would overflow that sum the + /// moment a second uncapped market joined the queue — the YieldGroup's whole capacity view + /// would revert and the Hub would read it as zero capacity with an immovable balance. At + /// `2^128 - 1` the sum cannot overflow for any realistic queue, while the value is still + /// unreachable for any real token (~3.4e20 whole units at 18 decimals). Mirrors the Flux + /// family, where Fluid reports `int128.max` for the same reason. The binding controls on an + /// uncapped market are the YieldGroup's per-resource cap and the Hub's dual cap, not this. + uint256 public constant UNCAPPED_DEPOSIT_ROOM = type(uint128).max; + + /// @notice Scaling factor between mantissa-rate (`1e18`) and BPS (`1e4`). + uint256 internal constant MANTISSA_TO_BPS = 1e14; + + /// @notice `Action.MINT` in the isolated-pools Comptroller enum. + uint8 internal constant ACTION_MINT = 0; + + /// @notice `Action.REDEEM` in the isolated-pools Comptroller enum. + uint8 internal constant ACTION_REDEEM = 1; + + /// @notice Fraction of raw supply-cap headroom withheld to absorb interest accrued between a + /// `maxDeposit` read and the mint it sizes (`1/1000` = 0.1%). + uint256 internal constant CAP_TRIM_DIVISOR = 1000; + + // ============================== Errors =================================== + + /// @notice A mutating function was invoked directly (not via delegatecall). + error NotDelegateCall(); + + /// @notice A vToken `mint` call returned a non-zero error code. + /// @param resource Market that rejected the mint. + /// @param errorCode Compound-style error code returned by the market. + error VTokenMintFailed(address resource, uint256 errorCode); + + /// @notice A vToken `redeemUnderlying` call returned a non-zero error code. + /// @param resource Market that rejected the redeem. + /// @param errorCode Compound-style error code returned by the market. + error VTokenRedeemFailed(address resource, uint256 errorCode); + + /// @notice A vToken `accrueInterest` call returned a non-zero error code. + /// @param resource Market that failed to accrue. + /// @param errorCode Compound-style error code returned by the market. + error VTokenAccrueFailed(address resource, uint256 errorCode); + + /** + * @notice A redeem succeeded but delivered less than the requested amount. Indicates a + * fee-on-transfer underlying or a market bug — an isolated-pools redeem otherwise + * delivers at least what was asked for. + * @param resource Resource that under-delivered. + * @param requested Underlying units expected. + * @param actual Underlying units actually received. + */ + error VTokenUnderfilled(address resource, uint256 requested, uint256 actual); + + /** + * @notice The deposit is too small to mint a single vToken, so the market would keep the + * underlying and issue nothing for it. + * @dev Reverting hands the leg back to the YieldGroup's deposit cascade, which routes around it; + * minting zero would strand the amount silently. Only reachable for a sub-one-vToken-unit + * remainder, which {maxDeposit} already declines to advertise. + * @param resource Market that would have minted zero. + * @param amount Underlying units offered. + * @param minimum Underlying units needed to mint one vToken. + */ + error DepositBelowOneVToken(address resource, uint256 amount, uint256 minimum); + + /** + * @notice The market's supply allowlist is enabled and the prospective supplier is not on it. + * @param resource Market whose allowlist rejected the supplier. + * @param supplier Account that would be credited with the vTokens (the YieldGroup). + */ + error SupplyNotAllowed(address resource, address supplier); + + // ============================== Modifiers ================================ + + /// @notice Reverts {NotDelegateCall} when the call is not arriving via delegatecall. + /// @dev During a delegatecall, `address(this)` is the caller's address; during a direct call it + /// equals `_ADAPTER_SELF` (the address captured at construction). + modifier onlyDelegateCall() { + if (address(this) == _ADAPTER_SELF) revert NotDelegateCall(); + _; + } + + // ============================== Constructor ============================== + + /// @notice Captures the adapter's own address into an immutable for {onlyDelegateCall}. + constructor() { + _ADAPTER_SELF = address(this); + } + + // ============================== External — mutating ======================= + + /// @inheritdoc IResourceAdapter + /// @dev `address(this)` here is the YieldGroup (delegatecall context), which already holds + /// `amount` of underlying. We approve the market and mint; delegatecall preserves the caller + /// context, so the market sees the YieldGroup as `msg.sender` and credits the vTokens there. + /// That is also the account its supply allowlist checks. + function deposit(address resource, uint256 amount) external override onlyDelegateCall returns (uint256 deposited) { + uint256 minimum = _oneVTokenUnit(IVTokenIsolated(resource).exchangeRateStored()); + if (amount < minimum) revert DepositBelowOneVToken(resource, amount, minimum); + + IERC20 assetToken = IERC20(IVTokenIsolated(resource).underlying()); + assetToken.forceApprove(resource, amount); + uint256 errCode = IVTokenIsolated(resource).mint(amount); + if (errCode != 0) revert VTokenMintFailed(resource, errCode); + return amount; + } + + /// @inheritdoc IResourceAdapter + /// @dev `address(this)` here is the YieldGroup, so `redeemUnderlying` burns its vTokens and + /// credits the underlying back to it. The market over-delivers: it burns + /// `ceil(request / exchangeRate)` tokens and pays out `truncate(exchangeRate x tokens)`, + /// which exceeds the request by up to one vToken unit. We forward exactly `amount` and leave + /// the surplus as idle on the YieldGroup, where `totalAssets` counts it and the next + /// withdrawal consumes it idle-first — the same treatment `AdapterCoreV1` gives its + /// gross-up surplus, and what keeps the YieldGroup's `received == requested` invariant true. + function withdraw(address resource, uint256 amount, address to) external override onlyDelegateCall { + IERC20 assetToken = IERC20(IVTokenIsolated(resource).underlying()); + + uint256 exchangeRate = IVTokenIsolated(resource).exchangeRateStored(); + uint256 preBal = assetToken.balanceOf(address(this)); + uint256 errCode = IVTokenIsolated(resource).redeemUnderlying(_bumpToSettleable(exchangeRate, amount)); + if (errCode != 0) revert VTokenRedeemFailed(resource, errCode); + uint256 received = assetToken.balanceOf(address(this)) - preBal; + if (received < amount) revert VTokenUnderfilled(resource, amount, received); + + assetToken.safeTransfer(to, amount); + } + + /// @inheritdoc IResourceAdapter + /// @dev Plain CALL, no `onlyDelegateCall`: `accrueInterest` mutates the market's own global + /// index, not this caller's position, so it is safe (and cheaper) to run in the adapter's + /// context. After it returns, every view below is fresh to the current block or timestamp. + function accrue(address resource) external override { + uint256 errCode = IVTokenIsolated(resource).accrueInterest(); + if (errCode != 0) revert VTokenAccrueFailed(resource, errCode); + } + + // ============================== External — view ========================== + + /// @inheritdoc IResourceAdapter + function asset(address resource) external view override returns (address) { + return IVTokenIsolated(resource).underlying(); + } + + /// @inheritdoc IResourceAdapter + /// @dev The position's RECOVERABLE value: its pro-rata share of `cash + totalBorrows - + /// totalReserves`, i.e. the market's exchange rate with `badDebt` excluded from the + /// numerator. See the contract NatSpec for why the market's own rate is unusable here. + /// Deliberately conservative in one direction: because a redeem still burns tokens at the + /// market's un-haircut rate, an exit costs fewer tokens than this mark implies, so the mark + /// can never overstate what the position delivers. + function totalAssets(address resource, address holder) external view override returns (uint256) { + uint256 vBal = IVTokenIsolated(resource).balanceOf(holder); + if (vBal == 0) return 0; + return _recoverableValue(resource, vBal); + } + + /// @inheritdoc IResourceAdapter + /// @dev Honors, in order: the market's supply allowlist (against `msg.sender`, the prospective + /// supplier — see the contract NatSpec), its MINT pause, and its supply cap. The headroom + /// math mirrors `preMintHook`'s `nextTotalSupply = totalSupply x exchangeRate + mintAmount` + /// check and so uses the market's own (badDebt-inclusive) exchange rate, NOT the valuation + /// basis {totalAssets} uses. Room below one vToken unit is reported as zero, because a + /// deposit of it would mint nothing. + function maxDeposit(address resource) external view override returns (uint256) { + ISpokeComptroller comptrollerContract = ISpokeComptroller(IVTokenIsolated(resource).comptroller()); + if ( + comptrollerContract.isSupplyAllowlistEnabled(resource) && + !comptrollerContract.isAllowedSupplier(resource, msg.sender) + ) { + return 0; + } + if (comptrollerContract.actionPaused(resource, ACTION_MINT)) return 0; + + uint256 supplyCap = comptrollerContract.supplyCaps(resource); + // A cap of zero is a real cap here, not an "unset" sentinel: `preMintHook` rejects every + // non-zero mint against it. The uncapped sentinel is `type(uint256).max`. + if (supplyCap == 0) return 0; + if (supplyCap == type(uint256).max) return UNCAPPED_DEPOSIT_ROOM; + + uint256 exchangeRate = IVTokenIsolated(resource).exchangeRateStored(); + uint256 supplied = (IVTokenIsolated(resource).totalSupply() * exchangeRate) / EXP_SCALE; + if (supplied >= supplyCap) return 0; + + uint256 room = supplyCap - supplied; + // `exchangeRateStored` is pre-accrual, but the market re-checks the cap at mint AFTER + // `accrueInterest` raises the rate, so the raw headroom is slightly optimistic on an + // interest-accruing near-cap market. Withhold a small conservative margin so a normal accrual + // between this view and execution cannot push `deposit(maxDeposit())` over the cap. The + // routing cascade is the backstop for larger staleness (an over-cap leg is skipped, not + // bubbled), so this only needs to absorb a typical inter-slot accrual. + room -= room / CAP_TRIM_DIVISOR; + return room < _oneVTokenUnit(exchangeRate) ? 0 : room; + } + + /// @inheritdoc IResourceAdapter + /// @dev Two constraints: the position's recoverable value, and the market's payable cash + /// (`getCash - totalReserves` — reserves sit inside cash but are not redeemable, and + /// `_redeemFresh` gates on the difference). Subtracting reserves also makes this figure + /// invariant across the reserve sweep `accrueInterest` performs, which lowers cash and + /// reserves by the same amount. + /// + /// The cash side is floored to what a WHOLE number of vTokens is worth, because a redeem + /// rounds its burn up: asking for an amount that is not a whole-token multiple pays out the + /// next token up, and asking for exactly the payable cash would therefore overshoot it and + /// revert `RedeemTransferOutNotPossible`. Flooring is exact rather than conservative — the + /// round-up can never exceed the token count this floor is taken at — so unlike a flat + /// margin it still lets the last vToken out, which is what lets a market be drained to zero + /// and deregistered. + /// + /// The floor is necessary but not sufficient, so the bound is finally checked against the + /// market's own redeem arithmetic: whatever request {withdraw} would issue for it has to + /// produce a non-zero payout AND settle within the payable cash, or this reports zero. That + /// check is exact rather than a safety margin, so a market holding real liquidity still + /// reports all of it. + /// + /// Reads stored state, so the figure is exact only for a caller that has already settled + /// this market's interest in the same transaction — the Hub's routing paths all do, via + /// {accrue}. Against an unaccrued market it is optimistic by the reserves the next accrual + /// will book, since those come out of the payable cash. + function maxWithdraw(address resource, address holder) external view override returns (uint256) { + ISpokeComptroller comptrollerContract = ISpokeComptroller(IVTokenIsolated(resource).comptroller()); + if (comptrollerContract.actionPaused(resource, ACTION_REDEEM)) return 0; + + uint256 vBal = IVTokenIsolated(resource).balanceOf(holder); + if (vBal == 0) return 0; + uint256 ourValue = _recoverableValue(resource, vBal); + if (ourValue == 0) return 0; + + uint256 cash = IVTokenIsolated(resource).getCash(); + uint256 reserves = IVTokenIsolated(resource).totalReserves(); + if (cash <= reserves) return 0; + + uint256 exchangeRate = IVTokenIsolated(resource).exchangeRateStored(); + if (exchangeRate == 0) return 0; + uint256 payableTokens = ((cash - reserves) * EXP_SCALE) / exchangeRate; + uint256 cashBound = (payableTokens * exchangeRate) / EXP_SCALE; + + uint256 liquid = ourValue < cashBound ? ourValue : cashBound; + if (liquid == 0) return 0; + + // Certify the bound only if the redeem it implies actually settles. Re-run the market's own + // arithmetic on the request {withdraw} would issue for `liquid`, because the whole-vToken + // floor above is not sufficient on its own in two cases. A bound worth less than one vToken + // is raised by {_bumpToSettleable} to a request whose round-up burns a SECOND token, + // doubling the payout past the cash this bound was sized for. And at an exchange rate below + // `1e18` — normal for an underlying with fewer decimals — the payout can truncate to zero, + // which the market rejects outright. Mirroring beats a blanket safety margin here: a market + // holding real liquidity still reports all of it, so a position stays fully drainable. + uint256 payout = _redeemPayout(exchangeRate, _bumpToSettleable(exchangeRate, liquid)); + if (payout == 0 || payout > cash - reserves) return 0; + return liquid; + } + + /// @inheritdoc IResourceAdapter + /// @dev The `blocksPerYear` parameter is IGNORED. An isolated-pools market carries its own + /// annualiser as an immutable and can be block-based or time-based, so a YieldGroup-level + /// constant would misprice whichever kind it was not configured for; the market's own value + /// always matches the unit of its own rate. Deploy the spoke YieldGroup with + /// `blocksPerYear = 0`, as the Flux family already does. + function spotAPYBps(address resource, uint256 /* blocksPerYear */) external view override returns (uint64) { + uint256 annualised = (IVTokenIsolated(resource).supplyRatePerBlock() * + IVTokenIsolated(resource).blocksOrSecondsPerYear()) / MANTISSA_TO_BPS; + // forge-lint: disable-next-line(unsafe-typecast) + return annualised > type(uint64).max ? type(uint64).max : uint64(annualised); + } + + /// @inheritdoc IResourceAdapter + function receiptBalance(address resource, address holder) external view override returns (uint256) { + return IVTokenIsolated(resource).balanceOf(holder); + } + + /// @inheritdoc IResourceAdapter + /// @dev Rejects a market whose supply allowlist is enabled without the registering YieldGroup on + /// it: minting would revert on every deposit, so the resource would occupy a queue slot it + /// can never fill. `msg.sender` is the YieldGroup (see the contract NatSpec), which is the + /// account the market's allowlist gates. + /// + /// This doubles as the check that `resource` really belongs to a spoke pool: the allowlist + /// accessors exist only on `SpokeComptroller`, so the call itself reverts against any other + /// Comptroller. Nothing else is asserted. In particular an unset `deviationBoundedOracle` + /// is NOT grounds for rejection: it blocks borrowing, and so the market's yield, but leaves + /// the Hub's own paths intact — `preMintHook` never reads a price, and `preRedeemHook` + /// returns before the priced liquidity check for an account that is not a market member, + /// which a supply-only YieldGroup never becomes. + function validateRegistration(address resource) external view override { + ISpokeComptroller comptrollerContract = ISpokeComptroller(IVTokenIsolated(resource).comptroller()); + if ( + comptrollerContract.isSupplyAllowlistEnabled(resource) && + !comptrollerContract.isAllowedSupplier(resource, msg.sender) + ) { + revert SupplyNotAllowed(resource, msg.sender); + } + } + + // ============================== Private — view =========================== + + /** + * @notice Value `vBal` vTokens at the market's backing EXCLUDING written-off debt. + * @dev `vBal x (cash + totalBorrows - totalReserves) / totalSupply`, in one rounding step. This + * is `exchangeRateStored` with `badDebt` dropped from the numerator. + * @param resource Market holding the position. + * @param vBal vToken units held (caller has already established this is non-zero). + * @return value Recoverable underlying value of the position. + */ + function _recoverableValue(address resource, uint256 vBal) private view returns (uint256 value) { + uint256 supply = IVTokenIsolated(resource).totalSupply(); + // Unreachable while `vBal` is non-zero, which every caller has already established. Kept as + // a division guard rather than an assumption about a contract this one does not own. + if (supply == 0) return 0; + + uint256 backing = IVTokenIsolated(resource).getCash() + IVTokenIsolated(resource).totalBorrows(); + uint256 reserves = IVTokenIsolated(resource).totalReserves(); + // The market keeps `cash >= totalReserves` (both the redeem and borrow paths check cash net + // of reserves), so this only guards a pathological state rather than an expected one. + if (backing <= reserves) return 0; + + return (vBal * (backing - reserves)) / supply; + } + + /** + * @notice Underlying value of exactly one vToken, rounded up. + * @param exchangeRate Market's stored exchange rate, scaled by `1e18`. + * @return unit `ceil(exchangeRate / 1e18)`. + */ + function _oneVTokenUnit(uint256 exchangeRate) private pure returns (uint256 unit) { + return (exchangeRate + EXP_SCALE - 1) / EXP_SCALE; + } + + /** + * @notice Raise a redeem the market would settle for nothing up to the smallest one it settles. + * @dev The market burns `floor(amount x 1e18 / exchangeRate)` vTokens, rounds that up when those + * tokens are not worth exactly `amount`, then truncates the payout back to underlying — and + * reverts `"redeemAmount is zero"` if the payout truncates away. A withdraw cascade can + * legitimately hand this adapter a dust `amount` (e.g. a 1-wei remainder left after an + * upstream ERC-4626 resource rounds its own redeem down), which would otherwise revert an + * entirely valid, within-{maxWithdraw} withdrawal. Redeem the smallest settleable amount + * instead; {withdraw} still forwards only `amount` and leaves the surplus idle on the + * YieldGroup. A no-op for normal-sized redeems. + * + * The trigger is the PAYOUT truncating to zero, not the burn. Those coincide only while the + * rate is at or above `1e18`. Below it — the normal state for an underlying with fewer + * decimals than the vToken, e.g. a 6-decimal stablecoin listed at a `1e16` initial rate — a + * dust request burns plenty of tokens and still pays out nothing, so a burn-based test + * would never fire on exactly the markets that need it. + * @param exchangeRate Market's stored exchange rate, scaled by `1e18`. + * @param amount Underlying units the caller intends to redeem. + * @return bumped `amount`, or the value of the fewest vTokens worth a non-zero payout. + */ + function _bumpToSettleable(uint256 exchangeRate, uint256 amount) private pure returns (uint256 bumped) { + if (exchangeRate == 0) return amount; + if (_redeemPayout(exchangeRate, amount) != 0) return amount; + + // Fewest vTokens worth at least one unit of underlying, then the smallest request that + // redeems that many. Reduces to `ceil(exchangeRate / 1e18)` — one whole vToken — whenever the + // rate is at or above `1e18`. + uint256 minTokens = (EXP_SCALE + exchangeRate - 1) / exchangeRate; + return (minTokens * exchangeRate + EXP_SCALE - 1) / EXP_SCALE; + } + + /** + * @notice Underlying the market would pay out for a `redeemUnderlying(request)` call. + * @dev A line-for-line mirror of `_redeemFresh`: floor the request into vTokens, round the burn + * up when that many tokens are not worth exactly the request, then truncate the payout back + * to underlying. The payout is therefore `>= request` (the over-delivery {withdraw} retains + * as idle) except when it truncates to `0`, which is the market's own reject condition. + * Used by {maxWithdraw} to avoid certifying a bound the market will not settle. + * @param exchangeRate Market's stored exchange rate, scaled by `1e18`. Caller has established + * this is non-zero. + * @param request Underlying units that would be passed to `redeemUnderlying`. + * @return payout Underlying units the market would transfer out. + */ + function _redeemPayout(uint256 exchangeRate, uint256 request) private pure returns (uint256 payout) { + uint256 tokens = (request * EXP_SCALE) / exchangeRate; + uint256 probe = (tokens * exchangeRate) / EXP_SCALE; + if (probe != 0 && probe != request) ++tokens; + return (tokens * exchangeRate) / EXP_SCALE; + } +} diff --git a/contracts/test/HubSpoke/interfaces/IResourceAdapter.sol b/contracts/test/HubSpoke/interfaces/IResourceAdapter.sol new file mode 100644 index 000000000..31e7ef87d --- /dev/null +++ b/contracts/test/HubSpoke/interfaces/IResourceAdapter.sol @@ -0,0 +1,161 @@ +// SPDX-License-Identifier: BSD-3-Clause +pragma solidity 0.8.25; + +/** + * @title IResourceAdapter + * @author Venus + * @notice Boundary between a `YieldGroup` and a specific yield-protocol ABI (e.g. Compound- + * style vTokens for Core V1, a different shape for Core V2, Fluid for Flux, etc.). + * A single deployment of each implementation is shared across every YieldGroup that + * registers a resource of the matching protocol family. + * @dev Dispatch model — load-bearing for security review: + * - **Mutating functions (`deposit`, `withdraw`) MUST be invoked via DELEGATECALL** from + * the YieldGroup. They execute in the YieldGroup's storage context so that + * receipt-token credits and debits land on the YieldGroup, not the adapter. Adapter + * implementations enforce this with an `address(this) != _ADAPTER_SELF` guard. + * - **View functions are invoked via normal CALL / STATICCALL.** They take an explicit + * `holder` parameter (the YieldGroup) where their answer depends on whose position is + * being queried. + * - **`accrue` is invoked via normal CALL** (not delegatecall). It settles the resource's + * own global interest state and credits nothing to the holder, so it needs neither the + * YieldGroup's storage context nor an `onlyDelegateCall` guard. + * + * Adapter implementation invariants (required of every adapter; enforced in-code): + * 1. The contract declares zero storage variables; only `immutable` values are + * permitted (immutables live in bytecode, not storage slots, so they're safe across + * delegatecall). + * 2. No inline assembly performs `sstore`. + * 3. External calls go only to (a) the supplied `resource`, (b) addresses the resource + * itself reports — its asset (`underlying()` on a vToken, `asset()` on an ERC-4626 + * resource) and, where applicable, its `comptroller()` — and (c) trusted, chain-fixed + * addresses captured as immutables at construction (e.g. AdapterFlux's `LENDING_RESOLVER`, + * read-only). Never an arbitrary user-supplied address. The exact target set is + * per-adapter; see each implementation's NatSpec for its list. + * 4. Mutating functions revert when called outside a delegatecall context, preventing + * accidental direct invocation that would orphan receipt tokens on the adapter. + */ +interface IResourceAdapter { + // -------------------------------- Mutating ------------------------------- + + /** + * @notice Deposit `amount` of the resource's underlying into `resource`. + * @dev MUST be delegatecalled from the YieldGroup. Pre-conditions: + * - The YieldGroup already holds `amount` of `underlying` (transferred in by the + * caller of `YieldGroup.deposit`). + * Post-conditions: + * - Receipt tokens (e.g. vTokens) are credited to the YieldGroup. + * - The full `amount` of `underlying` leaves the YieldGroup into `resource` (under + * delegatecall the code runs in the YieldGroup's context, so the adapter contract + * itself never custodies funds). + * @param resource Underlying yield protocol address (e.g. a Venus vToken). + * @param amount Underlying units to deposit. + * @return deposited Actual amount placed; equals `amount` on success. + */ + function deposit(address resource, uint256 amount) external returns (uint256 deposited); + + /** + * @notice Redeem exactly `amount` of underlying from `resource` and deliver it to `to`. + * @dev MUST be delegatecalled from the YieldGroup. Pre-conditions: + * - The YieldGroup holds enough receipt tokens to back the redeem. + * Post-conditions: + * - `to` receives exactly `amount` of underlying. + * - Some adapters redeem slightly more than `amount` and leave the excess as idle on + * the YieldGroup (AdapterCoreV1's treasury-fee gross-up and its sub-one-vToken dust + * bump; AdapterSpokeV1 on every redeem, because an isolated-pools market rounds its + * burn up and then recomputes the payout from the rounded-up token count). Such + * surplus is counted in `totalAssets` and consumed idle-first on the next call. + * Fee-free adapters (Flux, FRV) leave no surplus. + * @param resource Underlying yield protocol address. + * @param amount Net underlying units to deliver to `to`. + * @param to Recipient of the underlying. + */ + function withdraw(address resource, uint256 amount, address to) external; + + /** + * @notice Settle `resource`'s accrued interest up to the current block, so a subsequent + * {totalAssets} read values the position at a fresh exchange rate rather than a + * one-cycle-stale one. + * @dev Invoked via normal CALL (not delegatecall): it mutates the resource's own global + * interest state, not the holder's position, so receipt tokens never move. Only + * block-lazy adapters do real work here — Core and Spoke poke their vToken's + * `accrueInterest`; Flux and FRV implement it as a no-op (their NAV is not a block-lazy + * exchange rate). + * @param resource Underlying yield protocol address to settle interest on. + */ + function accrue(address resource) external; + + // ---------------------------------- Views -------------------------------- + + /** + * @notice The ERC-20 underlying accepted by `resource`. YieldGroup uses this to validate + * that a resource's asset matches its own at registration time. + * @param resource Underlying yield protocol address. + * @return underlying Address of the asset token. + */ + function asset(address resource) external view returns (address underlying); + + /** + * @notice Underlying value held by `holder` via `resource`, using stale (non-accruing) + * exchange-rate state. Cheap; safe to call from Hub-level totalAssets aggregation. + * @param resource Underlying yield protocol address. + * @param holder Address whose position to value (the YieldGroup). + * @return value Underlying units `holder` holds via this resource. + */ + function totalAssets(address resource, address holder) external view returns (uint256 value); + + /** + * @notice Spare deposit headroom on `resource` right now (capacity remaining before the + * resource rejects mint via its own caps or pause). + * @param resource Underlying yield protocol address. + * @return capacity Underlying units a holder can still place into this resource. + */ + function maxDeposit(address resource) external view returns (uint256 capacity); + + /** + * @notice Underlying that `holder` can withdraw from `resource` right now, NET of any + * protocol fees that will be taken on redeem. Bounded by the lesser of `holder`'s + * position value and the resource's available cash. + * @param resource Underlying yield protocol address. + * @param holder Address whose position to query (the YieldGroup). + * @return liquid Underlying units deliverable to a recipient on a redeem call. + */ + function maxWithdraw(address resource, address holder) external view returns (uint256 liquid); + + /** + * @notice Spot supply-side APY for `resource` in BPS. + * @dev `blocksPerYear` is consumed only by block-rate adapters (e.g. Core annualises a + * per-block supply rate with it). Adapters whose yieldGroup is already annualised — Flux + * (Fluid APR) and FRV (time-based fixed APY) — ignore the parameter, as does Spoke, whose + * markets each carry their own annualiser and may be block- or time-based. + * @param resource Underlying yield protocol address. + * @param blocksPerYear Per-chain block cadence (chain-dependent; ~10_512_000 on BNB Chain); + * used only by block-rate adapters, ignored by the rest. + * @return apyBps Spot APY in basis points (capped at `uint64.max`). + */ + function spotAPYBps(address resource, uint256 blocksPerYear) external view returns (uint64 apyBps); + + /** + * @notice Raw receipt-token balance `holder` owns via `resource` (vToken / fToken / FRV + * shares), in receipt-token units — NOT underlying value. + * @dev Used by `YieldGroup.removeResource` as a share-based emptiness gate. A value-based + * check (`totalAssets`) rounds a small-but-non-zero receipt balance down to 0, which + * would let removal orphan those receipt tokens; reading the raw balance closes that gap. + * @param resource Underlying yield protocol address. + * @param holder Address whose receipt balance to read (the YieldGroup). + * @return shares Receipt-token units `holder` holds in this resource. + */ + function receiptBalance(address resource, address holder) external view returns (uint256 shares); + + /** + * @notice Revert if `resource` fails a protocol-specific precondition for registration. + * @dev Called by `YieldGroup.addResource`. AdapterCoreV1 rejects a vToken whose Comptroller + * charges a non-zero `treasuryPercent` — that exit fee leaves the pool on every redeem + * without burning shares, so it is unmodeled in the Hub's gross NAV and must not be + * registered. AdapterSpokeV1 rejects a market whose supply allowlist is enabled without + * the registering YieldGroup on it, since every deposit would revert; that call also + * proves the market belongs to a spoke pool, because no other Comptroller exposes the + * allowlist. FRV and Flux have no per-redeem pool fee and implement this as a no-op. + * @param resource Underlying yield protocol address being registered. + */ + function validateRegistration(address resource) external view; +} diff --git a/contracts/test/HubSpoke/interfaces/external/ISpokeComptroller.sol b/contracts/test/HubSpoke/interfaces/external/ISpokeComptroller.sol new file mode 100644 index 000000000..4069cc26d --- /dev/null +++ b/contracts/test/HubSpoke/interfaces/external/ISpokeComptroller.sol @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: BSD-3-Clause +pragma solidity 0.8.25; + +import { SpokeComptrollerViewInterface } from "../../../../Spoke/SpokeComptrollerInterface.sol"; + +/** + * @title ISpokeComptroller + * @author Venus + * @notice The name `AdapterSpokeV1` imports for its view of a spoke pool. In `venus-liquidity-hub` this is a + * hand-written copy of the same four getters, because that repo does not depend on this one. Here the name is + * bound to this repo's own `SpokeComptrollerViewInterface`, so the adapter compiles against the declarations + * the pool actually ships rather than against a second copy of them that nothing keeps in step. + * @dev The hub's copy still exists on its side of the boundary and still has to match. `tests/hardhat/Fork/HubSpoke/ + * interfaces.ts` asserts that it does, selector for selector and return type for return type, against both this + * interface and the deployed `SpokeComptroller`. + */ +// solhint-disable-next-line no-empty-blocks +interface ISpokeComptroller is SpokeComptrollerViewInterface { + +} diff --git a/contracts/test/HubSpoke/interfaces/external/IVTokenIsolated.sol b/contracts/test/HubSpoke/interfaces/external/IVTokenIsolated.sol new file mode 100644 index 000000000..5a7d918c1 --- /dev/null +++ b/contracts/test/HubSpoke/interfaces/external/IVTokenIsolated.sol @@ -0,0 +1,145 @@ +// SPDX-License-Identifier: BSD-3-Clause +pragma solidity 0.8.25; + +/** + * @title IVTokenIsolated + * @author Venus + * @notice Minimal subset of the Venus isolated-pools `VToken` interface needed by the Spoke Yield + * Group adapter. Kept narrow on purpose, and kept SEPARATE from the Core `IVToken`: the two + * share most selectors but differ in semantics on every line that matters to an adapter, so + * one interface serving both would attach the wrong contract to the wrong reader. + * @dev The four semantic differences from Core's `VBep20`, each verified against + * `isolated-pools/contracts/VToken.sol`: + * + * 1. **`badDebt` is inside the exchange rate.** `_exchangeRateStored` computes + * `(totalCash + totalBorrows + badDebt - totalReserves) / totalSupply`. `healAccount` moves + * an unrecoverable shortfall out of `totalBorrows` and into `badDebt`, leaving that + * numerator unchanged — so the rate does NOT fall when a loss is recorded and no supplier is + * marked down. Valuing a position at `balance x exchangeRateStored` therefore reports value + * that can never be redeemed. `AdapterSpokeV1` values the position off the components + * instead, excluding `badDebt`; that is why `totalBorrows`, `totalReserves` and `badDebt` + * are declared here at all. + * 2. **Redeem is bounded by cash NET of reserves.** `_redeemFresh` reverts + * `RedeemTransferOutNotPossible` when `getCash() - totalReserves < redeemAmount`, not when + * `getCash() < redeemAmount`. Reserves are not payable liquidity. + * 3. **`redeemUnderlying` OVER-delivers.** It burns `ceil(redeemAmount / exchangeRate)` tokens + * and then transfers `truncate(exchangeRate x tokens)`, which is `>= redeemAmount` by up to + * one vToken unit of underlying. Callers must transfer the exact requested amount onward and + * retain the surplus; they must NOT assume an exact delivery. + * 4. **No per-redeem pool fee.** Isolated pools take their cut through `reserveFactorMantissa` + * (already netted out of the exchange rate) and route it to the ProtocolShareReserve. There + * is no `treasuryPercent` equivalent, so there is nothing to gross up on redeem. + * + * Shared with Core: `mint` pulls `mintAmount` from `msg.sender` and credits vTokens to + * `msg.sender`; both mutating calls return a Compound-style error code that isolated pools + * always sets to `NO_ERROR` (failures revert instead); `mintBehalf` / + * `redeemUnderlyingBehalf` exist but are on no path here, because the adapter is delegatecalled + * and so is already `msg.sender` from the vToken's perspective. + */ +interface IVTokenIsolated { + // -------------------------------- Mutating ------------------------------- + + /** + * @notice Supply `mintAmount` of underlying and receive vTokens. + * @dev Accrues interest first, so the supply-cap check in `preMintHook` runs against a + * POST-accrual exchange rate — one notch tighter than the rate a `maxDeposit` view reads. + * @param mintAmount Underlying units to deposit. + * @return errorCode Compound-style error code; always `0` (failures revert). + */ + function mint(uint256 mintAmount) external returns (uint256 errorCode); + + /** + * @notice Burn vTokens and receive AT LEAST `redeemAmount` of underlying. + * @dev Over-delivers by up to one vToken unit of underlying (see note 3 in the contract NatSpec). + * Reverts `"redeemAmount is zero"` when `redeemAmount x 1e18 < exchangeRateStored()`, i.e. + * when the burn would round to zero tokens. + * @param redeemAmount Underlying units to withdraw. + * @return errorCode Compound-style error code; always `0` (failures revert). + */ + function redeemUnderlying(uint256 redeemAmount) external returns (uint256 errorCode); + + /** + * @notice Settle accrued interest into the stored exchange rate up to the current block or + * timestamp. + * @dev Also sweeps accrued reserves to the ProtocolShareReserve once + * `reduceReservesBlockDelta` has elapsed. That lowers `getCash()` and `totalReserves()` by + * the same amount, so any quantity derived from `getCash() - totalReserves()` is invariant + * across the sweep. + * @return errorCode Compound-style error code; always `0` (failures revert). + */ + function accrueInterest() external returns (uint256 errorCode); + + // ---------------------------------- Views -------------------------------- + + /** + * @notice vToken balance of `account`, in vToken units (NOT underlying). + * @param account Holder to query. + * @return balance vToken units held by `account`. + */ + function balanceOf(address account) external view returns (uint256 balance); + + /// @notice Total vToken supply, in vToken units. + /// @return supply Total outstanding vTokens. + function totalSupply() external view returns (uint256 supply); + + /** + * @notice Last-settled underlying-per-vToken exchange rate, scaled by `1e18`. + * @dev INCLUDES `badDebt` in its numerator, so it is the rate the market redeems and prices caps + * at, but NOT a sound basis for valuing a position. Use it for supply-cap math and for + * sizing a redeem; use the components for NAV. + * @return rate Exchange rate scaled by `1e18`. + */ + function exchangeRateStored() external view returns (uint256 rate); + + /** + * @notice Underlying cash the market tracks internally (`internalCash`, not the token balance). + * @dev Reserves sit inside this figure but are not redeemable; subtract `totalReserves()` before + * treating it as payable liquidity. + * @return cash Underlying units tracked by the market. + */ + function getCash() external view returns (uint256 cash); + + /// @notice Outstanding borrows, in underlying units. Excludes anything already written off to + /// `badDebt`. + /// @return borrows Underlying units currently borrowed. + function totalBorrows() external view returns (uint256 borrows); + + /// @notice Accrued protocol reserves, in underlying units. Not payable to suppliers. + /// @return reserves Underlying units reserved for the protocol. + function totalReserves() external view returns (uint256 reserves); + + /** + * @notice Debt written off as unrecoverable by `healAccount`, in underlying units. + * @dev Recovered by the Shortfall auction, which transfers the winning bid into this market and + * then calls `badDebtRecovered` — raising `getCash()` and lowering this figure by the same + * amount, so a recovery lifts a `badDebt`-excluding valuation back up on its own. + * @return debt Underlying units of unrecoverable debt. + */ + function badDebt() external view returns (uint256 debt); + + /// @notice The ERC-20 underlying this market is backed by. + /// @return token Address of the underlying ERC-20. + function underlying() external view returns (address token); + + /// @notice Comptroller of the pool this market belongs to. + /// @return comptrollerAddress Address of the pool's Comptroller. + function comptroller() external view returns (address comptrollerAddress); + + /** + * @notice Current supply interest rate per slot, scaled by `1e18`. + * @dev A "slot" is a block on a block-based market and a second on a time-based one; annualise + * with {blocksOrSecondsPerYear}, never with a chain-level constant. + * @return rate Per-slot supply rate, scaled by `1e18`. + */ + function supplyRatePerBlock() external view returns (uint256 rate); + + /** + * @notice Slots per year for this market: blocks per year on a block-based market, seconds per + * year on a time-based one. + * @dev An immutable set at construction (`TimeManagerV8`), so it is the only annualiser that + * matches this market's rate unit. A YieldGroup-level `blocksPerYear` cannot, because one + * YieldGroup can hold markets of both kinds. + * @return periods Slots per year. + */ + function blocksOrSecondsPerYear() external view returns (uint256 periods); +} diff --git a/contracts/test/UpgradedSpokeComptroller.sol b/contracts/test/UpgradedSpokeComptroller.sol new file mode 100644 index 000000000..419b4e951 --- /dev/null +++ b/contracts/test/UpgradedSpokeComptroller.sol @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: BSD-3-Clause +pragma solidity 0.8.25; + +import { SpokeComptroller } from "../Spoke/SpokeComptroller.sol"; + +/** + * @title UpgradedSpokeComptroller + * @notice Test-only successor implementation for the spoke beacon. + * @dev Appends one variable and one function, which is the shape a real upgrade takes. Lets the tests prove that the + * existing storage survives a beacon upgrade and that the appended slot starts empty. Never deployed to a network. + */ +contract UpgradedSpokeComptroller is SpokeComptroller { + /// @notice Value only the upgraded implementation knows about + uint256 public addedAfterUpgrade; + + /// @custom:oz-upgrades-unsafe-allow constructor + constructor(address poolRegistry_) SpokeComptroller(poolRegistry_) {} + + /// @notice Writes the appended variable, so a test can tell a live upgrade from a no-op + function setAddedAfterUpgrade(uint256 value) external { + addedAfterUpgrade = value; + } +} diff --git a/deploy/007-deploy-pool-lens.ts b/deploy/007-deploy-pool-lens.ts index 0c204f7ef..61eded788 100644 --- a/deploy/007-deploy-pool-lens.ts +++ b/deploy/007-deploy-pool-lens.ts @@ -10,6 +10,8 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { const { isTimeBased, blocksPerYear } = getBlockOrTimestampBasedDeploymentInfo(hre.getNetworkName()); + // A redeploy overwrites deployments//PoolLens.json, and that record is the only one of the revision + // still live at the old address. Archive it as PoolLensR.json first, next to the source in Lens/legacy. await deploy("PoolLens", { from: deployer, args: [isTimeBased, blocksPerYear], diff --git a/deploy/024-deploy-spoke-pool-registry.ts b/deploy/024-deploy-spoke-pool-registry.ts new file mode 100644 index 000000000..d1fbe8b98 --- /dev/null +++ b/deploy/024-deploy-spoke-pool-registry.ts @@ -0,0 +1,125 @@ +import { ethers } from "hardhat"; +import { DeployResult } from "hardhat-deploy/dist/types"; +import { DeployFunction } from "hardhat-deploy/types"; +import { HardhatRuntimeEnvironment } from "hardhat/types"; + +import { getConfig } from "../helpers/deploymentConfig"; +import { readBackAddress, sameAddress, toAddress, verifyProxyDeployment } from "../helpers/deploymentUtils"; + +// A registry of its own, never the isolated-pools `PoolRegistry`. The registry is the directory every consumer reads to +// answer "which pools exist": `getAllPools` drives the indexer, the frontend pool list and the risk tooling, and +// `getVTokenForAsset` is what ProtocolShareReserve uses as a membership check. Registering a hub-funded spoke pool in +// the isolated-pools directory would hand all of them a pool whose supply, borrow and liquidation sides are restricted +// to known accounts, and every one of those consumers would then need a special case keyed on this pool's address. +// A separate registry gives them that separation for free, and keeps the two products independently upgradeable and +// independently permissioned: ACM roles are `keccak256(contractAddress, roleString)`, so a grant on this registry +// cannot reach the isolated pools, and a grant on theirs cannot reach this pool. +const DEPLOYMENT_NAME = "SpokePoolRegistry"; + +const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { + const { deployments, getNamedAccounts } = hre; + const { deploy } = deployments; + const { deployer } = await getNamedAccounts(); + const { preconfiguredAddresses } = await getConfig(hre.getNetworkName()); + + const accessControlManager = await toAddress(preconfiguredAddresses.AccessControlManager || "AccessControlManager"); + const ownerAddress = await toAddress(preconfiguredAddresses.NormalTimelock || "account:deployer"); + if (sameAddress(ownerAddress, deployer)) { + console.log(`WARNING: no NormalTimelock configured, ${DEPLOYMENT_NAME} will be left owned by ${deployer}`); + } + + // The reason for this is that the contracts `OptimizedTransparentUpgradeableProxy` and `DefaultProxyAdmin` that the + // hardhat-deploy plugin fetches from the artifact is not zk compatible causing the deployments to fail. So we bought + // it one level up to our repo, added them to compile using zksync compiler. It is compatible for all networks. + const defaultProxyAdmin = await hre.artifacts.readArtifact( + "hardhat-deploy/solc_0.8/openzeppelin/proxy/transparent/ProxyAdmin.sol:ProxyAdmin", + ); + + // `viaAdminContract` resolves the chain's existing `DefaultProxyAdmin` when there is one, and only deploys a fresh + // admin on a chain that has none. Reusing it is deliberate: it is the admin the isolated pools already upgrade + // through, it is owned by governance, and a second admin would be one more contract with its own ownership to track. + const registryDeployment: DeployResult = await deploy(DEPLOYMENT_NAME, { + from: deployer, + contract: "PoolRegistry", + proxy: { + owner: ownerAddress, + proxyContract: "OptimizedTransparentUpgradeableProxy", + execute: { + methodName: "initialize", + args: [accessControlManager], + }, + viaAdminContract: { + name: "DefaultProxyAdmin", + artifact: defaultProxyAdmin, + }, + upgradeIndex: 0, + }, + autoMine: true, + log: true, + skipIfAlreadyDeployed: true, + }); + + // Submitted here rather than at the end, because the checks below can stop the run and by the next run neither the + // implementation nor the proxy is newly deployed, which is what `verifyProxyDeployment` keys off. + await verifyProxyDeployment(hre, DEPLOYMENT_NAME, registryDeployment); + + const registry = await ethers.getContract(DEPLOYMENT_NAME); + + // The access control manager is the only address this registry is initialized with, and `setAccessControlManager` is + // owner-gated, so a wrong value here becomes governance's problem the moment ownership moves. Read it back first. + const wiredAccessControlManager = await readBackAddress(() => registry.accessControlManager(), accessControlManager); + if (!sameAddress(wiredAccessControlManager, accessControlManager)) { + throw new Error( + `Refusing to transfer ownership: ${DEPLOYMENT_NAME} access control manager is ${wiredAccessControlManager}, ` + + `expected ${accessControlManager}`, + ); + } + console.log(`Verified ${DEPLOYMENT_NAME} access control manager: ${wiredAccessControlManager}`); + + // A fresh registry must be empty. A non-empty one means this name resolved to a registry that is already in use, and + // pointing the spoke implementation at it would defeat the separation this deployment exists for. + const registeredPools = await registry.getAllPools(); + if (registeredPools.length !== 0) { + throw new Error( + `Refusing to transfer ownership: ${DEPLOYMENT_NAME} at ${registry.address} already holds ` + + `${registeredPools.length} pool(s), so it is not the empty registry this deployment expects`, + ); + } + + // `PoolRegistry` is `Ownable2Step`, so this only nominates. The deployer stays the live owner until the listing VIP + // calls `acceptOwnership`. + if (sameAddress(await registry.owner(), ownerAddress)) { + console.log(`${DEPLOYMENT_NAME} is already owned by ${ownerAddress}`); + } else if (sameAddress(await registry.pendingOwner(), ownerAddress)) { + console.log(`${DEPLOYMENT_NAME} already nominated ${ownerAddress}, awaiting acceptOwnership in the VIP`); + } else { + await (await registry.transferOwnership(ownerAddress)).wait(1); + const nominated = await readBackAddress(() => registry.pendingOwner(), ownerAddress); + console.log( + `${DEPLOYMENT_NAME} nominated ${nominated}; ${deployer} stays the owner until the VIP ` + `calls acceptOwnership`, + ); + } + + // Two things this registry needs that only governance can give it, both belonging in the listing VIP: + // + // 1. ACM grants, in both directions, none of which the isolated-pools registry's grants cover. Roles are + // `keccak256(contractAddress, roleString)` and the account is the caller, so: + // - on this registry, governance needs `addPool(string,address,uint256,uint256,uint256)`, + // `addMarket(AddMarketInput)`, `setPoolName(address,string)` and + // `updatePoolMetadata(address,VenusPoolMetaData)`; + // - on the comptroller, this registry needs the six setters `addPool` and `addMarket` drive as the caller: + // `setCloseFactor(uint256)`, `setLiquidationIncentive(uint256)`, `setMinLiquidatableCollateral(uint256)`, + // `setCollateralFactor(address,uint256,uint256)`, `setMarketSupplyCaps(address[],uint256[])` and + // `setMarketBorrowCaps(address[],uint256[])`. On the isolated pools those six are covered by wildcard grants + // keyed on `address(0)`, but the account named in them is the isolated-pools registry, not this one, so + // `addPool` reverts at execution without them. + // + // 2. ProtocolShareReserve support for more than one registry. It stores a single `poolRegistry` address and rejects + // any non-core pool whose vToken that one registry does not know, so pointing it here would break `reduceReserves` + // and, more seriously, every liquidation in the existing isolated pools. That change ships from its own repo and + // has to be live before this registry is wired into it. +}; + +func.tags = [DEPLOYMENT_NAME, "HubSpoke"]; + +export default func; diff --git a/deploy/025-deploy-spoke-comptroller.ts b/deploy/025-deploy-spoke-comptroller.ts new file mode 100644 index 000000000..2460cf9a2 --- /dev/null +++ b/deploy/025-deploy-spoke-comptroller.ts @@ -0,0 +1,173 @@ +import { ethers } from "hardhat"; +import { DeployResult } from "hardhat-deploy/dist/types"; +import { DeployFunction } from "hardhat-deploy/types"; +import { HardhatRuntimeEnvironment } from "hardhat/types"; + +import { getConfig } from "../helpers/deploymentConfig"; +import { readBackAddress, readBackUntil, sameAddress, toAddress, verifyDeployment } from "../helpers/deploymentUtils"; + +// Identifies the spoke pool in the artifact names below. Deliberately not read from `poolConfig`: the standard scripts +// iterate that list and would deploy this pool behind the shared `ComptrollerBeacon`, claiming these names first. +const POOL_ID = "HubSpoke"; + +// Deployed by `024-deploy-spoke-pool-registry.ts`, which explains why this pool does not share the isolated-pools one. +const POOL_REGISTRY_NAME = "SpokePoolRegistry"; + +const MAX_LOOPS_LIMIT = 100; + +const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { + const { deployments, getNamedAccounts } = hre; + const { deploy } = deployments; + const { deployer } = await getNamedAccounts(); + const { preconfiguredAddresses } = await getConfig(hre.getNetworkName()); + + const accessControlManager = await toAddress(preconfiguredAddresses.AccessControlManager || "AccessControlManager"); + const ownerAddress = await toAddress(preconfiguredAddresses.NormalTimelock || "account:deployer"); + if (sameAddress(ownerAddress, deployer)) { + console.log(`WARNING: no NormalTimelock configured, the spoke pool will be left owned by the deployer ${deployer}`); + } + + // The pool registry is baked into the implementation as an immutable, so a wrong value cannot be fixed by any setter, + // only by redeploying the implementation and re-pointing the beacon. Read it back before using it, and refuse the + // isolated-pools registry outright: this pool has to stay out of the directory every existing consumer iterates. + const poolRegistry = await ethers.getContract(POOL_REGISTRY_NAME); + const isolatedPoolRegistry = await deployments.getOrNull("PoolRegistry"); + if (isolatedPoolRegistry && sameAddress(poolRegistry.address, isolatedPoolRegistry.address)) { + throw new Error( + `${POOL_REGISTRY_NAME} resolved to the isolated-pools PoolRegistry at ${isolatedPoolRegistry.address}. The ` + + `spoke pool needs a registry of its own; run 024-deploy-spoke-pool-registry.ts first.`, + ); + } + console.log(`Spoke implementation will be constructed with ${POOL_REGISTRY_NAME} ${poolRegistry.address}`); + + // The implementation is the one deployment here that does not set `skipIfAlreadyDeployed`. That flag makes + // hardhat-deploy return the recorded address before it compares anything, so a re-run after a source change would + // hand back the old implementation and the checks below would compare stale state against itself and report success. + // Left off, hardhat-deploy compares the original creation transaction and redeploys only when the bytecode or the + // constructor argument actually changed. The beacon and the proxy keep the flag, because their constructor arguments + // carry the implementation address and comparing those would build a second beacon and orphan the pool. + const implArgs = [poolRegistry.address]; + const spokeComptrollerImpl: DeployResult = await deploy("SpokeComptrollerImpl", { + contract: "SpokeComptroller", + from: deployer, + args: implArgs, + log: true, + autoMine: true, + }); + // Submitted here rather than at the end, because the checks below can stop the run and by the next run this + // implementation is no longer newly deployed, which is what `verifyDeployment` keys off. + await verifyDeployment(hre, "SpokeComptrollerImpl", spokeComptrollerImpl, implArgs); + + // A beacon of its own, never the shared `ComptrollerBeacon`. Sharing it would put every other pool in this repo on + // the spoke implementation the moment either side is upgraded. + const beaconArgs = [spokeComptrollerImpl.address]; + const spokeComptrollerBeacon: DeployResult = await deploy("SpokeComptrollerBeacon", { + contract: "UpgradeableBeacon", + from: deployer, + args: beaconArgs, + log: true, + autoMine: true, + skipIfAlreadyDeployed: true, + }); + + const SpokeComptroller = await ethers.getContractFactory("SpokeComptroller"); + const proxyArgs = [ + spokeComptrollerBeacon.address, + SpokeComptroller.interface.encodeFunctionData("initialize", [MAX_LOOPS_LIMIT, accessControlManager]), + ]; + const comptrollerProxy: DeployResult = await deploy(`Comptroller_${POOL_ID}`, { + contract: "BeaconProxy", + from: deployer, + args: proxyArgs, + log: true, + autoMine: true, + skipIfAlreadyDeployed: true, + }); + + // Read the wiring back from the chain before handing anything over. While the deployer still owns the beacon a + // mistake here costs one `upgradeTo`; once the Timelock owns it, the same fix needs a VIP. + const beacon = await ethers.getContractAt("UpgradeableBeacon", spokeComptrollerBeacon.address); + const comptroller = await ethers.getContractAt("SpokeComptroller", comptrollerProxy.address); + + // Checked on its own, because it is the one mismatch with a recovery procedure rather than a redeployment: a fresh + // implementation from the step above leaves the beacon behind until something points it forward. + const beaconImplementation = await readBackAddress(() => beacon.implementation(), spokeComptrollerImpl.address); + if (!sameAddress(beaconImplementation, spokeComptrollerImpl.address)) { + throw new Error( + `Beacon ${spokeComptrollerBeacon.address} still points at ${beaconImplementation}, while this run produced ` + + `implementation ${spokeComptrollerImpl.address}. Point the beacon forward with upgradeTo, through a VIP if ` + + `governance already owns it, then re-run this script to verify.`, + ); + } + console.log(`Verified beacon implementation: ${beaconImplementation}`); + + const addressChecks: [string, string, string][] = [ + [ + `comptroller pool registry (${POOL_REGISTRY_NAME})`, + await readBackAddress(() => comptroller.poolRegistry(), poolRegistry.address), + poolRegistry.address, + ], + [ + "comptroller access control manager", + await readBackAddress(() => comptroller.accessControlManager(), accessControlManager), + accessControlManager, + ], + ]; + for (const [label, actual, expected] of addressChecks) { + if (!sameAddress(actual, expected)) { + throw new Error(`Refusing to transfer ownership: ${label} is ${actual}, expected ${expected}`); + } + console.log(`Verified ${label}: ${actual}`); + } + + const maxLoopsLimit = await readBackUntil( + async () => (await comptroller.maxLoopsLimit()).toString(), + value => value === MAX_LOOPS_LIMIT.toString(), + ); + if (maxLoopsLimit !== MAX_LOOPS_LIMIT.toString()) { + throw new Error( + `Refusing to transfer ownership: comptroller max loops limit is ${maxLoopsLimit}, expected ${MAX_LOOPS_LIMIT}`, + ); + } + console.log(`Verified comptroller max loops limit: ${maxLoopsLimit}`); + + // `UpgradeableBeacon` is plain `Ownable`, so this hands over within this transaction. + if (sameAddress(await beacon.owner(), ownerAddress)) { + console.log(`SpokeComptrollerBeacon is already owned by ${ownerAddress}`); + } else { + await (await beacon.transferOwnership(ownerAddress)).wait(1); + const beaconOwner = await readBackAddress(() => beacon.owner(), ownerAddress); + console.log(`SpokeComptrollerBeacon ownership transferred to ${beaconOwner}`); + } + + // The comptroller is `Ownable2Step`, so this only nominates. The deployer stays the live owner until the listing VIP + // calls `acceptOwnership`, which is why that call has to come first in the VIP, before any owner-gated setter. + if (sameAddress(await comptroller.owner(), ownerAddress)) { + console.log(`Comptroller_${POOL_ID} is already owned by ${ownerAddress}`); + } else if (sameAddress(await comptroller.pendingOwner(), ownerAddress)) { + console.log(`Comptroller_${POOL_ID} already nominated ${ownerAddress}, awaiting acceptOwnership in the VIP`); + } else { + await (await comptroller.transferOwnership(ownerAddress)).wait(1); + const nominated = await readBackAddress(() => comptroller.pendingOwner(), ownerAddress); + console.log( + `Comptroller_${POOL_ID} nominated ${nominated}; ${deployer} stays the owner until the ` + + `VIP calls acceptOwnership`, + ); + } + + await verifyDeployment(hre, "SpokeComptrollerBeacon", spokeComptrollerBeacon, beaconArgs); + await verifyDeployment(hre, `Comptroller_${POOL_ID}`, comptrollerProxy, proxyArgs); + + // Everything else this pool needs is governance-owned and belongs in the listing VIP, in this order: accept ownership + // of both the comptroller and the spoke pool registry, grant the ACM roles both of them need (listed in + // 024-deploy-spoke-pool-registry.ts, and they go in both directions), `setPriceOracle` and + // `setDeviationBoundedOracle` (the latter is dereferenced without a zero check, so borrow and redeem fail closed + // until it is set), then `SpokePoolRegistry.addPool`, which requires a nonzero oracle, and only then the markets. +}; + +func.tags = ["HubSpokeComptroller", "HubSpoke"]; +// Tag-selected runs (`--tags HubSpokeComptroller`, and the fork suite's fixture) skip everything not tagged, so the +// registry has to be named as a dependency rather than left to the file ordering a full run relies on. +func.dependencies = [POOL_REGISTRY_NAME]; + +export default func; diff --git a/deploy/026-deploy-spoke-pool-lens.ts b/deploy/026-deploy-spoke-pool-lens.ts new file mode 100644 index 000000000..16573ba55 --- /dev/null +++ b/deploy/026-deploy-spoke-pool-lens.ts @@ -0,0 +1,34 @@ +import { DeployResult } from "hardhat-deploy/dist/types"; +import { DeployFunction } from "hardhat-deploy/types"; +import { HardhatRuntimeEnvironment } from "hardhat/types"; + +import { getBlockOrTimestampBasedDeploymentInfo, verifyDeployment } from "../helpers/deploymentUtils"; + +// Reads a spoke pool the way `PoolLens` reads a pooled one, plus the state particular to a spoke pool. Holds no +// state and takes the registry to read as a call argument, so one deployment serves every spoke pool on the network. +// +// Deployed alongside `PoolLens` rather than replacing it: this one answers about spoke pools, that one about the +// pools in the shared registry. +const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { + const { deployments, getNamedAccounts } = hre; + const { deploy } = deployments; + const { deployer } = await getNamedAccounts(); + + const { isTimeBased, blocksPerYear } = getBlockOrTimestampBasedDeploymentInfo(hre.getNetworkName()); + + // A redeploy overwrites deployments//SpokePoolLens.json, and that record is the only one of the revision + // still live at the old address. Archive it as SpokePoolLensR.json first, next to the source in Lens/legacy. + const args = [isTimeBased, blocksPerYear]; + const spokePoolLens: DeployResult = await deploy("SpokePoolLens", { + from: deployer, + args, + log: true, + autoMine: true, + }); + + await verifyDeployment(hre, "SpokePoolLens", spokePoolLens, args); +}; + +func.tags = ["SpokePoolLens", "HubSpoke"]; + +export default func; diff --git a/deploy/027-deploy-spoke-vtoken-beacon.ts b/deploy/027-deploy-spoke-vtoken-beacon.ts new file mode 100644 index 000000000..a68f3f9c9 --- /dev/null +++ b/deploy/027-deploy-spoke-vtoken-beacon.ts @@ -0,0 +1,95 @@ +import { ethers } from "hardhat"; +import { DeployResult } from "hardhat-deploy/dist/types"; +import { DeployFunction } from "hardhat-deploy/types"; +import { HardhatRuntimeEnvironment } from "hardhat/types"; + +import { getConfig, getMaxBorrowRateMantissa } from "../helpers/deploymentConfig"; +import { + getBlockOrTimestampBasedDeploymentInfo, + readBackAddress, + sameAddress, + toAddress, + verifyDeployment, +} from "../helpers/deploymentUtils"; + +// A VToken beacon of its own, never the shared `VTokenBeacon`. `UpgradeableBeacon.upgradeTo` moves every proxy behind +// the beacon in one call, so a spoke market sharing that beacon could only take a VToken change that every isolated +// market on the chain takes at the same time, and the other way round. +// +// A fresh implementation rather than the one the shared beacon already points at, because on both BSC networks that one +// is an older `VToken` than this repo builds, 20,052 bytes against 20,424. Pointing this beacon at it would list the +// spoke markets on code the repo no longer has. Same constructor arguments, so the immutables match, but the code does +// not: compare the two before deploying and expect a difference, rather than reading these markets as behaving +// identically to the isolated ones on day one. +// +// The markets are not deployed here. Each is a `BeaconProxy` pointed at `SpokeVTokenBeacon`, created and registered by +// the listing VIP after `SpokePoolRegistry.addPool`. +const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { + const { deployments, getNamedAccounts } = hre; + const { deploy } = deployments; + const { deployer } = await getNamedAccounts(); + const { preconfiguredAddresses } = await getConfig(hre.getNetworkName()); + + const { isTimeBased, blocksPerYear } = getBlockOrTimestampBasedDeploymentInfo(hre.getNetworkName()); + const maxBorrowRateMantissa = getMaxBorrowRateMantissa(hre.network.name); + + const ownerAddress = await toAddress(preconfiguredAddresses.NormalTimelock || "account:deployer"); + if (sameAddress(ownerAddress, deployer)) { + console.log(`WARNING: no NormalTimelock configured, SpokeVTokenBeacon will be left owned by ${deployer}`); + } + + // As in 025-deploy-spoke-comptroller.ts, the implementation is the one deployment here that does not set + // `skipIfAlreadyDeployed`: that flag returns the recorded address before comparing anything, so a re-run after a + // source change would hand back the old implementation and the check below would compare stale state against itself. + // The beacon keeps the flag, because its constructor argument carries the implementation address and comparing that + // would build a second beacon and orphan the markets already behind the first. + const implArgs = [isTimeBased, blocksPerYear, maxBorrowRateMantissa]; + const spokeVTokenImpl: DeployResult = await deploy("SpokeVTokenImpl", { + contract: "VToken", + from: deployer, + args: implArgs, + log: true, + autoMine: true, + }); + // Submitted here rather than at the end, because the check below can stop the run and by the next run this + // implementation is no longer newly deployed, which is what `verifyDeployment` keys off. + await verifyDeployment(hre, "SpokeVTokenImpl", spokeVTokenImpl, implArgs); + + const beaconArgs = [spokeVTokenImpl.address]; + const spokeVTokenBeacon: DeployResult = await deploy("SpokeVTokenBeacon", { + contract: "UpgradeableBeacon", + from: deployer, + args: beaconArgs, + log: true, + autoMine: true, + skipIfAlreadyDeployed: true, + }); + + // Read the wiring back from the chain before handing the beacon over. While the deployer still owns it a mistake + // costs one `upgradeTo`; once the Timelock owns it, the same fix needs a VIP. + const beacon = await ethers.getContractAt("UpgradeableBeacon", spokeVTokenBeacon.address); + const beaconImplementation = await readBackAddress(() => beacon.implementation(), spokeVTokenImpl.address); + if (!sameAddress(beaconImplementation, spokeVTokenImpl.address)) { + throw new Error( + `Beacon ${spokeVTokenBeacon.address} still points at ${beaconImplementation}, while this run produced ` + + `implementation ${spokeVTokenImpl.address}. Point the beacon forward with upgradeTo, through a VIP if ` + + `governance already owns it, then re-run this script to verify.`, + ); + } + console.log(`Verified beacon implementation: ${beaconImplementation}`); + + // `UpgradeableBeacon` is plain `Ownable`, so this hands over within this transaction. + if (sameAddress(await beacon.owner(), ownerAddress)) { + console.log(`SpokeVTokenBeacon is already owned by ${ownerAddress}`); + } else { + await (await beacon.transferOwnership(ownerAddress)).wait(1); + const owner = await readBackAddress(() => beacon.owner(), ownerAddress); + console.log(`SpokeVTokenBeacon ownership transferred to ${owner}`); + } + + await verifyDeployment(hre, "SpokeVTokenBeacon", spokeVTokenBeacon, beaconArgs); +}; + +func.tags = ["HubSpokeVTokenBeacon", "HubSpoke"]; + +export default func; diff --git a/deploy/028-deploy-spoke-vtokens.ts b/deploy/028-deploy-spoke-vtokens.ts new file mode 100644 index 000000000..7c539e5ac --- /dev/null +++ b/deploy/028-deploy-spoke-vtokens.ts @@ -0,0 +1,212 @@ +import { parseUnits } from "ethers/lib/utils"; +import { ethers } from "hardhat"; +import { DeployResult } from "hardhat-deploy/dist/types"; +import { DeployFunction } from "hardhat-deploy/types"; +import { HardhatRuntimeEnvironment } from "hardhat/types"; + +import { InterestRateModels, getConfig, getTokenConfig } from "../helpers/deploymentConfig"; +import { + getBlockOrTimestampBasedDeploymentInfo, + readBackAddress, + sameAddress, + toAddress, + verifyDeployment, +} from "../helpers/deploymentUtils"; +import { getRateModelName, getRateModelParams } from "../helpers/rateModelHelpers"; +import { getSpokePoolConfig } from "../helpers/spokeDeploymentConfig"; +import { AddressOne } from "../helpers/utils"; + +// The markets of the hub-funded spoke pool: one `BeaconProxy` per asset in front of `SpokeVTokenBeacon`, the beacon +// `027-deploy-spoke-vtoken-beacon.ts` deploys. Markets are their own script rather than part of that one because a +// beacon is deployed once per chain while markets are listed one at a time, and because `009-deploy-vtokens.ts` cannot +// be reused: it iterates `deploymentConfig.ts`'s `poolConfig` and points every market it builds at the shared +// `VTokenBeacon`. Which assets this pool lists, and with what risk parameters, is in `helpers/spokeDeploymentConfig.ts`. +// +// This script only builds the markets. Registering them is `SpokePoolRegistry.addMarket`, which is ACM gated and +// belongs to the listing VIP, in the order `024-deploy-spoke-pool-registry.ts` spells out: accept ownership, grant the +// roles, set both oracles, `addPool`, then `addMarket` per market. Until that runs these markets exist but no +// comptroller knows them, which is the same state `009-deploy-vtokens.ts` leaves the isolated markets in. +const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { + const { deployments, getNamedAccounts } = hre; + const { deploy } = deployments; + const { deployer } = await getNamedAccounts(); + const networkName = hre.getNetworkName(); + + const spokePool = getSpokePoolConfig(networkName); + if (!spokePool) { + console.log(`No spoke pool configured for ${networkName}, skipping spoke market deployment`); + return; + } + + const { tokensConfig, preconfiguredAddresses } = await getConfig(networkName); + const { isTimeBased, blocksPerYear } = getBlockOrTimestampBasedDeploymentInfo(networkName); + + const accessControlManager = await toAddress(preconfiguredAddresses.AccessControlManager || "AccessControlManager"); + + // Every market is minted from this beacon, never the chain's shared `VTokenBeacon`. `upgradeTo` moves every proxy + // behind a beacon in one call, so a market on the shared one could only take a VToken change that every isolated + // market on the chain takes with it, and the other way round. + const spokeVTokenBeacon = await ethers.getContract("SpokeVTokenBeacon"); + // The deployment record carries the `BeaconProxy` ABI it was created with, so attach the implementation's ABI to the + // proxy address to read pool state through it, as `025-deploy-spoke-comptroller.ts` does. + const comptrollerProxy = await deployments.get(`Comptroller_${spokePool.id}`); + const comptroller = await ethers.getContractAt("SpokeComptroller", comptrollerProxy.address); + + // The proxy is what the markets are initialized against and what the VIP later registers, so a market built against + // some other pool's comptroller would be silently wrong. The implementation bakes the spoke registry in as an + // immutable, so reading `poolRegistry` back is what proves this is the spoke pool and not an isolated one. + const wiredPoolRegistry = await comptroller.poolRegistry(); + const spokePoolRegistry = await ethers.getContract("SpokePoolRegistry"); + if (!sameAddress(wiredPoolRegistry, spokePoolRegistry.address)) { + throw new Error( + `Comptroller_${spokePool.id} at ${comptroller.address} reads pool registry ${wiredPoolRegistry}, expected the ` + + `SpokePoolRegistry at ${spokePoolRegistry.address}. Run 024 and 025 first.`, + ); + } + console.log(`Deploying ${spokePool.vtokens.length} market(s) for Comptroller_${spokePool.id} ${comptroller.address}`); + + // Both are the chain's live contracts, matching what the fork suite's listing model asserts of these markets. The + // ProtocolShareReserve still resolves vTokens through a single pool registry, so `reduceReserves` on these markets + // stays broken until that repo's multi-registry change is live, as `024-deploy-spoke-pool-registry.ts` notes. + const protocolShareReserve = (await ethers.getContract("ProtocolShareReserve")).address; + const shortfall = preconfiguredAddresses.Shortfall ? await toAddress(preconfiguredAddresses.Shortfall) : AddressOne; + + // `009-deploy-vtokens.ts` hands the isolated markets to the timelock at initialize time and leaves the deployer as + // owner only where there is no timelock. Same rule here, so the markets need no ownership step of their own. + const vTokenOwner = + hre.network.live && preconfiguredAddresses.NormalTimelock + ? await toAddress(preconfiguredAddresses.NormalTimelock) + : deployer; + console.log(`Markets will be owned by ${vTokenOwner}`); + + for (const vTokenConfig of spokePool.vtokens) { + const { name, asset, symbol, reserveFactor } = vTokenConfig; + + const token = getTokenConfig(asset, tokensConfig); + const underlying = token.isMock + ? await ethers.getContract(`Mock${token.symbol}`) + : await ethers.getContractAt("@openzeppelin/contracts/token/ERC20/ERC20.sol:ERC20", token.tokenAddress); + + // The rate model name is a pure function of the curve, so a market whose curve matches one already deployed on this + // chain reuses that contract. That is the isolated pools' behaviour too, and it is deliberate: a rate curve is a + // risk parameter the same team maintains across pools, not an axis this pool needs to be independent on. + const rateModelParams = getRateModelParams(vTokenConfig); + const rateModelName = getRateModelName(rateModelParams, { isTimeBased, blocksPerYear }); + let rateModelArgs: unknown[]; + let rateModelContract: string; + if (rateModelParams.model === InterestRateModels.JumpRate) { + rateModelContract = "JumpRateModelV2"; + rateModelArgs = [ + rateModelParams.baseRatePerYear, + rateModelParams.multiplierPerYear, + rateModelParams.jumpMultiplierPerYear, + rateModelParams.kink, + accessControlManager, + isTimeBased, + blocksPerYear, + ]; + } else if (rateModelParams.model === InterestRateModels.WhitePaper) { + rateModelContract = "WhitePaperInterestRateModel"; + rateModelArgs = [rateModelParams.baseRatePerYear, rateModelParams.multiplierPerYear, isTimeBased, blocksPerYear]; + } else if (rateModelParams.model === InterestRateModels.TwoKinks) { + rateModelContract = "TwoKinksInterestRateModel"; + rateModelArgs = [ + rateModelParams.baseRatePerYear, + rateModelParams.multiplierPerYear, + rateModelParams.kink, + rateModelParams.multiplierPerYear2, + rateModelParams.baseRatePerYear2, + rateModelParams.kink2, + rateModelParams.jumpMultiplierPerYear, + isTimeBased, + blocksPerYear, + ]; + } else { + throw new Error(`Unreachable ${rateModelParams}`); + } + + const rateModel: DeployResult = await deploy(rateModelName, { + from: deployer, + contract: rateModelContract, + args: rateModelArgs, + log: true, + autoMine: true, + skipIfAlreadyDeployed: true, + }); + await verifyDeployment(hre, rateModelName, rateModel, rateModelArgs); + + const underlyingDecimals = Number(await underlying.decimals()); + const vTokenDecimals = 8; + const initArgs = [ + underlying.address, + comptroller.address, + rateModel.address, + // 10 ** (18 + underlyingDecimals - vTokenDecimals), the rate every live isolated market is listed at. + parseUnits("1", 18 + underlyingDecimals - vTokenDecimals), + name, + symbol, + vTokenDecimals, + vTokenOwner, + accessControlManager, + [shortfall, protocolShareReserve], + reserveFactor, + ]; + + // `VToken_` as the isolated markets use, where the symbol carries the pool id as its suffix. That suffix is + // what tells `VToken_vUSDT_HubSpoke` from `VToken_vUSDT_Stablecoins`, the same way `Comptroller_HubSpoke` is told + // from `Comptroller_Stablecoins`. The `Spoke` prefix is reserved for the pool-wide singletons. + const args = [ + spokeVTokenBeacon.address, + (await ethers.getContractFactory("VToken")).interface.encodeFunctionData("initialize", initArgs), + ]; + const market: DeployResult = await deploy(`VToken_${symbol}`, { + from: deployer, + contract: "BeaconProxy", + args, + log: true, + autoMine: true, + skipIfAlreadyDeployed: true, + }); + + // A market built against the wrong beacon or comptroller cannot be repointed, only redeployed, and by the time the + // VIP calls `addMarket` it is governance's problem. Read both back off the chain now. + const vToken = await ethers.getContractAt("VToken", market.address); + const checks: [string, string, string][] = [ + [ + `${symbol} comptroller`, + await readBackAddress(() => vToken.comptroller(), comptroller.address), + comptroller.address, + ], + [ + `${symbol} underlying`, + await readBackAddress(() => vToken.underlying(), underlying.address), + underlying.address, + ], + [ + `${symbol} interest rate model`, + await readBackAddress(() => vToken.interestRateModel(), rateModel.address), + rateModel.address, + ], + ]; + for (const [label, actual, expected] of checks) { + if (!sameAddress(actual, expected)) { + throw new Error(`${label} is ${actual}, expected ${expected}`); + } + } + console.log(`Verified ${symbol} at ${market.address}: comptroller, underlying and rate model`); + + await verifyDeployment(hre, `VToken_${symbol}`, market, args); + console.log(`-----------------------------------------`); + } + + // What the listing VIP still owes these markets, beyond `addMarket`: the spoke-only state lives on + // `SpokeComptroller`, not on the vToken, so each market needs its liquidation threshold and, where it differs from + // the pool default, its own liquidation incentive, plus the supply allowlist entries this pool restricts supply with. +}; + +func.tags = ["HubSpokeVTokens", "HubSpoke"]; +// Tag-selected runs skip everything not tagged, so the comptroller and the beacon these markets are built from have to +// be named rather than left to the file ordering a full run relies on. +func.dependencies = ["HubSpokeComptroller", "HubSpokeVTokenBeacon"]; + +export default func; diff --git a/deployments/bsctestnet.json b/deployments/bsctestnet.json index d8c47f292..22b0e46ef 100644 --- a/deployments/bsctestnet.json +++ b/deployments/bsctestnet.json @@ -5674,6 +5674,80 @@ } ] }, + "Comptroller_HubSpoke": { + "address": "0x11960c84d6c4F2a978a12372721C3A6A88C78f4c", + "abi": [ + { + "inputs": [ + { + "internalType": "address", + "name": "beacon", + "type": "address" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "stateMutability": "payable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "previousAdmin", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "newAdmin", + "type": "address" + } + ], + "name": "AdminChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "beacon", + "type": "address" + } + ], + "name": "BeaconUpgraded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "implementation", + "type": "address" + } + ], + "name": "Upgraded", + "type": "event" + }, + { + "stateMutability": "payable", + "type": "fallback" + }, + { + "stateMutability": "payable", + "type": "receive" + } + ] + }, "Comptroller_LiquidStakedBNB": { "address": "0x596B11acAACF03217287939f88d63b51d3771704", "abi": [ @@ -69583,29 +69657,121 @@ } ] }, - "SwapRouter_BTC": { - "address": "0x98C108285E389A5a12EdDa585b70b30C786AFb86", + "SpokeComptrollerBeacon": { + "address": "0x076f3fb34C8937a62aD562c33249798367542d75", "abi": [ { "inputs": [ { "internalType": "address", - "name": "WBNB_", + "name": "implementation_", "type": "address" - }, + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ { + "indexed": true, "internalType": "address", - "name": "factory_", + "name": "previousOwner", "type": "address" }, { + "indexed": true, "internalType": "address", - "name": "_comptrollerAddress", + "name": "newOwner", "type": "address" - }, + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ { + "indexed": true, "internalType": "address", - "name": "_vBNBAddress", + "name": "implementation", + "type": "address" + } + ], + "name": "Upgraded", + "type": "event" + }, + { + "inputs": [], + "name": "implementation", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newImplementation", + "type": "address" + } + ], + "name": "upgradeTo", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } + ] + }, + "SpokeComptrollerImpl": { + "address": "0x7F81dC61F3D75569A67155fb188171Aef173a52b", + "abi": [ + { + "inputs": [ + { + "internalType": "address", + "name": "poolRegistry_", "type": "address" } ], @@ -69615,189 +69781,257 @@ { "inputs": [ { - "internalType": "uint256", - "name": "amount", - "type": "uint256" + "internalType": "address", + "name": "market", + "type": "address" + }, + { + "internalType": "enum Action", + "name": "action", + "type": "uint8" + } + ], + "name": "ActionPaused", + "type": "error" + }, + { + "inputs": [], + "name": "BorrowActionNotPaused", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "market", + "type": "address" }, { "internalType": "uint256", - "name": "amountMax", + "name": "cap", "type": "uint256" } ], - "name": "ExcessiveInputAmount", + "name": "BorrowCapExceeded", "type": "error" }, { "inputs": [], - "name": "IdenticalAddresses", + "name": "BorrowCapIsNotZero", "type": "error" }, { "inputs": [ { "internalType": "uint256", - "name": "amountIn", + "name": "borrows", "type": "uint256" }, { "internalType": "uint256", - "name": "amountIntMax", + "name": "maxClearableDebt", "type": "uint256" } ], - "name": "InputAmountAboveMaximum", + "name": "CollateralCoversDebt", "type": "error" }, { "inputs": [ { "internalType": "uint256", - "name": "sweepAmount", + "name": "expectedLessThanOrEqualTo", "type": "uint256" }, { "internalType": "uint256", - "name": "balance", + "name": "actual", "type": "uint256" } ], - "name": "InsufficientBalance", - "type": "error" - }, - { - "inputs": [], - "name": "InsufficientInputAmount", - "type": "error" - }, - { - "inputs": [], - "name": "InsufficientLiquidity", + "name": "CollateralExceedsThreshold", "type": "error" }, { "inputs": [], - "name": "InsufficientOutputAmount", + "name": "CollateralFactorIsNotZero", "type": "error" }, { "inputs": [], - "name": "InvalidPath", + "name": "ComptrollerMismatch", "type": "error" }, { "inputs": [ { "internalType": "uint256", - "name": "amountOut", + "name": "borrows", "type": "uint256" }, { "internalType": "uint256", - "name": "amountOutMin", + "name": "maxClearableDebt", "type": "uint256" } ], - "name": "OutputAmountBelowMinimum", + "name": "DebtExceedsClearableAmount", "type": "error" }, { "inputs": [], - "name": "ReentrantCheck", + "name": "DelegationStatusUnchanged", "type": "error" }, { - "inputs": [ - { - "internalType": "address", - "name": "repayer", - "type": "address" - }, - { - "internalType": "address", - "name": "vToken", - "type": "address" - }, - { - "internalType": "uint256", - "name": "errorCode", - "type": "uint256" - } - ], - "name": "RepayError", + "inputs": [], + "name": "EnterMarketActionNotPaused", "type": "error" }, { "inputs": [], - "name": "SafeApproveFailed", + "name": "ExitMarketActionNotPaused", "type": "error" }, { "inputs": [], - "name": "SafeTransferBNBFailed", + "name": "InsufficientLiquidity", "type": "error" }, { "inputs": [], - "name": "SafeTransferFailed", + "name": "InsufficientShortfall", "type": "error" }, { "inputs": [], - "name": "SafeTransferFromFailed", + "name": "InvalidArrayLength", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidCloseFactor", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidCollateralFactor", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidLiquidationIncentive", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidLiquidationThreshold", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidVToken", + "type": "error" + }, + { + "inputs": [], + "name": "LiquidateActionNotPaused", "type": "error" }, { "inputs": [ { "internalType": "address", - "name": "supplier", + "name": "liquidator", "type": "address" - }, + } + ], + "name": "LiquidationNotAllowed", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "market", + "type": "address" + } + ], + "name": "MarketAlreadyListed", + "type": "error" + }, + { + "inputs": [ { "internalType": "address", "name": "vToken", "type": "address" }, { - "internalType": "uint256", - "name": "errorCode", - "type": "uint256" + "internalType": "address", + "name": "user", + "type": "address" } ], - "name": "SupplyError", + "name": "MarketNotCollateral", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "market", + "type": "address" + } + ], + "name": "MarketNotListed", "type": "error" }, { "inputs": [ { "internalType": "uint256", - "name": "swapAmount", + "name": "loopsLimit", "type": "uint256" }, { "internalType": "uint256", - "name": "amountOutMin", + "name": "requiredLoops", "type": "uint256" } ], - "name": "SwapAmountLessThanAmountOutMin", + "name": "MaxLoopsLimitExceeded", "type": "error" }, { "inputs": [ { "internalType": "uint256", - "name": "deadline", + "name": "expectedGreaterThan", "type": "uint256" }, { "internalType": "uint256", - "name": "timestemp", + "name": "actual", "type": "uint256" } ], - "name": "SwapDeadlineExpire", + "name": "MinimalCollateralViolated", + "type": "error" + }, + { + "inputs": [], + "name": "MintActionNotPaused", + "type": "error" + }, + { + "inputs": [], + "name": "NonzeroBorrowBalance", + "type": "error" + }, + { + "inputs": [], + "name": "NonzeroBorrowBalanceAfterLiquidation", "type": "error" }, { @@ -69808,102 +70042,157 @@ "type": "address" } ], - "name": "VTokenNotListed", + "name": "PriceError", + "type": "error" + }, + { + "inputs": [], + "name": "RedeemActionNotPaused", + "type": "error" + }, + { + "inputs": [], + "name": "RepayActionNotPaused", + "type": "error" + }, + { + "inputs": [], + "name": "RewardsDistributorAlreadyExists", + "type": "error" + }, + { + "inputs": [], + "name": "SeizeActionNotPaused", "type": "error" }, { "inputs": [ { "internalType": "address", - "name": "underlying", + "name": "vToken", + "type": "address" + }, + { + "internalType": "address", + "name": "user", "type": "address" } ], - "name": "VTokenUnderlyingInvalid", + "name": "SnapshotError", "type": "error" }, { "inputs": [ { "internalType": "address", - "name": "expectedAdddress", + "name": "market", + "type": "address" + }, + { + "internalType": "uint256", + "name": "cap", + "type": "uint256" + } + ], + "name": "SupplyCapExceeded", + "type": "error" + }, + { + "inputs": [], + "name": "SupplyCapIsNotZero", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "market", "type": "address" }, { "internalType": "address", - "name": "passedAddress", + "name": "supplier", "type": "address" } ], - "name": "WrongAddress", + "name": "SupplyNotAllowed", "type": "error" }, { "inputs": [], - "name": "ZeroAddress", + "name": "TooMuchRepay", + "type": "error" + }, + { + "inputs": [], + "name": "TransferActionNotPaused", "type": "error" }, { - "anonymous": false, "inputs": [ { - "indexed": true, "internalType": "address", - "name": "previousOwner", + "name": "sender", "type": "address" }, { - "indexed": true, "internalType": "address", - "name": "newOwner", + "name": "calledContract", "type": "address" + }, + { + "internalType": "string", + "name": "methodSignature", + "type": "string" } ], - "name": "OwnershipTransferStarted", - "type": "event" + "name": "Unauthorized", + "type": "error" }, { - "anonymous": false, "inputs": [ { - "indexed": true, "internalType": "address", - "name": "previousOwner", + "name": "expectedSender", "type": "address" }, { - "indexed": true, "internalType": "address", - "name": "newOwner", + "name": "actualSender", "type": "address" } ], - "name": "OwnershipTransferred", - "type": "event" + "name": "UnexpectedSender", + "type": "error" + }, + { + "inputs": [], + "name": "ZeroAddressNotAllowed", + "type": "error" }, { "anonymous": false, "inputs": [ { - "indexed": true, - "internalType": "address", - "name": "swapper", + "indexed": false, + "internalType": "contract VToken", + "name": "vToken", "type": "address" }, { - "indexed": true, - "internalType": "address[]", - "name": "path", - "type": "address[]" + "indexed": false, + "internalType": "enum Action", + "name": "action", + "type": "uint8" }, { - "indexed": true, - "internalType": "uint256[]", - "name": "amounts", - "type": "uint256[]" + "indexed": false, + "internalType": "bool", + "name": "pauseState", + "type": "bool" } ], - "name": "SwapBnbForTokens", + "name": "ActionPausedMarket", "type": "event" }, { @@ -69912,17 +70201,17 @@ { "indexed": true, "internalType": "address", - "name": "swapper", + "name": "liquidator", "type": "address" }, { - "indexed": true, - "internalType": "address[]", - "name": "path", - "type": "address[]" + "indexed": false, + "internalType": "bool", + "name": "allowed", + "type": "bool" } ], - "name": "SwapBnbForTokensAtSupportingFee", + "name": "AllowedLiquidatorUpdated", "type": "event" }, { @@ -69931,23 +70220,23 @@ { "indexed": true, "internalType": "address", - "name": "swapper", + "name": "vToken", "type": "address" }, { "indexed": true, - "internalType": "address[]", - "name": "path", - "type": "address[]" + "internalType": "address", + "name": "supplier", + "type": "address" }, { - "indexed": true, - "internalType": "uint256[]", - "name": "amounts", - "type": "uint256[]" + "indexed": false, + "internalType": "bool", + "name": "allowed", + "type": "bool" } ], - "name": "SwapTokensForBnb", + "name": "AllowedSupplierUpdated", "type": "event" }, { @@ -69956,18 +70245,7673 @@ { "indexed": true, "internalType": "address", - "name": "swapper", + "name": "approver", "type": "address" }, { "indexed": true, - "internalType": "address[]", - "name": "path", - "type": "address[]" - } - ], - "name": "SwapTokensForBnbAtSupportingFee", - "type": "event" + "internalType": "address", + "name": "delegate", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ], + "name": "DelegateUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint8", + "name": "version", + "type": "uint8" + } + ], + "name": "Initialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "vToken", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "enable", + "type": "bool" + } + ], + "name": "IsForcedLiquidationEnabledUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bool", + "name": "enabled", + "type": "bool" + } + ], + "name": "LiquidationAllowlistEnabledUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "contract VToken", + "name": "vToken", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "MarketEntered", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "contract VToken", + "name": "vToken", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "MarketExited", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "contract VToken", + "name": "vToken", + "type": "address" + } + ], + "name": "MarketSupported", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "vToken", + "type": "address" + } + ], + "name": "MarketUnlisted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "oldMaxLoopsLimit", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newmaxLoopsLimit", + "type": "uint256" + } + ], + "name": "MaxLoopsLimitUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "oldAccessControlManager", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "newAccessControlManager", + "type": "address" + } + ], + "name": "NewAccessControlManager", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "contract VToken", + "name": "vToken", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newBorrowCap", + "type": "uint256" + } + ], + "name": "NewBorrowCap", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "oldCloseFactorMantissa", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newCloseFactorMantissa", + "type": "uint256" + } + ], + "name": "NewCloseFactor", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "contract VToken", + "name": "vToken", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "oldCollateralFactorMantissa", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newCollateralFactorMantissa", + "type": "uint256" + } + ], + "name": "NewCollateralFactor", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "contract IDeviationBoundedOracle", + "name": "oldBoundedOracle", + "type": "address" + }, + { + "indexed": false, + "internalType": "contract IDeviationBoundedOracle", + "name": "newBoundedOracle", + "type": "address" + } + ], + "name": "NewDeviationBoundedOracle", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "oldLiquidationIncentiveMantissa", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newLiquidationIncentiveMantissa", + "type": "uint256" + } + ], + "name": "NewLiquidationIncentive", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "contract VToken", + "name": "vToken", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "oldLiquidationThresholdMantissa", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newLiquidationThresholdMantissa", + "type": "uint256" + } + ], + "name": "NewLiquidationThreshold", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "vToken", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "oldLiquidationIncentiveMantissa", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newLiquidationIncentiveMantissa", + "type": "uint256" + } + ], + "name": "NewMarketLiquidationIncentive", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "oldMinLiquidatableCollateral", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newMinLiquidatableCollateral", + "type": "uint256" + } + ], + "name": "NewMinLiquidatableCollateral", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "contract ResilientOracleInterface", + "name": "oldPriceOracle", + "type": "address" + }, + { + "indexed": false, + "internalType": "contract ResilientOracleInterface", + "name": "newPriceOracle", + "type": "address" + } + ], + "name": "NewPriceOracle", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "rewardsDistributor", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "rewardToken", + "type": "address" + } + ], + "name": "NewRewardsDistributor", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "contract VToken", + "name": "vToken", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newSupplyCap", + "type": "uint256" + } + ], + "name": "NewSupplyCap", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferStarted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "vToken", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "enabled", + "type": "bool" + } + ], + "name": "SupplyAllowlistEnabledUpdated", + "type": "event" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "accessControlManager", + "outputs": [ + { + "internalType": "contract IAccessControlManagerV8", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "accountAssets", + "outputs": [ + { + "internalType": "contract VToken", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "market", + "type": "address" + }, + { + "internalType": "enum Action", + "name": "action", + "type": "uint8" + } + ], + "name": "actionPaused", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract RewardsDistributor", + "name": "_rewardsDistributor", + "type": "address" + } + ], + "name": "addRewardsDistributor", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "allMarkets", + "outputs": [ + { + "internalType": "contract VToken", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "approvedDelegates", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "borrowCaps", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "borrowVerify", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "contract VToken", + "name": "vToken", + "type": "address" + } + ], + "name": "checkMembership", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "closeFactorMantissa", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "deviationBoundedOracle", + "outputs": [ + { + "internalType": "contract IDeviationBoundedOracle", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "vToken", + "type": "address" + } + ], + "name": "effectiveLiquidationIncentive", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "vToken", + "type": "address" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "enterMarketBehalf", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "vTokens", + "type": "address[]" + } + ], + "name": "enterMarkets", + "outputs": [ + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "vTokenAddress", + "type": "address" + } + ], + "name": "exitMarket", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "getAccountLiquidity", + "outputs": [ + { + "internalType": "uint256", + "name": "error", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "liquidity", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "shortfall", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getAllMarkets", + "outputs": [ + { + "internalType": "contract VToken[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "getAssetsIn", + "outputs": [ + { + "internalType": "contract VToken[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "getBorrowingPower", + "outputs": [ + { + "internalType": "uint256", + "name": "error", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "liquidity", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "shortfall", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "address", + "name": "vTokenModify", + "type": "address" + }, + { + "internalType": "uint256", + "name": "redeemTokens", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "borrowAmount", + "type": "uint256" + } + ], + "name": "getHypotheticalAccountLiquidity", + "outputs": [ + { + "internalType": "uint256", + "name": "error", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "liquidity", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "shortfall", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getRewardDistributors", + "outputs": [ + { + "internalType": "contract RewardsDistributor[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "vToken", + "type": "address" + } + ], + "name": "getRewardsByMarket", + "outputs": [ + { + "components": [ + { + "internalType": "address", + "name": "rewardToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "supplySpeed", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "borrowSpeed", + "type": "uint256" + } + ], + "internalType": "struct SpokeComptrollerStorage.RewardSpeeds[]", + "name": "rewardSpeeds", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "user", + "type": "address" + } + ], + "name": "healAccount", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "loopLimit", + "type": "uint256" + }, + { + "internalType": "address", + "name": "accessControlManager", + "type": "address" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "isAllowedLiquidator", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "isAllowedSupplier", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "isComptroller", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "isForcedLiquidationEnabled", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "isLiquidationAllowlistEnabled", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract VToken", + "name": "vToken", + "type": "address" + } + ], + "name": "isMarketListed", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "isSupplyAllowlistEnabled", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "borrower", + "type": "address" + }, + { + "components": [ + { + "internalType": "contract VToken", + "name": "vTokenCollateral", + "type": "address" + }, + { + "internalType": "contract VToken", + "name": "vTokenBorrowed", + "type": "address" + }, + { + "internalType": "uint256", + "name": "repayAmount", + "type": "uint256" + } + ], + "internalType": "struct SpokeComptrollerStorage.LiquidationOrder[]", + "name": "orders", + "type": "tuple[]" + } + ], + "name": "liquidateAccount", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "liquidateBorrowVerify", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "vTokenBorrowed", + "type": "address" + }, + { + "internalType": "address", + "name": "vTokenCollateral", + "type": "address" + }, + { + "internalType": "uint256", + "name": "actualRepayAmount", + "type": "uint256" + } + ], + "name": "liquidateCalculateSeizeTokens", + "outputs": [ + { + "internalType": "uint256", + "name": "error", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "tokensToSeize", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "liquidationIncentiveMantissa", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "liquidationIncentives", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "markets", + "outputs": [ + { + "internalType": "bool", + "name": "isListed", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "collateralFactorMantissa", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "liquidationThresholdMantissa", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "maxLoopsLimit", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "minLiquidatableCollateral", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "mintVerify", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "oracle", + "outputs": [ + { + "internalType": "contract ResilientOracleInterface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pendingOwner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "poolRegistry", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "vToken", + "type": "address" + }, + { + "internalType": "address", + "name": "borrower", + "type": "address" + }, + { + "internalType": "uint256", + "name": "borrowAmount", + "type": "uint256" + } + ], + "name": "preBorrowHook", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "vTokenBorrowed", + "type": "address" + }, + { + "internalType": "address", + "name": "vTokenCollateral", + "type": "address" + }, + { + "internalType": "address", + "name": "borrower", + "type": "address" + }, + { + "internalType": "uint256", + "name": "repayAmount", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "skipLiquidityCheck", + "type": "bool" + } + ], + "name": "preLiquidateHook", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "vToken", + "type": "address" + }, + { + "internalType": "address", + "name": "minter", + "type": "address" + }, + { + "internalType": "uint256", + "name": "mintAmount", + "type": "uint256" + } + ], + "name": "preMintHook", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "vToken", + "type": "address" + }, + { + "internalType": "address", + "name": "redeemer", + "type": "address" + }, + { + "internalType": "uint256", + "name": "redeemTokens", + "type": "uint256" + } + ], + "name": "preRedeemHook", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "vToken", + "type": "address" + }, + { + "internalType": "address", + "name": "borrower", + "type": "address" + } + ], + "name": "preRepayHook", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "vTokenCollateral", + "type": "address" + }, + { + "internalType": "address", + "name": "seizerContract", + "type": "address" + }, + { + "internalType": "address", + "name": "liquidator", + "type": "address" + }, + { + "internalType": "address", + "name": "borrower", + "type": "address" + } + ], + "name": "preSeizeHook", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "vToken", + "type": "address" + }, + { + "internalType": "address", + "name": "src", + "type": "address" + }, + { + "internalType": "address", + "name": "dst", + "type": "address" + }, + { + "internalType": "uint256", + "name": "transferTokens", + "type": "uint256" + } + ], + "name": "preTransferHook", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "redeemVerify", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "repayBorrowVerify", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "seizeVerify", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "accessControlManager_", + "type": "address" + } + ], + "name": "setAccessControlManager", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract VToken[]", + "name": "marketsList", + "type": "address[]" + }, + { + "internalType": "enum Action[]", + "name": "actionsList", + "type": "uint8[]" + }, + { + "internalType": "bool", + "name": "paused", + "type": "bool" + } + ], + "name": "setActionsPaused", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "liquidator", + "type": "address" + }, + { + "internalType": "bool", + "name": "allowed", + "type": "bool" + } + ], + "name": "setAllowedLiquidator", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "vToken", + "type": "address" + }, + { + "internalType": "address", + "name": "supplier", + "type": "address" + }, + { + "internalType": "bool", + "name": "allowed", + "type": "bool" + } + ], + "name": "setAllowedSupplier", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "newCloseFactorMantissa", + "type": "uint256" + } + ], + "name": "setCloseFactor", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract VToken", + "name": "vToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "newCollateralFactorMantissa", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "newLiquidationThresholdMantissa", + "type": "uint256" + } + ], + "name": "setCollateralFactor", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IDeviationBoundedOracle", + "name": "newBoundedOracle", + "type": "address" + } + ], + "name": "setDeviationBoundedOracle", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "vTokenBorrowed", + "type": "address" + }, + { + "internalType": "bool", + "name": "enable", + "type": "bool" + } + ], + "name": "setForcedLiquidation", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "enabled", + "type": "bool" + } + ], + "name": "setLiquidationAllowlistEnabled", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "newLiquidationIncentiveMantissa", + "type": "uint256" + } + ], + "name": "setLiquidationIncentive", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract VToken[]", + "name": "vTokens", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "newBorrowCaps", + "type": "uint256[]" + } + ], + "name": "setMarketBorrowCaps", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "vToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "newLiquidationIncentiveMantissa", + "type": "uint256" + } + ], + "name": "setMarketLiquidationIncentive", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract VToken[]", + "name": "vTokens", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "newSupplyCaps", + "type": "uint256[]" + } + ], + "name": "setMarketSupplyCaps", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "limit", + "type": "uint256" + } + ], + "name": "setMaxLoopsLimit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "newMinLiquidatableCollateral", + "type": "uint256" + } + ], + "name": "setMinLiquidatableCollateral", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract ResilientOracleInterface", + "name": "newOracle", + "type": "address" + } + ], + "name": "setPriceOracle", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "vToken", + "type": "address" + }, + { + "internalType": "bool", + "name": "enabled", + "type": "bool" + } + ], + "name": "setSupplyAllowlistEnabled", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "supplyCaps", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract VToken", + "name": "vToken", + "type": "address" + } + ], + "name": "supportMarket", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "transferVerify", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "market", + "type": "address" + } + ], + "name": "unlistMarket", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "delegate", + "type": "address" + }, + { + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ], + "name": "updateDelegate", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "updatePrices", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } + ] + }, + "SpokePoolLens": { + "address": "0xfbCBFF4ca8b2fFe3231b0c0BBEa25C79F4B0597c", + "abi": [ + { + "inputs": [ + { + "internalType": "bool", + "name": "timeBased_", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "blocksPerYear_", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "InvalidBlocksPerYear", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidTimeBasedConfiguration", + "type": "error" + }, + { + "inputs": [], + "name": "blocksOrSecondsPerYear", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "poolRegistryAddress", + "type": "address" + } + ], + "name": "getAllSpokePools", + "outputs": [ + { + "components": [ + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "internalType": "address", + "name": "creator", + "type": "address" + }, + { + "internalType": "address", + "name": "comptroller", + "type": "address" + }, + { + "internalType": "uint256", + "name": "blockPosted", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "timestampPosted", + "type": "uint256" + }, + { + "internalType": "string", + "name": "category", + "type": "string" + }, + { + "internalType": "string", + "name": "logoURL", + "type": "string" + }, + { + "internalType": "string", + "name": "description", + "type": "string" + }, + { + "internalType": "address", + "name": "priceOracle", + "type": "address" + }, + { + "internalType": "uint256", + "name": "closeFactor", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "poolLiquidationIncentiveMantissa", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minLiquidatableCollateral", + "type": "uint256" + }, + { + "internalType": "address", + "name": "deviationBoundedOracle", + "type": "address" + }, + { + "internalType": "bool", + "name": "liquidationAllowlistEnabled", + "type": "bool" + }, + { + "components": [ + { + "internalType": "address", + "name": "vToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "exchangeRateCurrent", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "supplyRatePerBlockOrTimestamp", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "borrowRatePerBlockOrTimestamp", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "reserveFactorMantissa", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "supplyCaps", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "borrowCaps", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "totalBorrows", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "totalReserves", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "totalSupply", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "totalCash", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "isListed", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "collateralFactorMantissa", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "liquidationThresholdMantissa", + "type": "uint256" + }, + { + "internalType": "address", + "name": "underlyingAssetAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "vTokenDecimals", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "underlyingDecimals", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "pausedActions", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "effectiveLiquidationIncentiveMantissa", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "ownLiquidationIncentiveMantissa", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "supplyAllowlistEnabled", + "type": "bool" + }, + { + "internalType": "bool", + "name": "forcedLiquidationEnabled", + "type": "bool" + } + ], + "internalType": "struct SpokePoolLens.SpokeVTokenMetadata[]", + "name": "vTokens", + "type": "tuple[]" + } + ], + "internalType": "struct SpokePoolLens.SpokePoolData[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getBlockNumberOrTimestamp", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "address", + "name": "comptrollerAddress", + "type": "address" + } + ], + "name": "getPendingRewards", + "outputs": [ + { + "components": [ + { + "internalType": "address", + "name": "distributorAddress", + "type": "address" + }, + { + "internalType": "address", + "name": "rewardTokenAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "totalRewards", + "type": "uint256" + }, + { + "components": [ + { + "internalType": "address", + "name": "vTokenAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "internalType": "struct SpokePoolLens.PendingReward[]", + "name": "pendingRewards", + "type": "tuple[]" + } + ], + "internalType": "struct SpokePoolLens.RewardSummary[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "comptrollerAddress", + "type": "address" + } + ], + "name": "getPoolBadDebt", + "outputs": [ + { + "components": [ + { + "internalType": "address", + "name": "comptroller", + "type": "address" + }, + { + "internalType": "uint256", + "name": "totalBadDebtUsd", + "type": "uint256" + }, + { + "components": [ + { + "internalType": "address", + "name": "vTokenAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "badDebtUsd", + "type": "uint256" + } + ], + "internalType": "struct SpokePoolLens.BadDebt[]", + "name": "badDebts", + "type": "tuple[]" + } + ], + "internalType": "struct SpokePoolLens.BadDebtSummary", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "poolRegistryAddress", + "type": "address" + }, + { + "internalType": "address", + "name": "asset", + "type": "address" + } + ], + "name": "getPoolsSupportedByAsset", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "poolRegistryAddress", + "type": "address" + }, + { + "internalType": "address", + "name": "comptroller", + "type": "address" + } + ], + "name": "getSpokePoolByComptroller", + "outputs": [ + { + "components": [ + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "internalType": "address", + "name": "creator", + "type": "address" + }, + { + "internalType": "address", + "name": "comptroller", + "type": "address" + }, + { + "internalType": "uint256", + "name": "blockPosted", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "timestampPosted", + "type": "uint256" + }, + { + "internalType": "string", + "name": "category", + "type": "string" + }, + { + "internalType": "string", + "name": "logoURL", + "type": "string" + }, + { + "internalType": "string", + "name": "description", + "type": "string" + }, + { + "internalType": "address", + "name": "priceOracle", + "type": "address" + }, + { + "internalType": "uint256", + "name": "closeFactor", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "poolLiquidationIncentiveMantissa", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minLiquidatableCollateral", + "type": "uint256" + }, + { + "internalType": "address", + "name": "deviationBoundedOracle", + "type": "address" + }, + { + "internalType": "bool", + "name": "liquidationAllowlistEnabled", + "type": "bool" + }, + { + "components": [ + { + "internalType": "address", + "name": "vToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "exchangeRateCurrent", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "supplyRatePerBlockOrTimestamp", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "borrowRatePerBlockOrTimestamp", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "reserveFactorMantissa", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "supplyCaps", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "borrowCaps", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "totalBorrows", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "totalReserves", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "totalSupply", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "totalCash", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "isListed", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "collateralFactorMantissa", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "liquidationThresholdMantissa", + "type": "uint256" + }, + { + "internalType": "address", + "name": "underlyingAssetAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "vTokenDecimals", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "underlyingDecimals", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "pausedActions", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "effectiveLiquidationIncentiveMantissa", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "ownLiquidationIncentiveMantissa", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "supplyAllowlistEnabled", + "type": "bool" + }, + { + "internalType": "bool", + "name": "forcedLiquidationEnabled", + "type": "bool" + } + ], + "internalType": "struct SpokePoolLens.SpokeVTokenMetadata[]", + "name": "vTokens", + "type": "tuple[]" + } + ], + "internalType": "struct SpokePoolLens.SpokePoolData", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "poolRegistryAddress", + "type": "address" + }, + { + "components": [ + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "internalType": "address", + "name": "creator", + "type": "address" + }, + { + "internalType": "address", + "name": "comptroller", + "type": "address" + }, + { + "internalType": "uint256", + "name": "blockPosted", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "timestampPosted", + "type": "uint256" + } + ], + "internalType": "struct PoolRegistryInterface.VenusPool", + "name": "venusPool", + "type": "tuple" + } + ], + "name": "getSpokePoolData", + "outputs": [ + { + "components": [ + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "internalType": "address", + "name": "creator", + "type": "address" + }, + { + "internalType": "address", + "name": "comptroller", + "type": "address" + }, + { + "internalType": "uint256", + "name": "blockPosted", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "timestampPosted", + "type": "uint256" + }, + { + "internalType": "string", + "name": "category", + "type": "string" + }, + { + "internalType": "string", + "name": "logoURL", + "type": "string" + }, + { + "internalType": "string", + "name": "description", + "type": "string" + }, + { + "internalType": "address", + "name": "priceOracle", + "type": "address" + }, + { + "internalType": "uint256", + "name": "closeFactor", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "poolLiquidationIncentiveMantissa", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minLiquidatableCollateral", + "type": "uint256" + }, + { + "internalType": "address", + "name": "deviationBoundedOracle", + "type": "address" + }, + { + "internalType": "bool", + "name": "liquidationAllowlistEnabled", + "type": "bool" + }, + { + "components": [ + { + "internalType": "address", + "name": "vToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "exchangeRateCurrent", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "supplyRatePerBlockOrTimestamp", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "borrowRatePerBlockOrTimestamp", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "reserveFactorMantissa", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "supplyCaps", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "borrowCaps", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "totalBorrows", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "totalReserves", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "totalSupply", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "totalCash", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "isListed", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "collateralFactorMantissa", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "liquidationThresholdMantissa", + "type": "uint256" + }, + { + "internalType": "address", + "name": "underlyingAssetAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "vTokenDecimals", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "underlyingDecimals", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "pausedActions", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "effectiveLiquidationIncentiveMantissa", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "ownLiquidationIncentiveMantissa", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "supplyAllowlistEnabled", + "type": "bool" + }, + { + "internalType": "bool", + "name": "forcedLiquidationEnabled", + "type": "bool" + } + ], + "internalType": "struct SpokePoolLens.SpokeVTokenMetadata[]", + "name": "vTokens", + "type": "tuple[]" + } + ], + "internalType": "struct SpokePoolLens.SpokePoolData", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "poolRegistryAddress", + "type": "address" + }, + { + "internalType": "address", + "name": "comptroller", + "type": "address" + }, + { + "internalType": "address", + "name": "asset", + "type": "address" + } + ], + "name": "getVTokenForAsset", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "isTimeBased", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "comptroller", + "type": "address" + }, + { + "internalType": "address", + "name": "liquidator", + "type": "address" + } + ], + "name": "spokeLiquidationPermission", + "outputs": [ + { + "internalType": "bool", + "name": "allowlistEnabled", + "type": "bool" + }, + { + "internalType": "bool", + "name": "accountAllowlisted", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "comptroller", + "type": "address" + }, + { + "internalType": "contract VToken[]", + "name": "vTokens", + "type": "address[]" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "spokeSupplyPermissions", + "outputs": [ + { + "components": [ + { + "internalType": "address", + "name": "vToken", + "type": "address" + }, + { + "internalType": "bool", + "name": "allowlistEnabled", + "type": "bool" + }, + { + "internalType": "bool", + "name": "accountAllowlisted", + "type": "bool" + } + ], + "internalType": "struct SpokePoolLens.SpokeSupplyPermission[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract VToken", + "name": "vToken", + "type": "address" + } + ], + "name": "spokeVTokenMetadata", + "outputs": [ + { + "components": [ + { + "internalType": "address", + "name": "vToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "exchangeRateCurrent", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "supplyRatePerBlockOrTimestamp", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "borrowRatePerBlockOrTimestamp", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "reserveFactorMantissa", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "supplyCaps", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "borrowCaps", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "totalBorrows", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "totalReserves", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "totalSupply", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "totalCash", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "isListed", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "collateralFactorMantissa", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "liquidationThresholdMantissa", + "type": "uint256" + }, + { + "internalType": "address", + "name": "underlyingAssetAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "vTokenDecimals", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "underlyingDecimals", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "pausedActions", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "effectiveLiquidationIncentiveMantissa", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "ownLiquidationIncentiveMantissa", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "supplyAllowlistEnabled", + "type": "bool" + }, + { + "internalType": "bool", + "name": "forcedLiquidationEnabled", + "type": "bool" + } + ], + "internalType": "struct SpokePoolLens.SpokeVTokenMetadata", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract VToken[]", + "name": "vTokens", + "type": "address[]" + } + ], + "name": "spokeVTokenMetadataAll", + "outputs": [ + { + "components": [ + { + "internalType": "address", + "name": "vToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "exchangeRateCurrent", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "supplyRatePerBlockOrTimestamp", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "borrowRatePerBlockOrTimestamp", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "reserveFactorMantissa", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "supplyCaps", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "borrowCaps", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "totalBorrows", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "totalReserves", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "totalSupply", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "totalCash", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "isListed", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "collateralFactorMantissa", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "liquidationThresholdMantissa", + "type": "uint256" + }, + { + "internalType": "address", + "name": "underlyingAssetAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "vTokenDecimals", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "underlyingDecimals", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "pausedActions", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "effectiveLiquidationIncentiveMantissa", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "ownLiquidationIncentiveMantissa", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "supplyAllowlistEnabled", + "type": "bool" + }, + { + "internalType": "bool", + "name": "forcedLiquidationEnabled", + "type": "bool" + } + ], + "internalType": "struct SpokePoolLens.SpokeVTokenMetadata[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract VToken", + "name": "vToken", + "type": "address" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "vTokenBalances", + "outputs": [ + { + "components": [ + { + "internalType": "address", + "name": "vToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "balanceOf", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "borrowBalanceCurrent", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "balanceOfUnderlying", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "tokenBalance", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "tokenAllowance", + "type": "uint256" + } + ], + "internalType": "struct SpokePoolLens.VTokenBalances", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract VToken[]", + "name": "vTokens", + "type": "address[]" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "vTokenBalancesAll", + "outputs": [ + { + "components": [ + { + "internalType": "address", + "name": "vToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "balanceOf", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "borrowBalanceCurrent", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "balanceOfUnderlying", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "tokenBalance", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "tokenAllowance", + "type": "uint256" + } + ], + "internalType": "struct SpokePoolLens.VTokenBalances[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract VToken", + "name": "vToken", + "type": "address" + } + ], + "name": "vTokenUnderlyingPrice", + "outputs": [ + { + "components": [ + { + "internalType": "address", + "name": "vToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "underlyingPrice", + "type": "uint256" + } + ], + "internalType": "struct SpokePoolLens.VTokenUnderlyingPrice", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract VToken[]", + "name": "vTokens", + "type": "address[]" + } + ], + "name": "vTokenUnderlyingPriceAll", + "outputs": [ + { + "components": [ + { + "internalType": "address", + "name": "vToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "underlyingPrice", + "type": "uint256" + } + ], + "internalType": "struct SpokePoolLens.VTokenUnderlyingPrice[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + } + ] + }, + "SpokePoolRegistry": { + "address": "0xeAA45288d804971e5a76f33559e629F5b2b1Cb8B", + "abi": [ + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "previousAdmin", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "newAdmin", + "type": "address" + } + ], + "name": "AdminChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "beacon", + "type": "address" + } + ], + "name": "BeaconUpgraded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "implementation", + "type": "address" + } + ], + "name": "Upgraded", + "type": "event" + }, + { + "stateMutability": "payable", + "type": "fallback" + }, + { + "inputs": [], + "name": "admin", + "outputs": [ + { + "internalType": "address", + "name": "admin_", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "implementation", + "outputs": [ + { + "internalType": "address", + "name": "implementation_", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newImplementation", + "type": "address" + } + ], + "name": "upgradeTo", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newImplementation", + "type": "address" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "upgradeToAndCall", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "stateMutability": "payable", + "type": "receive" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "address", + "name": "calledContract", + "type": "address" + }, + { + "internalType": "string", + "name": "methodSignature", + "type": "string" + } + ], + "name": "Unauthorized", + "type": "error" + }, + { + "inputs": [], + "name": "ZeroAddressNotAllowed", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint8", + "name": "version", + "type": "uint8" + } + ], + "name": "Initialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "comptroller", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "vTokenAddress", + "type": "address" + } + ], + "name": "MarketAdded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "oldAccessControlManager", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "newAccessControlManager", + "type": "address" + } + ], + "name": "NewAccessControlManager", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferStarted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "comptroller", + "type": "address" + }, + { + "components": [ + { + "internalType": "string", + "name": "category", + "type": "string" + }, + { + "internalType": "string", + "name": "logoURL", + "type": "string" + }, + { + "internalType": "string", + "name": "description", + "type": "string" + } + ], + "indexed": false, + "internalType": "struct PoolRegistryInterface.VenusPoolMetaData", + "name": "oldMetadata", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "string", + "name": "category", + "type": "string" + }, + { + "internalType": "string", + "name": "logoURL", + "type": "string" + }, + { + "internalType": "string", + "name": "description", + "type": "string" + } + ], + "indexed": false, + "internalType": "struct PoolRegistryInterface.VenusPoolMetaData", + "name": "newMetadata", + "type": "tuple" + } + ], + "name": "PoolMetadataUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "comptroller", + "type": "address" + }, + { + "indexed": false, + "internalType": "string", + "name": "oldName", + "type": "string" + }, + { + "indexed": false, + "internalType": "string", + "name": "newName", + "type": "string" + } + ], + "name": "PoolNameSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "comptroller", + "type": "address" + }, + { + "components": [ + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "internalType": "address", + "name": "creator", + "type": "address" + }, + { + "internalType": "address", + "name": "comptroller", + "type": "address" + }, + { + "internalType": "uint256", + "name": "blockPosted", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "timestampPosted", + "type": "uint256" + } + ], + "indexed": false, + "internalType": "struct PoolRegistryInterface.VenusPool", + "name": "pool", + "type": "tuple" + } + ], + "name": "PoolRegistered", + "type": "event" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "accessControlManager", + "outputs": [ + { + "internalType": "contract IAccessControlManagerV8", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "contract VToken", + "name": "vToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "collateralFactor", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "liquidationThreshold", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "initialSupply", + "type": "uint256" + }, + { + "internalType": "address", + "name": "vTokenReceiver", + "type": "address" + }, + { + "internalType": "uint256", + "name": "supplyCap", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "borrowCap", + "type": "uint256" + } + ], + "internalType": "struct PoolRegistry.AddMarketInput", + "name": "input", + "type": "tuple" + } + ], + "name": "addMarket", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "internalType": "contract Comptroller", + "name": "comptroller", + "type": "address" + }, + { + "internalType": "uint256", + "name": "closeFactor", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "liquidationIncentive", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minLiquidatableCollateral", + "type": "uint256" + } + ], + "name": "addPool", + "outputs": [ + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "getAllPools", + "outputs": [ + { + "components": [ + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "internalType": "address", + "name": "creator", + "type": "address" + }, + { + "internalType": "address", + "name": "comptroller", + "type": "address" + }, + { + "internalType": "uint256", + "name": "blockPosted", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "timestampPosted", + "type": "uint256" + } + ], + "internalType": "struct PoolRegistryInterface.VenusPool[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "comptroller", + "type": "address" + } + ], + "name": "getPoolByComptroller", + "outputs": [ + { + "components": [ + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "internalType": "address", + "name": "creator", + "type": "address" + }, + { + "internalType": "address", + "name": "comptroller", + "type": "address" + }, + { + "internalType": "uint256", + "name": "blockPosted", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "timestampPosted", + "type": "uint256" + } + ], + "internalType": "struct PoolRegistryInterface.VenusPool", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "asset", + "type": "address" + } + ], + "name": "getPoolsSupportedByAsset", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "comptroller", + "type": "address" + }, + { + "internalType": "address", + "name": "asset", + "type": "address" + } + ], + "name": "getVTokenForAsset", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "comptroller", + "type": "address" + } + ], + "name": "getVenusPoolMetadata", + "outputs": [ + { + "components": [ + { + "internalType": "string", + "name": "category", + "type": "string" + }, + { + "internalType": "string", + "name": "logoURL", + "type": "string" + }, + { + "internalType": "string", + "name": "description", + "type": "string" + } + ], + "internalType": "struct PoolRegistryInterface.VenusPoolMetaData", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "accessControlManager_", + "type": "address" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "metadata", + "outputs": [ + { + "internalType": "string", + "name": "category", + "type": "string" + }, + { + "internalType": "string", + "name": "logoURL", + "type": "string" + }, + { + "internalType": "string", + "name": "description", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pendingOwner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "accessControlManager_", + "type": "address" + } + ], + "name": "setAccessControlManager", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "comptroller", + "type": "address" + }, + { + "internalType": "string", + "name": "name", + "type": "string" + } + ], + "name": "setPoolName", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "comptroller", + "type": "address" + }, + { + "components": [ + { + "internalType": "string", + "name": "category", + "type": "string" + }, + { + "internalType": "string", + "name": "logoURL", + "type": "string" + }, + { + "internalType": "string", + "name": "description", + "type": "string" + } + ], + "internalType": "struct PoolRegistryInterface.VenusPoolMetaData", + "name": "metadata_", + "type": "tuple" + } + ], + "name": "updatePoolMetadata", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_logic", + "type": "address" + }, + { + "internalType": "address", + "name": "admin_", + "type": "address" + }, + { + "internalType": "bytes", + "name": "_data", + "type": "bytes" + } + ], + "stateMutability": "payable", + "type": "constructor" + } + ] + }, + "SpokePoolRegistry_Implementation": { + "address": "0x266e65b6D81802F2BAFa0c07288A62891172DAD8", + "abi": [ + { + "inputs": [], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "address", + "name": "calledContract", + "type": "address" + }, + { + "internalType": "string", + "name": "methodSignature", + "type": "string" + } + ], + "name": "Unauthorized", + "type": "error" + }, + { + "inputs": [], + "name": "ZeroAddressNotAllowed", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint8", + "name": "version", + "type": "uint8" + } + ], + "name": "Initialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "comptroller", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "vTokenAddress", + "type": "address" + } + ], + "name": "MarketAdded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "oldAccessControlManager", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "newAccessControlManager", + "type": "address" + } + ], + "name": "NewAccessControlManager", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferStarted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "comptroller", + "type": "address" + }, + { + "components": [ + { + "internalType": "string", + "name": "category", + "type": "string" + }, + { + "internalType": "string", + "name": "logoURL", + "type": "string" + }, + { + "internalType": "string", + "name": "description", + "type": "string" + } + ], + "indexed": false, + "internalType": "struct PoolRegistryInterface.VenusPoolMetaData", + "name": "oldMetadata", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "string", + "name": "category", + "type": "string" + }, + { + "internalType": "string", + "name": "logoURL", + "type": "string" + }, + { + "internalType": "string", + "name": "description", + "type": "string" + } + ], + "indexed": false, + "internalType": "struct PoolRegistryInterface.VenusPoolMetaData", + "name": "newMetadata", + "type": "tuple" + } + ], + "name": "PoolMetadataUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "comptroller", + "type": "address" + }, + { + "indexed": false, + "internalType": "string", + "name": "oldName", + "type": "string" + }, + { + "indexed": false, + "internalType": "string", + "name": "newName", + "type": "string" + } + ], + "name": "PoolNameSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "comptroller", + "type": "address" + }, + { + "components": [ + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "internalType": "address", + "name": "creator", + "type": "address" + }, + { + "internalType": "address", + "name": "comptroller", + "type": "address" + }, + { + "internalType": "uint256", + "name": "blockPosted", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "timestampPosted", + "type": "uint256" + } + ], + "indexed": false, + "internalType": "struct PoolRegistryInterface.VenusPool", + "name": "pool", + "type": "tuple" + } + ], + "name": "PoolRegistered", + "type": "event" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "accessControlManager", + "outputs": [ + { + "internalType": "contract IAccessControlManagerV8", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "contract VToken", + "name": "vToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "collateralFactor", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "liquidationThreshold", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "initialSupply", + "type": "uint256" + }, + { + "internalType": "address", + "name": "vTokenReceiver", + "type": "address" + }, + { + "internalType": "uint256", + "name": "supplyCap", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "borrowCap", + "type": "uint256" + } + ], + "internalType": "struct PoolRegistry.AddMarketInput", + "name": "input", + "type": "tuple" + } + ], + "name": "addMarket", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "internalType": "contract Comptroller", + "name": "comptroller", + "type": "address" + }, + { + "internalType": "uint256", + "name": "closeFactor", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "liquidationIncentive", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minLiquidatableCollateral", + "type": "uint256" + } + ], + "name": "addPool", + "outputs": [ + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "getAllPools", + "outputs": [ + { + "components": [ + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "internalType": "address", + "name": "creator", + "type": "address" + }, + { + "internalType": "address", + "name": "comptroller", + "type": "address" + }, + { + "internalType": "uint256", + "name": "blockPosted", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "timestampPosted", + "type": "uint256" + } + ], + "internalType": "struct PoolRegistryInterface.VenusPool[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "comptroller", + "type": "address" + } + ], + "name": "getPoolByComptroller", + "outputs": [ + { + "components": [ + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "internalType": "address", + "name": "creator", + "type": "address" + }, + { + "internalType": "address", + "name": "comptroller", + "type": "address" + }, + { + "internalType": "uint256", + "name": "blockPosted", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "timestampPosted", + "type": "uint256" + } + ], + "internalType": "struct PoolRegistryInterface.VenusPool", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "asset", + "type": "address" + } + ], + "name": "getPoolsSupportedByAsset", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "comptroller", + "type": "address" + }, + { + "internalType": "address", + "name": "asset", + "type": "address" + } + ], + "name": "getVTokenForAsset", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "comptroller", + "type": "address" + } + ], + "name": "getVenusPoolMetadata", + "outputs": [ + { + "components": [ + { + "internalType": "string", + "name": "category", + "type": "string" + }, + { + "internalType": "string", + "name": "logoURL", + "type": "string" + }, + { + "internalType": "string", + "name": "description", + "type": "string" + } + ], + "internalType": "struct PoolRegistryInterface.VenusPoolMetaData", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "accessControlManager_", + "type": "address" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "metadata", + "outputs": [ + { + "internalType": "string", + "name": "category", + "type": "string" + }, + { + "internalType": "string", + "name": "logoURL", + "type": "string" + }, + { + "internalType": "string", + "name": "description", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pendingOwner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "accessControlManager_", + "type": "address" + } + ], + "name": "setAccessControlManager", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "comptroller", + "type": "address" + }, + { + "internalType": "string", + "name": "name", + "type": "string" + } + ], + "name": "setPoolName", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "comptroller", + "type": "address" + }, + { + "components": [ + { + "internalType": "string", + "name": "category", + "type": "string" + }, + { + "internalType": "string", + "name": "logoURL", + "type": "string" + }, + { + "internalType": "string", + "name": "description", + "type": "string" + } + ], + "internalType": "struct PoolRegistryInterface.VenusPoolMetaData", + "name": "metadata_", + "type": "tuple" + } + ], + "name": "updatePoolMetadata", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } + ] + }, + "SpokePoolRegistry_Proxy": { + "address": "0xeAA45288d804971e5a76f33559e629F5b2b1Cb8B", + "abi": [ + { + "inputs": [ + { + "internalType": "address", + "name": "_logic", + "type": "address" + }, + { + "internalType": "address", + "name": "admin_", + "type": "address" + }, + { + "internalType": "bytes", + "name": "_data", + "type": "bytes" + } + ], + "stateMutability": "payable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "previousAdmin", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "newAdmin", + "type": "address" + } + ], + "name": "AdminChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "beacon", + "type": "address" + } + ], + "name": "BeaconUpgraded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "implementation", + "type": "address" + } + ], + "name": "Upgraded", + "type": "event" + }, + { + "stateMutability": "payable", + "type": "fallback" + }, + { + "inputs": [], + "name": "admin", + "outputs": [ + { + "internalType": "address", + "name": "admin_", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "implementation", + "outputs": [ + { + "internalType": "address", + "name": "implementation_", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newImplementation", + "type": "address" + } + ], + "name": "upgradeTo", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newImplementation", + "type": "address" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "upgradeToAndCall", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "stateMutability": "payable", + "type": "receive" + } + ] + }, + "SpokeVTokenBeacon": { + "address": "0xB9c3b5A6f13FD5BEF51eE8B62ED3EA384a9d1B45", + "abi": [ + { + "inputs": [ + { + "internalType": "address", + "name": "implementation_", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "implementation", + "type": "address" + } + ], + "name": "Upgraded", + "type": "event" + }, + { + "inputs": [], + "name": "implementation", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newImplementation", + "type": "address" + } + ], + "name": "upgradeTo", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } + ] + }, + "SpokeVTokenImpl": { + "address": "0x0A2399b0fC9A5921D0d843f2960589c1300Ac601", + "abi": [ + { + "inputs": [ + { + "internalType": "bool", + "name": "timeBased_", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "blocksPerYear_", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxBorrowRateMantissa_", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "actualAddAmount", + "type": "uint256" + } + ], + "name": "AddReservesFactorFreshCheck", + "type": "error" + }, + { + "inputs": [], + "name": "BorrowCashNotAvailable", + "type": "error" + }, + { + "inputs": [], + "name": "BorrowFreshnessCheck", + "type": "error" + }, + { + "inputs": [], + "name": "DelegateNotApproved", + "type": "error" + }, + { + "inputs": [], + "name": "ForceLiquidateBorrowUnauthorized", + "type": "error" + }, + { + "inputs": [], + "name": "HealBorrowUnauthorized", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidBlocksPerYear", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidTimeBasedConfiguration", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "errorCode", + "type": "uint256" + } + ], + "name": "LiquidateAccrueCollateralInterestFailed", + "type": "error" + }, + { + "inputs": [], + "name": "LiquidateCloseAmountIsUintMax", + "type": "error" + }, + { + "inputs": [], + "name": "LiquidateCloseAmountIsZero", + "type": "error" + }, + { + "inputs": [], + "name": "LiquidateCollateralFreshnessCheck", + "type": "error" + }, + { + "inputs": [], + "name": "LiquidateFreshnessCheck", + "type": "error" + }, + { + "inputs": [], + "name": "LiquidateLiquidatorIsBorrower", + "type": "error" + }, + { + "inputs": [], + "name": "LiquidateSeizeLiquidatorIsBorrower", + "type": "error" + }, + { + "inputs": [], + "name": "MintFreshnessCheck", + "type": "error" + }, + { + "inputs": [], + "name": "ProtocolSeizeShareTooBig", + "type": "error" + }, + { + "inputs": [], + "name": "RedeemFreshnessCheck", + "type": "error" + }, + { + "inputs": [], + "name": "RedeemTransferOutNotPossible", + "type": "error" + }, + { + "inputs": [], + "name": "ReduceReservesCashNotAvailable", + "type": "error" + }, + { + "inputs": [], + "name": "ReduceReservesCashValidation", + "type": "error" + }, + { + "inputs": [], + "name": "ReduceReservesFreshCheck", + "type": "error" + }, + { + "inputs": [], + "name": "RepayBorrowFreshnessCheck", + "type": "error" + }, + { + "inputs": [], + "name": "SetInterestRateModelFreshCheck", + "type": "error" + }, + { + "inputs": [], + "name": "SetReserveFactorBoundsCheck", + "type": "error" + }, + { + "inputs": [], + "name": "SetReserveFactorFreshCheck", + "type": "error" + }, + { + "inputs": [], + "name": "TransferNotAllowed", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "address", + "name": "calledContract", + "type": "address" + }, + { + "internalType": "string", + "name": "methodSignature", + "type": "string" + } + ], + "name": "Unauthorized", + "type": "error" + }, + { + "inputs": [], + "name": "ZeroAddressNotAllowed", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "cashPrior", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "interestAccumulated", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "borrowIndex", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "totalBorrows", + "type": "uint256" + } + ], + "name": "AccrueInterest", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "borrower", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "badDebtDelta", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "badDebtOld", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "badDebtNew", + "type": "uint256" + } + ], + "name": "BadDebtIncreased", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "badDebtOld", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "badDebtNew", + "type": "uint256" + } + ], + "name": "BadDebtRecovered", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "borrower", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "borrowAmount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "accountBorrows", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "totalBorrows", + "type": "uint256" + } + ], + "name": "Borrow", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "oldInternalCash", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newInternalCash", + "type": "uint256" + } + ], + "name": "CashSynced", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "payer", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "borrower", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "repayAmount", + "type": "uint256" + } + ], + "name": "HealBorrow", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint8", + "name": "version", + "type": "uint8" + } + ], + "name": "Initialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "liquidator", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "borrower", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "repayAmount", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "vTokenCollateral", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "seizeTokens", + "type": "uint256" + } + ], + "name": "LiquidateBorrow", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "minter", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "mintAmount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "mintTokens", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "accountBalance", + "type": "uint256" + } + ], + "name": "Mint", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "oldAccessControlManager", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "newAccessControlManager", + "type": "address" + } + ], + "name": "NewAccessControlManager", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "contract ComptrollerInterface", + "name": "oldComptroller", + "type": "address" + }, + { + "indexed": true, + "internalType": "contract ComptrollerInterface", + "name": "newComptroller", + "type": "address" + } + ], + "name": "NewComptroller", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "contract InterestRateModel", + "name": "oldInterestRateModel", + "type": "address" + }, + { + "indexed": true, + "internalType": "contract InterestRateModel", + "name": "newInterestRateModel", + "type": "address" + } + ], + "name": "NewMarketInterestRateModel", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "oldProtocolSeizeShareMantissa", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newProtocolSeizeShareMantissa", + "type": "uint256" + } + ], + "name": "NewProtocolSeizeShare", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "oldProtocolShareReserve", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newProtocolShareReserve", + "type": "address" + } + ], + "name": "NewProtocolShareReserve", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "oldReduceReservesBlockOrTimestampDelta", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newReduceReservesBlockOrTimestampDelta", + "type": "uint256" + } + ], + "name": "NewReduceReservesBlockDelta", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "oldReserveFactorMantissa", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newReserveFactorMantissa", + "type": "uint256" + } + ], + "name": "NewReserveFactor", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "oldShortfall", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newShortfall", + "type": "address" + } + ], + "name": "NewShortfallContract", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferStarted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "ProtocolSeize", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "redeemer", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "redeemAmount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "redeemTokens", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "accountBalance", + "type": "uint256" + } + ], + "name": "Redeem", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "payer", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "borrower", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "repayAmount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "accountBorrows", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "totalBorrows", + "type": "uint256" + } + ], + "name": "RepayBorrow", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "benefactor", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "addAmount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newTotalReserves", + "type": "uint256" + } + ], + "name": "ReservesAdded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "protocolShareReserve", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "reduceAmount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newTotalReserves", + "type": "uint256" + } + ], + "name": "SpreadReservesReduced", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "token", + "type": "address" + } + ], + "name": "SweepToken", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "inputs": [], + "name": "NO_ERROR", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "accessControlManager", + "outputs": [ + { + "internalType": "contract IAccessControlManagerV8", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "accrualBlockNumber", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "accrueInterest", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "addAmount", + "type": "uint256" + } + ], + "name": "addReserves", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "badDebt", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "recoveredAmount_", + "type": "uint256" + } + ], + "name": "badDebtRecovered", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "balanceOfUnderlying", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "blocksOrSecondsPerYear", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "borrowAmount", + "type": "uint256" + } + ], + "name": "borrow", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "borrowBalanceCurrent", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "borrowBalanceStored", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "borrower", + "type": "address" + }, + { + "internalType": "uint256", + "name": "borrowAmount", + "type": "uint256" + } + ], + "name": "borrowBehalf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "borrowIndex", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "borrowRatePerBlock", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "comptroller", + "outputs": [ + { + "internalType": "contract ComptrollerInterface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "subtractedValue", + "type": "uint256" + } + ], + "name": "decreaseAllowance", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "exchangeRateCurrent", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "exchangeRateStored", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "liquidator", + "type": "address" + }, + { + "internalType": "address", + "name": "borrower", + "type": "address" + }, + { + "internalType": "uint256", + "name": "repayAmount", + "type": "uint256" + }, + { + "internalType": "contract VTokenInterface", + "name": "vTokenCollateral", + "type": "address" + }, + { + "internalType": "bool", + "name": "skipLiquidityCheck", + "type": "bool" + } + ], + "name": "forceLiquidateBorrow", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "getAccountSnapshot", + "outputs": [ + { + "internalType": "uint256", + "name": "error", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "vTokenBalance", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "borrowBalance", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "exchangeRate", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getBlockNumberOrTimestamp", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getCash", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "payer", + "type": "address" + }, + { + "internalType": "address", + "name": "borrower", + "type": "address" + }, + { + "internalType": "uint256", + "name": "repayAmount", + "type": "uint256" + } + ], + "name": "healBorrow", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "addedValue", + "type": "uint256" + } + ], + "name": "increaseAllowance", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "underlying_", + "type": "address" + }, + { + "internalType": "contract ComptrollerInterface", + "name": "comptroller_", + "type": "address" + }, + { + "internalType": "contract InterestRateModel", + "name": "interestRateModel_", + "type": "address" + }, + { + "internalType": "uint256", + "name": "initialExchangeRateMantissa_", + "type": "uint256" + }, + { + "internalType": "string", + "name": "name_", + "type": "string" + }, + { + "internalType": "string", + "name": "symbol_", + "type": "string" + }, + { + "internalType": "uint8", + "name": "decimals_", + "type": "uint8" + }, + { + "internalType": "address", + "name": "admin_", + "type": "address" + }, + { + "internalType": "address", + "name": "accessControlManager_", + "type": "address" + }, + { + "components": [ + { + "internalType": "address", + "name": "shortfall", + "type": "address" + }, + { + "internalType": "address payable", + "name": "protocolShareReserve", + "type": "address" + } + ], + "internalType": "struct VTokenInterface.RiskManagementInit", + "name": "riskManagement", + "type": "tuple" + }, + { + "internalType": "uint256", + "name": "reserveFactorMantissa_", + "type": "uint256" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "interestRateModel", + "outputs": [ + { + "internalType": "contract InterestRateModel", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "internalCash", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "isTimeBased", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "isVToken", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "borrower", + "type": "address" + }, + { + "internalType": "uint256", + "name": "repayAmount", + "type": "uint256" + }, + { + "internalType": "contract VTokenInterface", + "name": "vTokenCollateral", + "type": "address" + } + ], + "name": "liquidateBorrow", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "mintAmount", + "type": "uint256" + } + ], + "name": "mint", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "minter", + "type": "address" + }, + { + "internalType": "uint256", + "name": "mintAmount", + "type": "uint256" + } + ], + "name": "mintBehalf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pendingOwner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "protocolSeizeShareMantissa", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "protocolShareReserve", + "outputs": [ + { + "internalType": "address payable", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "redeemTokens", + "type": "uint256" + } + ], + "name": "redeem", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "redeemer", + "type": "address" + }, + { + "internalType": "uint256", + "name": "redeemTokens", + "type": "uint256" + } + ], + "name": "redeemBehalf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "redeemAmount", + "type": "uint256" + } + ], + "name": "redeemUnderlying", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "redeemer", + "type": "address" + }, + { + "internalType": "uint256", + "name": "redeemAmount", + "type": "uint256" + } + ], + "name": "redeemUnderlyingBehalf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "reduceAmount", + "type": "uint256" + } + ], + "name": "reduceReserves", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "reduceReservesBlockDelta", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "reduceReservesBlockNumber", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "repayAmount", + "type": "uint256" + } + ], + "name": "repayBorrow", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "borrower", + "type": "address" + }, + { + "internalType": "uint256", + "name": "repayAmount", + "type": "uint256" + } + ], + "name": "repayBorrowBehalf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "reserveFactorMantissa", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "liquidator", + "type": "address" + }, + { + "internalType": "address", + "name": "borrower", + "type": "address" + }, + { + "internalType": "uint256", + "name": "seizeTokens", + "type": "uint256" + } + ], + "name": "seize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "accessControlManager_", + "type": "address" + } + ], + "name": "setAccessControlManager", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract InterestRateModel", + "name": "newInterestRateModel", + "type": "address" + } + ], + "name": "setInterestRateModel", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "newProtocolSeizeShareMantissa_", + "type": "uint256" + } + ], + "name": "setProtocolSeizeShare", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address payable", + "name": "protocolShareReserve_", + "type": "address" + } + ], + "name": "setProtocolShareReserve", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_newReduceReservesBlockOrTimestampDelta", + "type": "uint256" + } + ], + "name": "setReduceReservesBlockDelta", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "newReserveFactorMantissa", + "type": "uint256" + } + ], + "name": "setReserveFactor", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "shortfall_", + "type": "address" + } + ], + "name": "setShortfallContract", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "shortfall", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "supplyRatePerBlock", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IERC20Upgradeable", + "name": "token", + "type": "address" + } + ], + "name": "sweepToken", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "syncCash", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "totalBorrows", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalBorrowsCurrent", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "totalReserves", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "dst", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "src", + "type": "address" + }, + { + "internalType": "address", + "name": "dst", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "underlying", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + } + ] + }, + "SwapRouter_BTC": { + "address": "0x98C108285E389A5a12EdDa585b70b30C786AFb86", + "abi": [ + { + "inputs": [ + { + "internalType": "address", + "name": "WBNB_", + "type": "address" + }, + { + "internalType": "address", + "name": "factory_", + "type": "address" + }, + { + "internalType": "address", + "name": "_comptrollerAddress", + "type": "address" + }, + { + "internalType": "address", + "name": "_vBNBAddress", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountMax", + "type": "uint256" + } + ], + "name": "ExcessiveInputAmount", + "type": "error" + }, + { + "inputs": [], + "name": "IdenticalAddresses", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amountIn", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountIntMax", + "type": "uint256" + } + ], + "name": "InputAmountAboveMaximum", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "sweepAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "balance", + "type": "uint256" + } + ], + "name": "InsufficientBalance", + "type": "error" + }, + { + "inputs": [], + "name": "InsufficientInputAmount", + "type": "error" + }, + { + "inputs": [], + "name": "InsufficientLiquidity", + "type": "error" + }, + { + "inputs": [], + "name": "InsufficientOutputAmount", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidPath", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amountOut", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountOutMin", + "type": "uint256" + } + ], + "name": "OutputAmountBelowMinimum", + "type": "error" + }, + { + "inputs": [], + "name": "ReentrantCheck", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "repayer", + "type": "address" + }, + { + "internalType": "address", + "name": "vToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "errorCode", + "type": "uint256" + } + ], + "name": "RepayError", + "type": "error" + }, + { + "inputs": [], + "name": "SafeApproveFailed", + "type": "error" + }, + { + "inputs": [], + "name": "SafeTransferBNBFailed", + "type": "error" + }, + { + "inputs": [], + "name": "SafeTransferFailed", + "type": "error" + }, + { + "inputs": [], + "name": "SafeTransferFromFailed", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "supplier", + "type": "address" + }, + { + "internalType": "address", + "name": "vToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "errorCode", + "type": "uint256" + } + ], + "name": "SupplyError", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "swapAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountOutMin", + "type": "uint256" + } + ], + "name": "SwapAmountLessThanAmountOutMin", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "timestemp", + "type": "uint256" + } + ], + "name": "SwapDeadlineExpire", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "vToken", + "type": "address" + } + ], + "name": "VTokenNotListed", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "underlying", + "type": "address" + } + ], + "name": "VTokenUnderlyingInvalid", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "expectedAdddress", + "type": "address" + }, + { + "internalType": "address", + "name": "passedAddress", + "type": "address" + } + ], + "name": "WrongAddress", + "type": "error" + }, + { + "inputs": [], + "name": "ZeroAddress", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferStarted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "swapper", + "type": "address" + }, + { + "indexed": true, + "internalType": "address[]", + "name": "path", + "type": "address[]" + }, + { + "indexed": true, + "internalType": "uint256[]", + "name": "amounts", + "type": "uint256[]" + } + ], + "name": "SwapBnbForTokens", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "swapper", + "type": "address" + }, + { + "indexed": true, + "internalType": "address[]", + "name": "path", + "type": "address[]" + } + ], + "name": "SwapBnbForTokensAtSupportingFee", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "swapper", + "type": "address" + }, + { + "indexed": true, + "internalType": "address[]", + "name": "path", + "type": "address[]" + }, + { + "indexed": true, + "internalType": "uint256[]", + "name": "amounts", + "type": "uint256[]" + } + ], + "name": "SwapTokensForBnb", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "swapper", + "type": "address" + }, + { + "indexed": true, + "internalType": "address[]", + "name": "path", + "type": "address[]" + } + ], + "name": "SwapTokensForBnbAtSupportingFee", + "type": "event" }, { "anonymous": false, @@ -88317,6 +96261,80 @@ } ] }, + "VToken_vUSDC_HubSpoke": { + "address": "0xD05514217FD359659aE7da7740e79C11947eBB32", + "abi": [ + { + "inputs": [ + { + "internalType": "address", + "name": "beacon", + "type": "address" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "stateMutability": "payable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "previousAdmin", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "newAdmin", + "type": "address" + } + ], + "name": "AdminChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "beacon", + "type": "address" + } + ], + "name": "BeaconUpgraded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "implementation", + "type": "address" + } + ], + "name": "Upgraded", + "type": "event" + }, + { + "stateMutability": "payable", + "type": "fallback" + }, + { + "stateMutability": "payable", + "type": "receive" + } + ] + }, "VToken_vUSDD_DeFi": { "address": "0xa109DE0abaeefC521Ec29D89eA42E64F37A6882E", "abi": [ @@ -88909,6 +96927,80 @@ } ] }, + "VToken_vUSDT_HubSpoke": { + "address": "0xC88bAF0bA49a98F15A00182752f6d10bd3932F6a", + "abi": [ + { + "inputs": [ + { + "internalType": "address", + "name": "beacon", + "type": "address" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "stateMutability": "payable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "previousAdmin", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "newAdmin", + "type": "address" + } + ], + "name": "AdminChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "beacon", + "type": "address" + } + ], + "name": "BeaconUpgraded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "implementation", + "type": "address" + } + ], + "name": "Upgraded", + "type": "event" + }, + { + "stateMutability": "payable", + "type": "fallback" + }, + { + "stateMutability": "payable", + "type": "receive" + } + ] + }, "VToken_vUSDT_LiquidStakedBNB": { "address": "0x2197d02cC9cd1ad51317A0a85A656a0c82383A7c", "abi": [ diff --git a/deployments/bsctestnet/Comptroller_HubSpoke.json b/deployments/bsctestnet/Comptroller_HubSpoke.json new file mode 100644 index 000000000..c0f3ecce2 --- /dev/null +++ b/deployments/bsctestnet/Comptroller_HubSpoke.json @@ -0,0 +1,187 @@ +{ + "address": "0x11960c84d6c4F2a978a12372721C3A6A88C78f4c", + "abi": [ + { + "inputs": [ + { + "internalType": "address", + "name": "beacon", + "type": "address" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "stateMutability": "payable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "previousAdmin", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "newAdmin", + "type": "address" + } + ], + "name": "AdminChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "beacon", + "type": "address" + } + ], + "name": "BeaconUpgraded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "implementation", + "type": "address" + } + ], + "name": "Upgraded", + "type": "event" + }, + { + "stateMutability": "payable", + "type": "fallback" + }, + { + "stateMutability": "payable", + "type": "receive" + } + ], + "transactionHash": "0x556dca7306e25f9f8d8f0d707d165be5339e5e8439eb2ed26d474dcf2ff4765d", + "receipt": { + "to": null, + "from": "0x33C6476F88eeA28D7E7900F759B4597704Ef95B7", + "contractAddress": "0x11960c84d6c4F2a978a12372721C3A6A88C78f4c", + "transactionIndex": 0, + "gasUsed": "280125", + "logsBloom": "0x00000000000000000000000000000000000000000000000000801000000000000000000000040000000000000000000000000000000000000000000000008000000000000000000000000000001000000401000002002000000000000000000000000000020000000000000100000800000000000000000000000000000000400000000000000000000000000000080010000000000080000000000000000000000000000000000000002000200400000000000000800000004000000000000000000000000000000000000001040000000000000000000000800000000020000000000200000000000000000000000000000800000000000000000000000000", + "blockHash": "0x445b3b93652639266c15e2f758c4e83bb76fc24521dca053e98f95d1162e6e3e", + "transactionHash": "0x556dca7306e25f9f8d8f0d707d165be5339e5e8439eb2ed26d474dcf2ff4765d", + "logs": [ + { + "transactionIndex": 0, + "blockNumber": 129845369, + "transactionHash": "0x556dca7306e25f9f8d8f0d707d165be5339e5e8439eb2ed26d474dcf2ff4765d", + "address": "0x11960c84d6c4F2a978a12372721C3A6A88C78f4c", + "topics": [ + "0x1cf3b03a6cf19fa2baba4df148e9dcabedea7f8a5c07840e207e5c089be95d3e", + "0x000000000000000000000000076f3fb34c8937a62ad562c33249798367542d75" + ], + "data": "0x", + "logIndex": 0, + "blockHash": "0x445b3b93652639266c15e2f758c4e83bb76fc24521dca053e98f95d1162e6e3e" + }, + { + "transactionIndex": 0, + "blockNumber": 129845369, + "transactionHash": "0x556dca7306e25f9f8d8f0d707d165be5339e5e8439eb2ed26d474dcf2ff4765d", + "address": "0x11960c84d6c4F2a978a12372721C3A6A88C78f4c", + "topics": [ + "0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x00000000000000000000000033c6476f88eea28d7e7900f759b4597704ef95b7" + ], + "data": "0x", + "logIndex": 1, + "blockHash": "0x445b3b93652639266c15e2f758c4e83bb76fc24521dca053e98f95d1162e6e3e" + }, + { + "transactionIndex": 0, + "blockNumber": 129845369, + "transactionHash": "0x556dca7306e25f9f8d8f0d707d165be5339e5e8439eb2ed26d474dcf2ff4765d", + "address": "0x11960c84d6c4F2a978a12372721C3A6A88C78f4c", + "topics": ["0x66fd58e82f7b31a2a5c30e0888f3093efe4e111b00cd2b0c31fe014601293aa0"], + "data": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000045f8a08f534f34a97187626e05d4b6648eeaa9aa", + "logIndex": 2, + "blockHash": "0x445b3b93652639266c15e2f758c4e83bb76fc24521dca053e98f95d1162e6e3e" + }, + { + "transactionIndex": 0, + "blockNumber": 129845369, + "transactionHash": "0x556dca7306e25f9f8d8f0d707d165be5339e5e8439eb2ed26d474dcf2ff4765d", + "address": "0x11960c84d6c4F2a978a12372721C3A6A88C78f4c", + "topics": ["0xc2d09fef144f7c8a86f71ea459f8fc17f675768eb1ae369cbd77fb31d467aafa"], + "data": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000064", + "logIndex": 3, + "blockHash": "0x445b3b93652639266c15e2f758c4e83bb76fc24521dca053e98f95d1162e6e3e" + }, + { + "transactionIndex": 0, + "blockNumber": 129845369, + "transactionHash": "0x556dca7306e25f9f8d8f0d707d165be5339e5e8439eb2ed26d474dcf2ff4765d", + "address": "0x11960c84d6c4F2a978a12372721C3A6A88C78f4c", + "topics": ["0x7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498"], + "data": "0x0000000000000000000000000000000000000000000000000000000000000001", + "logIndex": 4, + "blockHash": "0x445b3b93652639266c15e2f758c4e83bb76fc24521dca053e98f95d1162e6e3e" + } + ], + "blockNumber": 129845369, + "cumulativeGasUsed": "280125", + "status": 1, + "byzantium": true + }, + "args": [ + "0x076f3fb34C8937a62aD562c33249798367542d75", + "0xda35a26f000000000000000000000000000000000000000000000000000000000000006400000000000000000000000045f8a08f534f34a97187626e05d4b6648eeaa9aa" + ], + "numDeployments": 1, + "solcInputHash": "02cfe3974aa10474c22406a3f2870e89", + "metadata": "{\"compiler\":{\"version\":\"0.8.25+commit.b61c2a91\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"beacon\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"stateMutability\":\"payable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"previousAdmin\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"newAdmin\",\"type\":\"address\"}],\"name\":\"AdminChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"beacon\",\"type\":\"address\"}],\"name\":\"BeaconUpgraded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"implementation\",\"type\":\"address\"}],\"name\":\"Upgraded\",\"type\":\"event\"},{\"stateMutability\":\"payable\",\"type\":\"fallback\"},{\"stateMutability\":\"payable\",\"type\":\"receive\"}],\"devdoc\":{\"details\":\"This contract implements a proxy that gets the implementation address for each call from an {UpgradeableBeacon}. The beacon address is stored in storage slot `uint256(keccak256('eip1967.proxy.beacon')) - 1`, so that it doesn't conflict with the storage layout of the implementation behind the proxy. _Available since v3.4._\",\"events\":{\"AdminChanged(address,address)\":{\"details\":\"Emitted when the admin account has changed.\"},\"BeaconUpgraded(address)\":{\"details\":\"Emitted when the beacon is changed.\"},\"Upgraded(address)\":{\"details\":\"Emitted when the implementation is upgraded.\"}},\"kind\":\"dev\",\"methods\":{\"constructor\":{\"details\":\"Initializes the proxy with `beacon`. If `data` is nonempty, it's used as data in a delegate call to the implementation returned by the beacon. This will typically be an encoded function call, and allows initializing the storage of the proxy like a Solidity constructor. Requirements: - `beacon` must be a contract with the interface {IBeacon}.\"}},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"@openzeppelin/contracts/proxy/beacon/BeaconProxy.sol\":\"BeaconProxy\"},\"evmVersion\":\"paris\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\",\"useLiteralContent\":true},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[]},\"sources\":{\"@openzeppelin/contracts/interfaces/IERC1967.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts (last updated v4.9.0) (interfaces/IERC1967.sol)\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev ERC-1967: Proxy Storage Slots. This interface contains the events defined in the ERC.\\n *\\n * _Available since v4.8.3._\\n */\\ninterface IERC1967 {\\n /**\\n * @dev Emitted when the implementation is upgraded.\\n */\\n event Upgraded(address indexed implementation);\\n\\n /**\\n * @dev Emitted when the admin account has changed.\\n */\\n event AdminChanged(address previousAdmin, address newAdmin);\\n\\n /**\\n * @dev Emitted when the beacon is changed.\\n */\\n event BeaconUpgraded(address indexed beacon);\\n}\\n\",\"keccak256\":\"0x3cbef5ebc24b415252e2f8c0c9254555d30d9f085603b4b80d9b5ed20ab87e90\",\"license\":\"MIT\"},\"@openzeppelin/contracts/interfaces/draft-IERC1822.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts (last updated v4.5.0) (interfaces/draft-IERC1822.sol)\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev ERC1822: Universal Upgradeable Proxy Standard (UUPS) documents a method for upgradeability through a simplified\\n * proxy whose upgrades are fully controlled by the current implementation.\\n */\\ninterface IERC1822Proxiable {\\n /**\\n * @dev Returns the storage slot that the proxiable contract assumes is being used to store the implementation\\n * address.\\n *\\n * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks\\n * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this\\n * function revert if invoked through a proxy.\\n */\\n function proxiableUUID() external view returns (bytes32);\\n}\\n\",\"keccak256\":\"0x1d4afe6cb24200cc4545eed814ecf5847277dfe5d613a1707aad5fceecebcfff\",\"license\":\"MIT\"},\"@openzeppelin/contracts/proxy/ERC1967/ERC1967Upgrade.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts (last updated v4.9.0) (proxy/ERC1967/ERC1967Upgrade.sol)\\n\\npragma solidity ^0.8.2;\\n\\nimport \\\"../beacon/IBeacon.sol\\\";\\nimport \\\"../../interfaces/IERC1967.sol\\\";\\nimport \\\"../../interfaces/draft-IERC1822.sol\\\";\\nimport \\\"../../utils/Address.sol\\\";\\nimport \\\"../../utils/StorageSlot.sol\\\";\\n\\n/**\\n * @dev This abstract contract provides getters and event emitting update functions for\\n * https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots.\\n *\\n * _Available since v4.1._\\n */\\nabstract contract ERC1967Upgrade is IERC1967 {\\n // This is the keccak-256 hash of \\\"eip1967.proxy.rollback\\\" subtracted by 1\\n bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143;\\n\\n /**\\n * @dev Storage slot with the address of the current implementation.\\n * This is the keccak-256 hash of \\\"eip1967.proxy.implementation\\\" subtracted by 1, and is\\n * validated in the constructor.\\n */\\n bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;\\n\\n /**\\n * @dev Returns the current implementation address.\\n */\\n function _getImplementation() internal view returns (address) {\\n return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;\\n }\\n\\n /**\\n * @dev Stores a new address in the EIP1967 implementation slot.\\n */\\n function _setImplementation(address newImplementation) private {\\n require(Address.isContract(newImplementation), \\\"ERC1967: new implementation is not a contract\\\");\\n StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;\\n }\\n\\n /**\\n * @dev Perform implementation upgrade\\n *\\n * Emits an {Upgraded} event.\\n */\\n function _upgradeTo(address newImplementation) internal {\\n _setImplementation(newImplementation);\\n emit Upgraded(newImplementation);\\n }\\n\\n /**\\n * @dev Perform implementation upgrade with additional setup call.\\n *\\n * Emits an {Upgraded} event.\\n */\\n function _upgradeToAndCall(address newImplementation, bytes memory data, bool forceCall) internal {\\n _upgradeTo(newImplementation);\\n if (data.length > 0 || forceCall) {\\n Address.functionDelegateCall(newImplementation, data);\\n }\\n }\\n\\n /**\\n * @dev Perform implementation upgrade with security checks for UUPS proxies, and additional setup call.\\n *\\n * Emits an {Upgraded} event.\\n */\\n function _upgradeToAndCallUUPS(address newImplementation, bytes memory data, bool forceCall) internal {\\n // Upgrades from old implementations will perform a rollback test. This test requires the new\\n // implementation to upgrade back to the old, non-ERC1822 compliant, implementation. Removing\\n // this special case will break upgrade paths from old UUPS implementation to new ones.\\n if (StorageSlot.getBooleanSlot(_ROLLBACK_SLOT).value) {\\n _setImplementation(newImplementation);\\n } else {\\n try IERC1822Proxiable(newImplementation).proxiableUUID() returns (bytes32 slot) {\\n require(slot == _IMPLEMENTATION_SLOT, \\\"ERC1967Upgrade: unsupported proxiableUUID\\\");\\n } catch {\\n revert(\\\"ERC1967Upgrade: new implementation is not UUPS\\\");\\n }\\n _upgradeToAndCall(newImplementation, data, forceCall);\\n }\\n }\\n\\n /**\\n * @dev Storage slot with the admin of the contract.\\n * This is the keccak-256 hash of \\\"eip1967.proxy.admin\\\" subtracted by 1, and is\\n * validated in the constructor.\\n */\\n bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;\\n\\n /**\\n * @dev Returns the current admin.\\n */\\n function _getAdmin() internal view returns (address) {\\n return StorageSlot.getAddressSlot(_ADMIN_SLOT).value;\\n }\\n\\n /**\\n * @dev Stores a new address in the EIP1967 admin slot.\\n */\\n function _setAdmin(address newAdmin) private {\\n require(newAdmin != address(0), \\\"ERC1967: new admin is the zero address\\\");\\n StorageSlot.getAddressSlot(_ADMIN_SLOT).value = newAdmin;\\n }\\n\\n /**\\n * @dev Changes the admin of the proxy.\\n *\\n * Emits an {AdminChanged} event.\\n */\\n function _changeAdmin(address newAdmin) internal {\\n emit AdminChanged(_getAdmin(), newAdmin);\\n _setAdmin(newAdmin);\\n }\\n\\n /**\\n * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.\\n * This is bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)) and is validated in the constructor.\\n */\\n bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;\\n\\n /**\\n * @dev Returns the current beacon.\\n */\\n function _getBeacon() internal view returns (address) {\\n return StorageSlot.getAddressSlot(_BEACON_SLOT).value;\\n }\\n\\n /**\\n * @dev Stores a new beacon in the EIP1967 beacon slot.\\n */\\n function _setBeacon(address newBeacon) private {\\n require(Address.isContract(newBeacon), \\\"ERC1967: new beacon is not a contract\\\");\\n require(\\n Address.isContract(IBeacon(newBeacon).implementation()),\\n \\\"ERC1967: beacon implementation is not a contract\\\"\\n );\\n StorageSlot.getAddressSlot(_BEACON_SLOT).value = newBeacon;\\n }\\n\\n /**\\n * @dev Perform beacon upgrade with additional setup call. Note: This upgrades the address of the beacon, it does\\n * not upgrade the implementation contained in the beacon (see {UpgradeableBeacon-_setImplementation} for that).\\n *\\n * Emits a {BeaconUpgraded} event.\\n */\\n function _upgradeBeaconToAndCall(address newBeacon, bytes memory data, bool forceCall) internal {\\n _setBeacon(newBeacon);\\n emit BeaconUpgraded(newBeacon);\\n if (data.length > 0 || forceCall) {\\n Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data);\\n }\\n }\\n}\\n\",\"keccak256\":\"0x3b21ae06bf5957f73fa16754b0669c77b7abd8ba6c072d35c3281d446fdb86c2\",\"license\":\"MIT\"},\"@openzeppelin/contracts/proxy/Proxy.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts (last updated v4.6.0) (proxy/Proxy.sol)\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM\\n * instruction `delegatecall`. We refer to the second contract as the _implementation_ behind the proxy, and it has to\\n * be specified by overriding the virtual {_implementation} function.\\n *\\n * Additionally, delegation to the implementation can be triggered manually through the {_fallback} function, or to a\\n * different contract through the {_delegate} function.\\n *\\n * The success and return data of the delegated call will be returned back to the caller of the proxy.\\n */\\nabstract contract Proxy {\\n /**\\n * @dev Delegates the current call to `implementation`.\\n *\\n * This function does not return to its internal call site, it will return directly to the external caller.\\n */\\n function _delegate(address implementation) internal virtual {\\n assembly {\\n // Copy msg.data. We take full control of memory in this inline assembly\\n // block because it will not return to Solidity code. We overwrite the\\n // Solidity scratch pad at memory position 0.\\n calldatacopy(0, 0, calldatasize())\\n\\n // Call the implementation.\\n // out and outsize are 0 because we don't know the size yet.\\n let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)\\n\\n // Copy the returned data.\\n returndatacopy(0, 0, returndatasize())\\n\\n switch result\\n // delegatecall returns 0 on error.\\n case 0 {\\n revert(0, returndatasize())\\n }\\n default {\\n return(0, returndatasize())\\n }\\n }\\n }\\n\\n /**\\n * @dev This is a virtual function that should be overridden so it returns the address to which the fallback function\\n * and {_fallback} should delegate.\\n */\\n function _implementation() internal view virtual returns (address);\\n\\n /**\\n * @dev Delegates the current call to the address returned by `_implementation()`.\\n *\\n * This function does not return to its internal call site, it will return directly to the external caller.\\n */\\n function _fallback() internal virtual {\\n _beforeFallback();\\n _delegate(_implementation());\\n }\\n\\n /**\\n * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other\\n * function in the contract matches the call data.\\n */\\n fallback() external payable virtual {\\n _fallback();\\n }\\n\\n /**\\n * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if call data\\n * is empty.\\n */\\n receive() external payable virtual {\\n _fallback();\\n }\\n\\n /**\\n * @dev Hook that is called before falling back to the implementation. Can happen as part of a manual `_fallback`\\n * call, or as part of the Solidity `fallback` or `receive` functions.\\n *\\n * If overridden should call `super._beforeFallback()`.\\n */\\n function _beforeFallback() internal virtual {}\\n}\\n\",\"keccak256\":\"0xc130fe33f1b2132158531a87734153293f6d07bc263ff4ac90e85da9c82c0e27\",\"license\":\"MIT\"},\"@openzeppelin/contracts/proxy/beacon/BeaconProxy.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts (last updated v4.7.0) (proxy/beacon/BeaconProxy.sol)\\n\\npragma solidity ^0.8.0;\\n\\nimport \\\"./IBeacon.sol\\\";\\nimport \\\"../Proxy.sol\\\";\\nimport \\\"../ERC1967/ERC1967Upgrade.sol\\\";\\n\\n/**\\n * @dev This contract implements a proxy that gets the implementation address for each call from an {UpgradeableBeacon}.\\n *\\n * The beacon address is stored in storage slot `uint256(keccak256('eip1967.proxy.beacon')) - 1`, so that it doesn't\\n * conflict with the storage layout of the implementation behind the proxy.\\n *\\n * _Available since v3.4._\\n */\\ncontract BeaconProxy is Proxy, ERC1967Upgrade {\\n /**\\n * @dev Initializes the proxy with `beacon`.\\n *\\n * If `data` is nonempty, it's used as data in a delegate call to the implementation returned by the beacon. This\\n * will typically be an encoded function call, and allows initializing the storage of the proxy like a Solidity\\n * constructor.\\n *\\n * Requirements:\\n *\\n * - `beacon` must be a contract with the interface {IBeacon}.\\n */\\n constructor(address beacon, bytes memory data) payable {\\n _upgradeBeaconToAndCall(beacon, data, false);\\n }\\n\\n /**\\n * @dev Returns the current beacon address.\\n */\\n function _beacon() internal view virtual returns (address) {\\n return _getBeacon();\\n }\\n\\n /**\\n * @dev Returns the current implementation address of the associated beacon.\\n */\\n function _implementation() internal view virtual override returns (address) {\\n return IBeacon(_getBeacon()).implementation();\\n }\\n\\n /**\\n * @dev Changes the proxy to use a new beacon. Deprecated: see {_upgradeBeaconToAndCall}.\\n *\\n * If `data` is nonempty, it's used as data in a delegate call to the implementation returned by the beacon.\\n *\\n * Requirements:\\n *\\n * - `beacon` must be a contract.\\n * - The implementation returned by `beacon` must be a contract.\\n */\\n function _setBeacon(address beacon, bytes memory data) internal virtual {\\n _upgradeBeaconToAndCall(beacon, data, false);\\n }\\n}\\n\",\"keccak256\":\"0x85439e74ab467b6a23d45d32bdc9506cbc3760320289afd605f11638c4138e95\",\"license\":\"MIT\"},\"@openzeppelin/contracts/proxy/beacon/IBeacon.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts v4.4.1 (proxy/beacon/IBeacon.sol)\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev This is the interface that {BeaconProxy} expects of its beacon.\\n */\\ninterface IBeacon {\\n /**\\n * @dev Must return an address that can be used as a delegate call target.\\n *\\n * {BeaconProxy} will check that this address is a contract.\\n */\\n function implementation() external view returns (address);\\n}\\n\",\"keccak256\":\"0xd50a3421ac379ccb1be435fa646d66a65c986b4924f0849839f08692f39dde61\",\"license\":\"MIT\"},\"@openzeppelin/contracts/utils/Address.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)\\n\\npragma solidity ^0.8.1;\\n\\n/**\\n * @dev Collection of functions related to the address type\\n */\\nlibrary Address {\\n /**\\n * @dev Returns true if `account` is a contract.\\n *\\n * [IMPORTANT]\\n * ====\\n * It is unsafe to assume that an address for which this function returns\\n * false is an externally-owned account (EOA) and not a contract.\\n *\\n * Among others, `isContract` will return false for the following\\n * types of addresses:\\n *\\n * - an externally-owned account\\n * - a contract in construction\\n * - an address where a contract will be created\\n * - an address where a contract lived, but was destroyed\\n *\\n * Furthermore, `isContract` will also return true if the target contract within\\n * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,\\n * which only has an effect at the end of a transaction.\\n * ====\\n *\\n * [IMPORTANT]\\n * ====\\n * You shouldn't rely on `isContract` to protect against flash loan attacks!\\n *\\n * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets\\n * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract\\n * constructor.\\n * ====\\n */\\n function isContract(address account) internal view returns (bool) {\\n // This method relies on extcodesize/address.code.length, which returns 0\\n // for contracts in construction, since the code is only stored at the end\\n // of the constructor execution.\\n\\n return account.code.length > 0;\\n }\\n\\n /**\\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\\n * `recipient`, forwarding all available gas and reverting on errors.\\n *\\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\\n * imposed by `transfer`, making them unable to receive funds via\\n * `transfer`. {sendValue} removes this limitation.\\n *\\n * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].\\n *\\n * IMPORTANT: because control is transferred to `recipient`, care must be\\n * taken to not create reentrancy vulnerabilities. Consider using\\n * {ReentrancyGuard} or the\\n * https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\\n */\\n function sendValue(address payable recipient, uint256 amount) internal {\\n require(address(this).balance >= amount, \\\"Address: insufficient balance\\\");\\n\\n (bool success, ) = recipient.call{value: amount}(\\\"\\\");\\n require(success, \\\"Address: unable to send value, recipient may have reverted\\\");\\n }\\n\\n /**\\n * @dev Performs a Solidity function call using a low level `call`. A\\n * plain `call` is an unsafe replacement for a function call: use this\\n * function instead.\\n *\\n * If `target` reverts with a revert reason, it is bubbled up by this\\n * function (like regular Solidity function calls).\\n *\\n * Returns the raw returned data. To convert to the expected return value,\\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\\n *\\n * Requirements:\\n *\\n * - `target` must be a contract.\\n * - calling `target` with `data` must not revert.\\n *\\n * _Available since v3.1._\\n */\\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\\n return functionCallWithValue(target, data, 0, \\\"Address: low-level call failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\\n * `errorMessage` as a fallback revert reason when `target` reverts.\\n *\\n * _Available since v3.1._\\n */\\n function functionCall(\\n address target,\\n bytes memory data,\\n string memory errorMessage\\n ) internal returns (bytes memory) {\\n return functionCallWithValue(target, data, 0, errorMessage);\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\\n * but also transferring `value` wei to `target`.\\n *\\n * Requirements:\\n *\\n * - the calling contract must have an ETH balance of at least `value`.\\n * - the called Solidity function must be `payable`.\\n *\\n * _Available since v3.1._\\n */\\n function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {\\n return functionCallWithValue(target, data, value, \\\"Address: low-level call with value failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\\n * with `errorMessage` as a fallback revert reason when `target` reverts.\\n *\\n * _Available since v3.1._\\n */\\n function functionCallWithValue(\\n address target,\\n bytes memory data,\\n uint256 value,\\n string memory errorMessage\\n ) internal returns (bytes memory) {\\n require(address(this).balance >= value, \\\"Address: insufficient balance for call\\\");\\n (bool success, bytes memory returndata) = target.call{value: value}(data);\\n return verifyCallResultFromTarget(target, success, returndata, errorMessage);\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\\n * but performing a static call.\\n *\\n * _Available since v3.3._\\n */\\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\\n return functionStaticCall(target, data, \\\"Address: low-level static call failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\\n * but performing a static call.\\n *\\n * _Available since v3.3._\\n */\\n function functionStaticCall(\\n address target,\\n bytes memory data,\\n string memory errorMessage\\n ) internal view returns (bytes memory) {\\n (bool success, bytes memory returndata) = target.staticcall(data);\\n return verifyCallResultFromTarget(target, success, returndata, errorMessage);\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\\n * but performing a delegate call.\\n *\\n * _Available since v3.4._\\n */\\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\\n return functionDelegateCall(target, data, \\\"Address: low-level delegate call failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\\n * but performing a delegate call.\\n *\\n * _Available since v3.4._\\n */\\n function functionDelegateCall(\\n address target,\\n bytes memory data,\\n string memory errorMessage\\n ) internal returns (bytes memory) {\\n (bool success, bytes memory returndata) = target.delegatecall(data);\\n return verifyCallResultFromTarget(target, success, returndata, errorMessage);\\n }\\n\\n /**\\n * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling\\n * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.\\n *\\n * _Available since v4.8._\\n */\\n function verifyCallResultFromTarget(\\n address target,\\n bool success,\\n bytes memory returndata,\\n string memory errorMessage\\n ) internal view returns (bytes memory) {\\n if (success) {\\n if (returndata.length == 0) {\\n // only check isContract if the call was successful and the return data is empty\\n // otherwise we already know that it was a contract\\n require(isContract(target), \\\"Address: call to non-contract\\\");\\n }\\n return returndata;\\n } else {\\n _revert(returndata, errorMessage);\\n }\\n }\\n\\n /**\\n * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the\\n * revert reason or using the provided one.\\n *\\n * _Available since v4.3._\\n */\\n function verifyCallResult(\\n bool success,\\n bytes memory returndata,\\n string memory errorMessage\\n ) internal pure returns (bytes memory) {\\n if (success) {\\n return returndata;\\n } else {\\n _revert(returndata, errorMessage);\\n }\\n }\\n\\n function _revert(bytes memory returndata, string memory errorMessage) private pure {\\n // Look for revert reason and bubble it up if present\\n if (returndata.length > 0) {\\n // The easiest way to bubble the revert reason is using memory via assembly\\n /// @solidity memory-safe-assembly\\n assembly {\\n let returndata_size := mload(returndata)\\n revert(add(32, returndata), returndata_size)\\n }\\n } else {\\n revert(errorMessage);\\n }\\n }\\n}\\n\",\"keccak256\":\"0x006dd67219697fe68d7fbfdea512e7c4cb64a43565ed86171d67e844982da6fa\",\"license\":\"MIT\"},\"@openzeppelin/contracts/utils/StorageSlot.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts (last updated v4.9.0) (utils/StorageSlot.sol)\\n// This file was procedurally generated from scripts/generate/templates/StorageSlot.js.\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev Library for reading and writing primitive types to specific storage slots.\\n *\\n * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.\\n * This library helps with reading and writing to such slots without the need for inline assembly.\\n *\\n * The functions in this library return Slot structs that contain a `value` member that can be used to read or write.\\n *\\n * Example usage to set ERC1967 implementation slot:\\n * ```solidity\\n * contract ERC1967 {\\n * bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;\\n *\\n * function _getImplementation() internal view returns (address) {\\n * return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;\\n * }\\n *\\n * function _setImplementation(address newImplementation) internal {\\n * require(Address.isContract(newImplementation), \\\"ERC1967: new implementation is not a contract\\\");\\n * StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;\\n * }\\n * }\\n * ```\\n *\\n * _Available since v4.1 for `address`, `bool`, `bytes32`, `uint256`._\\n * _Available since v4.9 for `string`, `bytes`._\\n */\\nlibrary StorageSlot {\\n struct AddressSlot {\\n address value;\\n }\\n\\n struct BooleanSlot {\\n bool value;\\n }\\n\\n struct Bytes32Slot {\\n bytes32 value;\\n }\\n\\n struct Uint256Slot {\\n uint256 value;\\n }\\n\\n struct StringSlot {\\n string value;\\n }\\n\\n struct BytesSlot {\\n bytes value;\\n }\\n\\n /**\\n * @dev Returns an `AddressSlot` with member `value` located at `slot`.\\n */\\n function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {\\n /// @solidity memory-safe-assembly\\n assembly {\\n r.slot := slot\\n }\\n }\\n\\n /**\\n * @dev Returns an `BooleanSlot` with member `value` located at `slot`.\\n */\\n function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {\\n /// @solidity memory-safe-assembly\\n assembly {\\n r.slot := slot\\n }\\n }\\n\\n /**\\n * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.\\n */\\n function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {\\n /// @solidity memory-safe-assembly\\n assembly {\\n r.slot := slot\\n }\\n }\\n\\n /**\\n * @dev Returns an `Uint256Slot` with member `value` located at `slot`.\\n */\\n function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {\\n /// @solidity memory-safe-assembly\\n assembly {\\n r.slot := slot\\n }\\n }\\n\\n /**\\n * @dev Returns an `StringSlot` with member `value` located at `slot`.\\n */\\n function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {\\n /// @solidity memory-safe-assembly\\n assembly {\\n r.slot := slot\\n }\\n }\\n\\n /**\\n * @dev Returns an `StringSlot` representation of the string storage pointer `store`.\\n */\\n function getStringSlot(string storage store) internal pure returns (StringSlot storage r) {\\n /// @solidity memory-safe-assembly\\n assembly {\\n r.slot := store.slot\\n }\\n }\\n\\n /**\\n * @dev Returns an `BytesSlot` with member `value` located at `slot`.\\n */\\n function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {\\n /// @solidity memory-safe-assembly\\n assembly {\\n r.slot := slot\\n }\\n }\\n\\n /**\\n * @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.\\n */\\n function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) {\\n /// @solidity memory-safe-assembly\\n assembly {\\n r.slot := store.slot\\n }\\n }\\n}\\n\",\"keccak256\":\"0xf09e68aa0dc6722a25bc46490e8d48ed864466d17313b8a0b254c36b54e49899\",\"license\":\"MIT\"}},\"version\":1}", + "bytecode": "0x60806040526040516106cc3803806106cc83398101604081905261002291610420565b61002e82826000610035565b505061054a565b61003e836100f6565b6040516001600160a01b038416907f1cf3b03a6cf19fa2baba4df148e9dcabedea7f8a5c07840e207e5c089be95d3e90600090a260008251118061007f5750805b156100f1576100ef836001600160a01b0316635c60da1b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156100c5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906100e991906104e0565b8361027a565b505b505050565b6001600160a01b0381163b6101605760405162461bcd60e51b815260206004820152602560248201527f455243313936373a206e657720626561636f6e206973206e6f74206120636f6e6044820152641d1c9858dd60da1b60648201526084015b60405180910390fd5b6101d4816001600160a01b0316635c60da1b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156101a1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906101c591906104e0565b6001600160a01b03163b151590565b6102395760405162461bcd60e51b815260206004820152603060248201527f455243313936373a20626561636f6e20696d706c656d656e746174696f6e206960448201526f1cc81b9bdd08184818dbdb9d1c9858dd60821b6064820152608401610157565b7fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d5080546001600160a01b0319166001600160a01b0392909216919091179055565b606061029f83836040518060600160405280602781526020016106a5602791396102a6565b9392505050565b6060600080856001600160a01b0316856040516102c391906104fb565b600060405180830381855af49150503d80600081146102fe576040519150601f19603f3d011682016040523d82523d6000602084013e610303565b606091505b5090925090506103158683838761031f565b9695505050505050565b6060831561038e578251600003610387576001600160a01b0385163b6103875760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610157565b5081610398565b61039883836103a0565b949350505050565b8151156103b05781518083602001fd5b8060405162461bcd60e51b81526004016101579190610517565b80516001600160a01b03811681146103e157600080fd5b919050565b634e487b7160e01b600052604160045260246000fd5b60005b838110156104175781810151838201526020016103ff565b50506000910152565b6000806040838503121561043357600080fd5b61043c836103ca565b60208401519092506001600160401b038082111561045957600080fd5b818501915085601f83011261046d57600080fd5b81518181111561047f5761047f6103e6565b604051601f8201601f19908116603f011681019083821181831017156104a7576104a76103e6565b816040528281528860208487010111156104c057600080fd5b6104d18360208301602088016103fc565b80955050505050509250929050565b6000602082840312156104f257600080fd5b61029f826103ca565b6000825161050d8184602087016103fc565b9190910192915050565b60208152600082518060208401526105368160408501602087016103fc565b601f01601f19169190910160400192915050565b61014c806105596000396000f3fe60806040523661001357610011610017565b005b6100115b610027610022610029565b6100c2565b565b600061005c7fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50546001600160a01b031690565b6001600160a01b0316635c60da1b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610099573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906100bd91906100e6565b905090565b3660008037600080366000845af43d6000803e8080156100e1573d6000f35b3d6000fd5b6000602082840312156100f857600080fd5b81516001600160a01b038116811461010f57600080fd5b939250505056fea264697066735822122046b4f5e91a9c4ce5899089d64fe12e498987ddec7b0ba224fa5caa490cf7c92464736f6c63430008190033416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564", + "deployedBytecode": "0x60806040523661001357610011610017565b005b6100115b610027610022610029565b6100c2565b565b600061005c7fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50546001600160a01b031690565b6001600160a01b0316635c60da1b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610099573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906100bd91906100e6565b905090565b3660008037600080366000845af43d6000803e8080156100e1573d6000f35b3d6000fd5b6000602082840312156100f857600080fd5b81516001600160a01b038116811461010f57600080fd5b939250505056fea264697066735822122046b4f5e91a9c4ce5899089d64fe12e498987ddec7b0ba224fa5caa490cf7c92464736f6c63430008190033", + "devdoc": { + "details": "This contract implements a proxy that gets the implementation address for each call from an {UpgradeableBeacon}. The beacon address is stored in storage slot `uint256(keccak256('eip1967.proxy.beacon')) - 1`, so that it doesn't conflict with the storage layout of the implementation behind the proxy. _Available since v3.4._", + "events": { + "AdminChanged(address,address)": { + "details": "Emitted when the admin account has changed." + }, + "BeaconUpgraded(address)": { + "details": "Emitted when the beacon is changed." + }, + "Upgraded(address)": { + "details": "Emitted when the implementation is upgraded." + } + }, + "kind": "dev", + "methods": { + "constructor": { + "details": "Initializes the proxy with `beacon`. If `data` is nonempty, it's used as data in a delegate call to the implementation returned by the beacon. This will typically be an encoded function call, and allows initializing the storage of the proxy like a Solidity constructor. Requirements: - `beacon` must be a contract with the interface {IBeacon}." + } + }, + "version": 1 + }, + "userdoc": { + "kind": "user", + "methods": {}, + "version": 1 + }, + "storageLayout": { + "storage": [], + "types": null + } +} diff --git a/deployments/bsctestnet/SpokeComptrollerBeacon.json b/deployments/bsctestnet/SpokeComptrollerBeacon.json new file mode 100644 index 000000000..c61f870f2 --- /dev/null +++ b/deployments/bsctestnet/SpokeComptrollerBeacon.json @@ -0,0 +1,206 @@ +{ + "address": "0x076f3fb34C8937a62aD562c33249798367542d75", + "abi": [ + { + "inputs": [ + { + "internalType": "address", + "name": "implementation_", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "implementation", + "type": "address" + } + ], + "name": "Upgraded", + "type": "event" + }, + { + "inputs": [], + "name": "implementation", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newImplementation", + "type": "address" + } + ], + "name": "upgradeTo", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } + ], + "transactionHash": "0x1a08151b4bcc36c9b37f7b4123231828b90d0bbccd6f7f453062035dbedc6518", + "receipt": { + "to": null, + "from": "0x33C6476F88eeA28D7E7900F759B4597704Ef95B7", + "contractAddress": "0x076f3fb34C8937a62aD562c33249798367542d75", + "transactionIndex": 0, + "gasUsed": "288554", + "logsBloom": "0x00000000000000000000000000000000000000000000000000800000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000002000000000000000000000000000000020000000000000000000800000000200000000000000000000000400000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000004000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0x8fc090a4a8836fce44523d5105c5c298b8fd988c7e98b362ff8e60dcde73e346", + "transactionHash": "0x1a08151b4bcc36c9b37f7b4123231828b90d0bbccd6f7f453062035dbedc6518", + "logs": [ + { + "transactionIndex": 0, + "blockNumber": 129845201, + "transactionHash": "0x1a08151b4bcc36c9b37f7b4123231828b90d0bbccd6f7f453062035dbedc6518", + "address": "0x076f3fb34C8937a62aD562c33249798367542d75", + "topics": [ + "0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x00000000000000000000000033c6476f88eea28d7e7900f759b4597704ef95b7" + ], + "data": "0x", + "logIndex": 0, + "blockHash": "0x8fc090a4a8836fce44523d5105c5c298b8fd988c7e98b362ff8e60dcde73e346" + } + ], + "blockNumber": 129845201, + "cumulativeGasUsed": "288554", + "status": 1, + "byzantium": true + }, + "args": ["0x7F81dC61F3D75569A67155fb188171Aef173a52b"], + "numDeployments": 1, + "solcInputHash": "02cfe3974aa10474c22406a3f2870e89", + "metadata": "{\"compiler\":{\"version\":\"0.8.25+commit.b61c2a91\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"implementation_\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousOwner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"implementation\",\"type\":\"address\"}],\"name\":\"Upgraded\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"implementation\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"renounceOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newImplementation\",\"type\":\"address\"}],\"name\":\"upgradeTo\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}],\"devdoc\":{\"details\":\"This contract is used in conjunction with one or more instances of {BeaconProxy} to determine their implementation contract, which is where they will delegate all function calls. An owner is able to change the implementation the beacon points to, thus upgrading the proxies that use this beacon.\",\"events\":{\"Upgraded(address)\":{\"details\":\"Emitted when the implementation returned by the beacon is changed.\"}},\"kind\":\"dev\",\"methods\":{\"constructor\":{\"details\":\"Sets the address of the initial implementation, and the deployer account as the owner who can upgrade the beacon.\"},\"implementation()\":{\"details\":\"Returns the current implementation address.\"},\"owner()\":{\"details\":\"Returns the address of the current owner.\"},\"renounceOwnership()\":{\"details\":\"Leaves the contract without owner. It will not be possible to call `onlyOwner` functions. Can only be called by the current owner. NOTE: Renouncing ownership will leave the contract without an owner, thereby disabling any functionality that is only available to the owner.\"},\"transferOwnership(address)\":{\"details\":\"Transfers ownership of the contract to a new account (`newOwner`). Can only be called by the current owner.\"},\"upgradeTo(address)\":{\"details\":\"Upgrades the beacon to a new implementation. Emits an {Upgraded} event. Requirements: - msg.sender must be the owner of the contract. - `newImplementation` must be a contract.\"}},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol\":\"UpgradeableBeacon\"},\"evmVersion\":\"paris\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\",\"useLiteralContent\":true},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[]},\"sources\":{\"@openzeppelin/contracts/access/Ownable.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)\\n\\npragma solidity ^0.8.0;\\n\\nimport \\\"../utils/Context.sol\\\";\\n\\n/**\\n * @dev Contract module which provides a basic access control mechanism, where\\n * there is an account (an owner) that can be granted exclusive access to\\n * specific functions.\\n *\\n * By default, the owner account will be the one that deploys the contract. This\\n * can later be changed with {transferOwnership}.\\n *\\n * This module is used through inheritance. It will make available the modifier\\n * `onlyOwner`, which can be applied to your functions to restrict their use to\\n * the owner.\\n */\\nabstract contract Ownable is Context {\\n address private _owner;\\n\\n event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\\n\\n /**\\n * @dev Initializes the contract setting the deployer as the initial owner.\\n */\\n constructor() {\\n _transferOwnership(_msgSender());\\n }\\n\\n /**\\n * @dev Throws if called by any account other than the owner.\\n */\\n modifier onlyOwner() {\\n _checkOwner();\\n _;\\n }\\n\\n /**\\n * @dev Returns the address of the current owner.\\n */\\n function owner() public view virtual returns (address) {\\n return _owner;\\n }\\n\\n /**\\n * @dev Throws if the sender is not the owner.\\n */\\n function _checkOwner() internal view virtual {\\n require(owner() == _msgSender(), \\\"Ownable: caller is not the owner\\\");\\n }\\n\\n /**\\n * @dev Leaves the contract without owner. It will not be possible to call\\n * `onlyOwner` functions. Can only be called by the current owner.\\n *\\n * NOTE: Renouncing ownership will leave the contract without an owner,\\n * thereby disabling any functionality that is only available to the owner.\\n */\\n function renounceOwnership() public virtual onlyOwner {\\n _transferOwnership(address(0));\\n }\\n\\n /**\\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\\n * Can only be called by the current owner.\\n */\\n function transferOwnership(address newOwner) public virtual onlyOwner {\\n require(newOwner != address(0), \\\"Ownable: new owner is the zero address\\\");\\n _transferOwnership(newOwner);\\n }\\n\\n /**\\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\\n * Internal function without access restriction.\\n */\\n function _transferOwnership(address newOwner) internal virtual {\\n address oldOwner = _owner;\\n _owner = newOwner;\\n emit OwnershipTransferred(oldOwner, newOwner);\\n }\\n}\\n\",\"keccak256\":\"0xba43b97fba0d32eb4254f6a5a297b39a19a247082a02d6e69349e071e2946218\",\"license\":\"MIT\"},\"@openzeppelin/contracts/proxy/beacon/IBeacon.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts v4.4.1 (proxy/beacon/IBeacon.sol)\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev This is the interface that {BeaconProxy} expects of its beacon.\\n */\\ninterface IBeacon {\\n /**\\n * @dev Must return an address that can be used as a delegate call target.\\n *\\n * {BeaconProxy} will check that this address is a contract.\\n */\\n function implementation() external view returns (address);\\n}\\n\",\"keccak256\":\"0xd50a3421ac379ccb1be435fa646d66a65c986b4924f0849839f08692f39dde61\",\"license\":\"MIT\"},\"@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts v4.4.1 (proxy/beacon/UpgradeableBeacon.sol)\\n\\npragma solidity ^0.8.0;\\n\\nimport \\\"./IBeacon.sol\\\";\\nimport \\\"../../access/Ownable.sol\\\";\\nimport \\\"../../utils/Address.sol\\\";\\n\\n/**\\n * @dev This contract is used in conjunction with one or more instances of {BeaconProxy} to determine their\\n * implementation contract, which is where they will delegate all function calls.\\n *\\n * An owner is able to change the implementation the beacon points to, thus upgrading the proxies that use this beacon.\\n */\\ncontract UpgradeableBeacon is IBeacon, Ownable {\\n address private _implementation;\\n\\n /**\\n * @dev Emitted when the implementation returned by the beacon is changed.\\n */\\n event Upgraded(address indexed implementation);\\n\\n /**\\n * @dev Sets the address of the initial implementation, and the deployer account as the owner who can upgrade the\\n * beacon.\\n */\\n constructor(address implementation_) {\\n _setImplementation(implementation_);\\n }\\n\\n /**\\n * @dev Returns the current implementation address.\\n */\\n function implementation() public view virtual override returns (address) {\\n return _implementation;\\n }\\n\\n /**\\n * @dev Upgrades the beacon to a new implementation.\\n *\\n * Emits an {Upgraded} event.\\n *\\n * Requirements:\\n *\\n * - msg.sender must be the owner of the contract.\\n * - `newImplementation` must be a contract.\\n */\\n function upgradeTo(address newImplementation) public virtual onlyOwner {\\n _setImplementation(newImplementation);\\n emit Upgraded(newImplementation);\\n }\\n\\n /**\\n * @dev Sets the implementation contract address for this beacon\\n *\\n * Requirements:\\n *\\n * - `newImplementation` must be a contract.\\n */\\n function _setImplementation(address newImplementation) private {\\n require(Address.isContract(newImplementation), \\\"UpgradeableBeacon: implementation is not a contract\\\");\\n _implementation = newImplementation;\\n }\\n}\\n\",\"keccak256\":\"0x6ec71aef5659f3f74011169948d2fcda8c6599be5bb38f986380a8737f96cc0f\",\"license\":\"MIT\"},\"@openzeppelin/contracts/utils/Address.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)\\n\\npragma solidity ^0.8.1;\\n\\n/**\\n * @dev Collection of functions related to the address type\\n */\\nlibrary Address {\\n /**\\n * @dev Returns true if `account` is a contract.\\n *\\n * [IMPORTANT]\\n * ====\\n * It is unsafe to assume that an address for which this function returns\\n * false is an externally-owned account (EOA) and not a contract.\\n *\\n * Among others, `isContract` will return false for the following\\n * types of addresses:\\n *\\n * - an externally-owned account\\n * - a contract in construction\\n * - an address where a contract will be created\\n * - an address where a contract lived, but was destroyed\\n *\\n * Furthermore, `isContract` will also return true if the target contract within\\n * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,\\n * which only has an effect at the end of a transaction.\\n * ====\\n *\\n * [IMPORTANT]\\n * ====\\n * You shouldn't rely on `isContract` to protect against flash loan attacks!\\n *\\n * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets\\n * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract\\n * constructor.\\n * ====\\n */\\n function isContract(address account) internal view returns (bool) {\\n // This method relies on extcodesize/address.code.length, which returns 0\\n // for contracts in construction, since the code is only stored at the end\\n // of the constructor execution.\\n\\n return account.code.length > 0;\\n }\\n\\n /**\\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\\n * `recipient`, forwarding all available gas and reverting on errors.\\n *\\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\\n * imposed by `transfer`, making them unable to receive funds via\\n * `transfer`. {sendValue} removes this limitation.\\n *\\n * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].\\n *\\n * IMPORTANT: because control is transferred to `recipient`, care must be\\n * taken to not create reentrancy vulnerabilities. Consider using\\n * {ReentrancyGuard} or the\\n * https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\\n */\\n function sendValue(address payable recipient, uint256 amount) internal {\\n require(address(this).balance >= amount, \\\"Address: insufficient balance\\\");\\n\\n (bool success, ) = recipient.call{value: amount}(\\\"\\\");\\n require(success, \\\"Address: unable to send value, recipient may have reverted\\\");\\n }\\n\\n /**\\n * @dev Performs a Solidity function call using a low level `call`. A\\n * plain `call` is an unsafe replacement for a function call: use this\\n * function instead.\\n *\\n * If `target` reverts with a revert reason, it is bubbled up by this\\n * function (like regular Solidity function calls).\\n *\\n * Returns the raw returned data. To convert to the expected return value,\\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\\n *\\n * Requirements:\\n *\\n * - `target` must be a contract.\\n * - calling `target` with `data` must not revert.\\n *\\n * _Available since v3.1._\\n */\\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\\n return functionCallWithValue(target, data, 0, \\\"Address: low-level call failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\\n * `errorMessage` as a fallback revert reason when `target` reverts.\\n *\\n * _Available since v3.1._\\n */\\n function functionCall(\\n address target,\\n bytes memory data,\\n string memory errorMessage\\n ) internal returns (bytes memory) {\\n return functionCallWithValue(target, data, 0, errorMessage);\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\\n * but also transferring `value` wei to `target`.\\n *\\n * Requirements:\\n *\\n * - the calling contract must have an ETH balance of at least `value`.\\n * - the called Solidity function must be `payable`.\\n *\\n * _Available since v3.1._\\n */\\n function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {\\n return functionCallWithValue(target, data, value, \\\"Address: low-level call with value failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\\n * with `errorMessage` as a fallback revert reason when `target` reverts.\\n *\\n * _Available since v3.1._\\n */\\n function functionCallWithValue(\\n address target,\\n bytes memory data,\\n uint256 value,\\n string memory errorMessage\\n ) internal returns (bytes memory) {\\n require(address(this).balance >= value, \\\"Address: insufficient balance for call\\\");\\n (bool success, bytes memory returndata) = target.call{value: value}(data);\\n return verifyCallResultFromTarget(target, success, returndata, errorMessage);\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\\n * but performing a static call.\\n *\\n * _Available since v3.3._\\n */\\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\\n return functionStaticCall(target, data, \\\"Address: low-level static call failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\\n * but performing a static call.\\n *\\n * _Available since v3.3._\\n */\\n function functionStaticCall(\\n address target,\\n bytes memory data,\\n string memory errorMessage\\n ) internal view returns (bytes memory) {\\n (bool success, bytes memory returndata) = target.staticcall(data);\\n return verifyCallResultFromTarget(target, success, returndata, errorMessage);\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\\n * but performing a delegate call.\\n *\\n * _Available since v3.4._\\n */\\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\\n return functionDelegateCall(target, data, \\\"Address: low-level delegate call failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\\n * but performing a delegate call.\\n *\\n * _Available since v3.4._\\n */\\n function functionDelegateCall(\\n address target,\\n bytes memory data,\\n string memory errorMessage\\n ) internal returns (bytes memory) {\\n (bool success, bytes memory returndata) = target.delegatecall(data);\\n return verifyCallResultFromTarget(target, success, returndata, errorMessage);\\n }\\n\\n /**\\n * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling\\n * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.\\n *\\n * _Available since v4.8._\\n */\\n function verifyCallResultFromTarget(\\n address target,\\n bool success,\\n bytes memory returndata,\\n string memory errorMessage\\n ) internal view returns (bytes memory) {\\n if (success) {\\n if (returndata.length == 0) {\\n // only check isContract if the call was successful and the return data is empty\\n // otherwise we already know that it was a contract\\n require(isContract(target), \\\"Address: call to non-contract\\\");\\n }\\n return returndata;\\n } else {\\n _revert(returndata, errorMessage);\\n }\\n }\\n\\n /**\\n * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the\\n * revert reason or using the provided one.\\n *\\n * _Available since v4.3._\\n */\\n function verifyCallResult(\\n bool success,\\n bytes memory returndata,\\n string memory errorMessage\\n ) internal pure returns (bytes memory) {\\n if (success) {\\n return returndata;\\n } else {\\n _revert(returndata, errorMessage);\\n }\\n }\\n\\n function _revert(bytes memory returndata, string memory errorMessage) private pure {\\n // Look for revert reason and bubble it up if present\\n if (returndata.length > 0) {\\n // The easiest way to bubble the revert reason is using memory via assembly\\n /// @solidity memory-safe-assembly\\n assembly {\\n let returndata_size := mload(returndata)\\n revert(add(32, returndata), returndata_size)\\n }\\n } else {\\n revert(errorMessage);\\n }\\n }\\n}\\n\",\"keccak256\":\"0x006dd67219697fe68d7fbfdea512e7c4cb64a43565ed86171d67e844982da6fa\",\"license\":\"MIT\"},\"@openzeppelin/contracts/utils/Context.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts (last updated v4.9.4) (utils/Context.sol)\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev Provides information about the current execution context, including the\\n * sender of the transaction and its data. While these are generally available\\n * via msg.sender and msg.data, they should not be accessed in such a direct\\n * manner, since when dealing with meta-transactions the account sending and\\n * paying for execution may not be the actual sender (as far as an application\\n * is concerned).\\n *\\n * This contract is only required for intermediate, library-like contracts.\\n */\\nabstract contract Context {\\n function _msgSender() internal view virtual returns (address) {\\n return msg.sender;\\n }\\n\\n function _msgData() internal view virtual returns (bytes calldata) {\\n return msg.data;\\n }\\n\\n function _contextSuffixLength() internal view virtual returns (uint256) {\\n return 0;\\n }\\n}\\n\",\"keccak256\":\"0xa92e4fa126feb6907daa0513ddd816b2eb91f30a808de54f63c17d0e162c3439\",\"license\":\"MIT\"}},\"version\":1}", + "bytecode": "0x608060405234801561001057600080fd5b506040516104be3803806104be83398101604081905261002f9161013a565b61003833610047565b61004181610097565b5061016a565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6001600160a01b0381163b6101185760405162461bcd60e51b815260206004820152603360248201527f5570677261646561626c65426561636f6e3a20696d706c656d656e746174696f60448201527f6e206973206e6f74206120636f6e747261637400000000000000000000000000606482015260840160405180910390fd5b600180546001600160a01b0319166001600160a01b0392909216919091179055565b60006020828403121561014c57600080fd5b81516001600160a01b038116811461016357600080fd5b9392505050565b610345806101796000396000f3fe608060405234801561001057600080fd5b50600436106100575760003560e01c80633659cfe61461005c5780635c60da1b14610071578063715018a61461009a5780638da5cb5b146100a2578063f2fde38b146100b3575b600080fd5b61006f61006a3660046102df565b6100c6565b005b6001546001600160a01b03165b6040516001600160a01b03909116815260200160405180910390f35b61006f61010e565b6000546001600160a01b031661007e565b61006f6100c13660046102df565b610122565b6100ce6101a0565b6100d7816101fa565b6040516001600160a01b038216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b6101166101a0565b610120600061028f565b565b61012a6101a0565b6001600160a01b0381166101945760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b60648201526084015b60405180910390fd5b61019d8161028f565b50565b6000546001600160a01b031633146101205760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161018b565b6001600160a01b0381163b61026d5760405162461bcd60e51b815260206004820152603360248201527f5570677261646561626c65426561636f6e3a20696d706c656d656e746174696f6044820152721b881a5cc81b9bdd08184818dbdb9d1c9858dd606a1b606482015260840161018b565b600180546001600160a01b0319166001600160a01b0392909216919091179055565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6000602082840312156102f157600080fd5b81356001600160a01b038116811461030857600080fd5b939250505056fea2646970667358221220d545fd9e5dad1533895a65b3a05d1e6f1c1c47d5577fa2c59eec7f8e53dae96b64736f6c63430008190033", + "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106100575760003560e01c80633659cfe61461005c5780635c60da1b14610071578063715018a61461009a5780638da5cb5b146100a2578063f2fde38b146100b3575b600080fd5b61006f61006a3660046102df565b6100c6565b005b6001546001600160a01b03165b6040516001600160a01b03909116815260200160405180910390f35b61006f61010e565b6000546001600160a01b031661007e565b61006f6100c13660046102df565b610122565b6100ce6101a0565b6100d7816101fa565b6040516001600160a01b038216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b6101166101a0565b610120600061028f565b565b61012a6101a0565b6001600160a01b0381166101945760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b60648201526084015b60405180910390fd5b61019d8161028f565b50565b6000546001600160a01b031633146101205760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161018b565b6001600160a01b0381163b61026d5760405162461bcd60e51b815260206004820152603360248201527f5570677261646561626c65426561636f6e3a20696d706c656d656e746174696f6044820152721b881a5cc81b9bdd08184818dbdb9d1c9858dd606a1b606482015260840161018b565b600180546001600160a01b0319166001600160a01b0392909216919091179055565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6000602082840312156102f157600080fd5b81356001600160a01b038116811461030857600080fd5b939250505056fea2646970667358221220d545fd9e5dad1533895a65b3a05d1e6f1c1c47d5577fa2c59eec7f8e53dae96b64736f6c63430008190033", + "devdoc": { + "details": "This contract is used in conjunction with one or more instances of {BeaconProxy} to determine their implementation contract, which is where they will delegate all function calls. An owner is able to change the implementation the beacon points to, thus upgrading the proxies that use this beacon.", + "events": { + "Upgraded(address)": { + "details": "Emitted when the implementation returned by the beacon is changed." + } + }, + "kind": "dev", + "methods": { + "constructor": { + "details": "Sets the address of the initial implementation, and the deployer account as the owner who can upgrade the beacon." + }, + "implementation()": { + "details": "Returns the current implementation address." + }, + "owner()": { + "details": "Returns the address of the current owner." + }, + "renounceOwnership()": { + "details": "Leaves the contract without owner. It will not be possible to call `onlyOwner` functions. Can only be called by the current owner. NOTE: Renouncing ownership will leave the contract without an owner, thereby disabling any functionality that is only available to the owner." + }, + "transferOwnership(address)": { + "details": "Transfers ownership of the contract to a new account (`newOwner`). Can only be called by the current owner." + }, + "upgradeTo(address)": { + "details": "Upgrades the beacon to a new implementation. Emits an {Upgraded} event. Requirements: - msg.sender must be the owner of the contract. - `newImplementation` must be a contract." + } + }, + "version": 1 + }, + "userdoc": { + "kind": "user", + "methods": {}, + "version": 1 + }, + "storageLayout": { + "storage": [ + { + "astId": 3501, + "contract": "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol:UpgradeableBeacon", + "label": "_owner", + "offset": 0, + "slot": "0", + "type": "t_address" + }, + { + "astId": 4164, + "contract": "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol:UpgradeableBeacon", + "label": "_implementation", + "offset": 0, + "slot": "1", + "type": "t_address" + } + ], + "types": { + "t_address": { + "encoding": "inplace", + "label": "address", + "numberOfBytes": "20" + } + } + } +} diff --git a/deployments/bsctestnet/SpokeComptrollerImpl.json b/deployments/bsctestnet/SpokeComptrollerImpl.json new file mode 100644 index 000000000..fb94a8850 --- /dev/null +++ b/deployments/bsctestnet/SpokeComptrollerImpl.json @@ -0,0 +1,2982 @@ +{ + "address": "0x7F81dC61F3D75569A67155fb188171Aef173a52b", + "abi": [ + { + "inputs": [ + { + "internalType": "address", + "name": "poolRegistry_", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "market", + "type": "address" + }, + { + "internalType": "enum Action", + "name": "action", + "type": "uint8" + } + ], + "name": "ActionPaused", + "type": "error" + }, + { + "inputs": [], + "name": "BorrowActionNotPaused", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "market", + "type": "address" + }, + { + "internalType": "uint256", + "name": "cap", + "type": "uint256" + } + ], + "name": "BorrowCapExceeded", + "type": "error" + }, + { + "inputs": [], + "name": "BorrowCapIsNotZero", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "borrows", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxClearableDebt", + "type": "uint256" + } + ], + "name": "CollateralCoversDebt", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "expectedLessThanOrEqualTo", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "actual", + "type": "uint256" + } + ], + "name": "CollateralExceedsThreshold", + "type": "error" + }, + { + "inputs": [], + "name": "CollateralFactorIsNotZero", + "type": "error" + }, + { + "inputs": [], + "name": "ComptrollerMismatch", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "borrows", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxClearableDebt", + "type": "uint256" + } + ], + "name": "DebtExceedsClearableAmount", + "type": "error" + }, + { + "inputs": [], + "name": "DelegationStatusUnchanged", + "type": "error" + }, + { + "inputs": [], + "name": "EnterMarketActionNotPaused", + "type": "error" + }, + { + "inputs": [], + "name": "ExitMarketActionNotPaused", + "type": "error" + }, + { + "inputs": [], + "name": "InsufficientLiquidity", + "type": "error" + }, + { + "inputs": [], + "name": "InsufficientShortfall", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidArrayLength", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidCloseFactor", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidCollateralFactor", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidLiquidationIncentive", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidLiquidationThreshold", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidVToken", + "type": "error" + }, + { + "inputs": [], + "name": "LiquidateActionNotPaused", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "liquidator", + "type": "address" + } + ], + "name": "LiquidationNotAllowed", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "market", + "type": "address" + } + ], + "name": "MarketAlreadyListed", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "vToken", + "type": "address" + }, + { + "internalType": "address", + "name": "user", + "type": "address" + } + ], + "name": "MarketNotCollateral", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "market", + "type": "address" + } + ], + "name": "MarketNotListed", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "loopsLimit", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "requiredLoops", + "type": "uint256" + } + ], + "name": "MaxLoopsLimitExceeded", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "expectedGreaterThan", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "actual", + "type": "uint256" + } + ], + "name": "MinimalCollateralViolated", + "type": "error" + }, + { + "inputs": [], + "name": "MintActionNotPaused", + "type": "error" + }, + { + "inputs": [], + "name": "NonzeroBorrowBalance", + "type": "error" + }, + { + "inputs": [], + "name": "NonzeroBorrowBalanceAfterLiquidation", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "vToken", + "type": "address" + } + ], + "name": "PriceError", + "type": "error" + }, + { + "inputs": [], + "name": "RedeemActionNotPaused", + "type": "error" + }, + { + "inputs": [], + "name": "RepayActionNotPaused", + "type": "error" + }, + { + "inputs": [], + "name": "RewardsDistributorAlreadyExists", + "type": "error" + }, + { + "inputs": [], + "name": "SeizeActionNotPaused", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "vToken", + "type": "address" + }, + { + "internalType": "address", + "name": "user", + "type": "address" + } + ], + "name": "SnapshotError", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "market", + "type": "address" + }, + { + "internalType": "uint256", + "name": "cap", + "type": "uint256" + } + ], + "name": "SupplyCapExceeded", + "type": "error" + }, + { + "inputs": [], + "name": "SupplyCapIsNotZero", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "market", + "type": "address" + }, + { + "internalType": "address", + "name": "supplier", + "type": "address" + } + ], + "name": "SupplyNotAllowed", + "type": "error" + }, + { + "inputs": [], + "name": "TooMuchRepay", + "type": "error" + }, + { + "inputs": [], + "name": "TransferActionNotPaused", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "address", + "name": "calledContract", + "type": "address" + }, + { + "internalType": "string", + "name": "methodSignature", + "type": "string" + } + ], + "name": "Unauthorized", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "expectedSender", + "type": "address" + }, + { + "internalType": "address", + "name": "actualSender", + "type": "address" + } + ], + "name": "UnexpectedSender", + "type": "error" + }, + { + "inputs": [], + "name": "ZeroAddressNotAllowed", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "contract VToken", + "name": "vToken", + "type": "address" + }, + { + "indexed": false, + "internalType": "enum Action", + "name": "action", + "type": "uint8" + }, + { + "indexed": false, + "internalType": "bool", + "name": "pauseState", + "type": "bool" + } + ], + "name": "ActionPausedMarket", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "liquidator", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "allowed", + "type": "bool" + } + ], + "name": "AllowedLiquidatorUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "vToken", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "supplier", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "allowed", + "type": "bool" + } + ], + "name": "AllowedSupplierUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "approver", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "delegate", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ], + "name": "DelegateUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint8", + "name": "version", + "type": "uint8" + } + ], + "name": "Initialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "vToken", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "enable", + "type": "bool" + } + ], + "name": "IsForcedLiquidationEnabledUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bool", + "name": "enabled", + "type": "bool" + } + ], + "name": "LiquidationAllowlistEnabledUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "contract VToken", + "name": "vToken", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "MarketEntered", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "contract VToken", + "name": "vToken", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "MarketExited", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "contract VToken", + "name": "vToken", + "type": "address" + } + ], + "name": "MarketSupported", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "vToken", + "type": "address" + } + ], + "name": "MarketUnlisted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "oldMaxLoopsLimit", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newmaxLoopsLimit", + "type": "uint256" + } + ], + "name": "MaxLoopsLimitUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "oldAccessControlManager", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "newAccessControlManager", + "type": "address" + } + ], + "name": "NewAccessControlManager", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "contract VToken", + "name": "vToken", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newBorrowCap", + "type": "uint256" + } + ], + "name": "NewBorrowCap", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "oldCloseFactorMantissa", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newCloseFactorMantissa", + "type": "uint256" + } + ], + "name": "NewCloseFactor", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "contract VToken", + "name": "vToken", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "oldCollateralFactorMantissa", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newCollateralFactorMantissa", + "type": "uint256" + } + ], + "name": "NewCollateralFactor", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "contract IDeviationBoundedOracle", + "name": "oldBoundedOracle", + "type": "address" + }, + { + "indexed": false, + "internalType": "contract IDeviationBoundedOracle", + "name": "newBoundedOracle", + "type": "address" + } + ], + "name": "NewDeviationBoundedOracle", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "oldLiquidationIncentiveMantissa", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newLiquidationIncentiveMantissa", + "type": "uint256" + } + ], + "name": "NewLiquidationIncentive", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "contract VToken", + "name": "vToken", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "oldLiquidationThresholdMantissa", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newLiquidationThresholdMantissa", + "type": "uint256" + } + ], + "name": "NewLiquidationThreshold", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "vToken", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "oldLiquidationIncentiveMantissa", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newLiquidationIncentiveMantissa", + "type": "uint256" + } + ], + "name": "NewMarketLiquidationIncentive", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "oldMinLiquidatableCollateral", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newMinLiquidatableCollateral", + "type": "uint256" + } + ], + "name": "NewMinLiquidatableCollateral", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "contract ResilientOracleInterface", + "name": "oldPriceOracle", + "type": "address" + }, + { + "indexed": false, + "internalType": "contract ResilientOracleInterface", + "name": "newPriceOracle", + "type": "address" + } + ], + "name": "NewPriceOracle", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "rewardsDistributor", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "rewardToken", + "type": "address" + } + ], + "name": "NewRewardsDistributor", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "contract VToken", + "name": "vToken", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newSupplyCap", + "type": "uint256" + } + ], + "name": "NewSupplyCap", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferStarted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "vToken", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "enabled", + "type": "bool" + } + ], + "name": "SupplyAllowlistEnabledUpdated", + "type": "event" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "accessControlManager", + "outputs": [ + { + "internalType": "contract IAccessControlManagerV8", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "accountAssets", + "outputs": [ + { + "internalType": "contract VToken", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "market", + "type": "address" + }, + { + "internalType": "enum Action", + "name": "action", + "type": "uint8" + } + ], + "name": "actionPaused", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract RewardsDistributor", + "name": "_rewardsDistributor", + "type": "address" + } + ], + "name": "addRewardsDistributor", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "allMarkets", + "outputs": [ + { + "internalType": "contract VToken", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "approvedDelegates", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "borrowCaps", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "borrowVerify", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "contract VToken", + "name": "vToken", + "type": "address" + } + ], + "name": "checkMembership", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "closeFactorMantissa", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "deviationBoundedOracle", + "outputs": [ + { + "internalType": "contract IDeviationBoundedOracle", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "vToken", + "type": "address" + } + ], + "name": "effectiveLiquidationIncentive", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "vToken", + "type": "address" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "enterMarketBehalf", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "vTokens", + "type": "address[]" + } + ], + "name": "enterMarkets", + "outputs": [ + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "vTokenAddress", + "type": "address" + } + ], + "name": "exitMarket", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "getAccountLiquidity", + "outputs": [ + { + "internalType": "uint256", + "name": "error", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "liquidity", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "shortfall", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getAllMarkets", + "outputs": [ + { + "internalType": "contract VToken[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "getAssetsIn", + "outputs": [ + { + "internalType": "contract VToken[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "getBorrowingPower", + "outputs": [ + { + "internalType": "uint256", + "name": "error", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "liquidity", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "shortfall", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "address", + "name": "vTokenModify", + "type": "address" + }, + { + "internalType": "uint256", + "name": "redeemTokens", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "borrowAmount", + "type": "uint256" + } + ], + "name": "getHypotheticalAccountLiquidity", + "outputs": [ + { + "internalType": "uint256", + "name": "error", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "liquidity", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "shortfall", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getRewardDistributors", + "outputs": [ + { + "internalType": "contract RewardsDistributor[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "vToken", + "type": "address" + } + ], + "name": "getRewardsByMarket", + "outputs": [ + { + "components": [ + { + "internalType": "address", + "name": "rewardToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "supplySpeed", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "borrowSpeed", + "type": "uint256" + } + ], + "internalType": "struct SpokeComptrollerStorage.RewardSpeeds[]", + "name": "rewardSpeeds", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "user", + "type": "address" + } + ], + "name": "healAccount", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "loopLimit", + "type": "uint256" + }, + { + "internalType": "address", + "name": "accessControlManager", + "type": "address" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "isAllowedLiquidator", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "isAllowedSupplier", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "isComptroller", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "isForcedLiquidationEnabled", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "isLiquidationAllowlistEnabled", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract VToken", + "name": "vToken", + "type": "address" + } + ], + "name": "isMarketListed", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "isSupplyAllowlistEnabled", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "borrower", + "type": "address" + }, + { + "components": [ + { + "internalType": "contract VToken", + "name": "vTokenCollateral", + "type": "address" + }, + { + "internalType": "contract VToken", + "name": "vTokenBorrowed", + "type": "address" + }, + { + "internalType": "uint256", + "name": "repayAmount", + "type": "uint256" + } + ], + "internalType": "struct SpokeComptrollerStorage.LiquidationOrder[]", + "name": "orders", + "type": "tuple[]" + } + ], + "name": "liquidateAccount", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "liquidateBorrowVerify", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "vTokenBorrowed", + "type": "address" + }, + { + "internalType": "address", + "name": "vTokenCollateral", + "type": "address" + }, + { + "internalType": "uint256", + "name": "actualRepayAmount", + "type": "uint256" + } + ], + "name": "liquidateCalculateSeizeTokens", + "outputs": [ + { + "internalType": "uint256", + "name": "error", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "tokensToSeize", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "liquidationIncentiveMantissa", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "liquidationIncentives", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "markets", + "outputs": [ + { + "internalType": "bool", + "name": "isListed", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "collateralFactorMantissa", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "liquidationThresholdMantissa", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "maxLoopsLimit", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "minLiquidatableCollateral", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "mintVerify", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "oracle", + "outputs": [ + { + "internalType": "contract ResilientOracleInterface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pendingOwner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "poolRegistry", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "vToken", + "type": "address" + }, + { + "internalType": "address", + "name": "borrower", + "type": "address" + }, + { + "internalType": "uint256", + "name": "borrowAmount", + "type": "uint256" + } + ], + "name": "preBorrowHook", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "vTokenBorrowed", + "type": "address" + }, + { + "internalType": "address", + "name": "vTokenCollateral", + "type": "address" + }, + { + "internalType": "address", + "name": "borrower", + "type": "address" + }, + { + "internalType": "uint256", + "name": "repayAmount", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "skipLiquidityCheck", + "type": "bool" + } + ], + "name": "preLiquidateHook", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "vToken", + "type": "address" + }, + { + "internalType": "address", + "name": "minter", + "type": "address" + }, + { + "internalType": "uint256", + "name": "mintAmount", + "type": "uint256" + } + ], + "name": "preMintHook", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "vToken", + "type": "address" + }, + { + "internalType": "address", + "name": "redeemer", + "type": "address" + }, + { + "internalType": "uint256", + "name": "redeemTokens", + "type": "uint256" + } + ], + "name": "preRedeemHook", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "vToken", + "type": "address" + }, + { + "internalType": "address", + "name": "borrower", + "type": "address" + } + ], + "name": "preRepayHook", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "vTokenCollateral", + "type": "address" + }, + { + "internalType": "address", + "name": "seizerContract", + "type": "address" + }, + { + "internalType": "address", + "name": "liquidator", + "type": "address" + }, + { + "internalType": "address", + "name": "borrower", + "type": "address" + } + ], + "name": "preSeizeHook", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "vToken", + "type": "address" + }, + { + "internalType": "address", + "name": "src", + "type": "address" + }, + { + "internalType": "address", + "name": "dst", + "type": "address" + }, + { + "internalType": "uint256", + "name": "transferTokens", + "type": "uint256" + } + ], + "name": "preTransferHook", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "redeemVerify", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "repayBorrowVerify", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "seizeVerify", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "accessControlManager_", + "type": "address" + } + ], + "name": "setAccessControlManager", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract VToken[]", + "name": "marketsList", + "type": "address[]" + }, + { + "internalType": "enum Action[]", + "name": "actionsList", + "type": "uint8[]" + }, + { + "internalType": "bool", + "name": "paused", + "type": "bool" + } + ], + "name": "setActionsPaused", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "liquidator", + "type": "address" + }, + { + "internalType": "bool", + "name": "allowed", + "type": "bool" + } + ], + "name": "setAllowedLiquidator", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "vToken", + "type": "address" + }, + { + "internalType": "address", + "name": "supplier", + "type": "address" + }, + { + "internalType": "bool", + "name": "allowed", + "type": "bool" + } + ], + "name": "setAllowedSupplier", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "newCloseFactorMantissa", + "type": "uint256" + } + ], + "name": "setCloseFactor", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract VToken", + "name": "vToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "newCollateralFactorMantissa", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "newLiquidationThresholdMantissa", + "type": "uint256" + } + ], + "name": "setCollateralFactor", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IDeviationBoundedOracle", + "name": "newBoundedOracle", + "type": "address" + } + ], + "name": "setDeviationBoundedOracle", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "vTokenBorrowed", + "type": "address" + }, + { + "internalType": "bool", + "name": "enable", + "type": "bool" + } + ], + "name": "setForcedLiquidation", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "enabled", + "type": "bool" + } + ], + "name": "setLiquidationAllowlistEnabled", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "newLiquidationIncentiveMantissa", + "type": "uint256" + } + ], + "name": "setLiquidationIncentive", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract VToken[]", + "name": "vTokens", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "newBorrowCaps", + "type": "uint256[]" + } + ], + "name": "setMarketBorrowCaps", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "vToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "newLiquidationIncentiveMantissa", + "type": "uint256" + } + ], + "name": "setMarketLiquidationIncentive", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract VToken[]", + "name": "vTokens", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "newSupplyCaps", + "type": "uint256[]" + } + ], + "name": "setMarketSupplyCaps", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "limit", + "type": "uint256" + } + ], + "name": "setMaxLoopsLimit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "newMinLiquidatableCollateral", + "type": "uint256" + } + ], + "name": "setMinLiquidatableCollateral", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract ResilientOracleInterface", + "name": "newOracle", + "type": "address" + } + ], + "name": "setPriceOracle", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "vToken", + "type": "address" + }, + { + "internalType": "bool", + "name": "enabled", + "type": "bool" + } + ], + "name": "setSupplyAllowlistEnabled", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "supplyCaps", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract VToken", + "name": "vToken", + "type": "address" + } + ], + "name": "supportMarket", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "transferVerify", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "market", + "type": "address" + } + ], + "name": "unlistMarket", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "delegate", + "type": "address" + }, + { + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ], + "name": "updateDelegate", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "updatePrices", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } + ], + "transactionHash": "0xd32a60dea9b9dc17c4b807ba9a5a1de93d4d505a623361c0bb029a8ac0694c10", + "receipt": { + "to": null, + "from": "0x33C6476F88eeA28D7E7900F759B4597704Ef95B7", + "contractAddress": "0x7F81dC61F3D75569A67155fb188171Aef173a52b", + "transactionIndex": 0, + "gasUsed": "5385940", + "logsBloom": "0x00000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000010000000000000000400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0xf3d731a95225e9ea1bde97185556330d50948824edcbd2517fd678decb0670fb", + "transactionHash": "0xd32a60dea9b9dc17c4b807ba9a5a1de93d4d505a623361c0bb029a8ac0694c10", + "logs": [ + { + "transactionIndex": 0, + "blockNumber": 129845071, + "transactionHash": "0xd32a60dea9b9dc17c4b807ba9a5a1de93d4d505a623361c0bb029a8ac0694c10", + "address": "0x7F81dC61F3D75569A67155fb188171Aef173a52b", + "topics": ["0x7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498"], + "data": "0x00000000000000000000000000000000000000000000000000000000000000ff", + "logIndex": 0, + "blockHash": "0xf3d731a95225e9ea1bde97185556330d50948824edcbd2517fd678decb0670fb" + } + ], + "blockNumber": 129845071, + "cumulativeGasUsed": "5385940", + "status": 1, + "byzantium": true + }, + "args": ["0xeAA45288d804971e5a76f33559e629F5b2b1Cb8B"], + "numDeployments": 1, + "solcInputHash": "66f1153922b1795d76052d2879dd03ca", + "metadata": "{\"compiler\":{\"version\":\"0.8.25+commit.b61c2a91\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"poolRegistry_\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"market\",\"type\":\"address\"},{\"internalType\":\"enum Action\",\"name\":\"action\",\"type\":\"uint8\"}],\"name\":\"ActionPaused\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"BorrowActionNotPaused\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"market\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"cap\",\"type\":\"uint256\"}],\"name\":\"BorrowCapExceeded\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"BorrowCapIsNotZero\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"borrows\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxClearableDebt\",\"type\":\"uint256\"}],\"name\":\"CollateralCoversDebt\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"expectedLessThanOrEqualTo\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"actual\",\"type\":\"uint256\"}],\"name\":\"CollateralExceedsThreshold\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CollateralFactorIsNotZero\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ComptrollerMismatch\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"borrows\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxClearableDebt\",\"type\":\"uint256\"}],\"name\":\"DebtExceedsClearableAmount\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"DelegationStatusUnchanged\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"EnterMarketActionNotPaused\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ExitMarketActionNotPaused\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InsufficientLiquidity\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InsufficientShortfall\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidArrayLength\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidCloseFactor\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidCollateralFactor\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidLiquidationIncentive\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidLiquidationThreshold\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidVToken\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"LiquidateActionNotPaused\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"liquidator\",\"type\":\"address\"}],\"name\":\"LiquidationNotAllowed\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"market\",\"type\":\"address\"}],\"name\":\"MarketAlreadyListed\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"vToken\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"user\",\"type\":\"address\"}],\"name\":\"MarketNotCollateral\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"market\",\"type\":\"address\"}],\"name\":\"MarketNotListed\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"loopsLimit\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"requiredLoops\",\"type\":\"uint256\"}],\"name\":\"MaxLoopsLimitExceeded\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"expectedGreaterThan\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"actual\",\"type\":\"uint256\"}],\"name\":\"MinimalCollateralViolated\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"MintActionNotPaused\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NonzeroBorrowBalance\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NonzeroBorrowBalanceAfterLiquidation\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"vToken\",\"type\":\"address\"}],\"name\":\"PriceError\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RedeemActionNotPaused\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RepayActionNotPaused\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RewardsDistributorAlreadyExists\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"SeizeActionNotPaused\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"vToken\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"user\",\"type\":\"address\"}],\"name\":\"SnapshotError\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"market\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"cap\",\"type\":\"uint256\"}],\"name\":\"SupplyCapExceeded\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"SupplyCapIsNotZero\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"market\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"supplier\",\"type\":\"address\"}],\"name\":\"SupplyNotAllowed\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"TooMuchRepay\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"TransferActionNotPaused\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"calledContract\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"methodSignature\",\"type\":\"string\"}],\"name\":\"Unauthorized\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"expectedSender\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"actualSender\",\"type\":\"address\"}],\"name\":\"UnexpectedSender\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ZeroAddressNotAllowed\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"contract VToken\",\"name\":\"vToken\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"enum Action\",\"name\":\"action\",\"type\":\"uint8\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"pauseState\",\"type\":\"bool\"}],\"name\":\"ActionPausedMarket\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"liquidator\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"allowed\",\"type\":\"bool\"}],\"name\":\"AllowedLiquidatorUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"vToken\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"supplier\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"allowed\",\"type\":\"bool\"}],\"name\":\"AllowedSupplierUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"approver\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"delegate\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"approved\",\"type\":\"bool\"}],\"name\":\"DelegateUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"version\",\"type\":\"uint8\"}],\"name\":\"Initialized\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"vToken\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"enable\",\"type\":\"bool\"}],\"name\":\"IsForcedLiquidationEnabledUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"enabled\",\"type\":\"bool\"}],\"name\":\"LiquidationAllowlistEnabledUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"contract VToken\",\"name\":\"vToken\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"MarketEntered\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"contract VToken\",\"name\":\"vToken\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"MarketExited\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"contract VToken\",\"name\":\"vToken\",\"type\":\"address\"}],\"name\":\"MarketSupported\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"vToken\",\"type\":\"address\"}],\"name\":\"MarketUnlisted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"oldMaxLoopsLimit\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newmaxLoopsLimit\",\"type\":\"uint256\"}],\"name\":\"MaxLoopsLimitUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"oldAccessControlManager\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"newAccessControlManager\",\"type\":\"address\"}],\"name\":\"NewAccessControlManager\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"contract VToken\",\"name\":\"vToken\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newBorrowCap\",\"type\":\"uint256\"}],\"name\":\"NewBorrowCap\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"oldCloseFactorMantissa\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newCloseFactorMantissa\",\"type\":\"uint256\"}],\"name\":\"NewCloseFactor\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"contract VToken\",\"name\":\"vToken\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"oldCollateralFactorMantissa\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newCollateralFactorMantissa\",\"type\":\"uint256\"}],\"name\":\"NewCollateralFactor\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"contract IDeviationBoundedOracle\",\"name\":\"oldBoundedOracle\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"contract IDeviationBoundedOracle\",\"name\":\"newBoundedOracle\",\"type\":\"address\"}],\"name\":\"NewDeviationBoundedOracle\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"oldLiquidationIncentiveMantissa\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newLiquidationIncentiveMantissa\",\"type\":\"uint256\"}],\"name\":\"NewLiquidationIncentive\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"contract VToken\",\"name\":\"vToken\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"oldLiquidationThresholdMantissa\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newLiquidationThresholdMantissa\",\"type\":\"uint256\"}],\"name\":\"NewLiquidationThreshold\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"vToken\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"oldLiquidationIncentiveMantissa\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newLiquidationIncentiveMantissa\",\"type\":\"uint256\"}],\"name\":\"NewMarketLiquidationIncentive\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"oldMinLiquidatableCollateral\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newMinLiquidatableCollateral\",\"type\":\"uint256\"}],\"name\":\"NewMinLiquidatableCollateral\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"contract ResilientOracleInterface\",\"name\":\"oldPriceOracle\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"contract ResilientOracleInterface\",\"name\":\"newPriceOracle\",\"type\":\"address\"}],\"name\":\"NewPriceOracle\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"rewardsDistributor\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"rewardToken\",\"type\":\"address\"}],\"name\":\"NewRewardsDistributor\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"contract VToken\",\"name\":\"vToken\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newSupplyCap\",\"type\":\"uint256\"}],\"name\":\"NewSupplyCap\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousOwner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"OwnershipTransferStarted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousOwner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"vToken\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"enabled\",\"type\":\"bool\"}],\"name\":\"SupplyAllowlistEnabledUpdated\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"accessControlManager\",\"outputs\":[{\"internalType\":\"contract IAccessControlManagerV8\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"accountAssets\",\"outputs\":[{\"internalType\":\"contract VToken\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"market\",\"type\":\"address\"},{\"internalType\":\"enum Action\",\"name\":\"action\",\"type\":\"uint8\"}],\"name\":\"actionPaused\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract RewardsDistributor\",\"name\":\"_rewardsDistributor\",\"type\":\"address\"}],\"name\":\"addRewardsDistributor\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"allMarkets\",\"outputs\":[{\"internalType\":\"contract VToken\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"approvedDelegates\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"borrowCaps\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"borrowVerify\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"internalType\":\"contract VToken\",\"name\":\"vToken\",\"type\":\"address\"}],\"name\":\"checkMembership\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"closeFactorMantissa\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"deviationBoundedOracle\",\"outputs\":[{\"internalType\":\"contract IDeviationBoundedOracle\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"vToken\",\"type\":\"address\"}],\"name\":\"effectiveLiquidationIncentive\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"vToken\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"enterMarketBehalf\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"vTokens\",\"type\":\"address[]\"}],\"name\":\"enterMarkets\",\"outputs\":[{\"internalType\":\"uint256[]\",\"name\":\"\",\"type\":\"uint256[]\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"vTokenAddress\",\"type\":\"address\"}],\"name\":\"exitMarket\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"getAccountLiquidity\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"error\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"liquidity\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"shortfall\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getAllMarkets\",\"outputs\":[{\"internalType\":\"contract VToken[]\",\"name\":\"\",\"type\":\"address[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"getAssetsIn\",\"outputs\":[{\"internalType\":\"contract VToken[]\",\"name\":\"\",\"type\":\"address[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"getBorrowingPower\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"error\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"liquidity\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"shortfall\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"vTokenModify\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"redeemTokens\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"borrowAmount\",\"type\":\"uint256\"}],\"name\":\"getHypotheticalAccountLiquidity\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"error\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"liquidity\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"shortfall\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getRewardDistributors\",\"outputs\":[{\"internalType\":\"contract RewardsDistributor[]\",\"name\":\"\",\"type\":\"address[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"vToken\",\"type\":\"address\"}],\"name\":\"getRewardsByMarket\",\"outputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"rewardToken\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"supplySpeed\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"borrowSpeed\",\"type\":\"uint256\"}],\"internalType\":\"struct SpokeComptrollerStorage.RewardSpeeds[]\",\"name\":\"rewardSpeeds\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"user\",\"type\":\"address\"}],\"name\":\"healAccount\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"loopLimit\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"accessControlManager\",\"type\":\"address\"}],\"name\":\"initialize\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"isAllowedLiquidator\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"isAllowedSupplier\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"isComptroller\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"isForcedLiquidationEnabled\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"isLiquidationAllowlistEnabled\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract VToken\",\"name\":\"vToken\",\"type\":\"address\"}],\"name\":\"isMarketListed\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"isSupplyAllowlistEnabled\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"borrower\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"contract VToken\",\"name\":\"vTokenCollateral\",\"type\":\"address\"},{\"internalType\":\"contract VToken\",\"name\":\"vTokenBorrowed\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"repayAmount\",\"type\":\"uint256\"}],\"internalType\":\"struct SpokeComptrollerStorage.LiquidationOrder[]\",\"name\":\"orders\",\"type\":\"tuple[]\"}],\"name\":\"liquidateAccount\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"liquidateBorrowVerify\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"vTokenBorrowed\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"vTokenCollateral\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"actualRepayAmount\",\"type\":\"uint256\"}],\"name\":\"liquidateCalculateSeizeTokens\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"error\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"tokensToSeize\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"liquidationIncentiveMantissa\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"liquidationIncentives\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"markets\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"isListed\",\"type\":\"bool\"},{\"internalType\":\"uint256\",\"name\":\"collateralFactorMantissa\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"liquidationThresholdMantissa\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"maxLoopsLimit\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"minLiquidatableCollateral\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"mintVerify\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"oracle\",\"outputs\":[{\"internalType\":\"contract ResilientOracleInterface\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"pendingOwner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"poolRegistry\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"vToken\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"borrower\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"borrowAmount\",\"type\":\"uint256\"}],\"name\":\"preBorrowHook\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"vTokenBorrowed\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"vTokenCollateral\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"borrower\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"repayAmount\",\"type\":\"uint256\"},{\"internalType\":\"bool\",\"name\":\"skipLiquidityCheck\",\"type\":\"bool\"}],\"name\":\"preLiquidateHook\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"vToken\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"minter\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"mintAmount\",\"type\":\"uint256\"}],\"name\":\"preMintHook\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"vToken\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"redeemer\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"redeemTokens\",\"type\":\"uint256\"}],\"name\":\"preRedeemHook\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"vToken\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"borrower\",\"type\":\"address\"}],\"name\":\"preRepayHook\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"vTokenCollateral\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"seizerContract\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"liquidator\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"borrower\",\"type\":\"address\"}],\"name\":\"preSeizeHook\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"vToken\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"src\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"dst\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"transferTokens\",\"type\":\"uint256\"}],\"name\":\"preTransferHook\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"redeemVerify\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"renounceOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"repayBorrowVerify\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"seizeVerify\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"accessControlManager_\",\"type\":\"address\"}],\"name\":\"setAccessControlManager\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract VToken[]\",\"name\":\"marketsList\",\"type\":\"address[]\"},{\"internalType\":\"enum Action[]\",\"name\":\"actionsList\",\"type\":\"uint8[]\"},{\"internalType\":\"bool\",\"name\":\"paused\",\"type\":\"bool\"}],\"name\":\"setActionsPaused\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"liquidator\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"allowed\",\"type\":\"bool\"}],\"name\":\"setAllowedLiquidator\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"vToken\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"supplier\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"allowed\",\"type\":\"bool\"}],\"name\":\"setAllowedSupplier\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"newCloseFactorMantissa\",\"type\":\"uint256\"}],\"name\":\"setCloseFactor\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract VToken\",\"name\":\"vToken\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"newCollateralFactorMantissa\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"newLiquidationThresholdMantissa\",\"type\":\"uint256\"}],\"name\":\"setCollateralFactor\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IDeviationBoundedOracle\",\"name\":\"newBoundedOracle\",\"type\":\"address\"}],\"name\":\"setDeviationBoundedOracle\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"vTokenBorrowed\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"enable\",\"type\":\"bool\"}],\"name\":\"setForcedLiquidation\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bool\",\"name\":\"enabled\",\"type\":\"bool\"}],\"name\":\"setLiquidationAllowlistEnabled\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"newLiquidationIncentiveMantissa\",\"type\":\"uint256\"}],\"name\":\"setLiquidationIncentive\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract VToken[]\",\"name\":\"vTokens\",\"type\":\"address[]\"},{\"internalType\":\"uint256[]\",\"name\":\"newBorrowCaps\",\"type\":\"uint256[]\"}],\"name\":\"setMarketBorrowCaps\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"vToken\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"newLiquidationIncentiveMantissa\",\"type\":\"uint256\"}],\"name\":\"setMarketLiquidationIncentive\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract VToken[]\",\"name\":\"vTokens\",\"type\":\"address[]\"},{\"internalType\":\"uint256[]\",\"name\":\"newSupplyCaps\",\"type\":\"uint256[]\"}],\"name\":\"setMarketSupplyCaps\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"limit\",\"type\":\"uint256\"}],\"name\":\"setMaxLoopsLimit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"newMinLiquidatableCollateral\",\"type\":\"uint256\"}],\"name\":\"setMinLiquidatableCollateral\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract ResilientOracleInterface\",\"name\":\"newOracle\",\"type\":\"address\"}],\"name\":\"setPriceOracle\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"vToken\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"enabled\",\"type\":\"bool\"}],\"name\":\"setSupplyAllowlistEnabled\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"supplyCaps\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract VToken\",\"name\":\"vToken\",\"type\":\"address\"}],\"name\":\"supportMarket\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"transferVerify\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"market\",\"type\":\"address\"}],\"name\":\"unlistMarket\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"delegate\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"approved\",\"type\":\"bool\"}],\"name\":\"updateDelegate\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"updatePrices\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}],\"devdoc\":{\"author\":\"Venus\",\"details\":\"Fork of `Comptroller` (`contracts/Comptroller.sol`). It is a separate implementation because `Comptroller` is the one every other pool in this repo shares, here and on other chains, so policy that applies only to a spoke pool does not belong in it: bounded collateral pricing, the supply and liquidation allowlists, and per-market liquidation incentives. Nothing keeps the two in sync automatically: a change to `Comptroller` has to be reviewed and re-applied here by hand, and `diff contracts/Comptroller.sol contracts/Spoke/SpokeComptroller.sol` shows what this fork currently changes.\",\"errors\":{\"CollateralExceedsThreshold(uint256,uint256)\":[{\"details\":\"Same name, arguments and meaning as the shared `Comptroller`, so a decoder written against either one reads this correctly.\"}],\"DebtExceedsClearableAmount(uint256,uint256)\":[{\"details\":\"Deliberately not the shared `Comptroller`'s `InsufficientCollateral`: both arguments here are debt values rather than collateral values, and reusing that name would leave the two versions sharing one selector.\"}]},\"events\":{\"Initialized(uint8)\":{\"details\":\"Triggered when the contract has been initialized or reinitialized.\"}},\"kind\":\"dev\",\"methods\":{\"acceptOwnership()\":{\"details\":\"The new owner accepts the ownership transfer.\"},\"actionPaused(address,uint8)\":{\"params\":{\"action\":\"Action to check\",\"market\":\"vToken address\"},\"returns\":{\"_0\":\"paused True if the action is paused otherwise false\"}},\"addRewardsDistributor(address)\":{\"custom:access\":\"Only Governance\",\"custom:error\":\"RewardsDistributorAlreadyExists is thrown if this pool already has the given distributor\",\"custom:event\":\"Emits NewRewardsDistributor with distributor address\",\"details\":\"Only callable by the admin\",\"params\":{\"_rewardsDistributor\":\"Address of the RewardDistributor contract to add\"}},\"checkMembership(address,address)\":{\"params\":{\"account\":\"The address of the account to check\",\"vToken\":\"The vToken to check\"},\"returns\":{\"_0\":\"True if the account is in the market specified, otherwise false.\"}},\"constructor\":{\"custom:error\":\"ZeroAddressNotAllowed is thrown when pool registry address is zero\",\"custom:oz-upgrades-unsafe-allow\":\"constructor\",\"params\":{\"poolRegistry_\":\"Pool registry address\"}},\"effectiveLiquidationIncentive(address)\":{\"params\":{\"vToken\":\"The collateral market to read the discount of\"},\"returns\":{\"_0\":\"The market's own discount if it has one, otherwise the pool-wide discount, scaled by 1e18\"}},\"enterMarketBehalf(address,address)\":{\"custom:access\":\"Controlled by AccessControlManager\",\"custom:error\":\"ActionPaused error is thrown if entering the market is pausedMarketNotListed error is thrown if the market is not listedZeroAddressNotAllowed is thrown when the account address is zero\",\"custom:event\":\"MarketEntered is emitted on success\",\"details\":\"`enterMarkets` reads `msg.sender`, so a router supplying through `VToken.mintBehalf` would enter itself rather than the supplier. This enters the supplier, so both steps fit in one user transaction. Grant the permission only to a router that passes its own caller as `account`.\",\"params\":{\"account\":\"The account to enable the market for\",\"vToken\":\"The address of the vToken market to be enabled\"}},\"enterMarkets(address[])\":{\"custom:access\":\"Not restricted\",\"custom:error\":\"ActionPaused error is thrown if entering any of the markets is pausedMarketNotListed error is thrown if any of the markets is not listed\",\"custom:event\":\"MarketEntered is emitted for each market on success\",\"params\":{\"vTokens\":\"The list of addresses of the vToken markets to be enabled\"},\"returns\":{\"_0\":\"errors An array of NO_ERROR for compatibility with Venus core tooling\"}},\"exitMarket(address)\":{\"custom:access\":\"Not restricted\",\"custom:error\":\"ActionPaused error is thrown if exiting the market is pausedNonzeroBorrowBalance error is thrown if the user has an outstanding borrow in this marketMarketNotListed error is thrown when the market is not listedInsufficientLiquidity error is thrown if exiting the market would lead to user's insolvencySnapshotError is thrown if some vToken fails to return the account's supply and borrowsPriceError is thrown if the oracle returns an incorrect price for some asset\",\"custom:event\":\"MarketExited is emitted on success\",\"details\":\"Sender must not have an outstanding borrow balance in the asset, or be providing necessary collateral for an outstanding borrow.\",\"params\":{\"vTokenAddress\":\"The address of the asset to be removed\"},\"returns\":{\"_0\":\"error Always NO_ERROR for compatibility with Venus core tooling\"}},\"getAccountLiquidity(address)\":{\"details\":\"The interface of this function is intentionally kept compatible with Compound and Venus Core\",\"params\":{\"account\":\"The account get liquidity for\"},\"returns\":{\"error\":\"Always NO_ERROR for compatibility with Venus core tooling\",\"liquidity\":\"Account liquidity in excess of liquidation threshold requirements,\",\"shortfall\":\"Account shortfall below liquidation threshold requirements\"}},\"getAllMarkets()\":{\"details\":\"The automatic getter may be used to access an individual market.\",\"returns\":{\"_0\":\"markets The list of market addresses\"}},\"getAssetsIn(address)\":{\"params\":{\"account\":\"The address of the account to pull assets for\"},\"returns\":{\"_0\":\"A list with the assets the account has entered\"}},\"getBorrowingPower(address)\":{\"details\":\"The interface of this function is intentionally kept compatible with Compound and Venus Core\",\"params\":{\"account\":\"The account get liquidity for\"},\"returns\":{\"error\":\"Always NO_ERROR for compatibility with Venus core tooling\",\"liquidity\":\"Account liquidity in excess of collateral requirements,\",\"shortfall\":\"Account shortfall below collateral requirements\"}},\"getHypotheticalAccountLiquidity(address,address,uint256,uint256)\":{\"details\":\"The interface of this function is intentionally kept compatible with Compound and Venus Core\",\"params\":{\"account\":\"The account to determine liquidity for\",\"borrowAmount\":\"The amount of underlying to hypothetically borrow\",\"redeemTokens\":\"The number of tokens to hypothetically redeem\",\"vTokenModify\":\"The market to hypothetically redeem/borrow in\"},\"returns\":{\"error\":\"Always NO_ERROR for compatibility with Venus core tooling\",\"liquidity\":\"Hypothetical account liquidity in excess of collateral requirements,\",\"shortfall\":\"Hypothetical account shortfall below collateral requirements\"}},\"getRewardDistributors()\":{\"returns\":{\"_0\":\"Array of RewardDistributor addresses\"}},\"getRewardsByMarket(address)\":{\"params\":{\"vToken\":\"The vToken to get the reward speeds for\"},\"returns\":{\"rewardSpeeds\":\"Array of total supply and borrow speeds and reward token for all reward distributors\"}},\"healAccount(address)\":{\"custom:access\":\"Not restricted while the liquidation allowlist is disabled, otherwise restricted to the accounts on it\",\"custom:error\":\"LiquidationNotAllowed is thrown if the liquidation allowlist is enabled and the caller is not on itCollateralExceedsThreshold error is thrown when the collateral is too big for healingCollateralCoversDebt is thrown when the collateral can clear the whole debt, which leaves nothing to healSnapshotError is thrown if some vToken fails to return the account's supply and borrowsPriceError is thrown if the oracle returns an incorrect price for some asset\",\"params\":{\"user\":\"account to heal\"}},\"initialize(uint256,address)\":{\"params\":{\"accessControlManager\":\"Access control manager contract address\",\"loopLimit\":\"Limit for the loops can iterate to avoid the DOS\"}},\"isComptroller()\":{\"returns\":{\"_0\":\"Always true\"}},\"isMarketListed(address)\":{\"params\":{\"vToken\":\"vToken Address for the market to check\"},\"returns\":{\"_0\":\"listed True if listed otherwise false\"}},\"liquidateAccount(address,(address,address,uint256)[])\":{\"custom:access\":\"Not restricted while the liquidation allowlist is disabled, otherwise restricted to the accounts on it\",\"custom:error\":\"LiquidationNotAllowed is thrown by `preSeizeHook`, while seizing for the first order, if the liquidation allowlist is enabled and the caller is not on itCollateralExceedsThreshold error is thrown when the collateral is too big for a batch liquidationDebtExceedsClearableAmount is thrown when the collateral cannot clear the whole debt, which means the account has to go through `healAccount`NonzeroBorrowBalanceAfterLiquidation is thrown if the orders do not clear every borrowSnapshotError is thrown if some vToken fails to return the account's supply and borrowsPriceError is thrown if the oracle returns an incorrect price for some asset\",\"params\":{\"borrower\":\"the borrower address\",\"orders\":\"an array of liquidation orders\"}},\"liquidateCalculateSeizeTokens(address,address,uint256)\":{\"custom:error\":\"PriceError if the oracle returns an invalid price\",\"details\":\"Used in liquidation (called in vToken.liquidateBorrowFresh)\",\"params\":{\"actualRepayAmount\":\"The amount of vTokenBorrowed underlying to convert into vTokenCollateral tokens\",\"vTokenBorrowed\":\"The address of the borrowed vToken\",\"vTokenCollateral\":\"The address of the collateral vToken\"},\"returns\":{\"error\":\"Always NO_ERROR for compatibility with Venus core tooling\",\"tokensToSeize\":\"Number of vTokenCollateral tokens to be seized in a liquidation\"}},\"liquidationIncentiveMantissa()\":{\"details\":\"Answers for `msg.sender` instead of taking the market as an argument, because this is the getter `ComptrollerViewInterface` declares and `VToken` calls on itself: `_seize` divides the protocol seize share by it, and `setProtocolSeizeShare` bounds that share against it. Both have to see the discount that prices the calling market's own collateral, or the protocol takes more than its configured share of the repaid debt and the liquidator is paid less than the market's configured discount. Any caller that is not a market of this pool, a lens or a liquidation bot, reads the pool-wide discount. Ask about a specific market through `effectiveLiquidationIncentive`.\",\"returns\":{\"_0\":\"The discount that applies to the caller, scaled by 1e18\"}},\"owner()\":{\"details\":\"Returns the address of the current owner.\"},\"pendingOwner()\":{\"details\":\"Returns the address of the pending owner.\"},\"preLiquidateHook(address,address,address,uint256,bool)\":{\"custom:error\":\"ActionPaused error is thrown if liquidations are paused in this marketMarketNotListed error is thrown if either collateral or borrowed token is not listedTooMuchRepay error is thrown if the liquidator is trying to repay more than allowed by close factorMinimalCollateralViolated is thrown if the users' total collateral is lower than the threshold for non-batch liquidationsInsufficientShortfall is thrown when trying to liquidate a healthy accountSnapshotError is thrown if some vToken fails to return the account's supply and borrowsPriceError is thrown if the oracle returns an incorrect price for some asset\",\"params\":{\"borrower\":\"The address of the borrower\",\"repayAmount\":\"The amount of underlying being repaid\",\"skipLiquidityCheck\":\"Allows the borrow to be liquidated regardless of the account liquidity\",\"vTokenBorrowed\":\"Asset which was borrowed by the borrower\",\"vTokenCollateral\":\"Asset which was used as collateral and will be seized\"}},\"preMintHook(address,address,uint256)\":{\"custom:access\":\"Not restricted\",\"custom:error\":\"ActionPaused error is thrown if supplying to this market is pausedMarketNotListed error is thrown when the market is not listedSupplyNotAllowed error is thrown if the market's supply allowlist is enabled and the minter is not on itSupplyCapExceeded error is thrown if the total supply exceeds the cap after minting\",\"params\":{\"mintAmount\":\"The amount of underlying being supplied to the market in exchange for tokens\",\"minter\":\"The account which would get the minted tokens\",\"vToken\":\"The market to verify the mint against\"}},\"preRedeemHook(address,address,uint256)\":{\"custom:access\":\"Not restricted\",\"custom:error\":\"ActionPaused error is thrown if withdrawals are paused in this marketMarketNotListed error is thrown when the market is not listedInsufficientLiquidity error is thrown if the withdrawal would lead to user's insolvencySnapshotError is thrown if some vToken fails to return the account's supply and borrowsPriceError is thrown if the oracle returns an incorrect price for some asset\",\"params\":{\"redeemTokens\":\"The number of vTokens to exchange for the underlying asset in the market\",\"redeemer\":\"The account which would redeem the tokens\",\"vToken\":\"The market to verify the redeem against\"}},\"preRepayHook(address,address)\":{\"custom:access\":\"Not restricted\",\"custom:error\":\"ActionPaused error is thrown if repayments are paused in this marketMarketNotListed error is thrown when the market is not listed\",\"params\":{\"borrower\":\"The account which would borrowed the asset\",\"vToken\":\"The market to verify the repay against\"}},\"preSeizeHook(address,address,address,address)\":{\"custom:access\":\"Not restricted while the liquidation allowlist is disabled, otherwise the liquidator has to be on it\",\"custom:error\":\"ActionPaused error is thrown if seizing this type of collateral is pausedMarketNotListed error is thrown if either collateral or borrowed token is not listedComptrollerMismatch error is when seizer contract or seized asset belong to different poolsLiquidationNotAllowed is thrown if the liquidation allowlist is enabled and the liquidator is not on it\",\"params\":{\"borrower\":\"The address of the borrower\",\"liquidator\":\"The address repaying the borrow and seizing the collateral\",\"seizerContract\":\"Contract that tries to seize the asset (either borrowed vToken or Comptroller)\",\"vTokenCollateral\":\"Asset which was used as collateral and will be seized\"}},\"preTransferHook(address,address,address,uint256)\":{\"custom:access\":\"Not restricted\",\"custom:error\":\"ActionPaused error is thrown if withdrawals are paused in this marketMarketNotListed error is thrown when the market is not listedInsufficientLiquidity error is thrown if the withdrawal would lead to user's insolvencySnapshotError is thrown if some vToken fails to return the account's supply and borrowsPriceError is thrown if the oracle returns an incorrect price for some asset\",\"params\":{\"dst\":\"The account which receives the tokens\",\"src\":\"The account which sources the tokens\",\"transferTokens\":\"The number of vTokens to transfer\",\"vToken\":\"The market to verify the transfer against\"}},\"renounceOwnership()\":{\"details\":\"Leaves the contract without owner. It will not be possible to call `onlyOwner` functions. Can only be called by the current owner. NOTE: Renouncing ownership will leave the contract without an owner, thereby disabling any functionality that is only available to the owner.\"},\"setAccessControlManager(address)\":{\"custom:access\":\"Only Governance\",\"custom:event\":\"Emits NewAccessControlManager event\",\"details\":\"Admin function to set address of AccessControlManager\",\"params\":{\"accessControlManager_\":\"The new address of the AccessControlManager\"}},\"setActionsPaused(address[],uint8[],bool)\":{\"custom:access\":\"Controlled by AccessControlManager\",\"custom:error\":\"MarketNotListed is thrown if any of the markets is not listed\",\"details\":\"This function is restricted by the AccessControlManager\",\"params\":{\"actionsList\":\"List of action ids to pause/unpause\",\"marketsList\":\"Markets to pause/unpause the actions on\",\"paused\":\"The new paused state (true=paused, false=unpaused)\"}},\"setAllowedLiquidator(address,bool)\":{\"custom:access\":\"Controlled by AccessControlManager\",\"custom:error\":\"ZeroAddressNotAllowed is thrown if the account is the zero address\",\"custom:event\":\"Emits AllowedLiquidatorUpdated on success\",\"details\":\"Takes effect only while the liquidation allowlist is enabled.\",\"params\":{\"allowed\":\"Whether the account should be allowed to seize collateral\",\"liquidator\":\"The account to add or remove\"}},\"setAllowedSupplier(address,address,bool)\":{\"custom:access\":\"Controlled by AccessControlManager\",\"custom:error\":\"MarketNotListed is thrown if the market is not listedZeroAddressNotAllowed is thrown if the account is the zero address\",\"custom:event\":\"Emits AllowedSupplierUpdated on success\",\"details\":\"Takes effect only while the market's supply allowlist is enabled. Setting an account to the value it already holds is not an error, so a governance action that overlaps an earlier one still executes.\",\"params\":{\"allowed\":\"Whether the account should be allowed to supply\",\"supplier\":\"The account to add or remove\",\"vToken\":\"The market whose allowlist to update\"}},\"setCloseFactor(uint256)\":{\"custom:access\":\"Controlled by AccessControlManager\",\"custom:error\":\"InvalidCloseFactor is thrown if the new close factor is outside the allowed bounds\",\"custom:event\":\"Emits NewCloseFactor on success\",\"params\":{\"newCloseFactorMantissa\":\"New close factor, scaled by 1e18\"}},\"setCollateralFactor(address,uint256,uint256)\":{\"custom:access\":\"Controlled by AccessControlManager\",\"custom:error\":\"MarketNotListed error is thrown when the market is not listedInvalidCollateralFactor error is thrown when collateral factor is too highInvalidLiquidationThreshold error is thrown when liquidation threshold is lower than collateral factorPriceError is thrown when the oracle returns an invalid price for the asset\",\"custom:event\":\"Emits NewCollateralFactor when collateral factor is updated and NewLiquidationThreshold when liquidation threshold is updated\",\"details\":\"This function is restricted by the AccessControlManager\",\"params\":{\"newCollateralFactorMantissa\":\"The new collateral factor, scaled by 1e18\",\"newLiquidationThresholdMantissa\":\"The new liquidation threshold, scaled by 1e18\",\"vToken\":\"The market to set the factor on\"}},\"setDeviationBoundedOracle(address)\":{\"custom:error\":\"ZeroAddressNotAllowed is thrown when the new oracle address is zero\",\"custom:event\":\"Emits NewDeviationBoundedOracle on success\",\"details\":\"Only callable by the admin. Nothing falls back to spot when this is unset: both of the calls into the zero address revert, so borrow and redeem fail closed rather than running unbounded, and this has to be set before the pool serves either action. `_updateProtectionStates` is the one that fails first and it is the stronger of the two guards, because solc emits an `extcodesize` existence check ahead of a call whose return data it does not decode, which is exactly that call. `_safeGetUnderlyingPrices` gets no such check and instead relies on the ABI decoder rejecting the empty return data.\",\"params\":{\"newBoundedOracle\":\"Address of the new deviation-bounded oracle to set\"}},\"setForcedLiquidation(address,bool)\":{\"params\":{\"enable\":\"Whether to enable forced liquidations\",\"vTokenBorrowed\":\"Borrowed vToken\"}},\"setLiquidationAllowlistEnabled(bool)\":{\"custom:access\":\"Controlled by AccessControlManager\",\"custom:event\":\"Emits LiquidationAllowlistEnabledUpdated on success\",\"details\":\"Pool-wide rather than per market, for the reason given on `isLiquidationAllowlistEnabled`. Enabling this also restricts `healAccount`, so any keeper relied on to record bad debt has to be allowlisted too.\",\"params\":{\"enabled\":\"Whether seizing collateral should be restricted to allowlisted accounts\"}},\"setLiquidationIncentive(uint256)\":{\"custom:access\":\"Controlled by AccessControlManager\",\"custom:error\":\"InvalidLiquidationIncentive is thrown if the new incentive is below `MIN_POOL_LIQUIDATION_INCENTIVE_MANTISSA`\",\"custom:event\":\"Emits NewLiquidationIncentive on success\",\"details\":\"This function is restricted by the AccessControlManager`PoolRegistry.addPool` calls this while registering the pool, so the value clears the floor below by the time any market can be listed. This value has to stay at or above `1e18 + protocolSeizeShareMantissa` of every market that has no incentive of its own, or a liquidator of that market's collateral receives less than the debt it repaid. The shares of those markets are only readable market by market, so what is enforced here is the floor for a market at the default share: `MIN_POOL_LIQUIDATION_INCENTIVE_MANTISSA`. That covers a freshly listed market, which is the case nothing else was watching, and it is where this fork parts with upstream `Comptroller` - which stops at 1e18 and would let a pool be registered one that pays every default-share market's liquidator less than it repaid. A market whose share is raised above the default still needs an incentive of its own; `setMarketLiquidationIncentive` bounds that from one side and `VToken.setProtocolSeizeShare` from the other.\",\"params\":{\"newLiquidationIncentiveMantissa\":\"New liquidationIncentive scaled by 1e18\"}},\"setMarketBorrowCaps(address[],uint256[])\":{\"custom:access\":\"Controlled by AccessControlManager\",\"custom:error\":\"InvalidArrayLength is thrown if the arrays are empty or their lengths do not match\",\"details\":\"This function is restricted by the AccessControlManagerA borrow cap of type(uint256).max corresponds to unlimited borrowing.Borrow caps smaller than the current total borrows are accepted. This way, new borrows will not be allowed until the total borrows amount goes below the new borrow cap\",\"params\":{\"newBorrowCaps\":\"The new borrow cap values in underlying to be set. A value of type(uint256).max corresponds to unlimited borrowing.\",\"vTokens\":\"The addresses of the markets (tokens) to change the borrow caps for\"}},\"setMarketLiquidationIncentive(address,uint256)\":{\"custom:access\":\"Controlled by AccessControlManager\",\"custom:error\":\"MarketNotListed is thrown if the market is not listedInvalidLiquidationIncentive is thrown if the new incentive would leave the liquidator with less collateral than the debt it repaid\",\"custom:event\":\"Emits NewMarketLiquidationIncentive on success\",\"details\":\"This function is restricted by the AccessControlManagerKeyed on the collateral market, since that is what the discount prices. Setting it steers liquidators toward one collateral over another, and it feeds the routing between `liquidateAccount` and `healAccount` through `AccountLiquiditySnapshot.maxClearableDebt`. There is no way back to \\\"unset\\\" once a value is stored: `0` is the sentinel that means the pool-wide discount applies, and it is rejected here so that a mistaken zero cannot silently move a market back onto the pool-wide value. Pass that value explicitly to get the same effect.\",\"params\":{\"newLiquidationIncentiveMantissa\":\"New incentive for this market, scaled by 1e18, at least 1e18 + the market's `protocolSeizeShareMantissa`\",\"vToken\":\"The collateral market to set the incentive for\"}},\"setMarketSupplyCaps(address[],uint256[])\":{\"custom:access\":\"Controlled by AccessControlManager\",\"custom:error\":\"InvalidArrayLength is thrown if the arrays are empty or their lengths do not match\",\"details\":\"This function is restricted by the AccessControlManagerA supply cap of type(uint256).max corresponds to unlimited supply.Supply caps smaller than the current total supplies are accepted. This way, new supplies will not be allowed until the total supplies amount goes below the new supply cap\",\"params\":{\"newSupplyCaps\":\"The new supply cap values in underlying to be set. A value of type(uint256).max corresponds to unlimited supply.\",\"vTokens\":\"The addresses of the markets (tokens) to change the supply caps for\"}},\"setMaxLoopsLimit(uint256)\":{\"params\":{\"limit\":\"Limit for the max loops can execute at a time\"}},\"setMinLiquidatableCollateral(uint256)\":{\"custom:access\":\"Controlled by AccessControlManager\",\"details\":\"This function is restricted by the AccessControlManager\",\"params\":{\"newMinLiquidatableCollateral\":\"The new min liquidatable collateral (in USD).\"}},\"setPriceOracle(address)\":{\"custom:error\":\"ZeroAddressNotAllowed is thrown when the new oracle address is zero\",\"custom:event\":\"Emits NewPriceOracle on success\",\"details\":\"Only callable by the admin\",\"params\":{\"newOracle\":\"Address of the new price oracle to set\"}},\"setSupplyAllowlistEnabled(address,bool)\":{\"custom:access\":\"Controlled by AccessControlManager\",\"custom:error\":\"MarketNotListed is thrown if the market is not listed\",\"custom:event\":\"Emits SupplyAllowlistEnabledUpdated on success\",\"details\":\"Enforced in `preMintHook`, so only supply is metered. Redeeming is never restricted, and an account removed from the allowlist keeps the position it already holds and can still exit. Enabling it on a market that is already serving supply cuts off every account that is not on the list, the seed supplier included.\",\"params\":{\"enabled\":\"Whether the market should accept supply only from allowlisted accounts\",\"vToken\":\"The market to change the setting for\"}},\"supportMarket(address)\":{\"custom:access\":\"Only PoolRegistry\",\"custom:error\":\"MarketAlreadyListed is thrown if the market is already listed in this poolInvalidVToken is thrown if the market does not identify itself as a VToken\",\"details\":\"Only callable by the PoolRegistry\",\"params\":{\"vToken\":\"The address of the market (token) to list\"}},\"transferOwnership(address)\":{\"details\":\"Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one. Can only be called by the current owner.\"},\"unlistMarket(address)\":{\"custom:error\":\"MarketNotListed error is thrown when the market is not listedBorrowActionNotPaused error is thrown if borrow action is not pausedMintActionNotPaused error is thrown if mint action is not pausedRedeemActionNotPaused error is thrown if redeem action is not pausedRepayActionNotPaused error is thrown if repay action is not pausedEnterMarketActionNotPaused error is thrown if enter market action is not pausedLiquidateActionNotPaused error is thrown if liquidate action is not pausedBorrowCapIsNotZero error is thrown if borrow cap is not zeroSupplyCapIsNotZero error is thrown if supply cap is not zeroCollateralFactorIsNotZero error is thrown if collateral factor is not zero\",\"custom:event\":\"MarketUnlisted is emitted on success\",\"details\":\"Checks if all actions are paused, borrow/supply caps is set to 0 and collateral factor is to 0.\",\"params\":{\"market\":\"The address of the market (token) to unlist\"},\"returns\":{\"_0\":\"uint256 Always NO_ERROR for compatibility with Venus core tooling\"}},\"updateDelegate(address,bool)\":{\"custom:access\":\"Not restricted\",\"custom:error\":\"ZeroAddressNotAllowed is thrown when delegate address is zeroDelegationStatusUnchanged is thrown if approval status is already set to the requested value\",\"custom:event\":\"DelegateUpdated emits on success\",\"params\":{\"approved\":\"Whether to grant (true) or revoke (false) the borrowing or redeeming rights\",\"delegate\":\"The address to update the rights for\"}},\"updatePrices(address)\":{\"params\":{\"account\":\"Address of the account to get associated tokens with\"}}},\"stateVariables\":{\"poolRegistry\":{\"custom:oz-upgrades-unsafe-allow\":\"state-variable-immutable\"}},\"title\":\"SpokeComptroller\",\"version\":1},\"userdoc\":{\"errors\":{\"ActionPaused(address,uint8)\":[{\"notice\":\"Thrown when trying to perform an action that is paused\"}],\"BorrowActionNotPaused()\":[{\"notice\":\"Thrown when borrow action is not paused\"}],\"BorrowCapExceeded(address,uint256)\":[{\"notice\":\"Thrown if the borrow cap is exceeded\"}],\"BorrowCapIsNotZero()\":[{\"notice\":\"Thrown when borrow cap is not zero\"}],\"CollateralCoversDebt(uint256,uint256)\":[{\"notice\":\"Thrown by `healAccount` when the collateral can clear the whole debt at each market's own liquidation incentive, so healing would forgive nothing and `liquidateAccount` has to be used instead\"}],\"CollateralExceedsThreshold(uint256,uint256)\":[{\"notice\":\"Thrown by the batch operations when the account's total collateral is above `minLiquidatableCollateral`, which means it is large enough for a regular `VToken.liquidateBorrow`\"}],\"CollateralFactorIsNotZero()\":[{\"notice\":\"Thrown when collateral factor is not zero\"}],\"ComptrollerMismatch()\":[{\"notice\":\"Thrown when a market has an unexpected comptroller\"}],\"DebtExceedsClearableAmount(uint256,uint256)\":[{\"notice\":\"Thrown by `liquidateAccount` when the debt is too large for the collateral to clear, so `healAccount` has to be used instead. Reports the debt and the largest debt the collateral could have cleared.\"}],\"DelegationStatusUnchanged()\":[{\"notice\":\"Thrown if delegate approval status is already set to the requested value\"}],\"EnterMarketActionNotPaused()\":[{\"notice\":\"Thrown when enter market action is not paused\"}],\"ExitMarketActionNotPaused()\":[{\"notice\":\"Thrown when exit market action is not paused\"}],\"InsufficientLiquidity()\":[{\"notice\":\"Thrown when the account doesn't have enough liquidity to redeem or borrow\"}],\"InsufficientShortfall()\":[{\"notice\":\"Thrown when trying to liquidate a healthy account\"}],\"InvalidArrayLength()\":[{\"notice\":\"Thrown when an array argument is empty, or when two array arguments have different lengths\"}],\"InvalidCloseFactor()\":[{\"notice\":\"Thrown when the close factor is outside the bounds set by `MIN_CLOSE_FACTOR_MANTISSA` and `MAX_CLOSE_FACTOR_MANTISSA`\"}],\"InvalidCollateralFactor()\":[{\"notice\":\"Thrown when collateral factor exceeds the upper bound\"}],\"InvalidLiquidationIncentive()\":[{\"notice\":\"Thrown when a liquidation incentive is low enough that a liquidator would seize less value than it repaid: below `1e18 + protocolSeizeShareMantissa` for a single market, below `MIN_POOL_LIQUIDATION_INCENTIVE_MANTISSA` for the pool-wide fallback\"}],\"InvalidLiquidationThreshold()\":[{\"notice\":\"Thrown when liquidation threshold exceeds the collateral factor\"}],\"InvalidVToken()\":[{\"notice\":\"Thrown when the market being listed does not identify itself as a VToken\"}],\"LiquidateActionNotPaused()\":[{\"notice\":\"Thrown when liquidate action is not paused\"}],\"LiquidationNotAllowed(address)\":[{\"notice\":\"Thrown if the liquidation allowlist is enabled and the account liquidating is not on it\"}],\"MarketAlreadyListed(address)\":[{\"notice\":\"Thrown when trying to add a market that is already listed\"}],\"MarketNotCollateral(address,address)\":[{\"notice\":\"Thrown when user is not member of market\"}],\"MarketNotListed(address)\":[{\"notice\":\"Thrown when the market is not listed\"}],\"MaxLoopsLimitExceeded(uint256,uint256)\":[{\"notice\":\"Thrown an error on maxLoopsLimit exceeds for any loop\"}],\"MinimalCollateralViolated(uint256,uint256)\":[{\"notice\":\"Thrown during the liquidation if user's total collateral amount is lower than a predefined threshold. In this case only batch liquidations (either liquidateAccount or healAccount) are available.\"}],\"MintActionNotPaused()\":[{\"notice\":\"Thrown when mint action is not paused\"}],\"NonzeroBorrowBalance()\":[{\"notice\":\"Thrown if the user is trying to exit a market in which they have an outstanding debt\"}],\"NonzeroBorrowBalanceAfterLiquidation()\":[{\"notice\":\"Thrown if a debt remains in any of the borrower's markets once every liquidation order has been executed, which means the orders passed to `liquidateAccount` did not cover the whole position\"}],\"PriceError(address)\":[{\"notice\":\"Thrown when the oracle returns an invalid price for some asset\"}],\"RedeemActionNotPaused()\":[{\"notice\":\"Thrown when redeem action is not paused\"}],\"RepayActionNotPaused()\":[{\"notice\":\"Thrown when repay action is not paused\"}],\"RewardsDistributorAlreadyExists()\":[{\"notice\":\"Thrown when adding a rewards distributor that this pool already has\"}],\"SeizeActionNotPaused()\":[{\"notice\":\"Thrown when seize action is not paused\"}],\"SnapshotError(address,address)\":[{\"notice\":\"Thrown if VToken unexpectedly returned a nonzero error code while trying to get account snapshot\"}],\"SupplyCapExceeded(address,uint256)\":[{\"notice\":\"Thrown if the supply cap is exceeded\"}],\"SupplyCapIsNotZero()\":[{\"notice\":\"Thrown when supply cap is not zero\"}],\"SupplyNotAllowed(address,address)\":[{\"notice\":\"Thrown if the account being credited with the minted vTokens is not on the market's supply allowlist\"}],\"TooMuchRepay()\":[{\"notice\":\"Thrown when trying to repay more than allowed by close factor\"}],\"TransferActionNotPaused()\":[{\"notice\":\"Thrown when transfer action is not paused\"}],\"Unauthorized(address,address,string)\":[{\"notice\":\"Thrown when the action is prohibited by AccessControlManager\"}],\"UnexpectedSender(address,address)\":[{\"notice\":\"Thrown when the action is only available to specific sender, but the real sender was different\"}],\"ZeroAddressNotAllowed()\":[{\"notice\":\"Thrown if the supplied address is a zero address where it is not allowed\"}]},\"events\":{\"ActionPausedMarket(address,uint8,bool)\":{\"notice\":\"Emitted when an action is paused on a market\"},\"AllowedLiquidatorUpdated(address,bool)\":{\"notice\":\"Emitted when an account is added to or removed from the pool's liquidation allowlist\"},\"AllowedSupplierUpdated(address,address,bool)\":{\"notice\":\"Emitted when an account is added to or removed from a market's supply allowlist\"},\"DelegateUpdated(address,address,bool)\":{\"notice\":\"Emitted when the borrowing or redeeming delegate rights are updated for an account\"},\"IsForcedLiquidationEnabledUpdated(address,bool)\":{\"notice\":\"Emitted when forced liquidation is enabled or disabled for a market\"},\"LiquidationAllowlistEnabledUpdated(bool)\":{\"notice\":\"Emitted when the pool's liquidation allowlist is enabled or disabled\"},\"MarketEntered(address,address)\":{\"notice\":\"Emitted when an account enters a market\"},\"MarketExited(address,address)\":{\"notice\":\"Emitted when an account exits a market\"},\"MarketSupported(address)\":{\"notice\":\"Emitted when a market is supported\"},\"MarketUnlisted(address)\":{\"notice\":\"Emitted when a market is unlisted\"},\"MaxLoopsLimitUpdated(uint256,uint256)\":{\"notice\":\"Emitted when max loops limit is set\"},\"NewAccessControlManager(address,address)\":{\"notice\":\"Emitted when access control manager contract address is changed\"},\"NewBorrowCap(address,uint256)\":{\"notice\":\"Emitted when borrow cap for a vToken is changed\"},\"NewCloseFactor(uint256,uint256)\":{\"notice\":\"Emitted when close factor is changed by admin\"},\"NewCollateralFactor(address,uint256,uint256)\":{\"notice\":\"Emitted when a collateral factor is changed by admin\"},\"NewDeviationBoundedOracle(address,address)\":{\"notice\":\"Emitted when the deviation-bounded oracle is changed\"},\"NewLiquidationIncentive(uint256,uint256)\":{\"notice\":\"Emitted when liquidation incentive is changed by admin\"},\"NewLiquidationThreshold(address,uint256,uint256)\":{\"notice\":\"Emitted when liquidation threshold is changed by admin\"},\"NewMarketLiquidationIncentive(address,uint256,uint256)\":{\"notice\":\"Emitted when the liquidation incentive of a single market is changed by admin\"},\"NewMinLiquidatableCollateral(uint256,uint256)\":{\"notice\":\"Emitted when the collateral threshold (in USD) for non-batch liquidations is changed\"},\"NewPriceOracle(address,address)\":{\"notice\":\"Emitted when price oracle is changed\"},\"NewRewardsDistributor(address,address)\":{\"notice\":\"Emitted when a rewards distributor is added\"},\"NewSupplyCap(address,uint256)\":{\"notice\":\"Emitted when supply cap for a vToken is changed\"},\"SupplyAllowlistEnabledUpdated(address,bool)\":{\"notice\":\"Emitted when a market's supply allowlist is enabled or disabled\"}},\"kind\":\"user\",\"methods\":{\"accessControlManager()\":{\"notice\":\"Returns the address of the access control manager contract\"},\"accountAssets(address,uint256)\":{\"notice\":\"Per-account mapping of \\\"assets you are in\\\"\"},\"actionPaused(address,uint8)\":{\"notice\":\"Checks if a certain action is paused on a market\"},\"addRewardsDistributor(address)\":{\"notice\":\"Add a new RewardsDistributor and initialize it with all markets. We can add several RewardsDistributor contracts with the same rewardToken, and there could be overlaping among them considering the last reward slot (block or second)\"},\"allMarkets(uint256)\":{\"notice\":\"A list of all markets\"},\"approvedDelegates(address,address)\":{\"notice\":\"Whether the delegate is allowed to borrow or redeem on behalf of the user\"},\"borrowCaps(address)\":{\"notice\":\"Borrow caps enforced by borrowAllowed for each vToken address. Defaults to zero which restricts borrowing.\"},\"borrowVerify(address,address,uint256)\":{\"notice\":\"Called by the vToken once a borrow has succeeded. No-op, see the note above.\"},\"checkMembership(address,address)\":{\"notice\":\"Returns whether the given account is entered in a given market\"},\"closeFactorMantissa()\":{\"notice\":\"Multiplier used to calculate the maximum repayAmount when liquidating a borrow\"},\"deviationBoundedOracle()\":{\"notice\":\"Oracle that bounds an asset's price against a recent window, so a deviating print cannot inflate borrowing capacity. Read only where the collateral factor weights the position; the liquidation-threshold paths stay on `oracle`, because they route liquidations.\"},\"effectiveLiquidationIncentive(address)\":{\"notice\":\"Returns the discount a liquidator receives on the collateral it seizes from a market\"},\"enterMarketBehalf(address,address)\":{\"notice\":\"Add an asset to another account's liquidity calculation, enabling it as collateral for that account\"},\"enterMarkets(address[])\":{\"notice\":\"Add assets to be included in account liquidity calculation; enabling them to be used as collateral\"},\"exitMarket(address)\":{\"notice\":\"Removes asset from sender's account liquidity calculation; disabling them as collateral\"},\"getAccountLiquidity(address)\":{\"notice\":\"Determine the current account liquidity with respect to liquidation threshold requirements\"},\"getAllMarkets()\":{\"notice\":\"Return all of the markets\"},\"getAssetsIn(address)\":{\"notice\":\"Returns the assets an account has entered\"},\"getBorrowingPower(address)\":{\"notice\":\"Determine the current account liquidity with respect to collateral requirements\"},\"getHypotheticalAccountLiquidity(address,address,uint256,uint256)\":{\"notice\":\"Determine what the account liquidity would be if the given amounts were redeemed/borrowed\"},\"getRewardDistributors()\":{\"notice\":\"Return all reward distributors for this pool\"},\"getRewardsByMarket(address)\":{\"notice\":\"Returns reward speed given a vToken\"},\"healAccount(address)\":{\"notice\":\"Seizes all the remaining collateral, makes msg.sender repay the existing borrows, and treats the rest of the debt as bad debt (for each market). The sender has to repay a certain percentage of the debt, computed as `maxClearableDebt / borrows`: see the note on `AccountLiquiditySnapshot.maxClearableDebt`.\"},\"isAllowedLiquidator(address)\":{\"notice\":\"The accounts allowed to seize collateral in this pool while the liquidation allowlist is enabled\"},\"isAllowedSupplier(address,address)\":{\"notice\":\"The accounts a market accepts supply from while its supply allowlist is enabled. Keyed by market, then by account.\"},\"isComptroller()\":{\"notice\":\"A marker method that returns true for a valid Comptroller contract\"},\"isForcedLiquidationEnabled(address)\":{\"notice\":\"Flag indicating whether forced liquidation enabled for a market\"},\"isLiquidationAllowlistEnabled()\":{\"notice\":\"Whether seizing collateral in this pool is restricted to the accounts on the liquidation allowlist. Disabled by default.\"},\"isMarketListed(address)\":{\"notice\":\"Check if a market is marked as listed (active)\"},\"isSupplyAllowlistEnabled(address)\":{\"notice\":\"Whether a market accepts supply only from the accounts on its supply allowlist. Keyed by market, and disabled by default, so a newly listed market accepts supply from anyone.\"},\"liquidateAccount(address,(address,address,uint256)[])\":{\"notice\":\"Liquidates all borrows of the borrower. Callable only if the collateral is less than a predefined threshold, and the account collateral can be seized to cover all borrows. If the collateral is higher than the threshold, use regular liquidations. If the collateral is below the threshold, and the account is insolvent, use healAccount.\"},\"liquidateBorrowVerify(address,address,address,address,uint256,uint256)\":{\"notice\":\"Called by the vToken once a liquidation has succeeded. No-op, see the note above.\"},\"liquidateCalculateSeizeTokens(address,address,uint256)\":{\"notice\":\"Calculate number of tokens of collateral asset to seize given an underlying amount\"},\"liquidationIncentiveMantissa()\":{\"notice\":\"Returns the discount a liquidator receives on the collateral it seizes from the calling market\"},\"liquidationIncentives(address)\":{\"notice\":\"Per-market discount a liquidator receives on the collateral it seizes, scaled by 1e18. Keyed by the collateral market, since that is what the discount prices.\"},\"markets(address)\":{\"notice\":\"Official mapping of vTokens -> Market metadata\"},\"minLiquidatableCollateral()\":{\"notice\":\"Minimal collateral required for regular (non-batch) liquidations\"},\"mintVerify(address,address,uint256,uint256)\":{\"notice\":\"Called by the vToken once a mint has succeeded. No-op, see the note above.\"},\"oracle()\":{\"notice\":\"Oracle which gives the price of any given asset\"},\"preBorrowHook(address,address,uint256)\":{\"notice\":\"disable-eslint\"},\"preLiquidateHook(address,address,address,uint256,bool)\":{\"notice\":\"Checks if the liquidation should be allowed to occur\"},\"preMintHook(address,address,uint256)\":{\"notice\":\"Checks if the account should be allowed to mint tokens in the given market\"},\"preRedeemHook(address,address,uint256)\":{\"notice\":\"Checks if the account should be allowed to redeem tokens in the given market\"},\"preRepayHook(address,address)\":{\"notice\":\"Checks if the account should be allowed to repay a borrow in the given market\"},\"preSeizeHook(address,address,address,address)\":{\"notice\":\"Checks if the seizing of assets should be allowed to occur\"},\"preTransferHook(address,address,address,uint256)\":{\"notice\":\"Checks if the account should be allowed to transfer tokens in the given market\"},\"redeemVerify(address,address,uint256,uint256)\":{\"notice\":\"Called by the vToken once a redeem has succeeded. No-op, see the note above.\"},\"repayBorrowVerify(address,address,address,uint256,uint256)\":{\"notice\":\"Called by the vToken once a repayment has succeeded. No-op, see the note above.\"},\"seizeVerify(address,address,address,address,uint256)\":{\"notice\":\"Called by the vToken once a seizure has succeeded. No-op, see the note above.\"},\"setAccessControlManager(address)\":{\"notice\":\"Sets the address of AccessControlManager\"},\"setActionsPaused(address[],uint8[],bool)\":{\"notice\":\"Pause/unpause specified actions\"},\"setAllowedLiquidator(address,bool)\":{\"notice\":\"Adds an account to the pool's liquidation allowlist or removes it\"},\"setAllowedSupplier(address,address,bool)\":{\"notice\":\"Adds an account to a market's supply allowlist or removes it\"},\"setCloseFactor(uint256)\":{\"notice\":\"Sets the closeFactor to use when liquidating borrows\"},\"setCollateralFactor(address,uint256,uint256)\":{\"notice\":\"Sets the collateralFactor for a market\"},\"setDeviationBoundedOracle(address)\":{\"notice\":\"Sets a new deviation-bounded oracle for the Comptroller\"},\"setForcedLiquidation(address,bool)\":{\"notice\":\"Enables forced liquidations for a market. If forced liquidation is enabled, borrows in the market may be liquidated regardless of the account liquidity\"},\"setLiquidationAllowlistEnabled(bool)\":{\"notice\":\"Restricts seizing collateral in this pool to the accounts on the liquidation allowlist, or lifts the restriction\"},\"setLiquidationIncentive(uint256)\":{\"notice\":\"Sets the liquidation incentive applied to any market that has no incentive of its own\"},\"setMarketBorrowCaps(address[],uint256[])\":{\"notice\":\"Set the given borrow caps for the given vToken markets. Borrowing that brings total borrows to or above borrow cap will revert.\"},\"setMarketLiquidationIncentive(address,uint256)\":{\"notice\":\"Sets the discount a liquidator receives on the collateral it seizes from a single market\"},\"setMarketSupplyCaps(address[],uint256[])\":{\"notice\":\"Set the given supply caps for the given vToken markets. Supply that brings total Supply to or above supply cap will revert.\"},\"setMaxLoopsLimit(uint256)\":{\"notice\":\"Set the for loop iteration limit to avoid DOS\"},\"setMinLiquidatableCollateral(uint256)\":{\"notice\":\"Set the given collateral threshold for non-batch liquidations. Regular liquidations will fail if the collateral amount is less than this threshold. Liquidators should use batch operations like liquidateAccount or healAccount.\"},\"setPriceOracle(address)\":{\"notice\":\"Sets a new price oracle for the Comptroller\"},\"setSupplyAllowlistEnabled(address,bool)\":{\"notice\":\"Restricts supplying to a market to the accounts on its supply allowlist, or lifts the restriction\"},\"supplyCaps(address)\":{\"notice\":\"Supply caps enforced by mintAllowed for each vToken address. Defaults to zero which corresponds to minting not allowed\"},\"supportMarket(address)\":{\"notice\":\"Add the market to the markets mapping and set it as listed\"},\"transferVerify(address,address,address,uint256)\":{\"notice\":\"Called by the vToken once a transfer has succeeded. No-op, see the note above.\"},\"unlistMarket(address)\":{\"notice\":\"Unlist a market by setting isListed to false\"},\"updateDelegate(address,bool)\":{\"notice\":\"Grants or revokes the borrowing or redeeming delegate rights to / from an account If allowed, the delegate will be able to borrow funds on behalf of the sender Upon a delegated borrow, the delegate will receive the funds, and the borrower will see the debt on their account Upon a delegated redeem, the delegate will receive the redeemed amount and the approver will see a deduction in his vToken balance\"},\"updatePrices(address)\":{\"notice\":\"Update the prices of all the tokens associated with the provided account\"}},\"notice\":\"The `SpokeComptroller` provides checks for all minting, redeeming, transferring, borrowing, repaying, liquidating and seizing done by the `vToken` contract. It is the comptroller of a single pool and checks those interactions across every market in it: when a user interacts with a market by one of these actions, the market calls a corresponding hook here, which either allows or reverts the transaction. These hooks also update supply and borrow rewards as they are called. The comptroller holds the logic for assessing liquidity snapshots of an account via the collateral factor and liquidation threshold. This check determines the collateral needed for a borrow, as well as how much of a borrow may be liquidated. A user may borrow a portion of their collateral with the maximum amount determined by the market's collateral factor, applied to a collateral value the deviation-bounded oracle caps against the asset's recent price window. However, if their borrowed amount exceeds an amount calculated using the market's corresponding liquidation threshold, the borrow is eligible for liquidation. Liquidations themselves are priced at spot. The `SpokeComptroller` also includes two functions `liquidateAccount()` and `healAccount()`, which are meant to handle accounts that do not exceed the `minLiquidatableCollateral` for the `SpokeComptroller`: - `healAccount()`: This function is called to seize all of a given user's collateral, requiring the `msg.sender` repay a certain percentage of the debt calculated by `maxClearableDebt/borrows`, where `maxClearableDebt` is the sum over the user's collateral markets of `collateralValue/liquidationIncentive`, each market taken at its own incentive. The function can only be called if the calculated percentage does not exceed 100%, because otherwise no `badDebt` would be created and `liquidateAccount()` should be used instead. The difference in the actual amount of debt and debt paid off is recorded as `badDebt` for each market, which can then be auctioned off for the risk reserves of the pool. - `liquidateAccount()`: This function can only be called if the collateral seized will cover all borrows of an account, as well as the liquidation incentive of each collateral market, which is the same condition stated as `borrows < maxClearableDebt`. Otherwise, the pool will incur bad debt, in which case the function `healAccount()` should be used instead. This function skips the logic verifying that the repay amount does not exceed the close factor. The two conditions are complements, so every account below `minLiquidatableCollateral` is served by exactly one of them.\",\"version\":1}},\"settings\":{\"compilationTarget\":{\"contracts/Spoke/SpokeComptroller.sol\":\"SpokeComptroller\"},\"evmVersion\":\"paris\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":30},\"remappings\":[]},\"sources\":{\"@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol\":{\"keccak256\":\"0x9140dabc466abab21b48b72dbda26736b1183a310d0e677d3719d201df026510\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://75267b14b60dc216d01d596a4008189a6c44d3314e53eded0edb1e757d95be16\",\"dweb:/ipfs/QmQoMaxTRT6V7uQj9USfdQH9jh1crywB9auVjThzUSAbG2\"]},\"@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol\":{\"keccak256\":\"0x359a1ab89b46b9aba7bcad3fb651924baf4893d15153049b9976b0fc9be1358e\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://e89863421b4014b96a4b62be76eb3b9f0a8afe9684664a6f389124c0964bfe5c\",\"dweb:/ipfs/Qmbk7xr1irpDuU1WdxXgxELBXxs61rHhCgod7heVcvFx16\"]},\"@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol\":{\"keccak256\":\"0x89be10e757d242e9b18d5a32c9fbe2019f6d63052bbe46397a430a1d60d7f794\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://f103ee2e4aecd37aac6ceefe670709cdd7613dee25fa2d4d9feaf7fc0aaa155e\",\"dweb:/ipfs/QmRiNZLoJk5k3HPMYGPGjZFd2ke1ZxjhJZkM45Ec9GH9hv\"]},\"@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol\":{\"keccak256\":\"0x0e1f0f5f62f67a881cd1a9597acbc0a5e4071f3c2c10449a183b922ae7272e3f\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://c25f742ff154998d19a669e2508c3597b363e123ce9144cd0fcf6521229f401f\",\"dweb:/ipfs/QmQXRuFzStEWqeEPbhQU6cAg9PaSowxJVo4PDKyRod7dco\"]},\"@openzeppelin/contracts-upgradeable/token/ERC20/extensions/IERC20PermitUpgradeable.sol\":{\"keccak256\":\"0x07e881de3b9f6d2c07909f193f24b96c7fe4ea60013260f3f25aecd8bab3c2f8\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://1fed09b97ccb0ff9ba9b6a94224f1d489026bf6b4b7279bfe64fb6e8749dee4d\",\"dweb:/ipfs/QmcRAzaSP1UnGr4vrGkfJmB2L9aiTYoXfV1Lg9gqrVRWn8\"]},\"@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol\":{\"keccak256\":\"0x23b997be73d3dd46885262704f0f8cfc7273fdadfe303d37969a9561373972b5\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://d03ebe5406134f0c4a017dee625ff615031194493bd1e88504e5c8fae55bc166\",\"dweb:/ipfs/QmUZV5bMbgk2PAkV3coouSeSainHN2jhqaQDJaA7hQRyu2\"]},\"@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol\":{\"keccak256\":\"0x9c80f545915582e63fe206c6ce27cbe85a86fc10b9cd2a0e8c9488fb7c2ee422\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://310136ad60820af4177a11a61d77a3686faf5fca4942b600e08fc940db38396b\",\"dweb:/ipfs/QmbCzMNSTL7Zi7M4UCSqBrkHtp4jjxUnGbkneCZKdR1qeq\"]},\"@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol\":{\"keccak256\":\"0x75097e35253e7fb282ee4d7f27a80eaacfa759923185bf17302a89cbc059c5ef\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://8b06267c5f80bad727af3e48b1382333d591dad51376399ef2f6b0ee6d58bf95\",\"dweb:/ipfs/QmdU5La1agcQvghnfMpWZGDPz2TUDTCxUwTLKmuMRXBpAx\"]},\"@openzeppelin/contracts/access/IAccessControl.sol\":{\"keccak256\":\"0x59ce320a585d7e1f163cd70390a0ef2ff9cec832e2aa544293a00692465a7a57\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://bb2c137c343ef0c4c7ce7b18c1d108afdc9d315a04e48307288d2d05adcbde3a\",\"dweb:/ipfs/QmUxhrAQM3MM3FF5j7AtcXLXguWCJBHJ14BRdVtuoQc8Fh\"]},\"@venusprotocol/governance-contracts/contracts/Governance/AccessControlledV8.sol\":{\"keccak256\":\"0xfe0fd441c84ac907cabc88db69ef04f6d7532d770c7e6a1dfe6e7d6305debb49\",\"license\":\"BSD-3-Clause\",\"urls\":[\"bzz-raw://2d94a5944cd90dd9fb985a3fd225b7b2065745e9a381ada6065c25fb43989e3a\",\"dweb:/ipfs/Qmb3WJHLiqq37EWEDmz1pGVq5fSnr65qC7nCxPsL9rKT5s\"]},\"@venusprotocol/governance-contracts/contracts/Governance/IAccessControlManagerV8.sol\":{\"keccak256\":\"0xaa29b098440d0b3a131c5ecdf25ce548790c1b5ac7bf9b5c0264b6af6f7a1e0b\",\"license\":\"BSD-3-Clause\",\"urls\":[\"bzz-raw://8120bda3990193388d0cc5f551510ef1eab685387a58a88ab607b5149e51acde\",\"dweb:/ipfs/QmNSX9ai6GbN4wQukM29rFkcWDFhqStUTtKe6XtreTvRcN\"]},\"@venusprotocol/oracle/contracts/interfaces/IDeviationBoundedOracle.sol\":{\"keccak256\":\"0xe223d88c17c0d752fdb33fa223b63ce124a798512f3d69f3800e9508e7dff931\",\"license\":\"BSD-3-Clause\",\"urls\":[\"bzz-raw://31cf725e1603ededa4f8beff360efaecc50d1060d123c25f56f3858a2b4a54b5\",\"dweb:/ipfs/QmPjD6J45a8aezRSgQLZASwwzVTgUjwvwiwgWSobM1QxXK\"]},\"@venusprotocol/oracle/contracts/interfaces/OracleInterface.sol\":{\"keccak256\":\"0xd3bbb7c9eef19e8f467342df6034ef95399a00964646fb8c82b438968ae3a8c0\",\"license\":\"BSD-3-Clause\",\"urls\":[\"bzz-raw://e056fb5f1ce0c2b81641553d1f6415088bc91e6a8ceef1007bb0e149a806a9ea\",\"dweb:/ipfs/QmXg7TxRAtm5Rn8ZnyiZVG4szWze7q8c8hMH5saDn5Fhxp\"]},\"@venusprotocol/protocol-reserve/contracts/Interfaces/IProtocolShareReserve.sol\":{\"keccak256\":\"0x0f341bbef8f12885d79b7acaad7aaf11b84809e8f6b059ef4efc011c8f6c39a5\",\"license\":\"BSD-3-Clause\",\"urls\":[\"bzz-raw://0c0f5e6f1df481791b626baf01e4190ad720e3921f167bb86f62c9ac7a39f821\",\"dweb:/ipfs/QmbswkkXdHd2JQ7ppLJMWYNxjYuZRoeYsJH3yzz3UxsijG\"]},\"@venusprotocol/solidity-utilities/contracts/TimeManagerV8.sol\":{\"keccak256\":\"0x57a2bbb9b8e02b1c0a5c0e305fef1328a22db56c3d4b148c362010a6e767243c\",\"license\":\"BSD-3-Clause\",\"urls\":[\"bzz-raw://01c6af010cbff93563f2175d48702f5d901beb59b0e3315ed2a5583dfa53ee21\",\"dweb:/ipfs/QmY7sfvoQ1kEQtLhPdSA3bQdV4u3hT563RSvuCtgSrQUmx\"]},\"@venusprotocol/solidity-utilities/contracts/constants.sol\":{\"keccak256\":\"0x14de93ead464da249af31bea0e3bcfb62ec693bea3475fb4d90f055ac81dc5eb\",\"license\":\"BSD-3-Clause\",\"urls\":[\"bzz-raw://6134b92b2bc5bad1c6e088d0d092736eede6dfe2cf7dadc573f1396a9d690274\",\"dweb:/ipfs/QmXwKV4SY7CdCaCaDqXudcLxVLB4vUfbwMiH9kH6HhWpiy\"]},\"@venusprotocol/venus-protocol/contracts/Tokens/Prime/Interfaces/IPrime.sol\":{\"keccak256\":\"0xd112f60183416ad796093b4a83a80ddc1b8b655965f02ae55ca82e2a0c68f97d\",\"license\":\"BSD-3-Clause\",\"urls\":[\"bzz-raw://95b5d2eee4e994e248c66b95c1c23954e6aa81cce147b68705d0e9657de3871d\",\"dweb:/ipfs/Qmdf8sMZCm8f2j3rh2Jx7xzxqt1T5gyLKfMEeG6KtV4Fo7\"]},\"@venusprotocol/venus-protocol/contracts/Tokens/Prime/PrimeStorage.sol\":{\"keccak256\":\"0x5285c875114db2ea2be0b81b65722d5761806217022db733323c4e03365a95e7\",\"license\":\"BSD-3-Clause\",\"urls\":[\"bzz-raw://f7381f89c82fcbacdb5f8d66dd944d09e102c23ad243d96cb46b480621bfb62c\",\"dweb:/ipfs/QmNgyd8zgXHB6akfj78MUrgLDvzjKn8d8u3KE3fhb2pPJh\"]},\"contracts/Comptroller.sol\":{\"keccak256\":\"0x7c6e1c6264e4681f82a9ac1bcd9155197a930033291ee5561ad97a56006f5e9c\",\"license\":\"BSD-3-Clause\",\"urls\":[\"bzz-raw://9af5eb4fa4348f4bee0b0b4083c2eaf67dc6d05219882b298d82830316c6d40d\",\"dweb:/ipfs/QmR3iGJiWxQQSw8LQNVTMx4HNNixRsgVya2xCThE5FUv8T\"]},\"contracts/ComptrollerInterface.sol\":{\"keccak256\":\"0xcbf7f9977472650c61345b7cbde23e81f626e1f8863bab4c6ce3dacfc7604919\",\"license\":\"BSD-3-Clause\",\"urls\":[\"bzz-raw://79472ca5fcc29e5a41f1209fb33701c45852141d231ff7ac584b2ece3bda76c4\",\"dweb:/ipfs/QmUB8e8obVXN898oCMwFjeP1SFmrpu99iSwjmtPUZXCkXm\"]},\"contracts/ComptrollerStorage.sol\":{\"keccak256\":\"0x4df44cb65ac152bac2be4b4545430e703005a74a55b72e144100b16421bd8bfd\",\"license\":\"BSD-3-Clause\",\"urls\":[\"bzz-raw://a7b1f84e68406b41cadf240e111887ab79787fa385b87040c87d6cf25ff586ed\",\"dweb:/ipfs/QmV8JQog8CCz4HuiicsRShHfk4hfFKxUayT1NgpFvDRTdq\"]},\"contracts/ErrorReporter.sol\":{\"keccak256\":\"0x7a7ced1caaac2d9242659ebd50b99f308c725ce958ac9e11f7ada404b1d97a7b\",\"license\":\"BSD-3-Clause\",\"urls\":[\"bzz-raw://6bc5b36130029c245d9c2c6e4e6e3a6ad5596518e98764a2315f247e817e7c6b\",\"dweb:/ipfs/QmWMtrqThCk1wqPxgkK9mMaf8E3fuRUg51awuGL6KmxwrU\"]},\"contracts/ExponentialNoError.sol\":{\"keccak256\":\"0x8afbe8a24fe3539c124e718681ed3dcebd24c40dd53095786c0155998213b9b0\",\"license\":\"BSD-3-Clause\",\"urls\":[\"bzz-raw://64d6fe74721ef3112ecc641d868fe51738e3687d782b750fac63f139c0335900\",\"dweb:/ipfs/Qme9R3YKdc9WeLMymU4bW544usyXJU3PXDMBmyE7x7riV9\"]},\"contracts/InterestRateModel.sol\":{\"keccak256\":\"0xc4fda1ab75ebe4b187b707c4f10c58780f343cf343c537f641dc75d3cd28ab51\",\"license\":\"BSD-3-Clause\",\"urls\":[\"bzz-raw://8cf703e1c44ebd4977200275e7c8c480081f1f6f54bedb6b0a070af04a8c733d\",\"dweb:/ipfs/QmUNCCcYZxftVaf4SdqXUpjeeyNe9Kqr45dbNguBGY5X1h\"]},\"contracts/MaxLoopsLimitHelper.sol\":{\"keccak256\":\"0x4c25e30635485d162177effa384eee51768b0141a567a0da16ff6ad673274166\",\"license\":\"BSD-3-Clause\",\"urls\":[\"bzz-raw://5d71ed301eb76292bce7b3500482452c397b766507222d00f3982b65520d156b\",\"dweb:/ipfs/QmcF3yPyr6hWQUA6wfEi6Hq1TEUtbxFP9MAcYQy4D2tFHC\"]},\"contracts/Rewards/RewardsDistributor.sol\":{\"keccak256\":\"0x3cc824e59c923cfe25c4f1a875959b7e9410cde8e73cd405de13f301298c697f\",\"license\":\"BSD-3-Clause\",\"urls\":[\"bzz-raw://3cc9eaf325724c1a4906b2f91c4bd9ccb44fd721226e1d375832238d586b956b\",\"dweb:/ipfs/QmfTcWw1S3XonT8mc27RzD9aSWCu7qD7nqM9yD5wraCYZe\"]},\"contracts/Rewards/RewardsDistributorStorage.sol\":{\"keccak256\":\"0x70e5015a78a2acf95277949c8b4796e10b9ac194130be006d5e5217e158070c0\",\"license\":\"BSD-3-Clause\",\"urls\":[\"bzz-raw://e2bb293581e076a38032cf087a5982967a2ffdc8f444550b019a4e52214da3fe\",\"dweb:/ipfs/QmV5QWm6hitgFyTcTXWQb9PyquuGfnLuMZumR9i6RUFT5g\"]},\"contracts/Spoke/SpokeComptroller.sol\":{\"keccak256\":\"0xfed19936a2f34fe95f023661805f1a40e11a8ed016ab938b112fb9c5a1704fbf\",\"license\":\"BSD-3-Clause\",\"urls\":[\"bzz-raw://97ca83241dac8345141091d650bf0372a87e48305670e32b7d835adbc0966df5\",\"dweb:/ipfs/QmWTyAMDg1pPF48m7FGXsXmJDdvbEdWdS8Zw9WWUapfxRU\"]},\"contracts/Spoke/SpokeComptrollerInterface.sol\":{\"keccak256\":\"0x5e5c11b9d7f23436a40aa453fc6a56c34d73d22399f9adb605fc1b2a87afc6b7\",\"license\":\"BSD-3-Clause\",\"urls\":[\"bzz-raw://79561e36bda67dbf7b3d1c613a59f026f7fd52eb7547c3cabca0f4f62ca32e9e\",\"dweb:/ipfs/QmZJSkUWdt41RiWD73DbWB9QYW2ULLXnaDxHFZtyfnygDa\"]},\"contracts/Spoke/SpokeComptrollerStorage.sol\":{\"keccak256\":\"0xd185dc87e5a8c86a44d850e311ea84bc396a6c1c84b6021ab7dc836c79c15b8d\",\"license\":\"BSD-3-Clause\",\"urls\":[\"bzz-raw://41302a4c16b8f29fa37502d4d9b0a9a7761c26de4f305edff1ae0dd7286fa3da\",\"dweb:/ipfs/QmWaTAK83mRrTpepk9Zmuy1QrovKoBpyJFt1ehn8mfqdbS\"]},\"contracts/VToken.sol\":{\"keccak256\":\"0x7267f5337062ad01a5011f7725a86cc2ad0351cca0aaceadc167341f168cdef2\",\"license\":\"BSD-3-Clause\",\"urls\":[\"bzz-raw://52fdb9fd04664f5b0b90f620c0b2f72b2a7c311d7193249b2083bc7391117884\",\"dweb:/ipfs/QmSmCAobThKhHXHS7mEqfGH5egVH1nBfGG6XnwApjZzcpo\"]},\"contracts/VTokenInterfaces.sol\":{\"keccak256\":\"0x155684e9cb12cdd8be63d2314c9452b68ee44ff98a00e0d65cd1fd7d0bdd4391\",\"license\":\"BSD-3-Clause\",\"urls\":[\"bzz-raw://75da0c617e6eaba09484668c4b14d90a1063e087cd7f1a86c0955e620453f19d\",\"dweb:/ipfs/QmVTfEz6Mdd8R7C3PSBp49enYtqSY36WJDManvzCLHvstr\"]},\"contracts/lib/constants.sol\":{\"keccak256\":\"0x54ab3a6f3bc87569ed12370f3470a1ec84cea9796d4d0ccf3d07dd4280c044aa\",\"license\":\"BSD-3-Clause\",\"urls\":[\"bzz-raw://be1b725f8bf381f0a0e14b5fc676fcd38fbcbda868f6ec0b5163cfa4cb25e548\",\"dweb:/ipfs/QmdDLtw2sVdVy8RhHWeoNVaEzQJhykgbHdPrGeFRKRcPgw\"]},\"contracts/lib/validators.sol\":{\"keccak256\":\"0x93bdd5cfb100f0f9a1d446857e23c3633df6ab12c266333c978428edd96b1367\",\"license\":\"BSD-3-Clause\",\"urls\":[\"bzz-raw://4b3a06c0cc5166270e920a1b7a7a2126ac01ed1198ce695c483f7c6ce0791056\",\"dweb:/ipfs/QmcK9nwALKisL6WnrxdWjf38n2vds1tUvBgz4ii7Aj6ZHR\"]}},\"version\":1}", + "bytecode": "0x60a060405234801561001057600080fd5b5060405161617538038061617583398101604081905261002f9161013c565b61003881610053565b6001600160a01b03811660805261004d61007d565b5061016c565b6001600160a01b03811661007a576040516342bcdf7f60e11b815260040160405180910390fd5b50565b600054610100900460ff16156100e95760405162461bcd60e51b815260206004820152602760248201527f496e697469616c697a61626c653a20636f6e747261637420697320696e697469604482015266616c697a696e6760c81b606482015260840160405180910390fd5b60005460ff9081161461013a576000805460ff191660ff9081179091556040519081527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b565b60006020828403121561014e57600080fd5b81516001600160a01b038116811461016557600080fd5b9392505050565b608051615fe761018e60003960008181610895015261309b0152615fe76000f3fe608060405234801561001057600080fd5b50600436106103bf5760003560e01c80638a0ecd18116101f7578063c5c0542511610116578063c5c0542514610955578063cab4f84c14610975578063d136af4414610988578063d585c3c61461099b578063d7c46d2d146109ae578063da35a26f146109c1578063db5c65de146109d4578063dce15449146109dd578063ddbf54fd146109f0578063df71403b14610a03578063e30c397814610a16578063e85a296014610a1e578063e875544614610a31578063e89d51ad14610a3a578063eade3eed14610a4d578063ede4edd014610a60578063f2fde38b14610a73578063f45fe69f14610a86578063f582cffd14610aa9578063fe40768f14610ab657600080fd5b80638a0ecd18146107115780638b3113f6146107245780638c1ac18a146107375780638da5cb5b1461075a5780638e6470ea146107625780638e8f294b1461077557806392136395146107c5578063929fe9a1146107d85780639dcd31fd146108195780639f33322c1461083c578063a84310811461086a578063abfceffc1461087d578063afcff50f14610890578063b0772d0b146108b7578063b2068e84146108bf578063b4a0bdf3146108df578063be26317e146108f0578063c0891ba9146108fa578063c29982381461090d578063c488847b1461092d57600080fd5b8063520b6c74116102e3578063520b6c74146105a6578063528a174c146105b957806352d84d1e146105cc578063530e784f146105ec57806356aaee2d146105ff5780635c21b6c5146106125780635c778605146106255780635cc4fdeb146106385780635ec88c791461064b57806361252fd11461065e57806362bcf431146106735780636a12b637146106865780636a56947e146106995780636d0be88d146106a75780636d35bf91146106ba578063715018a6146106c857806379ba5097146106d05780637db94b95146106d85780637dc0d1d0146106eb57806380d45a2d146106fe57600080fd5b80627e3dd2146103c457806302c3bcbb146103dc5780630686dab61461040a578063073338971461041d5780630e32cb861461043257806310b983381461044557806312348e9614610473578063186db48f146104865780631bc41f28146104995780631ededc91146104ac57806324aaa220146104c15780632bce219c146104d45780633d98a1e5146104e757806341c728b91461051357806346c952051461052757806347ef3b3b1461053a5780634a584432146105505780634ada90af146105705780634e79238f1461057857806351dff98914610513575b600080fd5b60015b60405190151581526020015b60405180910390f35b6103fc6103ea366004615209565b60d16020526000908152604090205481565b6040519081526020016103d3565b6103fc610418366004615209565b610ac9565b61043061042b366004615234565b610d84565b005b610430610440366004615209565b610e56565b6103c761045336600461527f565b60d660209081526000928352604080842090915290825290205460ff1681565b6104306104813660046152b8565b610e6a565b610430610494366004615315565b610f39565b6104306104a7366004615380565b61108b565b6104306104ba3660046153dc565b5050505050565b6104306104cf366004615437565b61144e565b6104306104e23660046154ba565b61150b565b6103c76104f5366004615209565b6001600160a01b0316600090815260cd602052604090205460ff1690565b610430610521366004615541565b50505050565b610430610535366004615587565b61181c565b6104306105483660046155b5565b505050505050565b6103fc61055e366004615209565b60cf6020526000908152604090205481565b6103fc6118a6565b61058b610586366004615541565b6118b6565b604080519384526020840192909252908201526060016103d3565b6104306105b43660046152b8565b6118e6565b61058b6105c7366004615209565b611944565b6105df6105da3660046152b8565b61196e565b6040516103d39190615623565b6104306105fa366004615209565b611998565b61043061060d366004615209565b6119fc565b610430610620366004615209565b611bfc565b610430610633366004615637565b505050565b610430610646366004615678565b611c0d565b61058b610659366004615209565b611e1e565b610666611e2f565b6040516103d391906156ad565b610430610681366004615587565b611e91565b6104306106943660046156fa565b611f45565b610430610521366004615726565b6104306106b5366004615726565b612094565b6104306104ba366004615777565b610430612207565b61043061221b565b6103fc6106e6366004615209565b612296565b60c9546105df906001600160a01b031681565b61043061070c3660046152b8565b6122a7565b61043061071f3660046157db565b6122b8565b610430610732366004615587565b612320565b6103c7610745366004615209565b60d56020526000908152604090205460ff1681565b6105df6123dd565b610430610770366004615637565b6123ec565b6107a8610783366004615209565b60cd6020526000908152604090208054600182015460029092015460ff909116919083565b6040805193151584526020840192909252908201526060016103d3565b6104306107d3366004615209565b6124ff565b6103c76107e636600461527f565b6001600160a01b03808216600090815260cd60209081526040808320938616835260039093019052205460ff1692915050565b6103c7610827366004615209565b60da6020526000908152604090205460ff1681565b6103c761084a36600461527f565b60d860209081526000928352604080842090915290825290205460ff1681565b6104306108783660046152b8565b612725565b61066661088b366004615209565b6127ca565b6105df7f000000000000000000000000000000000000000000000000000000000000000081565b610666612944565b6108d26108cd366004615209565b6129a4565b6040516103d391906157f8565b6097546001600160a01b03166105df565b6103fc6101075481565b610430610908366004615637565b612be5565b61092061091b366004615870565b612ecb565b6040516103d39190615934565b61094061093b366004615637565b612f79565b604080519283526020830191909152016103d3565b6103fc610963366004615209565b60db6020526000908152604090205481565b610430610983366004615209565b613096565b610430610996366004615315565b613289565b6104306109a936600461527f565b6133d1565b60dc546105df906001600160a01b031681565b6104306109cf36600461596c565b613409565b6103fc60d05481565b6105df6109eb3660046156fa565b613525565b6104306109fe366004615587565b61355d565b610430610a11366004615637565b61361e565b6105df613a40565b6103c7610a2c3660046159a0565b613a4f565b6103fc60ca5481565b610430610a483660046159d5565b613aa6565b610430610a5b36600461527f565b613c99565b6103fc610a6e366004615209565b613eb7565b610430610a81366004615209565b614121565b6103c7610a94366004615209565b60d76020526000908152604090205460ff1681565b60d9546103c79060ff1681565b610430610ac4366004615209565b614187565b6000610b0160405180604001604052806015815260200174756e6c6973744d61726b657428616464726573732960581b8152506141eb565b6001600160a01b038216600090815260cd60205260409020805460ff16610b465782604051635a9a1eb960e11b8152600401610b3d9190615623565b60405180910390fd5b610b51836002613a4f565b610b6e57604051630309b07560e21b815260040160405180910390fd5b610b79836000613a4f565b610b96576040516306ed36fb60e41b815260040160405180910390fd5b610ba1836001613a4f565b610bbe57604051634e577b7d60e11b815260040160405180910390fd5b610bc9836003613a4f565b610be657604051632b1e340960e01b815260040160405180910390fd5b610bf1836004613a4f565b610c0e5760405163bb56e52560e01b815260040160405180910390fd5b610c19836007613a4f565b610c3657604051630a6a9a9d60e31b815260040160405180910390fd5b610c41836005613a4f565b610c5e5760405163b3cf04ad60e01b815260040160405180910390fd5b610c69836006613a4f565b610c86576040516308624f7160e21b815260040160405180910390fd5b610c91836008613a4f565b610cae57604051632f412a5560e21b815260040160405180910390fd5b6001600160a01b038316600090815260cf602052604090205415610ce55760405163668019b360e01b815260040160405180910390fd5b6001600160a01b038316600090815260d1602052604090205415610d1c57604051638603c8cf60e01b815260040160405180910390fd5b600181015415610d3f57604051630707987b60e11b815260040160405180910390fd5b805460ff191681556040516001600160a01b038416907f302feb03efd5741df80efe7f97f5d93d74d46a542a3d312d0faae64fa1f3e0e990600090a250600092915050565b610da5604051806060016040528060288152602001615de0602891396141eb565b610dae82614285565b6001600160a01b038316600090815260cd602052604090205460ff16610de95782604051635a9a1eb960e11b8152600401610b3d9190615623565b6001600160a01b03838116600081815260d86020908152604080832094871680845294825291829020805460ff191686151590811790915591519182527f8202825e3161f4b2cda66ca77da3debbf8c94eac81264005088eb8b9bc0b651f910160405180910390a3505050565b610e5e6142ac565b610e678161430b565b50565b610ea260405180604001604052806017815260200176736574436c6f7365466163746f722875696e743235362960481b8152506141eb565b670c7d713b49da0000811115610ecb5760405163ee0bdbdf60e01b815260040160405180910390fd5b66b1a2bc2ec50000811015610ef35760405163ee0bdbdf60e01b815260040160405180910390fd5b60ca80549082905560408051828152602081018490527f3b9670cf975d26958e754b57098eaa2ac914d8d2a31b83257997b9f346110fd991015b60405180910390a15050565b610f5a604051806060016040528060288152602001615e08602891396141eb565b8281811580610f695750808214155b15610f8757604051634ec4810560e11b815260040160405180910390fd5b610f90826143c2565b60005b8281101561108257848482818110610fad57610fad615a2f565b9050602002013560cf6000898985818110610fca57610fca615a2f565b9050602002016020810190610fdf9190615209565b6001600160a01b0316815260208101919091526040016000205586868281811061100b5761100b615a2f565b90506020020160208101906110209190615209565b6001600160a01b03167f6f1951b2aad10f3fc81b86d91105b413a5b3f847a34bbc5ce1904201b14438f686868481811061105c5761105c615a2f565b9050602002013560405161107291815260200190565b60405180910390a2600101610f93565b50505050505050565b6110968460046143f5565b6001600160a01b038416600090815260cd60205260409020805460ff166110d25784604051635a9a1eb960e11b8152600401610b3d9190615623565b306001600160a01b0385160361117a57306001600160a01b0316856001600160a01b0316635fe3b5676040518163ffffffff1660e01b8152600401602060405180830381865afa15801561112a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061114e9190615a45565b6001600160a01b03161461117557604051630c73eb0560e01b815260040160405180910390fd5b6112a9565b6001600160a01b038416600090815260cd602052604090205460ff166111b55783604051635a9a1eb960e11b8152600401610b3d9190615623565b836001600160a01b0316635fe3b5676040518163ffffffff1660e01b8152600401602060405180830381865afa1580156111f3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112179190615a45565b6001600160a01b0316856001600160a01b0316635fe3b5676040518163ffffffff1660e01b8152600401602060405180830381865afa15801561125e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112829190615a45565b6001600160a01b0316146112a957604051630c73eb0560e01b815260040160405180910390fd5b6001600160a01b038216600090815260038201602052604090205460ff166112e8578482604051630cdfb2db60e31b8152600401610b3d929190615a62565b6112f183614421565b60d35460005b8181101561108257600060d3828154811061131457611314615a2f565b60009182526020909120015460405163051d1d4f60e11b81526001600160a01b0390911691508190630a3a3a9e90611350908b90600401615623565b600060405180830381600087803b15801561136a57600080fd5b505af115801561137e573d6000803e3d6000fd5b505060405163db7954fd60e01b81526001600160a01b038416925063db7954fd91506113b0908b908990600401615a62565b600060405180830381600087803b1580156113ca57600080fd5b505af11580156113de573d6000803e3d6000fd5b505060405163db7954fd60e01b81526001600160a01b038416925063db7954fd9150611410908b908a90600401615a62565b600060405180830381600087803b15801561142a57600080fd5b505af115801561143e573d6000803e3d6000fd5b50505050508060010190506112f7565b61146f6040518060600160405280602a8152602001615eca602a91396141eb565b838261148361147e8284615a92565b6143c2565b60005b828110156115015760005b828110156114f8576114f08989848181106114ae576114ae615a2f565b90506020020160208101906114c39190615209565b8888848181106114d5576114d5615a2f565b90506020020160208101906114ea9190615aa9565b8761446c565b600101611491565b50600101611486565b5050505050505050565b6000611516846127ca565b90506115218161453c565b600061152e8560016145d9565b905060d054816000015111156115655760d0548151604051631a451c0f60e21b815260048101929092526024820152604401610b3d565b8060c001518160400151106115a25780604001518160c00151604051630936175b60e11b8152600401610b3d929190918252602082015260400190565b8060a001516000036115c75760405163095bf33360e01b815260040160405180910390fd5b826115d661147e600283615ac4565b60005b818110156117bd5760cd60008787848181106115f7576115f7615a2f565b905060600201602001602081019061160f9190615209565b6001600160a01b0316815260208101919091526040016000205460ff166116745785858281811061164257611642615a2f565b905060600201602001602081019061165a9190615209565b604051635a9a1eb960e11b8152600401610b3d9190615623565b60cd600087878481811061168a5761168a615a2f565b6116a09260206060909202019081019150615209565b6001600160a01b0316815260208101919091526040016000205460ff166116e9578585828181106116d3576116d3615a2f565b61165a9260206060909202019081019150615209565b368686838181106116fc576116fc615a2f565b90506060020190508060200160208101906117179190615209565b6001600160a01b0316638bbdb6db338a60408501356117396020870187615209565b60405160e086901b6001600160e01b03191681526001600160a01b0394851660048201529284166024840152604483019190915290911660648201526001608482015260a401600060405180830381600087803b15801561179957600080fd5b505af11580156117ad573d6000803e3d6000fd5b50505050508060010190506115d9565b50825160005b818110156115015760006117f08683815181106117e2576117e2615a2f565b60200260200101518a6145f7565b509150508015611813576040516319bc701360e11b815260040160405180910390fd5b506001016117c3565b61183d604051806060016040528060228152602001615e30602291396141eb565b61184682614285565b6001600160a01b038216600081815260da6020908152604091829020805460ff191685151590811790915591519182527f2fa1fd52a8d73f0cc9eef64ceb5d0b5e14cb8bef299666bf18ff6b193823e2c491015b60405180910390a25050565b60006118b13361469e565b905090565b6000806000806118ca8888888860006146c6565b608081015160a09091015160009a919950975095505050505050565b611907604051806060016040528060258152602001615e52602591396141eb565b60d080549082905560408051828152602081018490527eb4f4f153ad7f1397564a8830fef092481e8cf6a2cd3ff04f96d10ba51200a59101610f2d565b6000806000806119558560006145d9565b608081015160a090910151600097919650945092505050565b60ce818154811061197e57600080fd5b6000918252602090912001546001600160a01b0316905081565b6119a06142ac565b6119a981614285565b60c980546001600160a01b038381166001600160a01b03198316179092556040519116907fd52b2b9b7e9ee655fcb95d2e5b9e0c9f69e7ef2b8e9d2d0ea78402d576d22e2290610f2d9083908590615a62565b611a046142ac565b6001600160a01b038116600090815260d4602052604090205460ff1615611a3e5760405163c4a2984b60e01b815260040160405180910390fd5b60d354611a4f61147e826001615ae6565b60d3805460018082019092557f915c3eb987b20e1af620c1403197bf687fb7f18513b3a73fde6e78c7072c41a60180546001600160a01b0319166001600160a01b038516908117909155600090815260d460205260408120805460ff191690921790915560ce54905b81811015611b5557836001600160a01b0316632a869a4d60ce8381548110611ae257611ae2615a2f565b6000918252602090912001546040516001600160e01b031960e084901b168152611b18916001600160a01b031690600401615623565b600060405180830381600087803b158015611b3257600080fd5b505af1158015611b46573d6000803e3d6000fd5b50505050806001019050611ab8565b50826001600160a01b031663f7c618c16040518163ffffffff1660e01b8152600401602060405180830381865afa158015611b94573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611bb89190615a45565b6001600160a01b0316836001600160a01b03167f066a44d77db1581603d7d8ca1ca494756c0d359c7ffacd9b2c8f78dab7aceae260405160405180910390a3505050565b610e67611c08826127ca565b6147c8565b611c2e6040518060600160405280602c8152602001615e77602c91396141eb565b6001600160a01b038316600090815260cd60205260409020805460ff16611c6a5783604051635a9a1eb960e11b8152600401610b3d9190615623565b670d2f13f7789f0000831115611c93576040516302f22cad60e61b815260040160405180910390fd5b670de0b6b3a7640000821115611cbb5760405162f9474b60e61b815260040160405180910390fd5b82821015611cdb5760405162f9474b60e61b815260040160405180910390fd5b8215801590611d58575060c95460405163fc57d4df60e01b81526001600160a01b039091169063fc57d4df90611d15908790600401615623565b602060405180830381865afa158015611d32573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d569190615af9565b155b15611d77578360405162e52a7d60e41b8152600401610b3d9190615623565b6001810154838114611dc757600182018490556040517f70483e6592cd5182d45ac970e05bc62cdcc90e9d8ef2c2dbe686cf383bcd7fc590611dbe90879084908890615b12565b60405180910390a15b600282015483811461054857600283018490556040517f9e92c7d5fef69846094f3ddcadcb9402c6ba469c461368714f1cabd8ef48b59190611e0e90889084908890615b12565b60405180910390a1505050505050565b6000806000806119558560016145d9565b606060d3805480602002602001604051908101604052809291908181526020018280548015611e8757602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311611e69575b5050505050905090565b611eb2604051806060016040528060278152602001615ea3602791396141eb565b6001600160a01b038216600090815260cd602052604090205460ff16611eed5781604051635a9a1eb960e11b8152600401610b3d9190615623565b6001600160a01b038216600081815260d76020908152604091829020805460ff191685151590811790915591519182527f3e9b2c3906fcb5e1a77a78eb0d76a2f76c432f67c6c461756cc58e33d5b38ea6910161189a565b611f666040518060600160405280602e8152602001615ef4602e91396141eb565b6001600160a01b038216600090815260cd602052604090205460ff16611fa15781604051635a9a1eb960e11b8152600401610b3d9190615623565b816001600160a01b0316636752e7026040518163ffffffff1660e01b8152600401602060405180830381865afa158015611fdf573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120039190615af9565b61201590670de0b6b3a7640000615ae6565b811015612035576040516333987daf60e01b815260040160405180910390fd5b6001600160a01b038216600081815260db6020908152604091829020805490859055825181815291820185905292917f63d34d4644180c2a888e32f4ee4da6d02be25b407e61c64cf4b6a052d115977e910160405180910390a2505050565b61209f8460066143f5565b6120aa848483614863565b60d35460005b8181101561054857600060d382815481106120cd576120cd615a2f565b60009182526020909120015460405163051d1d4f60e11b81526001600160a01b0390911691508190630a3a3a9e90612109908a90600401615623565b600060405180830381600087803b15801561212357600080fd5b505af1158015612137573d6000803e3d6000fd5b505060405163db7954fd60e01b81526001600160a01b038416925063db7954fd9150612169908a908a90600401615a62565b600060405180830381600087803b15801561218357600080fd5b505af1158015612197573d6000803e3d6000fd5b505060405163db7954fd60e01b81526001600160a01b038416925063db7954fd91506121c9908a908990600401615a62565b600060405180830381600087803b1580156121e357600080fd5b505af11580156121f7573d6000803e3d6000fd5b50505050508060010190506120b0565b61220f6142ac565b612219600061491c565b565b3380612225613a40565b6001600160a01b03161461228d5760405162461bcd60e51b815260206004820152602960248201527f4f776e61626c6532537465703a2063616c6c6572206973206e6f7420746865206044820152683732bb9037bbb732b960b91b6064820152608401610b3d565b610e678161491c565b60006122a18261469e565b92915050565b6122af6142ac565b610e6781614935565b6122d9604051806060016040528060248152602001615f22602491396141eb565b60d9805460ff19168215159081179091556040519081527f80cb324e0c14fd08bfaa64a58bb9875493cba570fb5ed20ffae7dbd74500516a9060200160405180910390a150565b612341604051806060016040528060228152602001615f46602291396141eb565b61234a82614285565b6001600160a01b038216600090815260cd602052604090205460ff166123855781604051635a9a1eb960e11b8152600401610b3d9190615623565b6001600160a01b038216600081815260d56020908152604091829020805460ff191685151590811790915591519182527f03561d5280ebb02280893b1d60978e4a27e7654a149c5d0e7c2cf65389ce1694910161189a565b6033546001600160a01b031690565b6123f78360016143f5565b612402838383614863565b60d35460005b818110156104ba57600060d3828154811061242557612425615a2f565b60009182526020909120015460405163051d1d4f60e11b81526001600160a01b0390911691508190630a3a3a9e90612461908990600401615623565b600060405180830381600087803b15801561247b57600080fd5b505af115801561248f573d6000803e3d6000fd5b505060405163db7954fd60e01b81526001600160a01b038416925063db7954fd91506124c19089908990600401615a62565b600060405180830381600087803b1580156124db57600080fd5b505af11580156124ef573d6000803e3d6000fd5b5050505050806001019050612408565b61250833614421565b6000612513826127ca565b8051909150336125228361453c565b600061252f8560016145d9565b905060d054816000015111156125665760d0548151604051631a451c0f60e21b815260048101929092526024820152604401610b3d565b8060a0015160000361258b5760405163095bf33360e01b815260040160405180910390fd5b80604001518160c0015111156125c95780604001518160c00151604051630340492d60e41b8152600401610b3d929190918252602082015260400190565b60006125f960405180602001604052808460c00151815250604051806020016040528085604001518152506149d1565b905060005b8481101561108257600086828151811061261a5761261a615a2f565b60200260200101519050600080612631838b6145f7565b509150915060006126428683614a0d565b905082156126ad5760405163b2a02ff160e01b81526001600160a01b0385169063b2a02ff19061267a908b908f908890600401615b33565b600060405180830381600087803b15801561269457600080fd5b505af11580156126a8573d6000803e3d6000fd5b505050505b81156127165760405163227f37ff60e11b81526001600160a01b038516906344fe6ffe906126e3908b908f908690600401615b33565b600060405180830381600087803b1580156126fd57600080fd5b505af1158015612711573d6000803e3d6000fd5b505050505b505050508060010190506125fe565b6127636040518060400160405280602081526020017f7365744c69717569646174696f6e496e63656e746976652875696e74323536298152506141eb565b670e92596fd629000081101561278c576040516333987daf60e01b815260040160405180910390fd5b60cb80549082905560408051828152602081018490527faeba5a6c40a8ac138134bff1aaa65debf25971188a58804bad717f82f0ec13169101610f2d565b6001600160a01b038116600090815260cc60209081526040808320805482518185028101850190935280835260609493849392919083018282801561283857602002820191906000526020600020905b81546001600160a01b0316815260019091019060200180831161281a575b505050505090506000815190506000816001600160401b0381111561285f5761285f61585a565b604051908082528060200260200182016040528015612888578160200160208202803683370190505b50905060005b8281101561293757600060cd60008684815181106128ae576128ae615a2f565b6020908102919091018101516001600160a01b03168252810191909152604001600020805490915060ff161561292e578482815181106128f0576128f0615a2f565b602002602001015183878151811061290a5761290a615a2f565b6001600160a01b039092166020928302919091019091015261292b86615b57565b95505b5060010161288e565b5092835250909392505050565b606060ce805480602002602001604051908101604052809291908181526020018280548015611e87576020028201919060005260206000209081546001600160a01b03168152600190910190602001808311611e69575050505050905090565b60d354606090806001600160401b038111156129c2576129c261585a565b604051908082528060200260200182016040528015612a2057816020015b612a0d604051806060016040528060006001600160a01b0316815260200160008152602001600081525090565b8152602001906001900390816129e05790505b50915060005b81811015612bde57600060d38281548110612a4357612a43615a2f565b60009182526020808320909101546040805163f7c618c160e01b815290516001600160a01b039092169450849263f7c618c1926004808401938290030181865afa158015612a95573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ab99190615a45565b90506040518060600160405280826001600160a01b03168152602001836001600160a01b03166374c4c1cc896040518263ffffffff1660e01b8152600401612b019190615623565b602060405180830381865afa158015612b1e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b429190615af9565b8152602001836001600160a01b0316637c05a7c5896040518263ffffffff1660e01b8152600401612b739190615623565b602060405180830381865afa158015612b90573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612bb49190615af9565b815250858481518110612bc957612bc9615a2f565b60209081029190910101525050600101612a26565b5050919050565b612bf08360006143f5565b6001600160a01b038316600090815260cd602052604090205460ff16612c2b5782604051635a9a1eb960e11b8152600401610b3d9190615623565b6001600160a01b038316600090815260d7602052604090205460ff168015612c7957506001600160a01b03808416600090815260d8602090815260408083209386168352929052205460ff16155b15612c9b578282604051632dd7bb3560e21b8152600401610b3d929190615a62565b6001600160a01b038316600090815260d160205260409020546000198114612dce576000846001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612cfd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d219190615af9565b905060006040518060200160405280876001600160a01b031663182df0f56040518163ffffffff1660e01b8152600401602060405180830381865afa158015612d6e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d929190615af9565b905290506000612da3828487614a2d565b905083811115612dca57868460405163db33be3d60e01b8152600401610b3d929190615b70565b5050505b60d35460005b8181101561054857600060d38281548110612df157612df1615a2f565b60009182526020909120015460405163051d1d4f60e11b81526001600160a01b0390911691508190630a3a3a9e90612e2d908a90600401615623565b600060405180830381600087803b158015612e4757600080fd5b505af1158015612e5b573d6000803e3d6000fd5b505060405163db7954fd60e01b81526001600160a01b038416925063db7954fd9150612e8d908a908a90600401615a62565b600060405180830381600087803b158015612ea757600080fd5b505af1158015612ebb573d6000803e3d6000fd5b5050505050806001019050612dd4565b80516060906000816001600160401b03811115612eea57612eea61585a565b604051908082528060200260200182016040528015612f13578160200160208202803683370190505b50905060005b82811015612f71576000858281518110612f3557612f35615a2f565b60200260200101519050612f498133614a57565b6000838381518110612f5d57612f5d615a2f565b602090810291909101015250600101612f19565b509392505050565b6000806000612f8786614b4a565b90506000612f9486614b4a565b90506000866001600160a01b031663182df0f56040518163ffffffff1660e01b8152600401602060405180830381865afa158015612fd6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ffa9190615af9565b90506000613006615194565b61300e615194565b613016615194565b613042604051806020016040528061302d8e61469e565b90526040805160208101909152898152614be4565b925061306a604051806020016040528088815250604051806020016040528088815250614be4565b915061307683836149d1565b9050613082818b614a0d565b60009d909c509a5050505050505050505050565b6130bf7f0000000000000000000000000000000000000000000000000000000000000000614c1c565b6001600160a01b038116600090815260cd602052604090205460ff16156130fb578060405163d005ce4760e01b8152600401610b3d9190615623565b806001600160a01b0316633d9ea3a16040518163ffffffff1660e01b8152600401602060405180830381865afa158015613139573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061315d9190615b89565b61317a57604051633d21810360e21b815260040160405180910390fd5b6001600160a01b038116600090815260cd60205260408120805460ff191660019081178255810182905560028101919091556131b582614c49565b60d35460005b8181101561324c5760d381815481106131d6576131d6615a2f565b600091825260209091200154604051632a869a4d60e01b81526001600160a01b0390911690632a869a4d9061320f908790600401615623565b600060405180830381600087803b15801561322957600080fd5b505af115801561323d573d6000803e3d6000fd5b505050508060010190506131bb565b507faf16ad15f9e29d5140e8e81a30a92a755aa8edff3d301053c84392b70c0d09a38360405161327c9190615623565b60405180910390a1505050565b6132aa604051806060016040528060288152602001615f68602891396141eb565b828015806132b85750808214155b156132d657604051634ec4810560e11b815260040160405180910390fd5b6132df816143c2565b60005b81811015610548578383828181106132fc576132fc615a2f565b9050602002013560d1600088888581811061331957613319615a2f565b905060200201602081019061332e9190615209565b6001600160a01b0316815260208101919091526040016000205585858281811061335a5761335a615a2f565b905060200201602081019061336f9190615209565b6001600160a01b03167f9e0ad9cee10bdf36b7fbd38910c0bdff0f275ace679b45b922381c2723d676f88585848181106133ab576133ab615a2f565b905060200201356040516133c191815260200190565b60405180910390a26001016132e2565b6133f2604051806060016040528060228152602001615f90602291396141eb565b6133fb81614285565b6134058282614a57565b5050565b600054610100900460ff16158080156134295750600054600160ff909116105b806134435750303b158015613443575060005460ff166001145b6134a65760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b6064820152608401610b3d565b6000805460ff1916600117905580156134c9576000805461ff0019166101001790555b6134d1614d07565b6134da82614d36565b6134e383614935565b8015610633576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200161327c565b60cc602052816000526040600020818154811061354157600080fd5b6000918252602090912001546001600160a01b03169150829050565b61356682614285565b33600090815260d6602090815260408083206001600160a01b038616845290915290205481151560ff9091161515036135b25760405163db6c2c8360e01b815260040160405180910390fd5b33600081815260d6602090815260408083206001600160a01b03871680855290835292819020805460ff191686151590811790915590519081529192917fcb325b7784f78486e42849c7a50b8c5ee008d00cd90e108a58912c0fcb6288b4910160405180910390a35050565b6136298360026143f5565b6001600160a01b038316600090815260cd602052604090205460ff166136645782604051635a9a1eb960e11b8152600401610b3d9190615623565b6001600160a01b03808416600090815260cd60209081526040808320938616835260039093019052205460ff166136a85761369e83614c1c565b6136a83383614a57565b60006136b3836127ca565b90506136be816147c8565b6136c781614d5d565b60c95460405163fc57d4df60e01b81526001600160a01b039091169063fc57d4df906136f7908790600401615623565b602060405180830381865afa158015613714573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137389190615af9565b600003613759578360405162e52a7d60e41b8152600401610b3d9190615623565b6001600160a01b038416600090815260cf60205260409020546000198114613889576000856001600160a01b03166347bd37186040518163ffffffff1660e01b8152600401602060405180830381865afa1580156137bb573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137df9190615af9565b90506000866001600160a01b031663bbcac5576040518163ffffffff1660e01b8152600401602060405180830381865afa158015613821573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906138459190615af9565b90506000816138548785615ae6565b61385e9190615ae6565b905083811115613885578784604051632e649eed60e01b8152600401610b3d929190615b70565b5050505b600061389a858760008760006146c6565b60a0810151909150156138c05760405163bb55fd2760e01b815260040160405180910390fd5b60006040518060200160405280886001600160a01b031663aa5af0fd6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561390b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061392f9190615af9565b905260d35490915060005b81811015613a3557600060d3828154811061395757613957615a2f565b600091825260209091200154604051632352607960e01b81526001600160a01b0390911691508190632352607990613995908d908890600401615ba6565b600060405180830381600087803b1580156139af57600080fd5b505af11580156139c3573d6000803e3d6000fd5b5050604051636a95ddef60e01b81526001600160a01b0384169250636a95ddef91506139f7908d908d908990600401615bc0565b600060405180830381600087803b158015613a1157600080fd5b505af1158015613a25573d6000803e3d6000fd5b505050505080600101905061393a565b505050505050505050565b6065546001600160a01b031690565b6001600160a01b038216600090815260d26020526040812081836008811115613a7a57613a7a615be3565b6008811115613a8b57613a8b615be3565b815260208101919091526040016000205460ff169392505050565b613ab18560056143f5565b613aba83611bfc565b6001600160a01b038516600090815260cd602052604090205460ff16613af55784604051635a9a1eb960e11b8152600401610b3d9190615623565b6001600160a01b038416600090815260cd602052604090205460ff16613b305783604051635a9a1eb960e11b8152600401610b3d9190615623565b6040516395dd919360e01b81526000906001600160a01b038716906395dd919390613b5f908790600401615623565b602060405180830381865afa158015613b7c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613ba09190615af9565b90508180613bc657506001600160a01b038616600090815260d5602052604090205460ff165b15613bf25780831115613bec5760405163e46c155960e01b815260040160405180910390fd5b506104ba565b6000613bff8560016145d9565b905060d054816000015111613c355760d0548151604051636e61bb0560e11b815260048101929092526024820152604401610b3d565b8060a00151600003613c5a5760405163095bf33360e01b815260040160405180910390fd5b6000613c76604051806020016040528060ca5481525084614a0d565b9050808511156115015760405163e46c155960e01b815260040160405180910390fd5b613ca48260036143f5565b60c9546040516396e85ced60e01b81526001600160a01b03909116906396e85ced90613cd4908590600401615623565b600060405180830381600087803b158015613cee57600080fd5b505af1158015613d02573d6000803e3d6000fd5b505050506001600160a01b038216600090815260cd602052604090205460ff16613d415781604051635a9a1eb960e11b8152600401610b3d9190615623565b60d35460005b818110156105215760006040518060200160405280866001600160a01b031663aa5af0fd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613d9a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613dbe9190615af9565b8152509050600060d38381548110613dd857613dd8615a2f565b600091825260209091200154604051632352607960e01b81526001600160a01b0390911691508190632352607990613e169089908690600401615ba6565b600060405180830381600087803b158015613e3057600080fd5b505af1158015613e44573d6000803e3d6000fd5b5050604051636a95ddef60e01b81526001600160a01b0384169250636a95ddef9150613e7890899089908790600401615bc0565b600060405180830381600087803b158015613e9257600080fd5b505af1158015613ea6573d6000803e3d6000fd5b505050505050806001019050613d47565b6000613ec48260086143f5565b81600080613ed283336145f7565b509150915080600014613ef85760405163f8a5d66d60e01b815260040160405180910390fd5b613f03853384614863565b6001600160a01b038316600090815260cd60209081526040808320338452600381019092529091205460ff16613f3f5750600095945050505050565b3360009081526003820160209081526040808320805460ff1916905560cc825280832080548251818502810185019093528083529192909190830182828015613fb157602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311613f93575b5050835193945083925060009150505b8281101561400b57876001600160a01b0316848281518110613fe557613fe5615a2f565b60200260200101516001600160a01b0316036140035780915061400b565b600101613fc1565b5081811061401b5761401b615bf9565b33600090815260cc602052604090208054819061403a90600190615c0f565b8154811061404a5761404a615a2f565b9060005260206000200160009054906101000a90046001600160a01b031681838154811061407a5761407a615a2f565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550808054806140b8576140b8615c22565b600082815260208120820160001990810180546001600160a01b031916905590910190915560405133916001600160a01b038b16917fe699a64c18b07ac5b7301aa273f36a2287239eb9501d81950672794afba29a0d9190a35060009998505050505050505050565b6141296142ac565b606580546001600160a01b0319166001600160a01b03831690811790915561414f6123dd565b6001600160a01b03167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e2270060405160405180910390a350565b61418f6142ac565b61419881614285565b60dc80546001600160a01b038381166001600160a01b03198316179092556040519116907fc4877d82ec0586c93fbd01eb65785e064f0c31b594e7f413e5f16502f25cf27390610f2d9083908590615a62565b6097546040516318c5e8ab60e01b81526000916001600160a01b0316906318c5e8ab9061421e9033908690600401615c7e565b602060405180830381865afa15801561423b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061425f9190615b89565b90508061340557333083604051634a3fa29360e01b8152600401610b3d93929190615ca2565b6001600160a01b038116610e67576040516342bcdf7f60e11b815260040160405180910390fd5b336142b56123dd565b6001600160a01b0316146122195760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610b3d565b6001600160a01b03811661436f5760405162461bcd60e51b815260206004820152602560248201527f696e76616c696420616365737320636f6e74726f6c206d616e61676572206164604482015264647265737360d81b6064820152608401610b3d565b609780546001600160a01b038381166001600160a01b03198316179092556040519116907f66fd58e82f7b31a2a5c30e0888f3093efe4e111b00cd2b0c31fe014601293aa090610f2d9083908590615a62565b61010754811115610e67576101075460405163792bfb1b60e11b8152600481019190915260248101829052604401610b3d565b6143ff8282613a4f565b156134055781816040516313b3ccb160e31b8152600401610b3d929190615cf0565b60d95460ff16801561444c57506001600160a01b038116600090815260da602052604090205460ff16155b15610e67578060405163cdd0a0c760e01b8152600401610b3d9190615623565b6001600160a01b038316600090815260cd602052604090205460ff166144a75782604051635a9a1eb960e11b8152600401610b3d9190615623565b6001600160a01b038316600090815260d26020526040812082918460088111156144d3576144d3615be3565b60088111156144e4576144e4615be3565b815260200190815260200160002060006101000a81548160ff0219169083151502179055507f35007a986bcd36d2f73fc7f1b73762e12eadb4406dd163194950fd3b5a6a827d83838360405161327c93929190615d0d565b805160005b818110156145cf5782818151811061455b5761455b615a2f565b60200260200101516001600160a01b031663a6afed956040518163ffffffff1660e01b81526004016020604051808303816000875af11580156145a2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906145c69190615af9565b50600101614541565b50613405826147c8565b6145e16151a7565b6145f0836000806000866146c6565b9392505050565b600080600080856001600160a01b031663c37f68e2866040518263ffffffff1660e01b81526004016146299190615623565b608060405180830381865afa158015614646573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061466a9190615d3a565b919650945092509050801561469657858560405163015e34d960e61b8152600401610b3d929190615a62565b509250925092565b6001600160a01b038116600090815260db60205260408120548082036122a15760cb546145f0565b6146ce6151a7565b60006146d9876127ca565b805190915060005b8181101561476a5760008382815181106146fd576146fd615a2f565b6020026020010151905060008061471687848e8b614df8565b915091508a6001600160a01b0316836001600160a01b03160361475c57614742828b8960600151614a2d565b606088018190526147569082908b90614a2d565b60608801525b5050508060010190506146e1565b506000836060015184604001516147819190615ae6565b905080846020015111156147a85760208401518190036080850152600060a08501526147bc565b600060808501526020840151810360a08501525b50505095945050505050565b805160c9546001600160a01b031660005b8281101561052157816001600160a01b03166396e85ced85838151811061480257614802615a2f565b60200260200101516040518263ffffffff1660e01b81526004016148269190615623565b600060405180830381600087803b15801561484057600080fd5b505af1158015614854573d6000803e3d6000fd5b505050508060010190506147d9565b6001600160a01b038316600090815260cd60205260409020805460ff1661489f5783604051635a9a1eb960e11b8152600401610b3d9190615623565b6001600160a01b038316600090815260038201602052604090205460ff166148c75750505050565b60006148d2846127ca565b90506148dd816147c8565b6148e681614d5d565b60006148f68587866000806146c6565b60a0810151909150156105485760405163bb55fd2760e01b815260040160405180910390fd5b606580546001600160a01b0319169055610e6781614f15565b6101075481116149925760405162461bcd60e51b815260206004820152602260248201527f436f6d7074726f6c6c65723a20496e76616c6964206d61784c6f6f70734c696d6044820152611a5d60f21b6064820152608401610b3d565b61010780549082905560408051828152602081018490527fc2d09fef144f7c8a86f71ea459f8fc17f675768eb1ae369cbd77fb31d467aafa9101610f2d565b6149d9615194565b6040518060200160405280614a046149fd8660000151670de0b6b3a7640000614f67565b8551614f73565b90529392505050565b600080614a1a8484614f7f565b9050614a2581614fa0565b949350505050565b600080614a3a8585614f7f565b9050614a4e614a4882614fa0565b84614fb8565b95945050505050565b614a628260076143f5565b6001600160a01b038216600090815260cd60205260409020805460ff16614a9e5782604051635a9a1eb960e11b8152600401610b3d9190615623565b6001600160a01b038216600090815260038201602052604090205460ff1615614ac657505050565b6001600160a01b0380831660008181526003840160209081526040808320805460ff1916600190811790915560cc835281842080549182018155845291832090910180549488166001600160a01b031990951685179055519192917f3ab23ab0d51cccc0c3085aec51f99228625aa1a922b3a8ca89a26b0f2027a1a59190a3505050565b60c95460405163fc57d4df60e01b815260009182916001600160a01b039091169063fc57d4df90614b7f908690600401615623565b602060405180830381865afa158015614b9c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614bc09190615af9565b9050806000036122a1578260405162e52a7d60e41b8152600401610b3d9190615623565b614bec615194565b6040518060200160405280670de0b6b3a7640000614c1286600001518660000151614f67565b614a049190615ac4565b336001600160a01b03821614610e67578033604051634e9383fb60e11b8152600401610b3d929190615a62565b60ce5460005b81811015614cb157826001600160a01b031660ce8281548110614c7457614c74615a2f565b6000918252602090912001546001600160a01b031603614ca9578260405163d005ce4760e01b8152600401610b3d9190615623565b600101614c4f565b505060ce805460018101825560008290527fd36cd1c74ef8d7326d8021b776c18fb5a5724b7f7bc93c2f42e43e10ef27d12a0180546001600160a01b0319166001600160a01b03841617905554613405816143c2565b600054610100900460ff16614d2e5760405162461bcd60e51b8152600401610b3d90615d70565b612219614fc4565b600054610100900460ff16610e5e5760405162461bcd60e51b8152600401610b3d90615d70565b805160dc546001600160a01b031660005b8281101561052157816001600160a01b031663a9c3cab1858381518110614d9757614d97615a2f565b60200260200101516040518263ffffffff1660e01b8152600401614dbb9190615623565b600060405180830381600087803b158015614dd557600080fd5b505af1158015614de9573d6000803e3d6000fd5b50505050806001019050614d6e565b614e00615194565b614e08615194565b6000806000614e1788886145f7565b925092509250614e25615194565b614e2f8988614ff4565b80965081925050506000614e5160405180602001604052808581525083614be4565b9050614e66614e608b8a615117565b82614be4565b9650614e7787868d60200151614a2d565b60208c01528a51614e8b9082908790614a2d565b8b526001886001811115614ea157614ea1615be3565b148015614ead57508415155b15614eef57614eda614ebf8287614a0d565b6040518060200160405280614ed38e61469e565b9052615176565b8b60c001818151614eeb9190615ae6565b9052505b614efe86858d60400151614a2d565b8b6040018181525050505050505094509492505050565b603380546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b60006145f08284615a92565b60006145f08284615ac4565b614f87615194565b6040518060200160405280614a04856000015185614f67565b80516000906122a190670de0b6b3a764000090615ac4565b60006145f08284615ae6565b600054610100900460ff16614feb5760405162461bcd60e51b8152600401610b3d90615d70565b6122193361491c565b614ffc615194565b615004615194565b600183600181111561501857615018615be3565b0361504d57600061502885614b4a565b6040805160208082018352838252825190810190925291815290935091506151109050565b60dc546040516388142b6b60e01b815260009182916001600160a01b03909116906388142b6b90615082908990600401615623565b6040805180830381865afa15801561509e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906150c29190615dbb565b9150915081600014806150d3575080155b156150f2578560405162e52a7d60e41b8152600401610b3d9190615623565b60408051602080820183529381528151938401909152908252925090505b9250929050565b61511f615194565b6001600160a01b038316600090815260cd60209081526040808320815192830190915291819085600181111561515757615157615be3565b1461516657826002015461516c565b82600101545b9052949350505050565b60006145f061518d84670de0b6b3a7640000614f67565b8351614f73565b6040518060200160405280600081525090565b6040518060e00160405280600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b6001600160a01b0381168114610e6757600080fd5b8035615204816151e4565b919050565b60006020828403121561521b57600080fd5b81356145f0816151e4565b8015158114610e6757600080fd5b60008060006060848603121561524957600080fd5b8335615254816151e4565b92506020840135615264816151e4565b9150604084013561527481615226565b809150509250925092565b6000806040838503121561529257600080fd5b823561529d816151e4565b915060208301356152ad816151e4565b809150509250929050565b6000602082840312156152ca57600080fd5b5035919050565b60008083601f8401126152e357600080fd5b5081356001600160401b038111156152fa57600080fd5b6020830191508360208260051b850101111561511057600080fd5b6000806000806040858703121561532b57600080fd5b84356001600160401b038082111561534257600080fd5b61534e888389016152d1565b9096509450602087013591508082111561536757600080fd5b50615374878288016152d1565b95989497509550505050565b6000806000806080858703121561539657600080fd5b84356153a1816151e4565b935060208501356153b1816151e4565b925060408501356153c1816151e4565b915060608501356153d1816151e4565b939692955090935050565b600080600080600060a086880312156153f457600080fd5b85356153ff816151e4565b9450602086013561540f816151e4565b9350604086013561541f816151e4565b94979396509394606081013594506080013592915050565b60008060008060006060868803121561544f57600080fd5b85356001600160401b038082111561546657600080fd5b61547289838a016152d1565b9097509550602088013591508082111561548b57600080fd5b50615498888289016152d1565b90945092505060408601356154ac81615226565b809150509295509295909350565b6000806000604084860312156154cf57600080fd5b83356154da816151e4565b925060208401356001600160401b03808211156154f657600080fd5b818601915086601f83011261550a57600080fd5b81358181111561551957600080fd5b87602060608302850101111561552e57600080fd5b6020830194508093505050509250925092565b6000806000806080858703121561555757600080fd5b8435615562816151e4565b93506020850135615572816151e4565b93969395505050506040820135916060013590565b6000806040838503121561559a57600080fd5b82356155a5816151e4565b915060208301356152ad81615226565b60008060008060008060c087890312156155ce57600080fd5b86356155d9816151e4565b955060208701356155e9816151e4565b945060408701356155f9816151e4565b93506060870135615609816151e4565b9598949750929560808101359460a0909101359350915050565b6001600160a01b0391909116815260200190565b60008060006060848603121561564c57600080fd5b8335615657816151e4565b92506020840135615667816151e4565b929592945050506040919091013590565b60008060006060848603121561568d57600080fd5b8335615698816151e4565b95602085013595506040909401359392505050565b6020808252825182820181905260009190848201906040850190845b818110156156ee5783516001600160a01b0316835292840192918401916001016156c9565b50909695505050505050565b6000806040838503121561570d57600080fd5b8235615718816151e4565b946020939093013593505050565b6000806000806080858703121561573c57600080fd5b8435615747816151e4565b93506020850135615757816151e4565b92506040850135615767816151e4565b9396929550929360600135925050565b600080600080600060a0868803121561578f57600080fd5b853561579a816151e4565b945060208601356157aa816151e4565b935060408601356157ba816151e4565b925060608601356157ca816151e4565b949793965091946080013592915050565b6000602082840312156157ed57600080fd5b81356145f081615226565b602080825282518282018190526000919060409081850190868401855b8281101561584d57815180516001600160a01b0316855286810151878601528501518585015260609093019290850190600101615815565b5091979650505050505050565b634e487b7160e01b600052604160045260246000fd5b6000602080838503121561588357600080fd5b82356001600160401b038082111561589a57600080fd5b818501915085601f8301126158ae57600080fd5b8135818111156158c0576158c061585a565b8060051b604051601f19603f830116810181811085821117156158e5576158e561585a565b60405291825284820192508381018501918883111561590357600080fd5b938501935b8285101561592857615919856151f9565b84529385019392850192615908565b98975050505050505050565b6020808252825182820181905260009190848201906040850190845b818110156156ee57835183529284019291840191600101615950565b6000806040838503121561597f57600080fd5b8235915060208301356152ad816151e4565b80356009811061520457600080fd5b600080604083850312156159b357600080fd5b82356159be816151e4565b91506159cc60208401615991565b90509250929050565b600080600080600060a086880312156159ed57600080fd5b85356159f8816151e4565b94506020860135615a08816151e4565b93506040860135615a18816151e4565b92506060860135915060808601356154ac81615226565b634e487b7160e01b600052603260045260246000fd5b600060208284031215615a5757600080fd5b81516145f0816151e4565b6001600160a01b0392831681529116602082015260400190565b634e487b7160e01b600052601160045260246000fd5b80820281158282048414176122a1576122a1615a7c565b600060208284031215615abb57600080fd5b6145f082615991565b600082615ae157634e487b7160e01b600052601260045260246000fd5b500490565b808201808211156122a1576122a1615a7c565b600060208284031215615b0b57600080fd5b5051919050565b6001600160a01b039390931683526020830191909152604082015260600190565b6001600160a01b039384168152919092166020820152604081019190915260600190565b600060018201615b6957615b69615a7c565b5060010190565b6001600160a01b03929092168252602082015260400190565b600060208284031215615b9b57600080fd5b81516145f081615226565b6001600160a01b0392909216825251602082015260400190565b6001600160a01b0393841681529190921660208201529051604082015260600190565b634e487b7160e01b600052602160045260246000fd5b634e487b7160e01b600052600160045260246000fd5b818103818111156122a1576122a1615a7c565b634e487b7160e01b600052603160045260246000fd5b6000815180845260005b81811015615c5e57602081850181015186830182015201615c42565b506000602082860101526020601f19601f83011685010191505092915050565b6001600160a01b0383168152604060208201819052600090614a2590830184615c38565b6001600160a01b03848116825283166020820152606060408201819052600090614a4e90830184615c38565b60098110615cec57634e487b7160e01b600052602160045260246000fd5b9052565b6001600160a01b0383168152604081016145f06020830184615cce565b6001600160a01b038416815260608101615d2a6020830185615cce565b8215156040830152949350505050565b60008060008060808587031215615d5057600080fd5b505082516020840151604085015160609095015191969095509092509050565b6020808252602b908201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960408201526a6e697469616c697a696e6760a81b606082015260800190565b60008060408385031215615dce57600080fd5b50508051602090910151909290915056fe736574416c6c6f776564537570706c69657228616464726573732c616464726573732c626f6f6c297365744d61726b6574426f72726f774361707328616464726573735b5d2c75696e743235365b5d29736574416c6c6f7765644c697175696461746f7228616464726573732c626f6f6c297365744d696e4c6971756964617461626c65436f6c6c61746572616c2875696e7432353629736574436f6c6c61746572616c466163746f7228616464726573732c75696e743235362c75696e7432353629736574537570706c79416c6c6f776c697374456e61626c656428616464726573732c626f6f6c29736574416374696f6e7350617573656428616464726573735b5d2c75696e743235365b5d2c626f6f6c297365744d61726b65744c69717569646174696f6e496e63656e7469766528616464726573732c75696e74323536297365744c69717569646174696f6e416c6c6f776c697374456e61626c656428626f6f6c29736574466f726365644c69717569646174696f6e28616464726573732c626f6f6c297365744d61726b6574537570706c794361707328616464726573735b5d2c75696e743235365b5d29656e7465724d61726b6574426568616c6628616464726573732c6164647265737329a2646970667358221220d0096c0fe2d2f6c05ac03bd5c918aa2b6b37a63732f6c3bc612c8fc6f811e6dc64736f6c63430008190033", + "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106103bf5760003560e01c80638a0ecd18116101f7578063c5c0542511610116578063c5c0542514610955578063cab4f84c14610975578063d136af4414610988578063d585c3c61461099b578063d7c46d2d146109ae578063da35a26f146109c1578063db5c65de146109d4578063dce15449146109dd578063ddbf54fd146109f0578063df71403b14610a03578063e30c397814610a16578063e85a296014610a1e578063e875544614610a31578063e89d51ad14610a3a578063eade3eed14610a4d578063ede4edd014610a60578063f2fde38b14610a73578063f45fe69f14610a86578063f582cffd14610aa9578063fe40768f14610ab657600080fd5b80638a0ecd18146107115780638b3113f6146107245780638c1ac18a146107375780638da5cb5b1461075a5780638e6470ea146107625780638e8f294b1461077557806392136395146107c5578063929fe9a1146107d85780639dcd31fd146108195780639f33322c1461083c578063a84310811461086a578063abfceffc1461087d578063afcff50f14610890578063b0772d0b146108b7578063b2068e84146108bf578063b4a0bdf3146108df578063be26317e146108f0578063c0891ba9146108fa578063c29982381461090d578063c488847b1461092d57600080fd5b8063520b6c74116102e3578063520b6c74146105a6578063528a174c146105b957806352d84d1e146105cc578063530e784f146105ec57806356aaee2d146105ff5780635c21b6c5146106125780635c778605146106255780635cc4fdeb146106385780635ec88c791461064b57806361252fd11461065e57806362bcf431146106735780636a12b637146106865780636a56947e146106995780636d0be88d146106a75780636d35bf91146106ba578063715018a6146106c857806379ba5097146106d05780637db94b95146106d85780637dc0d1d0146106eb57806380d45a2d146106fe57600080fd5b80627e3dd2146103c457806302c3bcbb146103dc5780630686dab61461040a578063073338971461041d5780630e32cb861461043257806310b983381461044557806312348e9614610473578063186db48f146104865780631bc41f28146104995780631ededc91146104ac57806324aaa220146104c15780632bce219c146104d45780633d98a1e5146104e757806341c728b91461051357806346c952051461052757806347ef3b3b1461053a5780634a584432146105505780634ada90af146105705780634e79238f1461057857806351dff98914610513575b600080fd5b60015b60405190151581526020015b60405180910390f35b6103fc6103ea366004615209565b60d16020526000908152604090205481565b6040519081526020016103d3565b6103fc610418366004615209565b610ac9565b61043061042b366004615234565b610d84565b005b610430610440366004615209565b610e56565b6103c761045336600461527f565b60d660209081526000928352604080842090915290825290205460ff1681565b6104306104813660046152b8565b610e6a565b610430610494366004615315565b610f39565b6104306104a7366004615380565b61108b565b6104306104ba3660046153dc565b5050505050565b6104306104cf366004615437565b61144e565b6104306104e23660046154ba565b61150b565b6103c76104f5366004615209565b6001600160a01b0316600090815260cd602052604090205460ff1690565b610430610521366004615541565b50505050565b610430610535366004615587565b61181c565b6104306105483660046155b5565b505050505050565b6103fc61055e366004615209565b60cf6020526000908152604090205481565b6103fc6118a6565b61058b610586366004615541565b6118b6565b604080519384526020840192909252908201526060016103d3565b6104306105b43660046152b8565b6118e6565b61058b6105c7366004615209565b611944565b6105df6105da3660046152b8565b61196e565b6040516103d39190615623565b6104306105fa366004615209565b611998565b61043061060d366004615209565b6119fc565b610430610620366004615209565b611bfc565b610430610633366004615637565b505050565b610430610646366004615678565b611c0d565b61058b610659366004615209565b611e1e565b610666611e2f565b6040516103d391906156ad565b610430610681366004615587565b611e91565b6104306106943660046156fa565b611f45565b610430610521366004615726565b6104306106b5366004615726565b612094565b6104306104ba366004615777565b610430612207565b61043061221b565b6103fc6106e6366004615209565b612296565b60c9546105df906001600160a01b031681565b61043061070c3660046152b8565b6122a7565b61043061071f3660046157db565b6122b8565b610430610732366004615587565b612320565b6103c7610745366004615209565b60d56020526000908152604090205460ff1681565b6105df6123dd565b610430610770366004615637565b6123ec565b6107a8610783366004615209565b60cd6020526000908152604090208054600182015460029092015460ff909116919083565b6040805193151584526020840192909252908201526060016103d3565b6104306107d3366004615209565b6124ff565b6103c76107e636600461527f565b6001600160a01b03808216600090815260cd60209081526040808320938616835260039093019052205460ff1692915050565b6103c7610827366004615209565b60da6020526000908152604090205460ff1681565b6103c761084a36600461527f565b60d860209081526000928352604080842090915290825290205460ff1681565b6104306108783660046152b8565b612725565b61066661088b366004615209565b6127ca565b6105df7f000000000000000000000000000000000000000000000000000000000000000081565b610666612944565b6108d26108cd366004615209565b6129a4565b6040516103d391906157f8565b6097546001600160a01b03166105df565b6103fc6101075481565b610430610908366004615637565b612be5565b61092061091b366004615870565b612ecb565b6040516103d39190615934565b61094061093b366004615637565b612f79565b604080519283526020830191909152016103d3565b6103fc610963366004615209565b60db6020526000908152604090205481565b610430610983366004615209565b613096565b610430610996366004615315565b613289565b6104306109a936600461527f565b6133d1565b60dc546105df906001600160a01b031681565b6104306109cf36600461596c565b613409565b6103fc60d05481565b6105df6109eb3660046156fa565b613525565b6104306109fe366004615587565b61355d565b610430610a11366004615637565b61361e565b6105df613a40565b6103c7610a2c3660046159a0565b613a4f565b6103fc60ca5481565b610430610a483660046159d5565b613aa6565b610430610a5b36600461527f565b613c99565b6103fc610a6e366004615209565b613eb7565b610430610a81366004615209565b614121565b6103c7610a94366004615209565b60d76020526000908152604090205460ff1681565b60d9546103c79060ff1681565b610430610ac4366004615209565b614187565b6000610b0160405180604001604052806015815260200174756e6c6973744d61726b657428616464726573732960581b8152506141eb565b6001600160a01b038216600090815260cd60205260409020805460ff16610b465782604051635a9a1eb960e11b8152600401610b3d9190615623565b60405180910390fd5b610b51836002613a4f565b610b6e57604051630309b07560e21b815260040160405180910390fd5b610b79836000613a4f565b610b96576040516306ed36fb60e41b815260040160405180910390fd5b610ba1836001613a4f565b610bbe57604051634e577b7d60e11b815260040160405180910390fd5b610bc9836003613a4f565b610be657604051632b1e340960e01b815260040160405180910390fd5b610bf1836004613a4f565b610c0e5760405163bb56e52560e01b815260040160405180910390fd5b610c19836007613a4f565b610c3657604051630a6a9a9d60e31b815260040160405180910390fd5b610c41836005613a4f565b610c5e5760405163b3cf04ad60e01b815260040160405180910390fd5b610c69836006613a4f565b610c86576040516308624f7160e21b815260040160405180910390fd5b610c91836008613a4f565b610cae57604051632f412a5560e21b815260040160405180910390fd5b6001600160a01b038316600090815260cf602052604090205415610ce55760405163668019b360e01b815260040160405180910390fd5b6001600160a01b038316600090815260d1602052604090205415610d1c57604051638603c8cf60e01b815260040160405180910390fd5b600181015415610d3f57604051630707987b60e11b815260040160405180910390fd5b805460ff191681556040516001600160a01b038416907f302feb03efd5741df80efe7f97f5d93d74d46a542a3d312d0faae64fa1f3e0e990600090a250600092915050565b610da5604051806060016040528060288152602001615de0602891396141eb565b610dae82614285565b6001600160a01b038316600090815260cd602052604090205460ff16610de95782604051635a9a1eb960e11b8152600401610b3d9190615623565b6001600160a01b03838116600081815260d86020908152604080832094871680845294825291829020805460ff191686151590811790915591519182527f8202825e3161f4b2cda66ca77da3debbf8c94eac81264005088eb8b9bc0b651f910160405180910390a3505050565b610e5e6142ac565b610e678161430b565b50565b610ea260405180604001604052806017815260200176736574436c6f7365466163746f722875696e743235362960481b8152506141eb565b670c7d713b49da0000811115610ecb5760405163ee0bdbdf60e01b815260040160405180910390fd5b66b1a2bc2ec50000811015610ef35760405163ee0bdbdf60e01b815260040160405180910390fd5b60ca80549082905560408051828152602081018490527f3b9670cf975d26958e754b57098eaa2ac914d8d2a31b83257997b9f346110fd991015b60405180910390a15050565b610f5a604051806060016040528060288152602001615e08602891396141eb565b8281811580610f695750808214155b15610f8757604051634ec4810560e11b815260040160405180910390fd5b610f90826143c2565b60005b8281101561108257848482818110610fad57610fad615a2f565b9050602002013560cf6000898985818110610fca57610fca615a2f565b9050602002016020810190610fdf9190615209565b6001600160a01b0316815260208101919091526040016000205586868281811061100b5761100b615a2f565b90506020020160208101906110209190615209565b6001600160a01b03167f6f1951b2aad10f3fc81b86d91105b413a5b3f847a34bbc5ce1904201b14438f686868481811061105c5761105c615a2f565b9050602002013560405161107291815260200190565b60405180910390a2600101610f93565b50505050505050565b6110968460046143f5565b6001600160a01b038416600090815260cd60205260409020805460ff166110d25784604051635a9a1eb960e11b8152600401610b3d9190615623565b306001600160a01b0385160361117a57306001600160a01b0316856001600160a01b0316635fe3b5676040518163ffffffff1660e01b8152600401602060405180830381865afa15801561112a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061114e9190615a45565b6001600160a01b03161461117557604051630c73eb0560e01b815260040160405180910390fd5b6112a9565b6001600160a01b038416600090815260cd602052604090205460ff166111b55783604051635a9a1eb960e11b8152600401610b3d9190615623565b836001600160a01b0316635fe3b5676040518163ffffffff1660e01b8152600401602060405180830381865afa1580156111f3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112179190615a45565b6001600160a01b0316856001600160a01b0316635fe3b5676040518163ffffffff1660e01b8152600401602060405180830381865afa15801561125e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112829190615a45565b6001600160a01b0316146112a957604051630c73eb0560e01b815260040160405180910390fd5b6001600160a01b038216600090815260038201602052604090205460ff166112e8578482604051630cdfb2db60e31b8152600401610b3d929190615a62565b6112f183614421565b60d35460005b8181101561108257600060d3828154811061131457611314615a2f565b60009182526020909120015460405163051d1d4f60e11b81526001600160a01b0390911691508190630a3a3a9e90611350908b90600401615623565b600060405180830381600087803b15801561136a57600080fd5b505af115801561137e573d6000803e3d6000fd5b505060405163db7954fd60e01b81526001600160a01b038416925063db7954fd91506113b0908b908990600401615a62565b600060405180830381600087803b1580156113ca57600080fd5b505af11580156113de573d6000803e3d6000fd5b505060405163db7954fd60e01b81526001600160a01b038416925063db7954fd9150611410908b908a90600401615a62565b600060405180830381600087803b15801561142a57600080fd5b505af115801561143e573d6000803e3d6000fd5b50505050508060010190506112f7565b61146f6040518060600160405280602a8152602001615eca602a91396141eb565b838261148361147e8284615a92565b6143c2565b60005b828110156115015760005b828110156114f8576114f08989848181106114ae576114ae615a2f565b90506020020160208101906114c39190615209565b8888848181106114d5576114d5615a2f565b90506020020160208101906114ea9190615aa9565b8761446c565b600101611491565b50600101611486565b5050505050505050565b6000611516846127ca565b90506115218161453c565b600061152e8560016145d9565b905060d054816000015111156115655760d0548151604051631a451c0f60e21b815260048101929092526024820152604401610b3d565b8060c001518160400151106115a25780604001518160c00151604051630936175b60e11b8152600401610b3d929190918252602082015260400190565b8060a001516000036115c75760405163095bf33360e01b815260040160405180910390fd5b826115d661147e600283615ac4565b60005b818110156117bd5760cd60008787848181106115f7576115f7615a2f565b905060600201602001602081019061160f9190615209565b6001600160a01b0316815260208101919091526040016000205460ff166116745785858281811061164257611642615a2f565b905060600201602001602081019061165a9190615209565b604051635a9a1eb960e11b8152600401610b3d9190615623565b60cd600087878481811061168a5761168a615a2f565b6116a09260206060909202019081019150615209565b6001600160a01b0316815260208101919091526040016000205460ff166116e9578585828181106116d3576116d3615a2f565b61165a9260206060909202019081019150615209565b368686838181106116fc576116fc615a2f565b90506060020190508060200160208101906117179190615209565b6001600160a01b0316638bbdb6db338a60408501356117396020870187615209565b60405160e086901b6001600160e01b03191681526001600160a01b0394851660048201529284166024840152604483019190915290911660648201526001608482015260a401600060405180830381600087803b15801561179957600080fd5b505af11580156117ad573d6000803e3d6000fd5b50505050508060010190506115d9565b50825160005b818110156115015760006117f08683815181106117e2576117e2615a2f565b60200260200101518a6145f7565b509150508015611813576040516319bc701360e11b815260040160405180910390fd5b506001016117c3565b61183d604051806060016040528060228152602001615e30602291396141eb565b61184682614285565b6001600160a01b038216600081815260da6020908152604091829020805460ff191685151590811790915591519182527f2fa1fd52a8d73f0cc9eef64ceb5d0b5e14cb8bef299666bf18ff6b193823e2c491015b60405180910390a25050565b60006118b13361469e565b905090565b6000806000806118ca8888888860006146c6565b608081015160a09091015160009a919950975095505050505050565b611907604051806060016040528060258152602001615e52602591396141eb565b60d080549082905560408051828152602081018490527eb4f4f153ad7f1397564a8830fef092481e8cf6a2cd3ff04f96d10ba51200a59101610f2d565b6000806000806119558560006145d9565b608081015160a090910151600097919650945092505050565b60ce818154811061197e57600080fd5b6000918252602090912001546001600160a01b0316905081565b6119a06142ac565b6119a981614285565b60c980546001600160a01b038381166001600160a01b03198316179092556040519116907fd52b2b9b7e9ee655fcb95d2e5b9e0c9f69e7ef2b8e9d2d0ea78402d576d22e2290610f2d9083908590615a62565b611a046142ac565b6001600160a01b038116600090815260d4602052604090205460ff1615611a3e5760405163c4a2984b60e01b815260040160405180910390fd5b60d354611a4f61147e826001615ae6565b60d3805460018082019092557f915c3eb987b20e1af620c1403197bf687fb7f18513b3a73fde6e78c7072c41a60180546001600160a01b0319166001600160a01b038516908117909155600090815260d460205260408120805460ff191690921790915560ce54905b81811015611b5557836001600160a01b0316632a869a4d60ce8381548110611ae257611ae2615a2f565b6000918252602090912001546040516001600160e01b031960e084901b168152611b18916001600160a01b031690600401615623565b600060405180830381600087803b158015611b3257600080fd5b505af1158015611b46573d6000803e3d6000fd5b50505050806001019050611ab8565b50826001600160a01b031663f7c618c16040518163ffffffff1660e01b8152600401602060405180830381865afa158015611b94573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611bb89190615a45565b6001600160a01b0316836001600160a01b03167f066a44d77db1581603d7d8ca1ca494756c0d359c7ffacd9b2c8f78dab7aceae260405160405180910390a3505050565b610e67611c08826127ca565b6147c8565b611c2e6040518060600160405280602c8152602001615e77602c91396141eb565b6001600160a01b038316600090815260cd60205260409020805460ff16611c6a5783604051635a9a1eb960e11b8152600401610b3d9190615623565b670d2f13f7789f0000831115611c93576040516302f22cad60e61b815260040160405180910390fd5b670de0b6b3a7640000821115611cbb5760405162f9474b60e61b815260040160405180910390fd5b82821015611cdb5760405162f9474b60e61b815260040160405180910390fd5b8215801590611d58575060c95460405163fc57d4df60e01b81526001600160a01b039091169063fc57d4df90611d15908790600401615623565b602060405180830381865afa158015611d32573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d569190615af9565b155b15611d77578360405162e52a7d60e41b8152600401610b3d9190615623565b6001810154838114611dc757600182018490556040517f70483e6592cd5182d45ac970e05bc62cdcc90e9d8ef2c2dbe686cf383bcd7fc590611dbe90879084908890615b12565b60405180910390a15b600282015483811461054857600283018490556040517f9e92c7d5fef69846094f3ddcadcb9402c6ba469c461368714f1cabd8ef48b59190611e0e90889084908890615b12565b60405180910390a1505050505050565b6000806000806119558560016145d9565b606060d3805480602002602001604051908101604052809291908181526020018280548015611e8757602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311611e69575b5050505050905090565b611eb2604051806060016040528060278152602001615ea3602791396141eb565b6001600160a01b038216600090815260cd602052604090205460ff16611eed5781604051635a9a1eb960e11b8152600401610b3d9190615623565b6001600160a01b038216600081815260d76020908152604091829020805460ff191685151590811790915591519182527f3e9b2c3906fcb5e1a77a78eb0d76a2f76c432f67c6c461756cc58e33d5b38ea6910161189a565b611f666040518060600160405280602e8152602001615ef4602e91396141eb565b6001600160a01b038216600090815260cd602052604090205460ff16611fa15781604051635a9a1eb960e11b8152600401610b3d9190615623565b816001600160a01b0316636752e7026040518163ffffffff1660e01b8152600401602060405180830381865afa158015611fdf573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120039190615af9565b61201590670de0b6b3a7640000615ae6565b811015612035576040516333987daf60e01b815260040160405180910390fd5b6001600160a01b038216600081815260db6020908152604091829020805490859055825181815291820185905292917f63d34d4644180c2a888e32f4ee4da6d02be25b407e61c64cf4b6a052d115977e910160405180910390a2505050565b61209f8460066143f5565b6120aa848483614863565b60d35460005b8181101561054857600060d382815481106120cd576120cd615a2f565b60009182526020909120015460405163051d1d4f60e11b81526001600160a01b0390911691508190630a3a3a9e90612109908a90600401615623565b600060405180830381600087803b15801561212357600080fd5b505af1158015612137573d6000803e3d6000fd5b505060405163db7954fd60e01b81526001600160a01b038416925063db7954fd9150612169908a908a90600401615a62565b600060405180830381600087803b15801561218357600080fd5b505af1158015612197573d6000803e3d6000fd5b505060405163db7954fd60e01b81526001600160a01b038416925063db7954fd91506121c9908a908990600401615a62565b600060405180830381600087803b1580156121e357600080fd5b505af11580156121f7573d6000803e3d6000fd5b50505050508060010190506120b0565b61220f6142ac565b612219600061491c565b565b3380612225613a40565b6001600160a01b03161461228d5760405162461bcd60e51b815260206004820152602960248201527f4f776e61626c6532537465703a2063616c6c6572206973206e6f7420746865206044820152683732bb9037bbb732b960b91b6064820152608401610b3d565b610e678161491c565b60006122a18261469e565b92915050565b6122af6142ac565b610e6781614935565b6122d9604051806060016040528060248152602001615f22602491396141eb565b60d9805460ff19168215159081179091556040519081527f80cb324e0c14fd08bfaa64a58bb9875493cba570fb5ed20ffae7dbd74500516a9060200160405180910390a150565b612341604051806060016040528060228152602001615f46602291396141eb565b61234a82614285565b6001600160a01b038216600090815260cd602052604090205460ff166123855781604051635a9a1eb960e11b8152600401610b3d9190615623565b6001600160a01b038216600081815260d56020908152604091829020805460ff191685151590811790915591519182527f03561d5280ebb02280893b1d60978e4a27e7654a149c5d0e7c2cf65389ce1694910161189a565b6033546001600160a01b031690565b6123f78360016143f5565b612402838383614863565b60d35460005b818110156104ba57600060d3828154811061242557612425615a2f565b60009182526020909120015460405163051d1d4f60e11b81526001600160a01b0390911691508190630a3a3a9e90612461908990600401615623565b600060405180830381600087803b15801561247b57600080fd5b505af115801561248f573d6000803e3d6000fd5b505060405163db7954fd60e01b81526001600160a01b038416925063db7954fd91506124c19089908990600401615a62565b600060405180830381600087803b1580156124db57600080fd5b505af11580156124ef573d6000803e3d6000fd5b5050505050806001019050612408565b61250833614421565b6000612513826127ca565b8051909150336125228361453c565b600061252f8560016145d9565b905060d054816000015111156125665760d0548151604051631a451c0f60e21b815260048101929092526024820152604401610b3d565b8060a0015160000361258b5760405163095bf33360e01b815260040160405180910390fd5b80604001518160c0015111156125c95780604001518160c00151604051630340492d60e41b8152600401610b3d929190918252602082015260400190565b60006125f960405180602001604052808460c00151815250604051806020016040528085604001518152506149d1565b905060005b8481101561108257600086828151811061261a5761261a615a2f565b60200260200101519050600080612631838b6145f7565b509150915060006126428683614a0d565b905082156126ad5760405163b2a02ff160e01b81526001600160a01b0385169063b2a02ff19061267a908b908f908890600401615b33565b600060405180830381600087803b15801561269457600080fd5b505af11580156126a8573d6000803e3d6000fd5b505050505b81156127165760405163227f37ff60e11b81526001600160a01b038516906344fe6ffe906126e3908b908f908690600401615b33565b600060405180830381600087803b1580156126fd57600080fd5b505af1158015612711573d6000803e3d6000fd5b505050505b505050508060010190506125fe565b6127636040518060400160405280602081526020017f7365744c69717569646174696f6e496e63656e746976652875696e74323536298152506141eb565b670e92596fd629000081101561278c576040516333987daf60e01b815260040160405180910390fd5b60cb80549082905560408051828152602081018490527faeba5a6c40a8ac138134bff1aaa65debf25971188a58804bad717f82f0ec13169101610f2d565b6001600160a01b038116600090815260cc60209081526040808320805482518185028101850190935280835260609493849392919083018282801561283857602002820191906000526020600020905b81546001600160a01b0316815260019091019060200180831161281a575b505050505090506000815190506000816001600160401b0381111561285f5761285f61585a565b604051908082528060200260200182016040528015612888578160200160208202803683370190505b50905060005b8281101561293757600060cd60008684815181106128ae576128ae615a2f565b6020908102919091018101516001600160a01b03168252810191909152604001600020805490915060ff161561292e578482815181106128f0576128f0615a2f565b602002602001015183878151811061290a5761290a615a2f565b6001600160a01b039092166020928302919091019091015261292b86615b57565b95505b5060010161288e565b5092835250909392505050565b606060ce805480602002602001604051908101604052809291908181526020018280548015611e87576020028201919060005260206000209081546001600160a01b03168152600190910190602001808311611e69575050505050905090565b60d354606090806001600160401b038111156129c2576129c261585a565b604051908082528060200260200182016040528015612a2057816020015b612a0d604051806060016040528060006001600160a01b0316815260200160008152602001600081525090565b8152602001906001900390816129e05790505b50915060005b81811015612bde57600060d38281548110612a4357612a43615a2f565b60009182526020808320909101546040805163f7c618c160e01b815290516001600160a01b039092169450849263f7c618c1926004808401938290030181865afa158015612a95573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ab99190615a45565b90506040518060600160405280826001600160a01b03168152602001836001600160a01b03166374c4c1cc896040518263ffffffff1660e01b8152600401612b019190615623565b602060405180830381865afa158015612b1e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b429190615af9565b8152602001836001600160a01b0316637c05a7c5896040518263ffffffff1660e01b8152600401612b739190615623565b602060405180830381865afa158015612b90573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612bb49190615af9565b815250858481518110612bc957612bc9615a2f565b60209081029190910101525050600101612a26565b5050919050565b612bf08360006143f5565b6001600160a01b038316600090815260cd602052604090205460ff16612c2b5782604051635a9a1eb960e11b8152600401610b3d9190615623565b6001600160a01b038316600090815260d7602052604090205460ff168015612c7957506001600160a01b03808416600090815260d8602090815260408083209386168352929052205460ff16155b15612c9b578282604051632dd7bb3560e21b8152600401610b3d929190615a62565b6001600160a01b038316600090815260d160205260409020546000198114612dce576000846001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612cfd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d219190615af9565b905060006040518060200160405280876001600160a01b031663182df0f56040518163ffffffff1660e01b8152600401602060405180830381865afa158015612d6e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d929190615af9565b905290506000612da3828487614a2d565b905083811115612dca57868460405163db33be3d60e01b8152600401610b3d929190615b70565b5050505b60d35460005b8181101561054857600060d38281548110612df157612df1615a2f565b60009182526020909120015460405163051d1d4f60e11b81526001600160a01b0390911691508190630a3a3a9e90612e2d908a90600401615623565b600060405180830381600087803b158015612e4757600080fd5b505af1158015612e5b573d6000803e3d6000fd5b505060405163db7954fd60e01b81526001600160a01b038416925063db7954fd9150612e8d908a908a90600401615a62565b600060405180830381600087803b158015612ea757600080fd5b505af1158015612ebb573d6000803e3d6000fd5b5050505050806001019050612dd4565b80516060906000816001600160401b03811115612eea57612eea61585a565b604051908082528060200260200182016040528015612f13578160200160208202803683370190505b50905060005b82811015612f71576000858281518110612f3557612f35615a2f565b60200260200101519050612f498133614a57565b6000838381518110612f5d57612f5d615a2f565b602090810291909101015250600101612f19565b509392505050565b6000806000612f8786614b4a565b90506000612f9486614b4a565b90506000866001600160a01b031663182df0f56040518163ffffffff1660e01b8152600401602060405180830381865afa158015612fd6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ffa9190615af9565b90506000613006615194565b61300e615194565b613016615194565b613042604051806020016040528061302d8e61469e565b90526040805160208101909152898152614be4565b925061306a604051806020016040528088815250604051806020016040528088815250614be4565b915061307683836149d1565b9050613082818b614a0d565b60009d909c509a5050505050505050505050565b6130bf7f0000000000000000000000000000000000000000000000000000000000000000614c1c565b6001600160a01b038116600090815260cd602052604090205460ff16156130fb578060405163d005ce4760e01b8152600401610b3d9190615623565b806001600160a01b0316633d9ea3a16040518163ffffffff1660e01b8152600401602060405180830381865afa158015613139573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061315d9190615b89565b61317a57604051633d21810360e21b815260040160405180910390fd5b6001600160a01b038116600090815260cd60205260408120805460ff191660019081178255810182905560028101919091556131b582614c49565b60d35460005b8181101561324c5760d381815481106131d6576131d6615a2f565b600091825260209091200154604051632a869a4d60e01b81526001600160a01b0390911690632a869a4d9061320f908790600401615623565b600060405180830381600087803b15801561322957600080fd5b505af115801561323d573d6000803e3d6000fd5b505050508060010190506131bb565b507faf16ad15f9e29d5140e8e81a30a92a755aa8edff3d301053c84392b70c0d09a38360405161327c9190615623565b60405180910390a1505050565b6132aa604051806060016040528060288152602001615f68602891396141eb565b828015806132b85750808214155b156132d657604051634ec4810560e11b815260040160405180910390fd5b6132df816143c2565b60005b81811015610548578383828181106132fc576132fc615a2f565b9050602002013560d1600088888581811061331957613319615a2f565b905060200201602081019061332e9190615209565b6001600160a01b0316815260208101919091526040016000205585858281811061335a5761335a615a2f565b905060200201602081019061336f9190615209565b6001600160a01b03167f9e0ad9cee10bdf36b7fbd38910c0bdff0f275ace679b45b922381c2723d676f88585848181106133ab576133ab615a2f565b905060200201356040516133c191815260200190565b60405180910390a26001016132e2565b6133f2604051806060016040528060228152602001615f90602291396141eb565b6133fb81614285565b6134058282614a57565b5050565b600054610100900460ff16158080156134295750600054600160ff909116105b806134435750303b158015613443575060005460ff166001145b6134a65760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b6064820152608401610b3d565b6000805460ff1916600117905580156134c9576000805461ff0019166101001790555b6134d1614d07565b6134da82614d36565b6134e383614935565b8015610633576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200161327c565b60cc602052816000526040600020818154811061354157600080fd5b6000918252602090912001546001600160a01b03169150829050565b61356682614285565b33600090815260d6602090815260408083206001600160a01b038616845290915290205481151560ff9091161515036135b25760405163db6c2c8360e01b815260040160405180910390fd5b33600081815260d6602090815260408083206001600160a01b03871680855290835292819020805460ff191686151590811790915590519081529192917fcb325b7784f78486e42849c7a50b8c5ee008d00cd90e108a58912c0fcb6288b4910160405180910390a35050565b6136298360026143f5565b6001600160a01b038316600090815260cd602052604090205460ff166136645782604051635a9a1eb960e11b8152600401610b3d9190615623565b6001600160a01b03808416600090815260cd60209081526040808320938616835260039093019052205460ff166136a85761369e83614c1c565b6136a83383614a57565b60006136b3836127ca565b90506136be816147c8565b6136c781614d5d565b60c95460405163fc57d4df60e01b81526001600160a01b039091169063fc57d4df906136f7908790600401615623565b602060405180830381865afa158015613714573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137389190615af9565b600003613759578360405162e52a7d60e41b8152600401610b3d9190615623565b6001600160a01b038416600090815260cf60205260409020546000198114613889576000856001600160a01b03166347bd37186040518163ffffffff1660e01b8152600401602060405180830381865afa1580156137bb573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137df9190615af9565b90506000866001600160a01b031663bbcac5576040518163ffffffff1660e01b8152600401602060405180830381865afa158015613821573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906138459190615af9565b90506000816138548785615ae6565b61385e9190615ae6565b905083811115613885578784604051632e649eed60e01b8152600401610b3d929190615b70565b5050505b600061389a858760008760006146c6565b60a0810151909150156138c05760405163bb55fd2760e01b815260040160405180910390fd5b60006040518060200160405280886001600160a01b031663aa5af0fd6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561390b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061392f9190615af9565b905260d35490915060005b81811015613a3557600060d3828154811061395757613957615a2f565b600091825260209091200154604051632352607960e01b81526001600160a01b0390911691508190632352607990613995908d908890600401615ba6565b600060405180830381600087803b1580156139af57600080fd5b505af11580156139c3573d6000803e3d6000fd5b5050604051636a95ddef60e01b81526001600160a01b0384169250636a95ddef91506139f7908d908d908990600401615bc0565b600060405180830381600087803b158015613a1157600080fd5b505af1158015613a25573d6000803e3d6000fd5b505050505080600101905061393a565b505050505050505050565b6065546001600160a01b031690565b6001600160a01b038216600090815260d26020526040812081836008811115613a7a57613a7a615be3565b6008811115613a8b57613a8b615be3565b815260208101919091526040016000205460ff169392505050565b613ab18560056143f5565b613aba83611bfc565b6001600160a01b038516600090815260cd602052604090205460ff16613af55784604051635a9a1eb960e11b8152600401610b3d9190615623565b6001600160a01b038416600090815260cd602052604090205460ff16613b305783604051635a9a1eb960e11b8152600401610b3d9190615623565b6040516395dd919360e01b81526000906001600160a01b038716906395dd919390613b5f908790600401615623565b602060405180830381865afa158015613b7c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613ba09190615af9565b90508180613bc657506001600160a01b038616600090815260d5602052604090205460ff165b15613bf25780831115613bec5760405163e46c155960e01b815260040160405180910390fd5b506104ba565b6000613bff8560016145d9565b905060d054816000015111613c355760d0548151604051636e61bb0560e11b815260048101929092526024820152604401610b3d565b8060a00151600003613c5a5760405163095bf33360e01b815260040160405180910390fd5b6000613c76604051806020016040528060ca5481525084614a0d565b9050808511156115015760405163e46c155960e01b815260040160405180910390fd5b613ca48260036143f5565b60c9546040516396e85ced60e01b81526001600160a01b03909116906396e85ced90613cd4908590600401615623565b600060405180830381600087803b158015613cee57600080fd5b505af1158015613d02573d6000803e3d6000fd5b505050506001600160a01b038216600090815260cd602052604090205460ff16613d415781604051635a9a1eb960e11b8152600401610b3d9190615623565b60d35460005b818110156105215760006040518060200160405280866001600160a01b031663aa5af0fd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613d9a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613dbe9190615af9565b8152509050600060d38381548110613dd857613dd8615a2f565b600091825260209091200154604051632352607960e01b81526001600160a01b0390911691508190632352607990613e169089908690600401615ba6565b600060405180830381600087803b158015613e3057600080fd5b505af1158015613e44573d6000803e3d6000fd5b5050604051636a95ddef60e01b81526001600160a01b0384169250636a95ddef9150613e7890899089908790600401615bc0565b600060405180830381600087803b158015613e9257600080fd5b505af1158015613ea6573d6000803e3d6000fd5b505050505050806001019050613d47565b6000613ec48260086143f5565b81600080613ed283336145f7565b509150915080600014613ef85760405163f8a5d66d60e01b815260040160405180910390fd5b613f03853384614863565b6001600160a01b038316600090815260cd60209081526040808320338452600381019092529091205460ff16613f3f5750600095945050505050565b3360009081526003820160209081526040808320805460ff1916905560cc825280832080548251818502810185019093528083529192909190830182828015613fb157602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311613f93575b5050835193945083925060009150505b8281101561400b57876001600160a01b0316848281518110613fe557613fe5615a2f565b60200260200101516001600160a01b0316036140035780915061400b565b600101613fc1565b5081811061401b5761401b615bf9565b33600090815260cc602052604090208054819061403a90600190615c0f565b8154811061404a5761404a615a2f565b9060005260206000200160009054906101000a90046001600160a01b031681838154811061407a5761407a615a2f565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550808054806140b8576140b8615c22565b600082815260208120820160001990810180546001600160a01b031916905590910190915560405133916001600160a01b038b16917fe699a64c18b07ac5b7301aa273f36a2287239eb9501d81950672794afba29a0d9190a35060009998505050505050505050565b6141296142ac565b606580546001600160a01b0319166001600160a01b03831690811790915561414f6123dd565b6001600160a01b03167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e2270060405160405180910390a350565b61418f6142ac565b61419881614285565b60dc80546001600160a01b038381166001600160a01b03198316179092556040519116907fc4877d82ec0586c93fbd01eb65785e064f0c31b594e7f413e5f16502f25cf27390610f2d9083908590615a62565b6097546040516318c5e8ab60e01b81526000916001600160a01b0316906318c5e8ab9061421e9033908690600401615c7e565b602060405180830381865afa15801561423b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061425f9190615b89565b90508061340557333083604051634a3fa29360e01b8152600401610b3d93929190615ca2565b6001600160a01b038116610e67576040516342bcdf7f60e11b815260040160405180910390fd5b336142b56123dd565b6001600160a01b0316146122195760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610b3d565b6001600160a01b03811661436f5760405162461bcd60e51b815260206004820152602560248201527f696e76616c696420616365737320636f6e74726f6c206d616e61676572206164604482015264647265737360d81b6064820152608401610b3d565b609780546001600160a01b038381166001600160a01b03198316179092556040519116907f66fd58e82f7b31a2a5c30e0888f3093efe4e111b00cd2b0c31fe014601293aa090610f2d9083908590615a62565b61010754811115610e67576101075460405163792bfb1b60e11b8152600481019190915260248101829052604401610b3d565b6143ff8282613a4f565b156134055781816040516313b3ccb160e31b8152600401610b3d929190615cf0565b60d95460ff16801561444c57506001600160a01b038116600090815260da602052604090205460ff16155b15610e67578060405163cdd0a0c760e01b8152600401610b3d9190615623565b6001600160a01b038316600090815260cd602052604090205460ff166144a75782604051635a9a1eb960e11b8152600401610b3d9190615623565b6001600160a01b038316600090815260d26020526040812082918460088111156144d3576144d3615be3565b60088111156144e4576144e4615be3565b815260200190815260200160002060006101000a81548160ff0219169083151502179055507f35007a986bcd36d2f73fc7f1b73762e12eadb4406dd163194950fd3b5a6a827d83838360405161327c93929190615d0d565b805160005b818110156145cf5782818151811061455b5761455b615a2f565b60200260200101516001600160a01b031663a6afed956040518163ffffffff1660e01b81526004016020604051808303816000875af11580156145a2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906145c69190615af9565b50600101614541565b50613405826147c8565b6145e16151a7565b6145f0836000806000866146c6565b9392505050565b600080600080856001600160a01b031663c37f68e2866040518263ffffffff1660e01b81526004016146299190615623565b608060405180830381865afa158015614646573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061466a9190615d3a565b919650945092509050801561469657858560405163015e34d960e61b8152600401610b3d929190615a62565b509250925092565b6001600160a01b038116600090815260db60205260408120548082036122a15760cb546145f0565b6146ce6151a7565b60006146d9876127ca565b805190915060005b8181101561476a5760008382815181106146fd576146fd615a2f565b6020026020010151905060008061471687848e8b614df8565b915091508a6001600160a01b0316836001600160a01b03160361475c57614742828b8960600151614a2d565b606088018190526147569082908b90614a2d565b60608801525b5050508060010190506146e1565b506000836060015184604001516147819190615ae6565b905080846020015111156147a85760208401518190036080850152600060a08501526147bc565b600060808501526020840151810360a08501525b50505095945050505050565b805160c9546001600160a01b031660005b8281101561052157816001600160a01b03166396e85ced85838151811061480257614802615a2f565b60200260200101516040518263ffffffff1660e01b81526004016148269190615623565b600060405180830381600087803b15801561484057600080fd5b505af1158015614854573d6000803e3d6000fd5b505050508060010190506147d9565b6001600160a01b038316600090815260cd60205260409020805460ff1661489f5783604051635a9a1eb960e11b8152600401610b3d9190615623565b6001600160a01b038316600090815260038201602052604090205460ff166148c75750505050565b60006148d2846127ca565b90506148dd816147c8565b6148e681614d5d565b60006148f68587866000806146c6565b60a0810151909150156105485760405163bb55fd2760e01b815260040160405180910390fd5b606580546001600160a01b0319169055610e6781614f15565b6101075481116149925760405162461bcd60e51b815260206004820152602260248201527f436f6d7074726f6c6c65723a20496e76616c6964206d61784c6f6f70734c696d6044820152611a5d60f21b6064820152608401610b3d565b61010780549082905560408051828152602081018490527fc2d09fef144f7c8a86f71ea459f8fc17f675768eb1ae369cbd77fb31d467aafa9101610f2d565b6149d9615194565b6040518060200160405280614a046149fd8660000151670de0b6b3a7640000614f67565b8551614f73565b90529392505050565b600080614a1a8484614f7f565b9050614a2581614fa0565b949350505050565b600080614a3a8585614f7f565b9050614a4e614a4882614fa0565b84614fb8565b95945050505050565b614a628260076143f5565b6001600160a01b038216600090815260cd60205260409020805460ff16614a9e5782604051635a9a1eb960e11b8152600401610b3d9190615623565b6001600160a01b038216600090815260038201602052604090205460ff1615614ac657505050565b6001600160a01b0380831660008181526003840160209081526040808320805460ff1916600190811790915560cc835281842080549182018155845291832090910180549488166001600160a01b031990951685179055519192917f3ab23ab0d51cccc0c3085aec51f99228625aa1a922b3a8ca89a26b0f2027a1a59190a3505050565b60c95460405163fc57d4df60e01b815260009182916001600160a01b039091169063fc57d4df90614b7f908690600401615623565b602060405180830381865afa158015614b9c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614bc09190615af9565b9050806000036122a1578260405162e52a7d60e41b8152600401610b3d9190615623565b614bec615194565b6040518060200160405280670de0b6b3a7640000614c1286600001518660000151614f67565b614a049190615ac4565b336001600160a01b03821614610e67578033604051634e9383fb60e11b8152600401610b3d929190615a62565b60ce5460005b81811015614cb157826001600160a01b031660ce8281548110614c7457614c74615a2f565b6000918252602090912001546001600160a01b031603614ca9578260405163d005ce4760e01b8152600401610b3d9190615623565b600101614c4f565b505060ce805460018101825560008290527fd36cd1c74ef8d7326d8021b776c18fb5a5724b7f7bc93c2f42e43e10ef27d12a0180546001600160a01b0319166001600160a01b03841617905554613405816143c2565b600054610100900460ff16614d2e5760405162461bcd60e51b8152600401610b3d90615d70565b612219614fc4565b600054610100900460ff16610e5e5760405162461bcd60e51b8152600401610b3d90615d70565b805160dc546001600160a01b031660005b8281101561052157816001600160a01b031663a9c3cab1858381518110614d9757614d97615a2f565b60200260200101516040518263ffffffff1660e01b8152600401614dbb9190615623565b600060405180830381600087803b158015614dd557600080fd5b505af1158015614de9573d6000803e3d6000fd5b50505050806001019050614d6e565b614e00615194565b614e08615194565b6000806000614e1788886145f7565b925092509250614e25615194565b614e2f8988614ff4565b80965081925050506000614e5160405180602001604052808581525083614be4565b9050614e66614e608b8a615117565b82614be4565b9650614e7787868d60200151614a2d565b60208c01528a51614e8b9082908790614a2d565b8b526001886001811115614ea157614ea1615be3565b148015614ead57508415155b15614eef57614eda614ebf8287614a0d565b6040518060200160405280614ed38e61469e565b9052615176565b8b60c001818151614eeb9190615ae6565b9052505b614efe86858d60400151614a2d565b8b6040018181525050505050505094509492505050565b603380546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b60006145f08284615a92565b60006145f08284615ac4565b614f87615194565b6040518060200160405280614a04856000015185614f67565b80516000906122a190670de0b6b3a764000090615ac4565b60006145f08284615ae6565b600054610100900460ff16614feb5760405162461bcd60e51b8152600401610b3d90615d70565b6122193361491c565b614ffc615194565b615004615194565b600183600181111561501857615018615be3565b0361504d57600061502885614b4a565b6040805160208082018352838252825190810190925291815290935091506151109050565b60dc546040516388142b6b60e01b815260009182916001600160a01b03909116906388142b6b90615082908990600401615623565b6040805180830381865afa15801561509e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906150c29190615dbb565b9150915081600014806150d3575080155b156150f2578560405162e52a7d60e41b8152600401610b3d9190615623565b60408051602080820183529381528151938401909152908252925090505b9250929050565b61511f615194565b6001600160a01b038316600090815260cd60209081526040808320815192830190915291819085600181111561515757615157615be3565b1461516657826002015461516c565b82600101545b9052949350505050565b60006145f061518d84670de0b6b3a7640000614f67565b8351614f73565b6040518060200160405280600081525090565b6040518060e00160405280600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b6001600160a01b0381168114610e6757600080fd5b8035615204816151e4565b919050565b60006020828403121561521b57600080fd5b81356145f0816151e4565b8015158114610e6757600080fd5b60008060006060848603121561524957600080fd5b8335615254816151e4565b92506020840135615264816151e4565b9150604084013561527481615226565b809150509250925092565b6000806040838503121561529257600080fd5b823561529d816151e4565b915060208301356152ad816151e4565b809150509250929050565b6000602082840312156152ca57600080fd5b5035919050565b60008083601f8401126152e357600080fd5b5081356001600160401b038111156152fa57600080fd5b6020830191508360208260051b850101111561511057600080fd5b6000806000806040858703121561532b57600080fd5b84356001600160401b038082111561534257600080fd5b61534e888389016152d1565b9096509450602087013591508082111561536757600080fd5b50615374878288016152d1565b95989497509550505050565b6000806000806080858703121561539657600080fd5b84356153a1816151e4565b935060208501356153b1816151e4565b925060408501356153c1816151e4565b915060608501356153d1816151e4565b939692955090935050565b600080600080600060a086880312156153f457600080fd5b85356153ff816151e4565b9450602086013561540f816151e4565b9350604086013561541f816151e4565b94979396509394606081013594506080013592915050565b60008060008060006060868803121561544f57600080fd5b85356001600160401b038082111561546657600080fd5b61547289838a016152d1565b9097509550602088013591508082111561548b57600080fd5b50615498888289016152d1565b90945092505060408601356154ac81615226565b809150509295509295909350565b6000806000604084860312156154cf57600080fd5b83356154da816151e4565b925060208401356001600160401b03808211156154f657600080fd5b818601915086601f83011261550a57600080fd5b81358181111561551957600080fd5b87602060608302850101111561552e57600080fd5b6020830194508093505050509250925092565b6000806000806080858703121561555757600080fd5b8435615562816151e4565b93506020850135615572816151e4565b93969395505050506040820135916060013590565b6000806040838503121561559a57600080fd5b82356155a5816151e4565b915060208301356152ad81615226565b60008060008060008060c087890312156155ce57600080fd5b86356155d9816151e4565b955060208701356155e9816151e4565b945060408701356155f9816151e4565b93506060870135615609816151e4565b9598949750929560808101359460a0909101359350915050565b6001600160a01b0391909116815260200190565b60008060006060848603121561564c57600080fd5b8335615657816151e4565b92506020840135615667816151e4565b929592945050506040919091013590565b60008060006060848603121561568d57600080fd5b8335615698816151e4565b95602085013595506040909401359392505050565b6020808252825182820181905260009190848201906040850190845b818110156156ee5783516001600160a01b0316835292840192918401916001016156c9565b50909695505050505050565b6000806040838503121561570d57600080fd5b8235615718816151e4565b946020939093013593505050565b6000806000806080858703121561573c57600080fd5b8435615747816151e4565b93506020850135615757816151e4565b92506040850135615767816151e4565b9396929550929360600135925050565b600080600080600060a0868803121561578f57600080fd5b853561579a816151e4565b945060208601356157aa816151e4565b935060408601356157ba816151e4565b925060608601356157ca816151e4565b949793965091946080013592915050565b6000602082840312156157ed57600080fd5b81356145f081615226565b602080825282518282018190526000919060409081850190868401855b8281101561584d57815180516001600160a01b0316855286810151878601528501518585015260609093019290850190600101615815565b5091979650505050505050565b634e487b7160e01b600052604160045260246000fd5b6000602080838503121561588357600080fd5b82356001600160401b038082111561589a57600080fd5b818501915085601f8301126158ae57600080fd5b8135818111156158c0576158c061585a565b8060051b604051601f19603f830116810181811085821117156158e5576158e561585a565b60405291825284820192508381018501918883111561590357600080fd5b938501935b8285101561592857615919856151f9565b84529385019392850192615908565b98975050505050505050565b6020808252825182820181905260009190848201906040850190845b818110156156ee57835183529284019291840191600101615950565b6000806040838503121561597f57600080fd5b8235915060208301356152ad816151e4565b80356009811061520457600080fd5b600080604083850312156159b357600080fd5b82356159be816151e4565b91506159cc60208401615991565b90509250929050565b600080600080600060a086880312156159ed57600080fd5b85356159f8816151e4565b94506020860135615a08816151e4565b93506040860135615a18816151e4565b92506060860135915060808601356154ac81615226565b634e487b7160e01b600052603260045260246000fd5b600060208284031215615a5757600080fd5b81516145f0816151e4565b6001600160a01b0392831681529116602082015260400190565b634e487b7160e01b600052601160045260246000fd5b80820281158282048414176122a1576122a1615a7c565b600060208284031215615abb57600080fd5b6145f082615991565b600082615ae157634e487b7160e01b600052601260045260246000fd5b500490565b808201808211156122a1576122a1615a7c565b600060208284031215615b0b57600080fd5b5051919050565b6001600160a01b039390931683526020830191909152604082015260600190565b6001600160a01b039384168152919092166020820152604081019190915260600190565b600060018201615b6957615b69615a7c565b5060010190565b6001600160a01b03929092168252602082015260400190565b600060208284031215615b9b57600080fd5b81516145f081615226565b6001600160a01b0392909216825251602082015260400190565b6001600160a01b0393841681529190921660208201529051604082015260600190565b634e487b7160e01b600052602160045260246000fd5b634e487b7160e01b600052600160045260246000fd5b818103818111156122a1576122a1615a7c565b634e487b7160e01b600052603160045260246000fd5b6000815180845260005b81811015615c5e57602081850181015186830182015201615c42565b506000602082860101526020601f19601f83011685010191505092915050565b6001600160a01b0383168152604060208201819052600090614a2590830184615c38565b6001600160a01b03848116825283166020820152606060408201819052600090614a4e90830184615c38565b60098110615cec57634e487b7160e01b600052602160045260246000fd5b9052565b6001600160a01b0383168152604081016145f06020830184615cce565b6001600160a01b038416815260608101615d2a6020830185615cce565b8215156040830152949350505050565b60008060008060808587031215615d5057600080fd5b505082516020840151604085015160609095015191969095509092509050565b6020808252602b908201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960408201526a6e697469616c697a696e6760a81b606082015260800190565b60008060408385031215615dce57600080fd5b50508051602090910151909290915056fe736574416c6c6f776564537570706c69657228616464726573732c616464726573732c626f6f6c297365744d61726b6574426f72726f774361707328616464726573735b5d2c75696e743235365b5d29736574416c6c6f7765644c697175696461746f7228616464726573732c626f6f6c297365744d696e4c6971756964617461626c65436f6c6c61746572616c2875696e7432353629736574436f6c6c61746572616c466163746f7228616464726573732c75696e743235362c75696e7432353629736574537570706c79416c6c6f776c697374456e61626c656428616464726573732c626f6f6c29736574416374696f6e7350617573656428616464726573735b5d2c75696e743235365b5d2c626f6f6c297365744d61726b65744c69717569646174696f6e496e63656e7469766528616464726573732c75696e74323536297365744c69717569646174696f6e416c6c6f776c697374456e61626c656428626f6f6c29736574466f726365644c69717569646174696f6e28616464726573732c626f6f6c297365744d61726b6574537570706c794361707328616464726573735b5d2c75696e743235365b5d29656e7465724d61726b6574426568616c6628616464726573732c6164647265737329a2646970667358221220d0096c0fe2d2f6c05ac03bd5c918aa2b6b37a63732f6c3bc612c8fc6f811e6dc64736f6c63430008190033", + "storageLayout": { + "storage": [ + { + "astId": 246, + "contract": "contracts/Spoke/SpokeComptroller.sol:SpokeComptroller", + "label": "_initialized", + "offset": 0, + "slot": "0", + "type": "t_uint8" + }, + { + "astId": 249, + "contract": "contracts/Spoke/SpokeComptroller.sol:SpokeComptroller", + "label": "_initializing", + "offset": 1, + "slot": "0", + "type": "t_bool" + }, + { + "astId": 1278, + "contract": "contracts/Spoke/SpokeComptroller.sol:SpokeComptroller", + "label": "__gap", + "offset": 0, + "slot": "1", + "type": "t_array(t_uint256)50_storage" + }, + { + "astId": 118, + "contract": "contracts/Spoke/SpokeComptroller.sol:SpokeComptroller", + "label": "_owner", + "offset": 0, + "slot": "51", + "type": "t_address" + }, + { + "astId": 238, + "contract": "contracts/Spoke/SpokeComptroller.sol:SpokeComptroller", + "label": "__gap", + "offset": 0, + "slot": "52", + "type": "t_array(t_uint256)49_storage" + }, + { + "astId": 11, + "contract": "contracts/Spoke/SpokeComptroller.sol:SpokeComptroller", + "label": "_pendingOwner", + "offset": 0, + "slot": "101", + "type": "t_address" + }, + { + "astId": 105, + "contract": "contracts/Spoke/SpokeComptroller.sol:SpokeComptroller", + "label": "__gap", + "offset": 0, + "slot": "102", + "type": "t_array(t_uint256)49_storage" + }, + { + "astId": 1366, + "contract": "contracts/Spoke/SpokeComptroller.sol:SpokeComptroller", + "label": "_accessControlManager", + "offset": 0, + "slot": "151", + "type": "t_contract(IAccessControlManagerV8)1551" + }, + { + "astId": 1371, + "contract": "contracts/Spoke/SpokeComptroller.sol:SpokeComptroller", + "label": "__gap", + "offset": 0, + "slot": "152", + "type": "t_array(t_uint256)49_storage" + }, + { + "astId": 14526, + "contract": "contracts/Spoke/SpokeComptroller.sol:SpokeComptroller", + "label": "oracle", + "offset": 0, + "slot": "201", + "type": "t_contract(ResilientOracleInterface)2066" + }, + { + "astId": 14529, + "contract": "contracts/Spoke/SpokeComptroller.sol:SpokeComptroller", + "label": "closeFactorMantissa", + "offset": 0, + "slot": "202", + "type": "t_uint256" + }, + { + "astId": 14532, + "contract": "contracts/Spoke/SpokeComptroller.sol:SpokeComptroller", + "label": "_poolLiquidationIncentiveMantissa", + "offset": 0, + "slot": "203", + "type": "t_uint256" + }, + { + "astId": 14539, + "contract": "contracts/Spoke/SpokeComptroller.sol:SpokeComptroller", + "label": "accountAssets", + "offset": 0, + "slot": "204", + "type": "t_mapping(t_address,t_array(t_contract(VToken)17772)dyn_storage)" + }, + { + "astId": 14545, + "contract": "contracts/Spoke/SpokeComptroller.sol:SpokeComptroller", + "label": "markets", + "offset": 0, + "slot": "205", + "type": "t_mapping(t_address,t_struct(Market)14522_storage)" + }, + { + "astId": 14550, + "contract": "contracts/Spoke/SpokeComptroller.sol:SpokeComptroller", + "label": "allMarkets", + "offset": 0, + "slot": "206", + "type": "t_array(t_contract(VToken)17772)dyn_storage" + }, + { + "astId": 14555, + "contract": "contracts/Spoke/SpokeComptroller.sol:SpokeComptroller", + "label": "borrowCaps", + "offset": 0, + "slot": "207", + "type": "t_mapping(t_address,t_uint256)" + }, + { + "astId": 14558, + "contract": "contracts/Spoke/SpokeComptroller.sol:SpokeComptroller", + "label": "minLiquidatableCollateral", + "offset": 0, + "slot": "208", + "type": "t_uint256" + }, + { + "astId": 14563, + "contract": "contracts/Spoke/SpokeComptroller.sol:SpokeComptroller", + "label": "supplyCaps", + "offset": 0, + "slot": "209", + "type": "t_mapping(t_address,t_uint256)" + }, + { + "astId": 14571, + "contract": "contracts/Spoke/SpokeComptroller.sol:SpokeComptroller", + "label": "_actionPaused", + "offset": 0, + "slot": "210", + "type": "t_mapping(t_address,t_mapping(t_enum(Action)6669,t_bool))" + }, + { + "astId": 14575, + "contract": "contracts/Spoke/SpokeComptroller.sol:SpokeComptroller", + "label": "rewardsDistributors", + "offset": 0, + "slot": "211", + "type": "t_array(t_contract(RewardsDistributor)9776)dyn_storage" + }, + { + "astId": 14579, + "contract": "contracts/Spoke/SpokeComptroller.sol:SpokeComptroller", + "label": "rewardsDistributorExists", + "offset": 0, + "slot": "212", + "type": "t_mapping(t_address,t_bool)" + }, + { + "astId": 14584, + "contract": "contracts/Spoke/SpokeComptroller.sol:SpokeComptroller", + "label": "isForcedLiquidationEnabled", + "offset": 0, + "slot": "213", + "type": "t_mapping(t_address,t_bool)" + }, + { + "astId": 14606, + "contract": "contracts/Spoke/SpokeComptroller.sol:SpokeComptroller", + "label": "approvedDelegates", + "offset": 0, + "slot": "214", + "type": "t_mapping(t_address,t_mapping(t_address,t_bool))" + }, + { + "astId": 14611, + "contract": "contracts/Spoke/SpokeComptroller.sol:SpokeComptroller", + "label": "isSupplyAllowlistEnabled", + "offset": 0, + "slot": "215", + "type": "t_mapping(t_address,t_bool)" + }, + { + "astId": 14618, + "contract": "contracts/Spoke/SpokeComptroller.sol:SpokeComptroller", + "label": "isAllowedSupplier", + "offset": 0, + "slot": "216", + "type": "t_mapping(t_address,t_mapping(t_address,t_bool))" + }, + { + "astId": 14621, + "contract": "contracts/Spoke/SpokeComptroller.sol:SpokeComptroller", + "label": "isLiquidationAllowlistEnabled", + "offset": 0, + "slot": "217", + "type": "t_bool" + }, + { + "astId": 14626, + "contract": "contracts/Spoke/SpokeComptroller.sol:SpokeComptroller", + "label": "isAllowedLiquidator", + "offset": 0, + "slot": "218", + "type": "t_mapping(t_address,t_bool)" + }, + { + "astId": 14631, + "contract": "contracts/Spoke/SpokeComptroller.sol:SpokeComptroller", + "label": "liquidationIncentives", + "offset": 0, + "slot": "219", + "type": "t_mapping(t_address,t_uint256)" + }, + { + "astId": 14635, + "contract": "contracts/Spoke/SpokeComptroller.sol:SpokeComptroller", + "label": "deviationBoundedOracle", + "offset": 0, + "slot": "220", + "type": "t_contract(IDeviationBoundedOracle)2036" + }, + { + "astId": 14640, + "contract": "contracts/Spoke/SpokeComptroller.sol:SpokeComptroller", + "label": "__gap", + "offset": 0, + "slot": "221", + "type": "t_array(t_uint256)42_storage" + }, + { + "astId": 7792, + "contract": "contracts/Spoke/SpokeComptroller.sol:SpokeComptroller", + "label": "maxLoopsLimit", + "offset": 0, + "slot": "263", + "type": "t_uint256" + }, + { + "astId": 7797, + "contract": "contracts/Spoke/SpokeComptroller.sol:SpokeComptroller", + "label": "__gap", + "offset": 0, + "slot": "264", + "type": "t_array(t_uint256)49_storage" + } + ], + "types": { + "t_address": { + "encoding": "inplace", + "label": "address", + "numberOfBytes": "20" + }, + "t_array(t_contract(RewardsDistributor)9776)dyn_storage": { + "base": "t_contract(RewardsDistributor)9776", + "encoding": "dynamic_array", + "label": "contract RewardsDistributor[]", + "numberOfBytes": "32" + }, + "t_array(t_contract(VToken)17772)dyn_storage": { + "base": "t_contract(VToken)17772", + "encoding": "dynamic_array", + "label": "contract VToken[]", + "numberOfBytes": "32" + }, + "t_array(t_uint256)42_storage": { + "base": "t_uint256", + "encoding": "inplace", + "label": "uint256[42]", + "numberOfBytes": "1344" + }, + "t_array(t_uint256)49_storage": { + "base": "t_uint256", + "encoding": "inplace", + "label": "uint256[49]", + "numberOfBytes": "1568" + }, + "t_array(t_uint256)50_storage": { + "base": "t_uint256", + "encoding": "inplace", + "label": "uint256[50]", + "numberOfBytes": "1600" + }, + "t_bool": { + "encoding": "inplace", + "label": "bool", + "numberOfBytes": "1" + }, + "t_contract(IAccessControlManagerV8)1551": { + "encoding": "inplace", + "label": "contract IAccessControlManagerV8", + "numberOfBytes": "20" + }, + "t_contract(IDeviationBoundedOracle)2036": { + "encoding": "inplace", + "label": "contract IDeviationBoundedOracle", + "numberOfBytes": "20" + }, + "t_contract(ResilientOracleInterface)2066": { + "encoding": "inplace", + "label": "contract ResilientOracleInterface", + "numberOfBytes": "20" + }, + "t_contract(RewardsDistributor)9776": { + "encoding": "inplace", + "label": "contract RewardsDistributor", + "numberOfBytes": "20" + }, + "t_contract(VToken)17772": { + "encoding": "inplace", + "label": "contract VToken", + "numberOfBytes": "20" + }, + "t_enum(Action)6669": { + "encoding": "inplace", + "label": "enum Action", + "numberOfBytes": "1" + }, + "t_mapping(t_address,t_array(t_contract(VToken)17772)dyn_storage)": { + "encoding": "mapping", + "key": "t_address", + "label": "mapping(address => contract VToken[])", + "numberOfBytes": "32", + "value": "t_array(t_contract(VToken)17772)dyn_storage" + }, + "t_mapping(t_address,t_bool)": { + "encoding": "mapping", + "key": "t_address", + "label": "mapping(address => bool)", + "numberOfBytes": "32", + "value": "t_bool" + }, + "t_mapping(t_address,t_mapping(t_address,t_bool))": { + "encoding": "mapping", + "key": "t_address", + "label": "mapping(address => mapping(address => bool))", + "numberOfBytes": "32", + "value": "t_mapping(t_address,t_bool)" + }, + "t_mapping(t_address,t_mapping(t_enum(Action)6669,t_bool))": { + "encoding": "mapping", + "key": "t_address", + "label": "mapping(address => mapping(enum Action => bool))", + "numberOfBytes": "32", + "value": "t_mapping(t_enum(Action)6669,t_bool)" + }, + "t_mapping(t_address,t_struct(Market)14522_storage)": { + "encoding": "mapping", + "key": "t_address", + "label": "mapping(address => struct SpokeComptrollerStorage.Market)", + "numberOfBytes": "32", + "value": "t_struct(Market)14522_storage" + }, + "t_mapping(t_address,t_uint256)": { + "encoding": "mapping", + "key": "t_address", + "label": "mapping(address => uint256)", + "numberOfBytes": "32", + "value": "t_uint256" + }, + "t_mapping(t_enum(Action)6669,t_bool)": { + "encoding": "mapping", + "key": "t_enum(Action)6669", + "label": "mapping(enum Action => bool)", + "numberOfBytes": "32", + "value": "t_bool" + }, + "t_struct(Market)14522_storage": { + "encoding": "inplace", + "label": "struct SpokeComptrollerStorage.Market", + "members": [ + { + "astId": 14513, + "contract": "contracts/Spoke/SpokeComptroller.sol:SpokeComptroller", + "label": "isListed", + "offset": 0, + "slot": "0", + "type": "t_bool" + }, + { + "astId": 14515, + "contract": "contracts/Spoke/SpokeComptroller.sol:SpokeComptroller", + "label": "collateralFactorMantissa", + "offset": 0, + "slot": "1", + "type": "t_uint256" + }, + { + "astId": 14517, + "contract": "contracts/Spoke/SpokeComptroller.sol:SpokeComptroller", + "label": "liquidationThresholdMantissa", + "offset": 0, + "slot": "2", + "type": "t_uint256" + }, + { + "astId": 14521, + "contract": "contracts/Spoke/SpokeComptroller.sol:SpokeComptroller", + "label": "accountMembership", + "offset": 0, + "slot": "3", + "type": "t_mapping(t_address,t_bool)" + } + ], + "numberOfBytes": "128" + }, + "t_uint256": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + }, + "t_uint8": { + "encoding": "inplace", + "label": "uint8", + "numberOfBytes": "1" + } + } + } +} diff --git a/deployments/bsctestnet/SpokePoolLens.json b/deployments/bsctestnet/SpokePoolLens.json new file mode 100644 index 000000000..f3d0b5c98 --- /dev/null +++ b/deployments/bsctestnet/SpokePoolLens.json @@ -0,0 +1,1674 @@ +{ + "address": "0xfbCBFF4ca8b2fFe3231b0c0BBEa25C79F4B0597c", + "abi": [ + { + "inputs": [ + { + "internalType": "bool", + "name": "timeBased_", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "blocksPerYear_", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "InvalidBlocksPerYear", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidTimeBasedConfiguration", + "type": "error" + }, + { + "inputs": [], + "name": "blocksOrSecondsPerYear", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "poolRegistryAddress", + "type": "address" + } + ], + "name": "getAllSpokePools", + "outputs": [ + { + "components": [ + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "internalType": "address", + "name": "creator", + "type": "address" + }, + { + "internalType": "address", + "name": "comptroller", + "type": "address" + }, + { + "internalType": "uint256", + "name": "blockPosted", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "timestampPosted", + "type": "uint256" + }, + { + "internalType": "string", + "name": "category", + "type": "string" + }, + { + "internalType": "string", + "name": "logoURL", + "type": "string" + }, + { + "internalType": "string", + "name": "description", + "type": "string" + }, + { + "internalType": "address", + "name": "priceOracle", + "type": "address" + }, + { + "internalType": "uint256", + "name": "closeFactor", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "poolLiquidationIncentiveMantissa", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minLiquidatableCollateral", + "type": "uint256" + }, + { + "internalType": "address", + "name": "deviationBoundedOracle", + "type": "address" + }, + { + "internalType": "bool", + "name": "liquidationAllowlistEnabled", + "type": "bool" + }, + { + "components": [ + { + "internalType": "address", + "name": "vToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "exchangeRateCurrent", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "supplyRatePerBlockOrTimestamp", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "borrowRatePerBlockOrTimestamp", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "reserveFactorMantissa", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "supplyCaps", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "borrowCaps", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "totalBorrows", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "totalReserves", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "totalSupply", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "totalCash", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "isListed", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "collateralFactorMantissa", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "liquidationThresholdMantissa", + "type": "uint256" + }, + { + "internalType": "address", + "name": "underlyingAssetAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "vTokenDecimals", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "underlyingDecimals", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "pausedActions", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "effectiveLiquidationIncentiveMantissa", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "ownLiquidationIncentiveMantissa", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "supplyAllowlistEnabled", + "type": "bool" + }, + { + "internalType": "bool", + "name": "forcedLiquidationEnabled", + "type": "bool" + } + ], + "internalType": "struct SpokePoolLens.SpokeVTokenMetadata[]", + "name": "vTokens", + "type": "tuple[]" + } + ], + "internalType": "struct SpokePoolLens.SpokePoolData[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getBlockNumberOrTimestamp", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "address", + "name": "comptrollerAddress", + "type": "address" + } + ], + "name": "getPendingRewards", + "outputs": [ + { + "components": [ + { + "internalType": "address", + "name": "distributorAddress", + "type": "address" + }, + { + "internalType": "address", + "name": "rewardTokenAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "totalRewards", + "type": "uint256" + }, + { + "components": [ + { + "internalType": "address", + "name": "vTokenAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "internalType": "struct SpokePoolLens.PendingReward[]", + "name": "pendingRewards", + "type": "tuple[]" + } + ], + "internalType": "struct SpokePoolLens.RewardSummary[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "comptrollerAddress", + "type": "address" + } + ], + "name": "getPoolBadDebt", + "outputs": [ + { + "components": [ + { + "internalType": "address", + "name": "comptroller", + "type": "address" + }, + { + "internalType": "uint256", + "name": "totalBadDebtUsd", + "type": "uint256" + }, + { + "components": [ + { + "internalType": "address", + "name": "vTokenAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "badDebtUsd", + "type": "uint256" + } + ], + "internalType": "struct SpokePoolLens.BadDebt[]", + "name": "badDebts", + "type": "tuple[]" + } + ], + "internalType": "struct SpokePoolLens.BadDebtSummary", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "poolRegistryAddress", + "type": "address" + }, + { + "internalType": "address", + "name": "asset", + "type": "address" + } + ], + "name": "getPoolsSupportedByAsset", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "poolRegistryAddress", + "type": "address" + }, + { + "internalType": "address", + "name": "comptroller", + "type": "address" + } + ], + "name": "getSpokePoolByComptroller", + "outputs": [ + { + "components": [ + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "internalType": "address", + "name": "creator", + "type": "address" + }, + { + "internalType": "address", + "name": "comptroller", + "type": "address" + }, + { + "internalType": "uint256", + "name": "blockPosted", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "timestampPosted", + "type": "uint256" + }, + { + "internalType": "string", + "name": "category", + "type": "string" + }, + { + "internalType": "string", + "name": "logoURL", + "type": "string" + }, + { + "internalType": "string", + "name": "description", + "type": "string" + }, + { + "internalType": "address", + "name": "priceOracle", + "type": "address" + }, + { + "internalType": "uint256", + "name": "closeFactor", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "poolLiquidationIncentiveMantissa", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minLiquidatableCollateral", + "type": "uint256" + }, + { + "internalType": "address", + "name": "deviationBoundedOracle", + "type": "address" + }, + { + "internalType": "bool", + "name": "liquidationAllowlistEnabled", + "type": "bool" + }, + { + "components": [ + { + "internalType": "address", + "name": "vToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "exchangeRateCurrent", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "supplyRatePerBlockOrTimestamp", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "borrowRatePerBlockOrTimestamp", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "reserveFactorMantissa", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "supplyCaps", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "borrowCaps", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "totalBorrows", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "totalReserves", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "totalSupply", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "totalCash", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "isListed", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "collateralFactorMantissa", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "liquidationThresholdMantissa", + "type": "uint256" + }, + { + "internalType": "address", + "name": "underlyingAssetAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "vTokenDecimals", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "underlyingDecimals", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "pausedActions", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "effectiveLiquidationIncentiveMantissa", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "ownLiquidationIncentiveMantissa", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "supplyAllowlistEnabled", + "type": "bool" + }, + { + "internalType": "bool", + "name": "forcedLiquidationEnabled", + "type": "bool" + } + ], + "internalType": "struct SpokePoolLens.SpokeVTokenMetadata[]", + "name": "vTokens", + "type": "tuple[]" + } + ], + "internalType": "struct SpokePoolLens.SpokePoolData", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "poolRegistryAddress", + "type": "address" + }, + { + "components": [ + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "internalType": "address", + "name": "creator", + "type": "address" + }, + { + "internalType": "address", + "name": "comptroller", + "type": "address" + }, + { + "internalType": "uint256", + "name": "blockPosted", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "timestampPosted", + "type": "uint256" + } + ], + "internalType": "struct PoolRegistryInterface.VenusPool", + "name": "venusPool", + "type": "tuple" + } + ], + "name": "getSpokePoolData", + "outputs": [ + { + "components": [ + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "internalType": "address", + "name": "creator", + "type": "address" + }, + { + "internalType": "address", + "name": "comptroller", + "type": "address" + }, + { + "internalType": "uint256", + "name": "blockPosted", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "timestampPosted", + "type": "uint256" + }, + { + "internalType": "string", + "name": "category", + "type": "string" + }, + { + "internalType": "string", + "name": "logoURL", + "type": "string" + }, + { + "internalType": "string", + "name": "description", + "type": "string" + }, + { + "internalType": "address", + "name": "priceOracle", + "type": "address" + }, + { + "internalType": "uint256", + "name": "closeFactor", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "poolLiquidationIncentiveMantissa", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minLiquidatableCollateral", + "type": "uint256" + }, + { + "internalType": "address", + "name": "deviationBoundedOracle", + "type": "address" + }, + { + "internalType": "bool", + "name": "liquidationAllowlistEnabled", + "type": "bool" + }, + { + "components": [ + { + "internalType": "address", + "name": "vToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "exchangeRateCurrent", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "supplyRatePerBlockOrTimestamp", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "borrowRatePerBlockOrTimestamp", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "reserveFactorMantissa", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "supplyCaps", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "borrowCaps", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "totalBorrows", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "totalReserves", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "totalSupply", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "totalCash", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "isListed", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "collateralFactorMantissa", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "liquidationThresholdMantissa", + "type": "uint256" + }, + { + "internalType": "address", + "name": "underlyingAssetAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "vTokenDecimals", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "underlyingDecimals", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "pausedActions", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "effectiveLiquidationIncentiveMantissa", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "ownLiquidationIncentiveMantissa", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "supplyAllowlistEnabled", + "type": "bool" + }, + { + "internalType": "bool", + "name": "forcedLiquidationEnabled", + "type": "bool" + } + ], + "internalType": "struct SpokePoolLens.SpokeVTokenMetadata[]", + "name": "vTokens", + "type": "tuple[]" + } + ], + "internalType": "struct SpokePoolLens.SpokePoolData", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "poolRegistryAddress", + "type": "address" + }, + { + "internalType": "address", + "name": "comptroller", + "type": "address" + }, + { + "internalType": "address", + "name": "asset", + "type": "address" + } + ], + "name": "getVTokenForAsset", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "isTimeBased", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "comptroller", + "type": "address" + }, + { + "internalType": "address", + "name": "liquidator", + "type": "address" + } + ], + "name": "spokeLiquidationPermission", + "outputs": [ + { + "internalType": "bool", + "name": "allowlistEnabled", + "type": "bool" + }, + { + "internalType": "bool", + "name": "accountAllowlisted", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "comptroller", + "type": "address" + }, + { + "internalType": "contract VToken[]", + "name": "vTokens", + "type": "address[]" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "spokeSupplyPermissions", + "outputs": [ + { + "components": [ + { + "internalType": "address", + "name": "vToken", + "type": "address" + }, + { + "internalType": "bool", + "name": "allowlistEnabled", + "type": "bool" + }, + { + "internalType": "bool", + "name": "accountAllowlisted", + "type": "bool" + } + ], + "internalType": "struct SpokePoolLens.SpokeSupplyPermission[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract VToken", + "name": "vToken", + "type": "address" + } + ], + "name": "spokeVTokenMetadata", + "outputs": [ + { + "components": [ + { + "internalType": "address", + "name": "vToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "exchangeRateCurrent", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "supplyRatePerBlockOrTimestamp", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "borrowRatePerBlockOrTimestamp", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "reserveFactorMantissa", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "supplyCaps", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "borrowCaps", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "totalBorrows", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "totalReserves", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "totalSupply", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "totalCash", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "isListed", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "collateralFactorMantissa", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "liquidationThresholdMantissa", + "type": "uint256" + }, + { + "internalType": "address", + "name": "underlyingAssetAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "vTokenDecimals", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "underlyingDecimals", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "pausedActions", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "effectiveLiquidationIncentiveMantissa", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "ownLiquidationIncentiveMantissa", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "supplyAllowlistEnabled", + "type": "bool" + }, + { + "internalType": "bool", + "name": "forcedLiquidationEnabled", + "type": "bool" + } + ], + "internalType": "struct SpokePoolLens.SpokeVTokenMetadata", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract VToken[]", + "name": "vTokens", + "type": "address[]" + } + ], + "name": "spokeVTokenMetadataAll", + "outputs": [ + { + "components": [ + { + "internalType": "address", + "name": "vToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "exchangeRateCurrent", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "supplyRatePerBlockOrTimestamp", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "borrowRatePerBlockOrTimestamp", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "reserveFactorMantissa", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "supplyCaps", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "borrowCaps", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "totalBorrows", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "totalReserves", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "totalSupply", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "totalCash", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "isListed", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "collateralFactorMantissa", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "liquidationThresholdMantissa", + "type": "uint256" + }, + { + "internalType": "address", + "name": "underlyingAssetAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "vTokenDecimals", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "underlyingDecimals", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "pausedActions", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "effectiveLiquidationIncentiveMantissa", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "ownLiquidationIncentiveMantissa", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "supplyAllowlistEnabled", + "type": "bool" + }, + { + "internalType": "bool", + "name": "forcedLiquidationEnabled", + "type": "bool" + } + ], + "internalType": "struct SpokePoolLens.SpokeVTokenMetadata[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract VToken", + "name": "vToken", + "type": "address" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "vTokenBalances", + "outputs": [ + { + "components": [ + { + "internalType": "address", + "name": "vToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "balanceOf", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "borrowBalanceCurrent", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "balanceOfUnderlying", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "tokenBalance", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "tokenAllowance", + "type": "uint256" + } + ], + "internalType": "struct SpokePoolLens.VTokenBalances", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract VToken[]", + "name": "vTokens", + "type": "address[]" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "vTokenBalancesAll", + "outputs": [ + { + "components": [ + { + "internalType": "address", + "name": "vToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "balanceOf", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "borrowBalanceCurrent", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "balanceOfUnderlying", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "tokenBalance", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "tokenAllowance", + "type": "uint256" + } + ], + "internalType": "struct SpokePoolLens.VTokenBalances[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract VToken", + "name": "vToken", + "type": "address" + } + ], + "name": "vTokenUnderlyingPrice", + "outputs": [ + { + "components": [ + { + "internalType": "address", + "name": "vToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "underlyingPrice", + "type": "uint256" + } + ], + "internalType": "struct SpokePoolLens.VTokenUnderlyingPrice", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract VToken[]", + "name": "vTokens", + "type": "address[]" + } + ], + "name": "vTokenUnderlyingPriceAll", + "outputs": [ + { + "components": [ + { + "internalType": "address", + "name": "vToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "underlyingPrice", + "type": "uint256" + } + ], + "internalType": "struct SpokePoolLens.VTokenUnderlyingPrice[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + } + ], + "transactionHash": "0x61e4a4469ac967f3ba33eb79e79207fb3ecf5b751cece1aab3e3ef6dbbe857e7", + "receipt": { + "to": null, + "from": "0x33C6476F88eeA28D7E7900F759B4597704Ef95B7", + "contractAddress": "0xfbCBFF4ca8b2fFe3231b0c0BBEa25C79F4B0597c", + "transactionIndex": 0, + "gasUsed": "3940053", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0x94a44281409cb9c24d5003772823e9e7b6b55d9a724e9c2b373fdc133d853ddb", + "transactionHash": "0x61e4a4469ac967f3ba33eb79e79207fb3ecf5b751cece1aab3e3ef6dbbe857e7", + "logs": [], + "blockNumber": 129845398, + "cumulativeGasUsed": "3940053", + "status": 1, + "byzantium": true + }, + "args": [false, 70080000], + "numDeployments": 1, + "solcInputHash": "1e952eaff4761c9a4f4e4da7e68f34d1", + "metadata": "{\"compiler\":{\"version\":\"0.8.25+commit.b61c2a91\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[{\"internalType\":\"bool\",\"name\":\"timeBased_\",\"type\":\"bool\"},{\"internalType\":\"uint256\",\"name\":\"blocksPerYear_\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"InvalidBlocksPerYear\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidTimeBasedConfiguration\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"blocksOrSecondsPerYear\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"poolRegistryAddress\",\"type\":\"address\"}],\"name\":\"getAllSpokePools\",\"outputs\":[{\"components\":[{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"},{\"internalType\":\"address\",\"name\":\"creator\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"comptroller\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"blockPosted\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"timestampPosted\",\"type\":\"uint256\"},{\"internalType\":\"string\",\"name\":\"category\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"logoURL\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"description\",\"type\":\"string\"},{\"internalType\":\"address\",\"name\":\"priceOracle\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"closeFactor\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"poolLiquidationIncentiveMantissa\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"minLiquidatableCollateral\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"deviationBoundedOracle\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"liquidationAllowlistEnabled\",\"type\":\"bool\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"vToken\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"exchangeRateCurrent\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"supplyRatePerBlockOrTimestamp\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"borrowRatePerBlockOrTimestamp\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"reserveFactorMantissa\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"supplyCaps\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"borrowCaps\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"totalBorrows\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"totalReserves\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"totalSupply\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"totalCash\",\"type\":\"uint256\"},{\"internalType\":\"bool\",\"name\":\"isListed\",\"type\":\"bool\"},{\"internalType\":\"uint256\",\"name\":\"collateralFactorMantissa\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"liquidationThresholdMantissa\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"underlyingAssetAddress\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"vTokenDecimals\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"underlyingDecimals\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"pausedActions\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"effectiveLiquidationIncentiveMantissa\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"ownLiquidationIncentiveMantissa\",\"type\":\"uint256\"},{\"internalType\":\"bool\",\"name\":\"supplyAllowlistEnabled\",\"type\":\"bool\"},{\"internalType\":\"bool\",\"name\":\"forcedLiquidationEnabled\",\"type\":\"bool\"}],\"internalType\":\"struct SpokePoolLens.SpokeVTokenMetadata[]\",\"name\":\"vTokens\",\"type\":\"tuple[]\"}],\"internalType\":\"struct SpokePoolLens.SpokePoolData[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getBlockNumberOrTimestamp\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"comptrollerAddress\",\"type\":\"address\"}],\"name\":\"getPendingRewards\",\"outputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"distributorAddress\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"rewardTokenAddress\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"totalRewards\",\"type\":\"uint256\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"vTokenAddress\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"internalType\":\"struct SpokePoolLens.PendingReward[]\",\"name\":\"pendingRewards\",\"type\":\"tuple[]\"}],\"internalType\":\"struct SpokePoolLens.RewardSummary[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"comptrollerAddress\",\"type\":\"address\"}],\"name\":\"getPoolBadDebt\",\"outputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"comptroller\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"totalBadDebtUsd\",\"type\":\"uint256\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"vTokenAddress\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"badDebtUsd\",\"type\":\"uint256\"}],\"internalType\":\"struct SpokePoolLens.BadDebt[]\",\"name\":\"badDebts\",\"type\":\"tuple[]\"}],\"internalType\":\"struct SpokePoolLens.BadDebtSummary\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"poolRegistryAddress\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"asset\",\"type\":\"address\"}],\"name\":\"getPoolsSupportedByAsset\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"poolRegistryAddress\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"comptroller\",\"type\":\"address\"}],\"name\":\"getSpokePoolByComptroller\",\"outputs\":[{\"components\":[{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"},{\"internalType\":\"address\",\"name\":\"creator\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"comptroller\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"blockPosted\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"timestampPosted\",\"type\":\"uint256\"},{\"internalType\":\"string\",\"name\":\"category\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"logoURL\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"description\",\"type\":\"string\"},{\"internalType\":\"address\",\"name\":\"priceOracle\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"closeFactor\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"poolLiquidationIncentiveMantissa\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"minLiquidatableCollateral\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"deviationBoundedOracle\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"liquidationAllowlistEnabled\",\"type\":\"bool\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"vToken\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"exchangeRateCurrent\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"supplyRatePerBlockOrTimestamp\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"borrowRatePerBlockOrTimestamp\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"reserveFactorMantissa\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"supplyCaps\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"borrowCaps\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"totalBorrows\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"totalReserves\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"totalSupply\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"totalCash\",\"type\":\"uint256\"},{\"internalType\":\"bool\",\"name\":\"isListed\",\"type\":\"bool\"},{\"internalType\":\"uint256\",\"name\":\"collateralFactorMantissa\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"liquidationThresholdMantissa\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"underlyingAssetAddress\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"vTokenDecimals\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"underlyingDecimals\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"pausedActions\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"effectiveLiquidationIncentiveMantissa\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"ownLiquidationIncentiveMantissa\",\"type\":\"uint256\"},{\"internalType\":\"bool\",\"name\":\"supplyAllowlistEnabled\",\"type\":\"bool\"},{\"internalType\":\"bool\",\"name\":\"forcedLiquidationEnabled\",\"type\":\"bool\"}],\"internalType\":\"struct SpokePoolLens.SpokeVTokenMetadata[]\",\"name\":\"vTokens\",\"type\":\"tuple[]\"}],\"internalType\":\"struct SpokePoolLens.SpokePoolData\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"poolRegistryAddress\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"},{\"internalType\":\"address\",\"name\":\"creator\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"comptroller\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"blockPosted\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"timestampPosted\",\"type\":\"uint256\"}],\"internalType\":\"struct PoolRegistryInterface.VenusPool\",\"name\":\"venusPool\",\"type\":\"tuple\"}],\"name\":\"getSpokePoolData\",\"outputs\":[{\"components\":[{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"},{\"internalType\":\"address\",\"name\":\"creator\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"comptroller\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"blockPosted\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"timestampPosted\",\"type\":\"uint256\"},{\"internalType\":\"string\",\"name\":\"category\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"logoURL\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"description\",\"type\":\"string\"},{\"internalType\":\"address\",\"name\":\"priceOracle\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"closeFactor\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"poolLiquidationIncentiveMantissa\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"minLiquidatableCollateral\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"deviationBoundedOracle\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"liquidationAllowlistEnabled\",\"type\":\"bool\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"vToken\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"exchangeRateCurrent\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"supplyRatePerBlockOrTimestamp\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"borrowRatePerBlockOrTimestamp\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"reserveFactorMantissa\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"supplyCaps\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"borrowCaps\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"totalBorrows\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"totalReserves\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"totalSupply\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"totalCash\",\"type\":\"uint256\"},{\"internalType\":\"bool\",\"name\":\"isListed\",\"type\":\"bool\"},{\"internalType\":\"uint256\",\"name\":\"collateralFactorMantissa\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"liquidationThresholdMantissa\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"underlyingAssetAddress\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"vTokenDecimals\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"underlyingDecimals\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"pausedActions\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"effectiveLiquidationIncentiveMantissa\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"ownLiquidationIncentiveMantissa\",\"type\":\"uint256\"},{\"internalType\":\"bool\",\"name\":\"supplyAllowlistEnabled\",\"type\":\"bool\"},{\"internalType\":\"bool\",\"name\":\"forcedLiquidationEnabled\",\"type\":\"bool\"}],\"internalType\":\"struct SpokePoolLens.SpokeVTokenMetadata[]\",\"name\":\"vTokens\",\"type\":\"tuple[]\"}],\"internalType\":\"struct SpokePoolLens.SpokePoolData\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"poolRegistryAddress\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"comptroller\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"asset\",\"type\":\"address\"}],\"name\":\"getVTokenForAsset\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"isTimeBased\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"comptroller\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"liquidator\",\"type\":\"address\"}],\"name\":\"spokeLiquidationPermission\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"allowlistEnabled\",\"type\":\"bool\"},{\"internalType\":\"bool\",\"name\":\"accountAllowlisted\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"comptroller\",\"type\":\"address\"},{\"internalType\":\"contract VToken[]\",\"name\":\"vTokens\",\"type\":\"address[]\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"spokeSupplyPermissions\",\"outputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"vToken\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"allowlistEnabled\",\"type\":\"bool\"},{\"internalType\":\"bool\",\"name\":\"accountAllowlisted\",\"type\":\"bool\"}],\"internalType\":\"struct SpokePoolLens.SpokeSupplyPermission[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract VToken\",\"name\":\"vToken\",\"type\":\"address\"}],\"name\":\"spokeVTokenMetadata\",\"outputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"vToken\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"exchangeRateCurrent\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"supplyRatePerBlockOrTimestamp\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"borrowRatePerBlockOrTimestamp\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"reserveFactorMantissa\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"supplyCaps\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"borrowCaps\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"totalBorrows\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"totalReserves\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"totalSupply\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"totalCash\",\"type\":\"uint256\"},{\"internalType\":\"bool\",\"name\":\"isListed\",\"type\":\"bool\"},{\"internalType\":\"uint256\",\"name\":\"collateralFactorMantissa\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"liquidationThresholdMantissa\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"underlyingAssetAddress\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"vTokenDecimals\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"underlyingDecimals\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"pausedActions\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"effectiveLiquidationIncentiveMantissa\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"ownLiquidationIncentiveMantissa\",\"type\":\"uint256\"},{\"internalType\":\"bool\",\"name\":\"supplyAllowlistEnabled\",\"type\":\"bool\"},{\"internalType\":\"bool\",\"name\":\"forcedLiquidationEnabled\",\"type\":\"bool\"}],\"internalType\":\"struct SpokePoolLens.SpokeVTokenMetadata\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract VToken[]\",\"name\":\"vTokens\",\"type\":\"address[]\"}],\"name\":\"spokeVTokenMetadataAll\",\"outputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"vToken\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"exchangeRateCurrent\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"supplyRatePerBlockOrTimestamp\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"borrowRatePerBlockOrTimestamp\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"reserveFactorMantissa\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"supplyCaps\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"borrowCaps\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"totalBorrows\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"totalReserves\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"totalSupply\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"totalCash\",\"type\":\"uint256\"},{\"internalType\":\"bool\",\"name\":\"isListed\",\"type\":\"bool\"},{\"internalType\":\"uint256\",\"name\":\"collateralFactorMantissa\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"liquidationThresholdMantissa\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"underlyingAssetAddress\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"vTokenDecimals\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"underlyingDecimals\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"pausedActions\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"effectiveLiquidationIncentiveMantissa\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"ownLiquidationIncentiveMantissa\",\"type\":\"uint256\"},{\"internalType\":\"bool\",\"name\":\"supplyAllowlistEnabled\",\"type\":\"bool\"},{\"internalType\":\"bool\",\"name\":\"forcedLiquidationEnabled\",\"type\":\"bool\"}],\"internalType\":\"struct SpokePoolLens.SpokeVTokenMetadata[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract VToken\",\"name\":\"vToken\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"vTokenBalances\",\"outputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"vToken\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"balanceOf\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"borrowBalanceCurrent\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"balanceOfUnderlying\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"tokenBalance\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"tokenAllowance\",\"type\":\"uint256\"}],\"internalType\":\"struct SpokePoolLens.VTokenBalances\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract VToken[]\",\"name\":\"vTokens\",\"type\":\"address[]\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"vTokenBalancesAll\",\"outputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"vToken\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"balanceOf\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"borrowBalanceCurrent\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"balanceOfUnderlying\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"tokenBalance\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"tokenAllowance\",\"type\":\"uint256\"}],\"internalType\":\"struct SpokePoolLens.VTokenBalances[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract VToken\",\"name\":\"vToken\",\"type\":\"address\"}],\"name\":\"vTokenUnderlyingPrice\",\"outputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"vToken\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"underlyingPrice\",\"type\":\"uint256\"}],\"internalType\":\"struct SpokePoolLens.VTokenUnderlyingPrice\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract VToken[]\",\"name\":\"vTokens\",\"type\":\"address[]\"}],\"name\":\"vTokenUnderlyingPriceAll\",\"outputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"vToken\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"underlyingPrice\",\"type\":\"uint256\"}],\"internalType\":\"struct SpokePoolLens.VTokenUnderlyingPrice[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"}],\"devdoc\":{\"author\":\"Venus\",\"details\":\"Spoke pools expose additional state that is not included in the shared PoolLens data, including the deviation-bounded oracle, the supply and liquidation allowlists, liquidation thresholds and liquidation incentives. Those reads are named `spoke*` or `getSpokePool*` and return spoke-shaped structs. The rest of the surface mirrors `PoolLens` under the same names and struct shapes, so that reading a spoke pool takes one address rather than two. Those reads go through getters both kinds of pool share. Access-controlled call permissions are deliberately not reported: `AccessControlManager` derives the role from its own caller, so asked from a lens it answers `false` for an account that can in fact call. This contract holds no state and is versioned by redeployment.\",\"kind\":\"dev\",\"methods\":{\"constructor\":{\"custom:oz-upgrades-unsafe-allow\":\"constructor\",\"params\":{\"blocksPerYear_\":\"The number of blocks per year\",\"timeBased_\":\"A boolean indicating whether the contract is based on time or block\"}},\"getAllSpokePools(address)\":{\"details\":\"Not intended to be called in a transaction due to its gas cost. The registry must contain only spoke pools.\",\"params\":{\"poolRegistryAddress\":\"The registry to enumerate\"},\"returns\":{\"_0\":\"The data of every pool in the registry\"}},\"getBlockNumberOrTimestamp()\":{\"details\":\"Function to simply retrieve block number or block timestamp\",\"returns\":{\"_0\":\"Current block number or block timestamp\"}},\"getPendingRewards(address,address)\":{\"params\":{\"account\":\"The user account.\",\"comptrollerAddress\":\"address\"},\"returns\":{\"_0\":\"Pending rewards array\"}},\"getPoolBadDebt(address)\":{\"params\":{\"comptrollerAddress\":\"Address of the comptroller\"},\"returns\":{\"_0\":\"badDebtSummary A struct with comptroller address, total bad debut denominated in usd, and a break down of bad debt by market\"}},\"getPoolsSupportedByAsset(address,address)\":{\"params\":{\"asset\":\"The underlying asset of vToken\",\"poolRegistryAddress\":\"The address of the PoolRegistry contract\"},\"returns\":{\"_0\":\"A list of Comptroller contracts\"}},\"getSpokePoolByComptroller(address,address)\":{\"params\":{\"comptroller\":\"The pool's comptroller\",\"poolRegistryAddress\":\"The registry containing the pool\"},\"returns\":{\"_0\":\"The pool's data\"}},\"getSpokePoolData(address,(string,address,address,uint256,uint256))\":{\"params\":{\"poolRegistryAddress\":\"The registry containing the pool\",\"venusPool\":\"The pool's registry entry\"},\"returns\":{\"_0\":\"The pool's data, including its markets\"}},\"getVTokenForAsset(address,address,address)\":{\"params\":{\"asset\":\"The underlyingAsset of VToken\",\"comptroller\":\"The pool comptroller\",\"poolRegistryAddress\":\"The address of the PoolRegistry contract\"},\"returns\":{\"_0\":\"Address of the vToken\"}},\"spokeLiquidationPermission(address,address)\":{\"params\":{\"comptroller\":\"The spoke pool's comptroller\",\"liquidator\":\"The account to test\"},\"returns\":{\"accountAllowlisted\":\"Whether the account is allowlisted\",\"allowlistEnabled\":\"Whether liquidation is restricted to allowlisted accounts\"}},\"spokeSupplyPermissions(address,address[],address)\":{\"details\":\"The account checked is the one receiving the minted vTokens, which can differ from the payer when using `mintBehalf`.\",\"params\":{\"account\":\"The account to test\",\"comptroller\":\"The spoke pool's comptroller\",\"vTokens\":\"The markets to test\"},\"returns\":{\"_0\":\"One entry per market, in the order given\"}},\"spokeVTokenMetadata(address)\":{\"params\":{\"vToken\":\"The market to read\"},\"returns\":{\"_0\":\"The market's metadata\"}},\"spokeVTokenMetadataAll(address[])\":{\"params\":{\"vTokens\":\"The markets to read\"},\"returns\":{\"_0\":\"One entry per market, in the order given\"}},\"vTokenBalances(address,address)\":{\"params\":{\"account\":\"The user Account\",\"vToken\":\"vToken address\"},\"returns\":{\"_0\":\"A struct containing the balances data\"}},\"vTokenBalancesAll(address[],address)\":{\"params\":{\"account\":\"The user Account\",\"vTokens\":\"The list of vToken addresses\"},\"returns\":{\"_0\":\"A list of structs containing balances data\"}},\"vTokenUnderlyingPrice(address)\":{\"params\":{\"vToken\":\"vToken address\"},\"returns\":{\"_0\":\"The price data for each asset\"}},\"vTokenUnderlyingPriceAll(address[])\":{\"params\":{\"vTokens\":\"The list of vToken addresses\"},\"returns\":{\"_0\":\"An array containing the price data for each asset\"}}},\"title\":\"SpokePoolLens\",\"version\":1},\"userdoc\":{\"errors\":{\"InvalidBlocksPerYear()\":[{\"notice\":\"Thrown when blocks per year is invalid\"}],\"InvalidTimeBasedConfiguration()\":[{\"notice\":\"Thrown when time based but blocks per year is provided\"}]},\"kind\":\"user\",\"methods\":{\"blocksOrSecondsPerYear()\":{\"notice\":\"Stores blocksPerYear if isTimeBased is true else secondsPerYear is stored\"},\"getAllSpokePools(address)\":{\"notice\":\"Queries every pool and market in a spoke pool registry\"},\"getPendingRewards(address,address)\":{\"notice\":\"Returns the pending rewards for a user for a given pool.\"},\"getPoolBadDebt(address)\":{\"notice\":\"Returns a summary of a pool's bad debt broken down by market\"},\"getPoolsSupportedByAsset(address,address)\":{\"notice\":\"Returns all pools that support the specified underlying asset\"},\"getSpokePoolByComptroller(address,address)\":{\"notice\":\"Queries a spoke pool by its comptroller address\"},\"getSpokePoolData(address,(string,address,address,uint256,uint256))\":{\"notice\":\"Queries a spoke pool from its registry entry\"},\"getVTokenForAsset(address,address,address)\":{\"notice\":\"Returns vToken holding the specified underlying asset in the specified pool\"},\"isTimeBased()\":{\"notice\":\"Acknowledges if a contract is time based or not\"},\"spokeLiquidationPermission(address,address)\":{\"notice\":\"Returns whether an account may seize collateral in a spoke pool\"},\"spokeSupplyPermissions(address,address[],address)\":{\"notice\":\"Returns whether an account may supply to each specified market\"},\"spokeVTokenMetadata(address)\":{\"notice\":\"Queries the metadata of one market of a spoke pool\"},\"spokeVTokenMetadataAll(address[])\":{\"notice\":\"Queries the metadata of every given market of a spoke pool\"},\"vTokenBalances(address,address)\":{\"notice\":\"Queries the user's supply/borrow balances in the specified vToken\"},\"vTokenBalancesAll(address[],address)\":{\"notice\":\"Queries the user's supply/borrow balances in vTokens\"},\"vTokenUnderlyingPrice(address)\":{\"notice\":\"Returns the price data for the underlying asset of the specified vToken\"},\"vTokenUnderlyingPriceAll(address[])\":{\"notice\":\"Returns the price data for the underlying assets of the specified vTokens\"}},\"notice\":\"Reads pool and market state specific to a spoke pool\",\"version\":1}},\"settings\":{\"compilationTarget\":{\"contracts/Lens/SpokePoolLens.sol\":\"SpokePoolLens\"},\"evmVersion\":\"paris\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\",\"useLiteralContent\":true},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[]},\"sources\":{\"@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable2Step.sol)\\n\\npragma solidity ^0.8.0;\\n\\nimport \\\"./OwnableUpgradeable.sol\\\";\\nimport {Initializable} from \\\"../proxy/utils/Initializable.sol\\\";\\n\\n/**\\n * @dev Contract module which provides access control mechanism, where\\n * there is an account (an owner) that can be granted exclusive access to\\n * specific functions.\\n *\\n * By default, the owner account will be the one that deploys the contract. This\\n * can later be changed with {transferOwnership} and {acceptOwnership}.\\n *\\n * This module is used through inheritance. It will make available all functions\\n * from parent (Ownable).\\n */\\nabstract contract Ownable2StepUpgradeable is Initializable, OwnableUpgradeable {\\n address private _pendingOwner;\\n\\n event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner);\\n\\n function __Ownable2Step_init() internal onlyInitializing {\\n __Ownable_init_unchained();\\n }\\n\\n function __Ownable2Step_init_unchained() internal onlyInitializing {\\n }\\n /**\\n * @dev Returns the address of the pending owner.\\n */\\n function pendingOwner() public view virtual returns (address) {\\n return _pendingOwner;\\n }\\n\\n /**\\n * @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one.\\n * Can only be called by the current owner.\\n */\\n function transferOwnership(address newOwner) public virtual override onlyOwner {\\n _pendingOwner = newOwner;\\n emit OwnershipTransferStarted(owner(), newOwner);\\n }\\n\\n /**\\n * @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner.\\n * Internal function without access restriction.\\n */\\n function _transferOwnership(address newOwner) internal virtual override {\\n delete _pendingOwner;\\n super._transferOwnership(newOwner);\\n }\\n\\n /**\\n * @dev The new owner accepts the ownership transfer.\\n */\\n function acceptOwnership() public virtual {\\n address sender = _msgSender();\\n require(pendingOwner() == sender, \\\"Ownable2Step: caller is not the new owner\\\");\\n _transferOwnership(sender);\\n }\\n\\n /**\\n * @dev This empty reserved space is put in place to allow future versions to add new\\n * variables without shifting down storage in the inheritance chain.\\n * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\\n */\\n uint256[49] private __gap;\\n}\\n\",\"keccak256\":\"0x9140dabc466abab21b48b72dbda26736b1183a310d0e677d3719d201df026510\",\"license\":\"MIT\"},\"@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)\\n\\npragma solidity ^0.8.0;\\n\\nimport \\\"../utils/ContextUpgradeable.sol\\\";\\nimport {Initializable} from \\\"../proxy/utils/Initializable.sol\\\";\\n\\n/**\\n * @dev Contract module which provides a basic access control mechanism, where\\n * there is an account (an owner) that can be granted exclusive access to\\n * specific functions.\\n *\\n * By default, the owner account will be the one that deploys the contract. This\\n * can later be changed with {transferOwnership}.\\n *\\n * This module is used through inheritance. It will make available the modifier\\n * `onlyOwner`, which can be applied to your functions to restrict their use to\\n * the owner.\\n */\\nabstract contract OwnableUpgradeable is Initializable, ContextUpgradeable {\\n address private _owner;\\n\\n event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\\n\\n /**\\n * @dev Initializes the contract setting the deployer as the initial owner.\\n */\\n function __Ownable_init() internal onlyInitializing {\\n __Ownable_init_unchained();\\n }\\n\\n function __Ownable_init_unchained() internal onlyInitializing {\\n _transferOwnership(_msgSender());\\n }\\n\\n /**\\n * @dev Throws if called by any account other than the owner.\\n */\\n modifier onlyOwner() {\\n _checkOwner();\\n _;\\n }\\n\\n /**\\n * @dev Returns the address of the current owner.\\n */\\n function owner() public view virtual returns (address) {\\n return _owner;\\n }\\n\\n /**\\n * @dev Throws if the sender is not the owner.\\n */\\n function _checkOwner() internal view virtual {\\n require(owner() == _msgSender(), \\\"Ownable: caller is not the owner\\\");\\n }\\n\\n /**\\n * @dev Leaves the contract without owner. It will not be possible to call\\n * `onlyOwner` functions. Can only be called by the current owner.\\n *\\n * NOTE: Renouncing ownership will leave the contract without an owner,\\n * thereby disabling any functionality that is only available to the owner.\\n */\\n function renounceOwnership() public virtual onlyOwner {\\n _transferOwnership(address(0));\\n }\\n\\n /**\\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\\n * Can only be called by the current owner.\\n */\\n function transferOwnership(address newOwner) public virtual onlyOwner {\\n require(newOwner != address(0), \\\"Ownable: new owner is the zero address\\\");\\n _transferOwnership(newOwner);\\n }\\n\\n /**\\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\\n * Internal function without access restriction.\\n */\\n function _transferOwnership(address newOwner) internal virtual {\\n address oldOwner = _owner;\\n _owner = newOwner;\\n emit OwnershipTransferred(oldOwner, newOwner);\\n }\\n\\n /**\\n * @dev This empty reserved space is put in place to allow future versions to add new\\n * variables without shifting down storage in the inheritance chain.\\n * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\\n */\\n uint256[49] private __gap;\\n}\\n\",\"keccak256\":\"0x359a1ab89b46b9aba7bcad3fb651924baf4893d15153049b9976b0fc9be1358e\",\"license\":\"MIT\"},\"@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts (last updated v4.9.0) (proxy/utils/Initializable.sol)\\n\\npragma solidity ^0.8.2;\\n\\nimport \\\"../../utils/AddressUpgradeable.sol\\\";\\n\\n/**\\n * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed\\n * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an\\n * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer\\n * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.\\n *\\n * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be\\n * reused. This mechanism prevents re-execution of each \\\"step\\\" but allows the creation of new initialization steps in\\n * case an upgrade adds a module that needs to be initialized.\\n *\\n * For example:\\n *\\n * [.hljs-theme-light.nopadding]\\n * ```solidity\\n * contract MyToken is ERC20Upgradeable {\\n * function initialize() initializer public {\\n * __ERC20_init(\\\"MyToken\\\", \\\"MTK\\\");\\n * }\\n * }\\n *\\n * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {\\n * function initializeV2() reinitializer(2) public {\\n * __ERC20Permit_init(\\\"MyToken\\\");\\n * }\\n * }\\n * ```\\n *\\n * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as\\n * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.\\n *\\n * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure\\n * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.\\n *\\n * [CAUTION]\\n * ====\\n * Avoid leaving a contract uninitialized.\\n *\\n * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation\\n * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke\\n * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:\\n *\\n * [.hljs-theme-light.nopadding]\\n * ```\\n * /// @custom:oz-upgrades-unsafe-allow constructor\\n * constructor() {\\n * _disableInitializers();\\n * }\\n * ```\\n * ====\\n */\\nabstract contract Initializable {\\n /**\\n * @dev Indicates that the contract has been initialized.\\n * @custom:oz-retyped-from bool\\n */\\n uint8 private _initialized;\\n\\n /**\\n * @dev Indicates that the contract is in the process of being initialized.\\n */\\n bool private _initializing;\\n\\n /**\\n * @dev Triggered when the contract has been initialized or reinitialized.\\n */\\n event Initialized(uint8 version);\\n\\n /**\\n * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,\\n * `onlyInitializing` functions can be used to initialize parent contracts.\\n *\\n * Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a\\n * constructor.\\n *\\n * Emits an {Initialized} event.\\n */\\n modifier initializer() {\\n bool isTopLevelCall = !_initializing;\\n require(\\n (isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1),\\n \\\"Initializable: contract is already initialized\\\"\\n );\\n _initialized = 1;\\n if (isTopLevelCall) {\\n _initializing = true;\\n }\\n _;\\n if (isTopLevelCall) {\\n _initializing = false;\\n emit Initialized(1);\\n }\\n }\\n\\n /**\\n * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the\\n * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be\\n * used to initialize parent contracts.\\n *\\n * A reinitializer may be used after the original initialization step. This is essential to configure modules that\\n * are added through upgrades and that require initialization.\\n *\\n * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`\\n * cannot be nested. If one is invoked in the context of another, execution will revert.\\n *\\n * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in\\n * a contract, executing them in the right order is up to the developer or operator.\\n *\\n * WARNING: setting the version to 255 will prevent any future reinitialization.\\n *\\n * Emits an {Initialized} event.\\n */\\n modifier reinitializer(uint8 version) {\\n require(!_initializing && _initialized < version, \\\"Initializable: contract is already initialized\\\");\\n _initialized = version;\\n _initializing = true;\\n _;\\n _initializing = false;\\n emit Initialized(version);\\n }\\n\\n /**\\n * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the\\n * {initializer} and {reinitializer} modifiers, directly or indirectly.\\n */\\n modifier onlyInitializing() {\\n require(_initializing, \\\"Initializable: contract is not initializing\\\");\\n _;\\n }\\n\\n /**\\n * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.\\n * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized\\n * to any version. It is recommended to use this to lock implementation contracts that are designed to be called\\n * through proxies.\\n *\\n * Emits an {Initialized} event the first time it is successfully executed.\\n */\\n function _disableInitializers() internal virtual {\\n require(!_initializing, \\\"Initializable: contract is initializing\\\");\\n if (_initialized != type(uint8).max) {\\n _initialized = type(uint8).max;\\n emit Initialized(type(uint8).max);\\n }\\n }\\n\\n /**\\n * @dev Returns the highest version that has been initialized. See {reinitializer}.\\n */\\n function _getInitializedVersion() internal view returns (uint8) {\\n return _initialized;\\n }\\n\\n /**\\n * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.\\n */\\n function _isInitializing() internal view returns (bool) {\\n return _initializing;\\n }\\n}\\n\",\"keccak256\":\"0x89be10e757d242e9b18d5a32c9fbe2019f6d63052bbe46397a430a1d60d7f794\",\"license\":\"MIT\"},\"@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev Interface of the ERC20 standard as defined in the EIP.\\n */\\ninterface IERC20Upgradeable {\\n /**\\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\\n * another (`to`).\\n *\\n * Note that `value` may be zero.\\n */\\n event Transfer(address indexed from, address indexed to, uint256 value);\\n\\n /**\\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\\n * a call to {approve}. `value` is the new allowance.\\n */\\n event Approval(address indexed owner, address indexed spender, uint256 value);\\n\\n /**\\n * @dev Returns the amount of tokens in existence.\\n */\\n function totalSupply() external view returns (uint256);\\n\\n /**\\n * @dev Returns the amount of tokens owned by `account`.\\n */\\n function balanceOf(address account) external view returns (uint256);\\n\\n /**\\n * @dev Moves `amount` tokens from the caller's account to `to`.\\n *\\n * Returns a boolean value indicating whether the operation succeeded.\\n *\\n * Emits a {Transfer} event.\\n */\\n function transfer(address to, uint256 amount) external returns (bool);\\n\\n /**\\n * @dev Returns the remaining number of tokens that `spender` will be\\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\\n * zero by default.\\n *\\n * This value changes when {approve} or {transferFrom} are called.\\n */\\n function allowance(address owner, address spender) external view returns (uint256);\\n\\n /**\\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\\n *\\n * Returns a boolean value indicating whether the operation succeeded.\\n *\\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\\n * that someone may use both the old and the new allowance by unfortunate\\n * transaction ordering. One possible solution to mitigate this race\\n * condition is to first reduce the spender's allowance to 0 and set the\\n * desired value afterwards:\\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\\n *\\n * Emits an {Approval} event.\\n */\\n function approve(address spender, uint256 amount) external returns (bool);\\n\\n /**\\n * @dev Moves `amount` tokens from `from` to `to` using the\\n * allowance mechanism. `amount` is then deducted from the caller's\\n * allowance.\\n *\\n * Returns a boolean value indicating whether the operation succeeded.\\n *\\n * Emits a {Transfer} event.\\n */\\n function transferFrom(address from, address to, uint256 amount) external returns (bool);\\n}\\n\",\"keccak256\":\"0x0e1f0f5f62f67a881cd1a9597acbc0a5e4071f3c2c10449a183b922ae7272e3f\",\"license\":\"MIT\"},\"@openzeppelin/contracts-upgradeable/token/ERC20/extensions/IERC20PermitUpgradeable.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts (last updated v4.9.4) (token/ERC20/extensions/IERC20Permit.sol)\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in\\n * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].\\n *\\n * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by\\n * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't\\n * need to send a transaction, and thus is not required to hold Ether at all.\\n *\\n * ==== Security Considerations\\n *\\n * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature\\n * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be\\n * considered as an intention to spend the allowance in any specific way. The second is that because permits have\\n * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should\\n * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be\\n * generally recommended is:\\n *\\n * ```solidity\\n * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {\\n * try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}\\n * doThing(..., value);\\n * }\\n *\\n * function doThing(..., uint256 value) public {\\n * token.safeTransferFrom(msg.sender, address(this), value);\\n * ...\\n * }\\n * ```\\n *\\n * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of\\n * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also\\n * {SafeERC20-safeTransferFrom}).\\n *\\n * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so\\n * contracts should have entry points that don't rely on permit.\\n */\\ninterface IERC20PermitUpgradeable {\\n /**\\n * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,\\n * given ``owner``'s signed approval.\\n *\\n * IMPORTANT: The same issues {IERC20-approve} has related to transaction\\n * ordering also apply here.\\n *\\n * Emits an {Approval} event.\\n *\\n * Requirements:\\n *\\n * - `spender` cannot be the zero address.\\n * - `deadline` must be a timestamp in the future.\\n * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`\\n * over the EIP712-formatted function arguments.\\n * - the signature must use ``owner``'s current nonce (see {nonces}).\\n *\\n * For more information on the signature format, see the\\n * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP\\n * section].\\n *\\n * CAUTION: See Security Considerations above.\\n */\\n function permit(\\n address owner,\\n address spender,\\n uint256 value,\\n uint256 deadline,\\n uint8 v,\\n bytes32 r,\\n bytes32 s\\n ) external;\\n\\n /**\\n * @dev Returns the current nonce for `owner`. This value must be\\n * included whenever a signature is generated for {permit}.\\n *\\n * Every successful call to {permit} increases ``owner``'s nonce by one. This\\n * prevents a signature from being used multiple times.\\n */\\n function nonces(address owner) external view returns (uint256);\\n\\n /**\\n * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.\\n */\\n // solhint-disable-next-line func-name-mixedcase\\n function DOMAIN_SEPARATOR() external view returns (bytes32);\\n}\\n\",\"keccak256\":\"0x07e881de3b9f6d2c07909f193f24b96c7fe4ea60013260f3f25aecd8bab3c2f8\",\"license\":\"MIT\"},\"@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts (last updated v4.9.3) (token/ERC20/utils/SafeERC20.sol)\\n\\npragma solidity ^0.8.0;\\n\\nimport \\\"../IERC20Upgradeable.sol\\\";\\nimport \\\"../extensions/IERC20PermitUpgradeable.sol\\\";\\nimport \\\"../../../utils/AddressUpgradeable.sol\\\";\\n\\n/**\\n * @title SafeERC20\\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\\n * contract returns false). Tokens that return no value (and instead revert or\\n * throw on failure) are also supported, non-reverting calls are assumed to be\\n * successful.\\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\\n */\\nlibrary SafeERC20Upgradeable {\\n using AddressUpgradeable for address;\\n\\n /**\\n * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,\\n * non-reverting calls are assumed to be successful.\\n */\\n function safeTransfer(IERC20Upgradeable token, address to, uint256 value) internal {\\n _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\\n }\\n\\n /**\\n * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the\\n * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.\\n */\\n function safeTransferFrom(IERC20Upgradeable token, address from, address to, uint256 value) internal {\\n _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\\n }\\n\\n /**\\n * @dev Deprecated. This function has issues similar to the ones found in\\n * {IERC20-approve}, and its usage is discouraged.\\n *\\n * Whenever possible, use {safeIncreaseAllowance} and\\n * {safeDecreaseAllowance} instead.\\n */\\n function safeApprove(IERC20Upgradeable token, address spender, uint256 value) internal {\\n // safeApprove should only be called when setting an initial allowance,\\n // or when resetting it to zero. To increase and decrease it, use\\n // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\\n require(\\n (value == 0) || (token.allowance(address(this), spender) == 0),\\n \\\"SafeERC20: approve from non-zero to non-zero allowance\\\"\\n );\\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\\n }\\n\\n /**\\n * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,\\n * non-reverting calls are assumed to be successful.\\n */\\n function safeIncreaseAllowance(IERC20Upgradeable token, address spender, uint256 value) internal {\\n uint256 oldAllowance = token.allowance(address(this), spender);\\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));\\n }\\n\\n /**\\n * @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,\\n * non-reverting calls are assumed to be successful.\\n */\\n function safeDecreaseAllowance(IERC20Upgradeable token, address spender, uint256 value) internal {\\n unchecked {\\n uint256 oldAllowance = token.allowance(address(this), spender);\\n require(oldAllowance >= value, \\\"SafeERC20: decreased allowance below zero\\\");\\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));\\n }\\n }\\n\\n /**\\n * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,\\n * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval\\n * to be set to zero before setting it to a non-zero value, such as USDT.\\n */\\n function forceApprove(IERC20Upgradeable token, address spender, uint256 value) internal {\\n bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);\\n\\n if (!_callOptionalReturnBool(token, approvalCall)) {\\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));\\n _callOptionalReturn(token, approvalCall);\\n }\\n }\\n\\n /**\\n * @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.\\n * Revert on invalid signature.\\n */\\n function safePermit(\\n IERC20PermitUpgradeable token,\\n address owner,\\n address spender,\\n uint256 value,\\n uint256 deadline,\\n uint8 v,\\n bytes32 r,\\n bytes32 s\\n ) internal {\\n uint256 nonceBefore = token.nonces(owner);\\n token.permit(owner, spender, value, deadline, v, r, s);\\n uint256 nonceAfter = token.nonces(owner);\\n require(nonceAfter == nonceBefore + 1, \\\"SafeERC20: permit did not succeed\\\");\\n }\\n\\n /**\\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\\n * on the return value: the return value is optional (but if data is returned, it must not be false).\\n * @param token The token targeted by the call.\\n * @param data The call data (encoded using abi.encode or one of its variants).\\n */\\n function _callOptionalReturn(IERC20Upgradeable token, bytes memory data) private {\\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\\n // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that\\n // the target address contains contract code and also asserts for success in the low-level call.\\n\\n bytes memory returndata = address(token).functionCall(data, \\\"SafeERC20: low-level call failed\\\");\\n require(returndata.length == 0 || abi.decode(returndata, (bool)), \\\"SafeERC20: ERC20 operation did not succeed\\\");\\n }\\n\\n /**\\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\\n * on the return value: the return value is optional (but if data is returned, it must not be false).\\n * @param token The token targeted by the call.\\n * @param data The call data (encoded using abi.encode or one of its variants).\\n *\\n * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.\\n */\\n function _callOptionalReturnBool(IERC20Upgradeable token, bytes memory data) private returns (bool) {\\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\\n // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false\\n // and not revert is the subcall reverts.\\n\\n (bool success, bytes memory returndata) = address(token).call(data);\\n return\\n success && (returndata.length == 0 || abi.decode(returndata, (bool))) && AddressUpgradeable.isContract(address(token));\\n }\\n}\\n\",\"keccak256\":\"0x23b997be73d3dd46885262704f0f8cfc7273fdadfe303d37969a9561373972b5\",\"license\":\"MIT\"},\"@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)\\n\\npragma solidity ^0.8.1;\\n\\n/**\\n * @dev Collection of functions related to the address type\\n */\\nlibrary AddressUpgradeable {\\n /**\\n * @dev Returns true if `account` is a contract.\\n *\\n * [IMPORTANT]\\n * ====\\n * It is unsafe to assume that an address for which this function returns\\n * false is an externally-owned account (EOA) and not a contract.\\n *\\n * Among others, `isContract` will return false for the following\\n * types of addresses:\\n *\\n * - an externally-owned account\\n * - a contract in construction\\n * - an address where a contract will be created\\n * - an address where a contract lived, but was destroyed\\n *\\n * Furthermore, `isContract` will also return true if the target contract within\\n * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,\\n * which only has an effect at the end of a transaction.\\n * ====\\n *\\n * [IMPORTANT]\\n * ====\\n * You shouldn't rely on `isContract` to protect against flash loan attacks!\\n *\\n * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets\\n * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract\\n * constructor.\\n * ====\\n */\\n function isContract(address account) internal view returns (bool) {\\n // This method relies on extcodesize/address.code.length, which returns 0\\n // for contracts in construction, since the code is only stored at the end\\n // of the constructor execution.\\n\\n return account.code.length > 0;\\n }\\n\\n /**\\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\\n * `recipient`, forwarding all available gas and reverting on errors.\\n *\\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\\n * imposed by `transfer`, making them unable to receive funds via\\n * `transfer`. {sendValue} removes this limitation.\\n *\\n * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].\\n *\\n * IMPORTANT: because control is transferred to `recipient`, care must be\\n * taken to not create reentrancy vulnerabilities. Consider using\\n * {ReentrancyGuard} or the\\n * https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\\n */\\n function sendValue(address payable recipient, uint256 amount) internal {\\n require(address(this).balance >= amount, \\\"Address: insufficient balance\\\");\\n\\n (bool success, ) = recipient.call{value: amount}(\\\"\\\");\\n require(success, \\\"Address: unable to send value, recipient may have reverted\\\");\\n }\\n\\n /**\\n * @dev Performs a Solidity function call using a low level `call`. A\\n * plain `call` is an unsafe replacement for a function call: use this\\n * function instead.\\n *\\n * If `target` reverts with a revert reason, it is bubbled up by this\\n * function (like regular Solidity function calls).\\n *\\n * Returns the raw returned data. To convert to the expected return value,\\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\\n *\\n * Requirements:\\n *\\n * - `target` must be a contract.\\n * - calling `target` with `data` must not revert.\\n *\\n * _Available since v3.1._\\n */\\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\\n return functionCallWithValue(target, data, 0, \\\"Address: low-level call failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\\n * `errorMessage` as a fallback revert reason when `target` reverts.\\n *\\n * _Available since v3.1._\\n */\\n function functionCall(\\n address target,\\n bytes memory data,\\n string memory errorMessage\\n ) internal returns (bytes memory) {\\n return functionCallWithValue(target, data, 0, errorMessage);\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\\n * but also transferring `value` wei to `target`.\\n *\\n * Requirements:\\n *\\n * - the calling contract must have an ETH balance of at least `value`.\\n * - the called Solidity function must be `payable`.\\n *\\n * _Available since v3.1._\\n */\\n function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {\\n return functionCallWithValue(target, data, value, \\\"Address: low-level call with value failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\\n * with `errorMessage` as a fallback revert reason when `target` reverts.\\n *\\n * _Available since v3.1._\\n */\\n function functionCallWithValue(\\n address target,\\n bytes memory data,\\n uint256 value,\\n string memory errorMessage\\n ) internal returns (bytes memory) {\\n require(address(this).balance >= value, \\\"Address: insufficient balance for call\\\");\\n (bool success, bytes memory returndata) = target.call{value: value}(data);\\n return verifyCallResultFromTarget(target, success, returndata, errorMessage);\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\\n * but performing a static call.\\n *\\n * _Available since v3.3._\\n */\\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\\n return functionStaticCall(target, data, \\\"Address: low-level static call failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\\n * but performing a static call.\\n *\\n * _Available since v3.3._\\n */\\n function functionStaticCall(\\n address target,\\n bytes memory data,\\n string memory errorMessage\\n ) internal view returns (bytes memory) {\\n (bool success, bytes memory returndata) = target.staticcall(data);\\n return verifyCallResultFromTarget(target, success, returndata, errorMessage);\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\\n * but performing a delegate call.\\n *\\n * _Available since v3.4._\\n */\\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\\n return functionDelegateCall(target, data, \\\"Address: low-level delegate call failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\\n * but performing a delegate call.\\n *\\n * _Available since v3.4._\\n */\\n function functionDelegateCall(\\n address target,\\n bytes memory data,\\n string memory errorMessage\\n ) internal returns (bytes memory) {\\n (bool success, bytes memory returndata) = target.delegatecall(data);\\n return verifyCallResultFromTarget(target, success, returndata, errorMessage);\\n }\\n\\n /**\\n * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling\\n * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.\\n *\\n * _Available since v4.8._\\n */\\n function verifyCallResultFromTarget(\\n address target,\\n bool success,\\n bytes memory returndata,\\n string memory errorMessage\\n ) internal view returns (bytes memory) {\\n if (success) {\\n if (returndata.length == 0) {\\n // only check isContract if the call was successful and the return data is empty\\n // otherwise we already know that it was a contract\\n require(isContract(target), \\\"Address: call to non-contract\\\");\\n }\\n return returndata;\\n } else {\\n _revert(returndata, errorMessage);\\n }\\n }\\n\\n /**\\n * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the\\n * revert reason or using the provided one.\\n *\\n * _Available since v4.3._\\n */\\n function verifyCallResult(\\n bool success,\\n bytes memory returndata,\\n string memory errorMessage\\n ) internal pure returns (bytes memory) {\\n if (success) {\\n return returndata;\\n } else {\\n _revert(returndata, errorMessage);\\n }\\n }\\n\\n function _revert(bytes memory returndata, string memory errorMessage) private pure {\\n // Look for revert reason and bubble it up if present\\n if (returndata.length > 0) {\\n // The easiest way to bubble the revert reason is using memory via assembly\\n /// @solidity memory-safe-assembly\\n assembly {\\n let returndata_size := mload(returndata)\\n revert(add(32, returndata), returndata_size)\\n }\\n } else {\\n revert(errorMessage);\\n }\\n }\\n}\\n\",\"keccak256\":\"0x9c80f545915582e63fe206c6ce27cbe85a86fc10b9cd2a0e8c9488fb7c2ee422\",\"license\":\"MIT\"},\"@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts (last updated v4.9.4) (utils/Context.sol)\\n\\npragma solidity ^0.8.0;\\nimport {Initializable} from \\\"../proxy/utils/Initializable.sol\\\";\\n\\n/**\\n * @dev Provides information about the current execution context, including the\\n * sender of the transaction and its data. While these are generally available\\n * via msg.sender and msg.data, they should not be accessed in such a direct\\n * manner, since when dealing with meta-transactions the account sending and\\n * paying for execution may not be the actual sender (as far as an application\\n * is concerned).\\n *\\n * This contract is only required for intermediate, library-like contracts.\\n */\\nabstract contract ContextUpgradeable is Initializable {\\n function __Context_init() internal onlyInitializing {\\n }\\n\\n function __Context_init_unchained() internal onlyInitializing {\\n }\\n function _msgSender() internal view virtual returns (address) {\\n return msg.sender;\\n }\\n\\n function _msgData() internal view virtual returns (bytes calldata) {\\n return msg.data;\\n }\\n\\n function _contextSuffixLength() internal view virtual returns (uint256) {\\n return 0;\\n }\\n\\n /**\\n * @dev This empty reserved space is put in place to allow future versions to add new\\n * variables without shifting down storage in the inheritance chain.\\n * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\\n */\\n uint256[50] private __gap;\\n}\\n\",\"keccak256\":\"0x75097e35253e7fb282ee4d7f27a80eaacfa759923185bf17302a89cbc059c5ef\",\"license\":\"MIT\"},\"@openzeppelin/contracts/access/IAccessControl.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev External interface of AccessControl declared to support ERC165 detection.\\n */\\ninterface IAccessControl {\\n /**\\n * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`\\n *\\n * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite\\n * {RoleAdminChanged} not being emitted signaling this.\\n *\\n * _Available since v3.1._\\n */\\n event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);\\n\\n /**\\n * @dev Emitted when `account` is granted `role`.\\n *\\n * `sender` is the account that originated the contract call, an admin role\\n * bearer except when using {AccessControl-_setupRole}.\\n */\\n event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);\\n\\n /**\\n * @dev Emitted when `account` is revoked `role`.\\n *\\n * `sender` is the account that originated the contract call:\\n * - if using `revokeRole`, it is the admin role bearer\\n * - if using `renounceRole`, it is the role bearer (i.e. `account`)\\n */\\n event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);\\n\\n /**\\n * @dev Returns `true` if `account` has been granted `role`.\\n */\\n function hasRole(bytes32 role, address account) external view returns (bool);\\n\\n /**\\n * @dev Returns the admin role that controls `role`. See {grantRole} and\\n * {revokeRole}.\\n *\\n * To change a role's admin, use {AccessControl-_setRoleAdmin}.\\n */\\n function getRoleAdmin(bytes32 role) external view returns (bytes32);\\n\\n /**\\n * @dev Grants `role` to `account`.\\n *\\n * If `account` had not been already granted `role`, emits a {RoleGranted}\\n * event.\\n *\\n * Requirements:\\n *\\n * - the caller must have ``role``'s admin role.\\n */\\n function grantRole(bytes32 role, address account) external;\\n\\n /**\\n * @dev Revokes `role` from `account`.\\n *\\n * If `account` had been granted `role`, emits a {RoleRevoked} event.\\n *\\n * Requirements:\\n *\\n * - the caller must have ``role``'s admin role.\\n */\\n function revokeRole(bytes32 role, address account) external;\\n\\n /**\\n * @dev Revokes `role` from the calling account.\\n *\\n * Roles are often managed via {grantRole} and {revokeRole}: this function's\\n * purpose is to provide a mechanism for accounts to lose their privileges\\n * if they are compromised (such as when a trusted device is misplaced).\\n *\\n * If the calling account had been granted `role`, emits a {RoleRevoked}\\n * event.\\n *\\n * Requirements:\\n *\\n * - the caller must be `account`.\\n */\\n function renounceRole(bytes32 role, address account) external;\\n}\\n\",\"keccak256\":\"0x59ce320a585d7e1f163cd70390a0ef2ff9cec832e2aa544293a00692465a7a57\",\"license\":\"MIT\"},\"@openzeppelin/contracts/token/ERC20/IERC20.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev Interface of the ERC20 standard as defined in the EIP.\\n */\\ninterface IERC20 {\\n /**\\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\\n * another (`to`).\\n *\\n * Note that `value` may be zero.\\n */\\n event Transfer(address indexed from, address indexed to, uint256 value);\\n\\n /**\\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\\n * a call to {approve}. `value` is the new allowance.\\n */\\n event Approval(address indexed owner, address indexed spender, uint256 value);\\n\\n /**\\n * @dev Returns the amount of tokens in existence.\\n */\\n function totalSupply() external view returns (uint256);\\n\\n /**\\n * @dev Returns the amount of tokens owned by `account`.\\n */\\n function balanceOf(address account) external view returns (uint256);\\n\\n /**\\n * @dev Moves `amount` tokens from the caller's account to `to`.\\n *\\n * Returns a boolean value indicating whether the operation succeeded.\\n *\\n * Emits a {Transfer} event.\\n */\\n function transfer(address to, uint256 amount) external returns (bool);\\n\\n /**\\n * @dev Returns the remaining number of tokens that `spender` will be\\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\\n * zero by default.\\n *\\n * This value changes when {approve} or {transferFrom} are called.\\n */\\n function allowance(address owner, address spender) external view returns (uint256);\\n\\n /**\\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\\n *\\n * Returns a boolean value indicating whether the operation succeeded.\\n *\\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\\n * that someone may use both the old and the new allowance by unfortunate\\n * transaction ordering. One possible solution to mitigate this race\\n * condition is to first reduce the spender's allowance to 0 and set the\\n * desired value afterwards:\\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\\n *\\n * Emits an {Approval} event.\\n */\\n function approve(address spender, uint256 amount) external returns (bool);\\n\\n /**\\n * @dev Moves `amount` tokens from `from` to `to` using the\\n * allowance mechanism. `amount` is then deducted from the caller's\\n * allowance.\\n *\\n * Returns a boolean value indicating whether the operation succeeded.\\n *\\n * Emits a {Transfer} event.\\n */\\n function transferFrom(address from, address to, uint256 amount) external returns (bool);\\n}\\n\",\"keccak256\":\"0x287b55befed2961a7eabd7d7b1b2839cbca8a5b80ef8dcbb25ed3d4c2002c305\",\"license\":\"MIT\"},\"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)\\n\\npragma solidity ^0.8.0;\\n\\nimport \\\"../IERC20.sol\\\";\\n\\n/**\\n * @dev Interface for the optional metadata functions from the ERC20 standard.\\n *\\n * _Available since v4.1._\\n */\\ninterface IERC20Metadata is IERC20 {\\n /**\\n * @dev Returns the name of the token.\\n */\\n function name() external view returns (string memory);\\n\\n /**\\n * @dev Returns the symbol of the token.\\n */\\n function symbol() external view returns (string memory);\\n\\n /**\\n * @dev Returns the decimals places of the token.\\n */\\n function decimals() external view returns (uint8);\\n}\\n\",\"keccak256\":\"0x8de418a5503946cabe331f35fe242d3201a73f67f77aaeb7110acb1f30423aca\",\"license\":\"MIT\"},\"@venusprotocol/governance-contracts/contracts/Governance/AccessControlledV8.sol\":{\"content\":\"// SPDX-License-Identifier: BSD-3-Clause\\npragma solidity 0.8.25;\\n\\nimport \\\"@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol\\\";\\nimport \\\"@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol\\\";\\n\\nimport \\\"./IAccessControlManagerV8.sol\\\";\\n\\n/**\\n * @title AccessControlledV8\\n * @author Venus\\n * @notice This contract is helper between access control manager and actual contract. This contract further inherited by other contract (using solidity 0.8.13)\\n * to integrate access controlled mechanism. It provides initialise methods and verifying access methods.\\n */\\nabstract contract AccessControlledV8 is Initializable, Ownable2StepUpgradeable {\\n /// @notice Access control manager contract\\n IAccessControlManagerV8 internal _accessControlManager;\\n\\n /**\\n * @dev This empty reserved space is put in place to allow future versions to add new\\n * variables without shifting down storage in the inheritance chain.\\n * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\\n */\\n uint256[49] private __gap;\\n\\n /// @notice Emitted when access control manager contract address is changed\\n event NewAccessControlManager(address oldAccessControlManager, address newAccessControlManager);\\n\\n /// @notice Thrown when the action is prohibited by AccessControlManager\\n error Unauthorized(address sender, address calledContract, string methodSignature);\\n\\n function __AccessControlled_init(address accessControlManager_) internal onlyInitializing {\\n __Ownable2Step_init();\\n __AccessControlled_init_unchained(accessControlManager_);\\n }\\n\\n function __AccessControlled_init_unchained(address accessControlManager_) internal onlyInitializing {\\n _setAccessControlManager(accessControlManager_);\\n }\\n\\n /**\\n * @notice Sets the address of AccessControlManager\\n * @dev Admin function to set address of AccessControlManager\\n * @param accessControlManager_ The new address of the AccessControlManager\\n * @custom:event Emits NewAccessControlManager event\\n * @custom:access Only Governance\\n */\\n function setAccessControlManager(address accessControlManager_) external onlyOwner {\\n _setAccessControlManager(accessControlManager_);\\n }\\n\\n /**\\n * @notice Returns the address of the access control manager contract\\n */\\n function accessControlManager() external view returns (IAccessControlManagerV8) {\\n return _accessControlManager;\\n }\\n\\n /**\\n * @dev Internal function to set address of AccessControlManager\\n * @param accessControlManager_ The new address of the AccessControlManager\\n */\\n function _setAccessControlManager(address accessControlManager_) internal {\\n require(address(accessControlManager_) != address(0), \\\"invalid acess control manager address\\\");\\n address oldAccessControlManager = address(_accessControlManager);\\n _accessControlManager = IAccessControlManagerV8(accessControlManager_);\\n emit NewAccessControlManager(oldAccessControlManager, accessControlManager_);\\n }\\n\\n /**\\n * @notice Reverts if the call is not allowed by AccessControlManager\\n * @param signature Method signature\\n */\\n function _checkAccessAllowed(string memory signature) internal view {\\n bool isAllowedToCall = _accessControlManager.isAllowedToCall(msg.sender, signature);\\n\\n if (!isAllowedToCall) {\\n revert Unauthorized(msg.sender, address(this), signature);\\n }\\n }\\n}\\n\",\"keccak256\":\"0xfe0fd441c84ac907cabc88db69ef04f6d7532d770c7e6a1dfe6e7d6305debb49\",\"license\":\"BSD-3-Clause\"},\"@venusprotocol/governance-contracts/contracts/Governance/IAccessControlManagerV8.sol\":{\"content\":\"// SPDX-License-Identifier: BSD-3-Clause\\npragma solidity ^0.8.25;\\n\\nimport \\\"@openzeppelin/contracts/access/IAccessControl.sol\\\";\\n\\n/**\\n * @title IAccessControlManagerV8\\n * @author Venus\\n * @notice Interface implemented by the `AccessControlManagerV8` contract.\\n */\\ninterface IAccessControlManagerV8 is IAccessControl {\\n function giveCallPermission(address contractAddress, string calldata functionSig, address accountToPermit) external;\\n\\n function revokeCallPermission(\\n address contractAddress,\\n string calldata functionSig,\\n address accountToRevoke\\n ) external;\\n\\n function isAllowedToCall(address account, string calldata functionSig) external view returns (bool);\\n\\n function hasPermission(\\n address account,\\n address contractAddress,\\n string calldata functionSig\\n ) external view returns (bool);\\n}\\n\",\"keccak256\":\"0xaa29b098440d0b3a131c5ecdf25ce548790c1b5ac7bf9b5c0264b6af6f7a1e0b\",\"license\":\"BSD-3-Clause\"},\"@venusprotocol/oracle/contracts/interfaces/IDeviationBoundedOracle.sol\":{\"content\":\"// SPDX-License-Identifier: BSD-3-Clause\\npragma solidity 0.8.25;\\n\\ninterface IDeviationBoundedOracle {\\n // --- Enums ---\\n\\n /// @notice Identifies whether a price bound is a minimum or maximum\\n enum PriceBoundType {\\n MIN,\\n MAX\\n }\\n\\n /// @notice Identifies which keeper action a single syncPriceBoundsAndProtections item performs\\n enum KeeperAction {\\n SetMinPrice,\\n SetMaxPrice,\\n ExitProtectionMode\\n }\\n\\n // --- Structs ---\\n\\n /// @notice Per-asset protection state tracking the min/max price window\\n struct MarketProtectionState {\\n /// @notice Lowest price observed in the current window (packed with maxPrice in one slot)\\n uint128 minPrice;\\n /// @notice Highest price observed in the current window\\n uint128 maxPrice;\\n /// @notice Whether protected price is currently being used\\n bool currentlyUsingProtectedPrice;\\n /// @notice Whether this market is whitelisted for bounded pricing\\n bool isBoundedPricingEnabled;\\n /// @notice Timestamp of the last protection trigger \\u2014 reset on every trigger\\n uint64 lastProtectionTriggeredAt;\\n /// @notice Minimum time protection stays active after last trigger\\n uint64 cooldownPeriod;\\n /// @notice The underlying asset address, used to verify initialization\\n address asset;\\n /// @notice Entry deviation threshold (mantissa, e.g. 0.1667e18 = 16.67%); packed with resetThreshold\\n uint128 triggerThreshold;\\n /// @notice Exit threshold (mantissa); window must converge below this for protection to be disabled\\n uint128 resetThreshold;\\n /// @notice Whether transient caching of the bounded (collateral, debt) pair is enabled for this asset\\n bool cachingEnabled;\\n }\\n\\n /// @notice One item in an syncPriceBoundsAndProtections payload\\n /// @dev `value` is interpreted per-action: the new bound price for SetMinPrice / SetMaxPrice, ignored for ExitProtectionMode\\n struct KeeperActionItem {\\n address asset;\\n KeeperAction action;\\n uint256 value;\\n }\\n\\n /// @notice One item in a setTokenConfigs payload\\n struct TokenConfigInput {\\n /// @notice The underlying asset address\\n address asset;\\n /// @notice Minimum time protection stays active after the last trigger (seconds)\\n uint64 cooldownPeriod;\\n /// @notice Entry deviation threshold (mantissa). Must be between 5% and 50%.\\n uint256 triggerThreshold;\\n /// @notice Exit deviation threshold (mantissa). Must be non-zero and below triggerThreshold.\\n uint256 resetThreshold;\\n /// @notice Whether to enable bounded pricing immediately upon initialization\\n bool enableBoundedPricing;\\n /// @notice Whether transient caching of the bounded (collateral, debt) pair is enabled for this asset\\n bool enableCaching;\\n }\\n\\n // --- Events ---\\n\\n /// @notice Emitted when protection is initialized for an asset\\n event ProtectionInitialized(\\n address indexed asset,\\n uint128 minPrice,\\n uint128 maxPrice,\\n uint64 cooldownPeriod,\\n uint256 triggerThreshold\\n );\\n\\n /// @notice Emitted when protection mode is triggered for an asset\\n event ProtectionTriggered(address indexed asset, uint256 spotPrice, uint128 minPrice, uint128 maxPrice);\\n\\n /// @notice Emitted when protection mode is disabled for an asset\\n event ProtectionModeExited(address indexed asset);\\n\\n /// @notice Emitted when the keeper updates the minimum price for an asset\\n event MinPriceUpdated(address indexed asset, uint128 oldMin, uint128 newMin);\\n\\n /// @notice Emitted when the keeper updates the maximum price for an asset\\n event MaxPriceUpdated(address indexed asset, uint128 oldMax, uint128 newMax);\\n\\n /// @notice Emitted when the entry threshold is updated for an asset\\n event TriggerThresholdSet(address indexed asset, uint256 oldThreshold, uint256 newThreshold);\\n\\n /// @notice Emitted when the exit threshold is updated for an asset\\n event ResetThresholdSet(address indexed asset, uint256 oldExitThreshold, uint256 newExitThreshold);\\n\\n /// @notice Emitted when the cooldown period is updated for an asset\\n event CooldownPeriodSet(address indexed asset, uint64 oldCooldown, uint64 newCooldown);\\n\\n /// @notice Emitted when an asset's whitelist status changes\\n event BoundedPricingWhitelistUpdated(address indexed asset, bool whitelisted);\\n\\n /// @notice Emitted when the per-asset transient caching flag is toggled\\n event CachingEnabledUpdated(address indexed asset, bool oldEnabled, bool newEnabled);\\n\\n // --- Errors ---\\n\\n /// @notice Thrown when trying to use or update protection for an asset that has not been initialized\\n error MarketNotInitialized(address asset);\\n\\n /// @notice Thrown when trying to initialize an already initialized market\\n error MarketAlreadyInitialized(address asset);\\n\\n /// @notice Thrown when trying to disable protection that is not active\\n error ProtectedPriceInactive(address asset);\\n\\n /// @notice Thrown when trying to disable protection before cooldown has elapsed\\n error CooldownNotElapsed(address asset, uint64 lastProtectionTriggeredAt, uint64 cooldownPeriod);\\n\\n /// @notice Thrown when trying to disable protection before price range has converged\\n error PriceRangeNotConverged(address asset, uint256 currentRangeRatio, uint256 resetThreshold);\\n\\n /// @notice Thrown when keeper tries to set minPrice above current spot\\n error InvalidMinPrice(address asset, uint128 newMin, uint256 currentSpot);\\n\\n /// @notice Thrown when keeper tries to set maxPrice below current spot\\n error InvalidMaxPrice(address asset, uint128 newMax, uint256 currentSpot);\\n\\n /// @notice Thrown when threshold is set below the minimum allowed value\\n error ThresholdBelowMinimum(uint256 threshold, uint256 minimum);\\n\\n /// @notice Thrown when threshold is set above the maximum allowed value\\n error ThresholdAboveMaximum(uint256 threshold, uint256 maximum);\\n\\n /// @notice Thrown when a price exceeds uint128 max\\n error PriceExceedsUint128(uint256 price);\\n\\n /// @notice Thrown when a zero price is provided where a non-zero price is required\\n error ZeroPriceNotAllowed();\\n\\n /// @notice Thrown when trying to initialize protection for VAI\\n error VAINotAllowed();\\n\\n /// @notice Thrown when trying to disable bounded pricing for an asset while protection is active\\n error ProtectedPriceActive(address asset);\\n\\n /// @notice Thrown when the lengths of the arrays are not equal\\n error InvalidArrayLength();\\n\\n /// @notice Thrown when the exit threshold is set at or above the trigger threshold\\n error InvalidResetThreshold(uint256 resetThreshold);\\n\\n /// @notice Thrown when an syncPriceBoundsAndProtections item carries an unsupported action enum value\\n error InvalidKeeperAction(uint8 action);\\n\\n // --- Non-view price functions (update window + trigger protection) ---\\n\\n /**\\n * @notice Gets the bounded collateral price for a given vToken, updating protection state\\n * @param vToken vToken address\\n * @return collateralPrice The bounded collateral price\\n * @custom:error PriceExceedsUint128 if the spot price overflows uint128\\n * @custom:event MinPriceUpdated if a new window minimum is recorded\\n * @custom:event MaxPriceUpdated if a new window maximum is recorded\\n * @custom:event ProtectionTriggered if the spot price deviates beyond the threshold\\n */\\n function getBoundedCollateralPrice(address vToken) external returns (uint256 collateralPrice);\\n\\n /**\\n * @notice Gets the bounded debt price for a given vToken, updating protection state\\n * @param vToken vToken address\\n * @return debtPrice The bounded debt price\\n * @custom:error PriceExceedsUint128 if the spot price overflows uint128\\n * @custom:event MinPriceUpdated if a new window minimum is recorded\\n * @custom:event MaxPriceUpdated if a new window maximum is recorded\\n * @custom:event ProtectionTriggered if the spot price deviates beyond the threshold\\n */\\n function getBoundedDebtPrice(address vToken) external returns (uint256 debtPrice);\\n\\n /**\\n * @notice Gets both the bounded collateral and debt prices for a given vToken, updating protection state\\n * @param vToken vToken address\\n * @return collateralPrice The bounded collateral price\\n * @return debtPrice The bounded debt price\\n * @custom:error PriceExceedsUint128 if the spot price overflows uint128\\n * @custom:event MinPriceUpdated if a new window minimum is recorded\\n * @custom:event MaxPriceUpdated if a new window maximum is recorded\\n * @custom:event ProtectionTriggered if the spot price deviates beyond the threshold\\n */\\n function getBoundedPrices(address vToken) external returns (uint256 collateralPrice, uint256 debtPrice);\\n\\n // --- State update (call before view price reads to populate transient cache) ---\\n\\n /**\\n * @notice Updates the protection state for a given vToken, caching the resolved collateral and debt prices\\n * @dev Called by PolicyFacet before liquidity calculations so subsequent view price\\n * reads in the same transaction are served from transient storage. The transient\\n * cache is only populated when the asset's `cachingEnabled` flag is `true`.\\n * @param vToken vToken address\\n * @custom:error PriceExceedsUint128 if the spot price overflows uint128\\n * @custom:event MinPriceUpdated if a new window minimum is recorded\\n * @custom:event MaxPriceUpdated if a new window maximum is recorded\\n * @custom:event ProtectionTriggered if the spot price deviates beyond the threshold\\n */\\n function updateProtectionState(address vToken) external;\\n\\n // --- View price functions (read stored/cached state only) ---\\n\\n /**\\n * @notice Gets the bounded collateral price for a given vToken (view variant)\\n * @dev Reads from transient cache first when the asset's `cachingEnabled` flag is `true`;\\n * falls back to ResilientOracle on cache miss or when caching is disabled.\\n * @param vToken vToken address\\n * @return price The bounded collateral price\\n * @custom:error PriceExceedsUint128 if the spot price overflows uint128 (cache miss path only)\\n */\\n function getBoundedCollateralPriceView(address vToken) external view returns (uint256 price);\\n\\n /**\\n * @notice Gets the bounded debt price for a given vToken (view variant)\\n * @dev Reads from transient cache first when the asset's `cachingEnabled` flag is `true`;\\n * falls back to ResilientOracle on cache miss or when caching is disabled.\\n * @param vToken vToken address\\n * @return price The bounded debt price\\n * @custom:error PriceExceedsUint128 if the spot price overflows uint128 (cache miss path only)\\n */\\n function getBoundedDebtPriceView(address vToken) external view returns (uint256 price);\\n\\n /**\\n * @notice Gets both the bounded collateral and debt prices for a given vToken (view variant)\\n * @dev Reads from transient cache first when the asset's `cachingEnabled` flag is `true`;\\n * falls back to ResilientOracle on cache miss or when caching is disabled.\\n * @param vToken vToken address\\n * @return collateralPrice The bounded collateral price\\n * @return debtPrice The bounded debt price\\n * @custom:error PriceExceedsUint128 if the spot price overflows uint128 (cache miss path only)\\n */\\n function getBoundedPricesView(address vToken) external view returns (uint256 collateralPrice, uint256 debtPrice);\\n\\n // --- Keeper functions ---\\n\\n /**\\n * @notice Updates the minimum price in the rolling window for a given asset\\n * @param asset The underlying asset address\\n * @param newMin The new minimum price; must be at or below the current spot and below maxPrice\\n * @custom:access Only authorized keeper addresses\\n * @custom:error ZeroPriceNotAllowed if newMin is zero\\n * @custom:error MarketNotInitialized if the asset has not been initialized\\n * @custom:error InvalidMinPrice if newMin exceeds the current spot or is at or above maxPrice\\n * @custom:event MinPriceUpdated\\n */\\n function updateMinPrice(address asset, uint128 newMin) external;\\n\\n /**\\n * @notice Updates the maximum price in the rolling window for a given asset\\n * @param asset The underlying asset address\\n * @param newMax The new maximum price; must be at or above the current spot and above minPrice\\n * @custom:access Only authorized keeper addresses\\n * @custom:error ZeroPriceNotAllowed if newMax is zero\\n * @custom:error MarketNotInitialized if the asset has not been initialized\\n * @custom:error InvalidMaxPrice if newMax is below the current spot or is at or below minPrice\\n * @custom:event MaxPriceUpdated\\n */\\n function updateMaxPrice(address asset, uint128 newMax) external;\\n\\n /**\\n * @notice Exits protection mode for a given asset once conditions are met\\n * @param asset The underlying asset address\\n * @custom:access Only authorized monitor/keeper addresses\\n * @custom:error ProtectedPriceInactive if protection is not currently active\\n * @custom:error CooldownNotElapsed if the cooldown period has not elapsed since the last trigger\\n * @custom:error PriceRangeNotConverged if the window range is still above the exit threshold\\n * @custom:event ProtectionModeExited\\n */\\n function exitProtectionMode(address asset) external;\\n\\n /**\\n * @notice Dispatches a batch of keeper-only actions (set min, set max, or exit protection) under a single ACM check\\n * @dev Each item is processed in array order; any item revert rolls back the whole batch.\\n * `value` is interpreted as the new bound price for SetMinPrice / SetMaxPrice and ignored for ExitProtectionMode.\\n * Empty `actions` is a no-op success.\\n * @param actions The list of keeper actions to apply\\n * @custom:access Only authorized keeper addresses\\n * @custom:error InvalidKeeperAction if an item carries an unsupported action enum value\\n * @custom:error PriceExceedsUint128 if a SetMin/SetMax item value overflows uint128\\n * @custom:error ZeroPriceNotAllowed if a SetMin/SetMax item value is zero\\n * @custom:error MarketNotInitialized if any referenced asset has not been initialized\\n * @custom:error InvalidMinPrice if a SetMinPrice item violates the spot/maxPrice constraints\\n * @custom:error InvalidMaxPrice if a SetMaxPrice item violates the spot/minPrice constraints\\n * @custom:error ProtectedPriceInactive if an ExitProtectionMode item targets an asset whose protection is not active\\n * @custom:error CooldownNotElapsed if an ExitProtectionMode item is submitted before cooldown elapsed\\n * @custom:error PriceRangeNotConverged if an ExitProtectionMode item is submitted before window convergence\\n * @custom:event MinPriceUpdated, MaxPriceUpdated, ProtectionModeExited\\n */\\n function syncPriceBoundsAndProtections(KeeperActionItem[] calldata actions) external;\\n\\n // --- Admin functions (governance-gated) ---\\n\\n /**\\n * @notice Initializes protection parameters for a new asset\\n * @dev Seeds the initial min/max window from the current ResilientOracle spot price,\\n * confirming the oracle is live for this asset before it is listed.\\n * @param tokenConfig_ Token config input for the asset\\n * @custom:access Only Governance\\n * @custom:error ZeroAddressNotAllowed if asset is the zero address\\n * @custom:error ZeroValueNotAllowed if cooldownPeriod, triggerThreshold, or resetThreshold is zero\\n * @custom:error MarketAlreadyInitialized if the asset has already been initialized\\n * @custom:error ThresholdBelowMinimum if triggerThreshold is below 5%\\n * @custom:error ThresholdAboveMaximum if triggerThreshold is above 50%\\n * @custom:error InvalidResetThreshold if resetThreshold is at or above triggerThreshold\\n * @custom:error VAINotAllowed if asset is the VAI token\\n * @custom:error PriceExceedsUint128 if the spot price overflows uint128\\n * @custom:event ProtectionInitialized\\n * @custom:event BoundedPricingWhitelistUpdated\\n */\\n function setTokenConfig(TokenConfigInput calldata tokenConfig_) external;\\n\\n /**\\n * @notice Batch-initializes protection parameters for multiple assets in a single transaction\\n * @param tokenConfigs_ Array of token config inputs, one per asset\\n * @custom:access Only Governance\\n * @custom:error InvalidArrayLength if the input array is empty\\n * @custom:error ZeroAddressNotAllowed if any asset is the zero address\\n * @custom:error ZeroValueNotAllowed if any cooldownPeriod, triggerThreshold, or resetThreshold is zero\\n * @custom:error MarketAlreadyInitialized if any asset has already been initialized\\n * @custom:error ThresholdBelowMinimum if any triggerThreshold is below 5%\\n * @custom:error ThresholdAboveMaximum if any triggerThreshold is above 50%\\n * @custom:error InvalidResetThreshold if any resetThreshold is at or above its triggerThreshold\\n * @custom:error VAINotAllowed if any asset is the VAI token\\n * @custom:error PriceExceedsUint128 if the spot price for any asset overflows uint128\\n * @custom:event ProtectionInitialized for each asset\\n * @custom:event BoundedPricingWhitelistUpdated for each asset\\n */\\n function setTokenConfigs(TokenConfigInput[] calldata tokenConfigs_) external;\\n\\n /**\\n * @notice Sets the cooldown period for an asset\\n * @param asset The underlying asset address\\n * @param newCooldown The new cooldown period in seconds; must be non-zero\\n * @custom:access Only Governance\\n * @custom:error MarketNotInitialized if the asset has not been initialized\\n * @custom:event CooldownPeriodSet\\n */\\n function setCooldownPeriod(address asset, uint64 newCooldown) external;\\n\\n /**\\n * @notice Sets the trigger and reset thresholds for an asset\\n * @param asset The underlying asset address\\n * @param newTriggerThreshold The new trigger threshold (mantissa). Must be between 5% and 50% and above the reset threshold.\\n * @param newResetThreshold The new reset threshold (mantissa). Must be non-zero and below the trigger threshold.\\n * @custom:access Only Governance\\n * @custom:error MarketNotInitialized if the asset has not been initialized\\n * @custom:error ThresholdBelowMinimum if newTriggerThreshold is below 5%\\n * @custom:error ThresholdAboveMaximum if newTriggerThreshold is above 50%\\n * @custom:error InvalidResetThreshold if newResetThreshold is at or above newTriggerThreshold\\n * @custom:event TriggerThresholdSet if the trigger threshold changed\\n * @custom:event ResetThresholdSet if the reset threshold changed\\n */\\n function setThresholds(address asset, uint256 newTriggerThreshold, uint256 newResetThreshold) external;\\n\\n /**\\n * @notice Sets whether bounded pricing is enabled for an asset\\n * @param asset The underlying asset address\\n * @param enabled Whether bounded pricing should be enabled for the asset\\n * @custom:access Only Governance\\n * @custom:error MarketNotInitialized if the asset has not been initialized\\n * @custom:error ProtectedPriceActive if trying to disable an asset while protection is active\\n * @custom:event BoundedPricingWhitelistUpdated\\n */\\n function setAssetBoundedPricingEnabled(address asset, bool enabled) external;\\n\\n /**\\n * @notice Toggles transient caching of the bounded (collateral, debt) pair for an asset\\n * @dev When disabled, each view/non-view price call recomputes bounded prices from the\\n * live spot instead of reading or writing the transient slots. The initial value is\\n * set via the `enableCaching` argument of `setTokenConfig`.\\n * @param asset The underlying asset address\\n * @param enabled Whether transient caching is enabled for this asset\\n * @custom:access Only Governance\\n * @custom:error MarketNotInitialized if the asset has not been initialized\\n * @custom:event CachingEnabledUpdated\\n */\\n function setCachingEnabled(address asset, bool enabled) external;\\n\\n // --- View helpers ---\\n\\n /**\\n * @notice Returns the full protection state for an asset\\n * @param asset The underlying asset address\\n * @return minPrice Lowest price observed in the current window\\n * @return maxPrice Highest price observed in the current window\\n * @return currentlyUsingProtectedPrice Whether protected price is currently active\\n * @return isBoundedPricingEnabled Whether the asset is whitelisted for bounded pricing\\n * @return lastProtectionTriggeredAt Timestamp of the last protection trigger\\n * @return cooldownPeriod Minimum time protection stays active after last trigger\\n * @return assetAddr The underlying asset address stored in the struct\\n * @return triggerThreshold Entry deviation threshold (mantissa) that activates protection\\n * @return resetThreshold Exit deviation threshold (mantissa) below which protection can be disabled\\n * @return cachingEnabled Whether transient caching of the bounded pair is enabled for the asset\\n */\\n function assetProtectionConfig(\\n address asset\\n )\\n external\\n view\\n returns (\\n uint128 minPrice,\\n uint128 maxPrice,\\n bool currentlyUsingProtectedPrice,\\n bool isBoundedPricingEnabled,\\n uint64 lastProtectionTriggeredAt,\\n uint64 cooldownPeriod,\\n address assetAddr,\\n uint128 triggerThreshold,\\n uint128 resetThreshold,\\n bool cachingEnabled\\n );\\n\\n /**\\n * @notice Checks if an asset is whitelisted for bounded pricing\\n * @param asset The underlying asset address\\n * @return True if the asset is whitelisted\\n */\\n function isBoundedPricingEnabled(address asset) external view returns (bool);\\n\\n /**\\n * @notice Checks if the asset is currently using the protected (bounded) price\\n * @param asset The underlying asset address\\n * @return True if the asset is currently using the protected price instead of spot\\n */\\n function currentlyUsingProtectedPrice(address asset) external view returns (bool);\\n\\n /**\\n * @notice Checks if protection can be exited for a given asset\\n * @param asset The underlying asset address\\n * @return True if both the cooldown has elapsed and the price range has converged below the exit threshold\\n */\\n function canExitProtection(address asset) external view returns (bool);\\n\\n /**\\n * @notice Returns the initialized asset at the given index (auto-generated array getter)\\n * @param index Array index\\n * @return The asset address at the given index\\n */\\n function allAssets(uint256 index) external view returns (address);\\n\\n /**\\n * @notice Returns all currently whitelisted asset addresses\\n * @return result Array of whitelisted asset addresses\\n */\\n function getAllBoundedPricingEnabledAssets() external view returns (address[] memory result);\\n\\n /**\\n * @notice Returns all asset addresses that have ever been initialized\\n * @return Array of all initialized asset addresses\\n */\\n function getInitializedAssets() external view returns (address[] memory);\\n\\n /**\\n * @notice Batch-checks which assets' on-chain min/max have drifted beyond the keeper deadband\\n * @param assets Array of asset addresses to check\\n * @param proposedMins Keeper's proposed window minimum prices\\n * @param proposedMaxs Keeper's proposed window maximum prices\\n * @return needsMinUpdate Whether minPrice drift exceeds the deadband for each asset\\n * @return needsMaxUpdate Whether maxPrice drift exceeds the deadband for each asset\\n * @custom:error InvalidArrayLength if the input array lengths do not match\\n */\\n function checkAndGetWindowDrift(\\n address[] calldata assets,\\n uint128[] calldata proposedMins,\\n uint128[] calldata proposedMaxs\\n ) external view returns (bool[] memory needsMinUpdate, bool[] memory needsMaxUpdate);\\n}\\n\",\"keccak256\":\"0xe223d88c17c0d752fdb33fa223b63ce124a798512f3d69f3800e9508e7dff931\",\"license\":\"BSD-3-Clause\"},\"@venusprotocol/oracle/contracts/interfaces/OracleInterface.sol\":{\"content\":\"// SPDX-License-Identifier: BSD-3-Clause\\npragma solidity ^0.8.25;\\n\\ninterface OracleInterface {\\n function getPrice(address asset) external view returns (uint256);\\n}\\n\\ninterface ResilientOracleInterface is OracleInterface {\\n function updatePrice(address vToken) external;\\n\\n function updateAssetPrice(address asset) external;\\n\\n function getUnderlyingPrice(address vToken) external view returns (uint256);\\n}\\n\\ninterface BoundValidatorInterface {\\n function validatePriceWithAnchorPrice(\\n address asset,\\n uint256 reporterPrice,\\n uint256 anchorPrice\\n ) external view returns (bool);\\n}\\n\",\"keccak256\":\"0xd3bbb7c9eef19e8f467342df6034ef95399a00964646fb8c82b438968ae3a8c0\",\"license\":\"BSD-3-Clause\"},\"@venusprotocol/protocol-reserve/contracts/Interfaces/IProtocolShareReserve.sol\":{\"content\":\"// SPDX-License-Identifier: BSD-3-Clause\\npragma solidity ^0.8.25;\\n\\ninterface IProtocolShareReserve {\\n /// @notice it represents the type of vToken income\\n enum IncomeType {\\n SPREAD,\\n LIQUIDATION,\\n ERC4626_WRAPPER_REWARDS,\\n FLASHLOAN\\n }\\n\\n function updateAssetsState(\\n address comptroller,\\n address asset,\\n IncomeType incomeType\\n ) external;\\n}\\n\",\"keccak256\":\"0x0f341bbef8f12885d79b7acaad7aaf11b84809e8f6b059ef4efc011c8f6c39a5\",\"license\":\"BSD-3-Clause\"},\"@venusprotocol/solidity-utilities/contracts/TimeManagerV8.sol\":{\"content\":\"// SPDX-License-Identifier: BSD-3-Clause\\npragma solidity 0.8.25;\\n\\nimport { SECONDS_PER_YEAR } from \\\"./constants.sol\\\";\\n\\nabstract contract TimeManagerV8 {\\n /// @notice Stores blocksPerYear if isTimeBased is true else secondsPerYear is stored\\n /// @custom:oz-upgrades-unsafe-allow state-variable-immutable\\n uint256 public immutable blocksOrSecondsPerYear;\\n\\n /// @notice Acknowledges if a contract is time based or not\\n /// @custom:oz-upgrades-unsafe-allow state-variable-immutable\\n bool public immutable isTimeBased;\\n\\n /// @notice Stores the current block timestamp or block number depending on isTimeBased\\n /// @custom:oz-upgrades-unsafe-allow state-variable-immutable\\n function() view returns (uint256) private immutable _getCurrentSlot;\\n\\n /**\\n * @dev This empty reserved space is put in place to allow future versions to add new\\n * variables without shifting down storage in the inheritance chain\\n * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\\n */\\n uint256[48] private __gap;\\n\\n /// @notice Thrown when blocks per year is invalid\\n error InvalidBlocksPerYear();\\n\\n /// @notice Thrown when time based but blocks per year is provided\\n error InvalidTimeBasedConfiguration();\\n\\n /**\\n * @param timeBased_ A boolean indicating whether the contract is based on time or block\\n * If timeBased is true than blocksPerYear_ param is ignored as blocksOrSecondsPerYear is set to SECONDS_PER_YEAR\\n * @param blocksPerYear_ The number of blocks per year\\n * @custom:error InvalidBlocksPerYear is thrown if blocksPerYear entered is zero and timeBased is false\\n * @custom:error InvalidTimeBasedConfiguration is thrown if blocksPerYear entered is non zero and timeBased is true\\n * @custom:oz-upgrades-unsafe-allow constructor\\n */\\n constructor(bool timeBased_, uint256 blocksPerYear_) {\\n if (!timeBased_ && blocksPerYear_ == 0) {\\n revert InvalidBlocksPerYear();\\n }\\n\\n if (timeBased_ && blocksPerYear_ != 0) {\\n revert InvalidTimeBasedConfiguration();\\n }\\n\\n isTimeBased = timeBased_;\\n blocksOrSecondsPerYear = timeBased_ ? SECONDS_PER_YEAR : blocksPerYear_;\\n _getCurrentSlot = timeBased_ ? _getBlockTimestamp : _getBlockNumber;\\n }\\n\\n /**\\n * @dev Function to simply retrieve block number or block timestamp\\n * @return Current block number or block timestamp\\n */\\n function getBlockNumberOrTimestamp() public view virtual returns (uint256) {\\n return _getCurrentSlot();\\n }\\n\\n /**\\n * @dev Returns the current timestamp in seconds\\n * @return The current timestamp\\n */\\n function _getBlockTimestamp() private view returns (uint256) {\\n return block.timestamp;\\n }\\n\\n /**\\n * @dev Returns the current block number\\n * @return The current block number\\n */\\n function _getBlockNumber() private view returns (uint256) {\\n return block.number;\\n }\\n}\\n\",\"keccak256\":\"0x57a2bbb9b8e02b1c0a5c0e305fef1328a22db56c3d4b148c362010a6e767243c\",\"license\":\"BSD-3-Clause\"},\"@venusprotocol/solidity-utilities/contracts/constants.sol\":{\"content\":\"// SPDX-License-Identifier: BSD-3-Clause\\npragma solidity ^0.8.25;\\n\\n/// @dev Base unit for computations, usually used in scaling (multiplications, divisions)\\nuint256 constant EXP_SCALE = 1e18;\\n\\n/// @dev A unit (literal one) in EXP_SCALE, usually used in additions/subtractions\\nuint256 constant MANTISSA_ONE = EXP_SCALE;\\n\\n/// @dev The approximate number of seconds per year\\nuint256 constant SECONDS_PER_YEAR = 31_536_000;\\n\",\"keccak256\":\"0x14de93ead464da249af31bea0e3bcfb62ec693bea3475fb4d90f055ac81dc5eb\",\"license\":\"BSD-3-Clause\"},\"@venusprotocol/venus-protocol/contracts/Tokens/Prime/Interfaces/IPrime.sol\":{\"content\":\"// SPDX-License-Identifier: BSD-3-Clause\\npragma solidity ^0.8.25;\\n\\nimport { PrimeStorageV1 } from \\\"../PrimeStorage.sol\\\";\\n\\n/**\\n * @title IPrime\\n * @author Venus\\n * @notice Interface for Prime Token\\n */\\ninterface IPrime {\\n struct APRInfo {\\n // supply APR of the user in BPS\\n uint256 supplyAPR;\\n // borrow APR of the user in BPS\\n uint256 borrowAPR;\\n // total score of the market\\n uint256 totalScore;\\n // score of the user\\n uint256 userScore;\\n // capped XVS balance of the user\\n uint256 xvsBalanceForScore;\\n // capital of the user\\n uint256 capital;\\n // capped supply of the user\\n uint256 cappedSupply;\\n // capped borrow of the user\\n uint256 cappedBorrow;\\n // capped supply of user in USD\\n uint256 supplyCapUSD;\\n // capped borrow of user in USD\\n uint256 borrowCapUSD;\\n }\\n\\n struct Capital {\\n // capital of the user\\n uint256 capital;\\n // capped supply of the user\\n uint256 cappedSupply;\\n // capped borrow of the user\\n uint256 cappedBorrow;\\n // capped supply of user in USD\\n uint256 supplyCapUSD;\\n // capped borrow of user in USD\\n uint256 borrowCapUSD;\\n }\\n\\n /**\\n * @notice Returns boosted pending interest accrued for a user for all markets\\n * @param user the account for which to get the accrued interests\\n * @return pendingRewards the number of underlying tokens accrued by the user for all markets\\n */\\n function getPendingRewards(address user) external returns (PrimeStorageV1.PendingReward[] memory pendingRewards);\\n\\n /**\\n * @notice Update total score of multiple users and market\\n * @param users accounts for which we need to update score\\n */\\n function updateScores(address[] memory users) external;\\n\\n /**\\n * @notice Update value of alpha\\n * @param _alphaNumerator numerator of alpha. If alpha is 0.5 then numerator is 1\\n * @param _alphaDenominator denominator of alpha. If alpha is 0.5 then denominator is 2\\n */\\n function updateAlpha(uint128 _alphaNumerator, uint128 _alphaDenominator) external;\\n\\n /**\\n * @notice Update multipliers for a market\\n * @param market address of the market vToken\\n * @param supplyMultiplier new supply multiplier for the market, scaled by 1e18\\n * @param borrowMultiplier new borrow multiplier for the market, scaled by 1e18\\n */\\n function updateMultipliers(address market, uint256 supplyMultiplier, uint256 borrowMultiplier) external;\\n\\n /**\\n * @notice Add a market to prime program\\n * @param comptroller address of the comptroller\\n * @param market address of the market vToken\\n * @param supplyMultiplier the multiplier for supply cap. It should be converted to 1e18\\n * @param borrowMultiplier the multiplier for borrow cap. It should be converted to 1e18\\n */\\n function addMarket(\\n address comptroller,\\n address market,\\n uint256 supplyMultiplier,\\n uint256 borrowMultiplier\\n ) external;\\n\\n /**\\n * @notice Set limits for total tokens that can be minted\\n * @param _irrevocableLimit total number of irrevocable tokens that can be minted\\n * @param _revocableLimit total number of revocable tokens that can be minted\\n */\\n function setLimit(uint256 _irrevocableLimit, uint256 _revocableLimit) external;\\n\\n /**\\n * @notice Directly issue prime tokens to users\\n * @param isIrrevocable are the tokens being issued\\n * @param users list of address to issue tokens to\\n */\\n function issue(bool isIrrevocable, address[] calldata users) external;\\n\\n /**\\n * @notice Executed by XVSVault whenever user's XVSVault balance changes\\n * @param user the account address whose balance was updated\\n */\\n function xvsUpdated(address user) external;\\n\\n /**\\n * @notice accrues interest and updates score for an user for a specific market\\n * @param user the account address for which to accrue interest and update score\\n * @param market the market for which to accrue interest and update score\\n */\\n function accrueInterestAndUpdateScore(address user, address market) external;\\n\\n /**\\n * @notice For claiming prime token when staking period is completed\\n */\\n function claim() external;\\n\\n /**\\n * @notice For burning any prime token\\n * @param user the account address for which the prime token will be burned\\n */\\n function burn(address user) external;\\n\\n /**\\n * @notice To pause or unpause claiming of interest\\n */\\n function togglePause() external;\\n\\n /**\\n * @notice For user to claim boosted yield\\n * @param vToken the market for which claim the accrued interest\\n * @return amount the amount of tokens transferred to the user\\n */\\n function claimInterest(address vToken) external returns (uint256);\\n\\n /**\\n * @notice For user to claim boosted yield\\n * @param vToken the market for which claim the accrued interest\\n * @param user the user for which to claim the accrued interest\\n * @return amount the amount of tokens transferred to the user\\n */\\n function claimInterest(address vToken, address user) external returns (uint256);\\n\\n /**\\n * @notice Distributes income from market since last distribution\\n * @param vToken the market for which to distribute the income\\n */\\n function accrueInterest(address vToken) external;\\n\\n /**\\n * @notice Returns boosted interest accrued for a user\\n * @param vToken the market for which to fetch the accrued interest\\n * @param user the account for which to get the accrued interest\\n * @return interestAccrued the number of underlying tokens accrued by the user since the last accrual\\n */\\n function getInterestAccrued(address vToken, address user) external returns (uint256);\\n\\n /**\\n * @notice Retrieves an array of all available markets\\n * @return an array of addresses representing all available markets\\n */\\n function getAllMarkets() external view returns (address[] memory);\\n\\n /**\\n * @notice fetch the numbers of seconds remaining for staking period to complete\\n * @param user the account address for which we are checking the remaining time\\n * @return timeRemaining the number of seconds the user needs to wait to claim prime token\\n */\\n function claimTimeRemaining(address user) external view returns (uint256);\\n\\n /**\\n * @notice Returns supply and borrow APR for user for a given market\\n * @param market the market for which to fetch the APR\\n * @param user the account for which to get the APR\\n * @return aprInfo APR information for the user for the given market\\n */\\n function calculateAPR(address market, address user) external view returns (APRInfo memory aprInfo);\\n\\n /**\\n * @notice Returns supply and borrow APR for estimated supply, borrow and XVS staked\\n * @param market the market for which to fetch the APR\\n * @param user the account for which to get the APR\\n * @param borrow hypothetical borrow amount\\n * @param supply hypothetical supply amount\\n * @param xvsStaked hypothetical staked XVS amount\\n * @return aprInfo APR information for the user for the given market\\n */\\n function estimateAPR(\\n address market,\\n address user,\\n uint256 borrow,\\n uint256 supply,\\n uint256 xvsStaked\\n ) external view returns (APRInfo memory aprInfo);\\n\\n /**\\n * @notice the total income that's going to be distributed in a year to prime token holders\\n * @param vToken the market for which to fetch the total income that's going to distributed in a year\\n * @return amount the total income\\n */\\n function incomeDistributionYearly(address vToken) external view returns (uint256 amount);\\n\\n /**\\n * @notice Returns if user is a prime holder\\n * @return isPrimeHolder true if user is a prime holder\\n */\\n function isUserPrimeHolder(address user) external view returns (bool);\\n\\n /**\\n * @notice Set the limit for the loops can iterate to avoid the DOS\\n * @param loopsLimit Number of loops limit\\n */\\n function setMaxLoopsLimit(uint256 loopsLimit) external;\\n\\n /**\\n * @notice Update staked at timestamp for multiple users\\n * @param users accounts for which we need to update staked at timestamp\\n * @param timestamps new staked at timestamp for the users\\n */\\n function setStakedAt(address[] calldata users, uint256[] calldata timestamps) external;\\n}\\n\",\"keccak256\":\"0xd112f60183416ad796093b4a83a80ddc1b8b655965f02ae55ca82e2a0c68f97d\",\"license\":\"BSD-3-Clause\"},\"@venusprotocol/venus-protocol/contracts/Tokens/Prime/PrimeStorage.sol\":{\"content\":\"// SPDX-License-Identifier: BSD-3-Clause\\npragma solidity 0.8.25;\\n\\nimport { ResilientOracleInterface } from \\\"@venusprotocol/oracle/contracts/interfaces/OracleInterface.sol\\\";\\n\\n/**\\n * @title PrimeStorageV1\\n * @author Venus\\n * @notice Storage for Prime Token\\n */\\ncontract PrimeStorageV1 {\\n struct Token {\\n bool exists;\\n bool isIrrevocable;\\n }\\n\\n struct Market {\\n uint256 supplyMultiplier;\\n uint256 borrowMultiplier;\\n uint256 rewardIndex;\\n uint256 sumOfMembersScore;\\n bool exists;\\n }\\n\\n struct Interest {\\n uint256 accrued;\\n uint256 score;\\n uint256 rewardIndex;\\n }\\n\\n struct PendingReward {\\n address vToken;\\n address rewardToken;\\n uint256 amount;\\n }\\n\\n /// @notice Base unit for computations, usually used in scaling (multiplications, divisions)\\n uint256 internal constant EXP_SCALE = 1e18;\\n\\n /// @notice maximum BPS = 100%\\n uint256 internal constant MAXIMUM_BPS = 1e4;\\n\\n /// @notice Mapping to get prime token's metadata\\n mapping(address => Token) public tokens;\\n\\n /// @notice Tracks total irrevocable tokens minted\\n uint256 public totalIrrevocable;\\n\\n /// @notice Tracks total revocable tokens minted\\n uint256 public totalRevocable;\\n\\n /// @notice Indicates maximum revocable tokens that can be minted\\n uint256 public revocableLimit;\\n\\n /// @notice Indicates maximum irrevocable tokens that can be minted\\n uint256 public irrevocableLimit;\\n\\n /// @notice Tracks when prime token eligible users started staking for claiming prime token\\n mapping(address => uint256) public stakedAt;\\n\\n /// @notice vToken to market configuration\\n mapping(address => Market) public markets;\\n\\n /// @notice vToken to user to user index\\n mapping(address => mapping(address => Interest)) public interests;\\n\\n /// @notice A list of boosted markets\\n address[] internal _allMarkets;\\n\\n /// @notice numerator of alpha. Ex: if alpha is 0.5 then this will be 1\\n uint128 public alphaNumerator;\\n\\n /// @notice denominator of alpha. Ex: if alpha is 0.5 then this will be 2\\n uint128 public alphaDenominator;\\n\\n /// @notice address of XVS vault\\n address public xvsVault;\\n\\n /// @notice address of XVS vault reward token\\n address public xvsVaultRewardToken;\\n\\n /// @notice address of XVS vault pool id\\n uint256 public xvsVaultPoolId;\\n\\n /// @notice mapping to check if a account's score was updated in the round\\n mapping(uint256 => mapping(address => bool)) public isScoreUpdated;\\n\\n /// @notice unique id for next round\\n uint256 public nextScoreUpdateRoundId;\\n\\n /// @notice total number of accounts whose score needs to be updated\\n uint256 public totalScoreUpdatesRequired;\\n\\n /// @notice total number of accounts whose score is yet to be updated\\n uint256 public pendingScoreUpdates;\\n\\n /// @notice mapping used to find if an asset is part of prime markets\\n mapping(address => address) public vTokenForAsset;\\n\\n /// @notice Address of core pool comptroller contract\\n address internal corePoolComptroller;\\n\\n /// @notice unreleased income from PLP that's already distributed to prime holders\\n /// @dev mapping of asset address => amount\\n mapping(address => uint256) public unreleasedPLPIncome;\\n\\n /// @notice The address of PLP contract\\n address public primeLiquidityProvider;\\n\\n /// @notice The address of ResilientOracle contract\\n ResilientOracleInterface public oracle;\\n\\n /// @notice The address of PoolRegistry contract\\n address public poolRegistry;\\n\\n /// @dev This empty reserved space is put in place to allow future versions to add new\\n /// variables without shifting down storage in the inheritance chain.\\n uint256[26] private __gap;\\n}\\n\",\"keccak256\":\"0x5285c875114db2ea2be0b81b65722d5761806217022db733323c4e03365a95e7\",\"license\":\"BSD-3-Clause\"},\"contracts/Comptroller.sol\":{\"content\":\"// SPDX-License-Identifier: BSD-3-Clause\\npragma solidity 0.8.25;\\n\\nimport { Ownable2StepUpgradeable } from \\\"@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol\\\";\\nimport { ResilientOracleInterface } from \\\"@venusprotocol/oracle/contracts/interfaces/OracleInterface.sol\\\";\\nimport { AccessControlledV8 } from \\\"@venusprotocol/governance-contracts/contracts/Governance/AccessControlledV8.sol\\\";\\nimport { IPrime } from \\\"@venusprotocol/venus-protocol/contracts/Tokens/Prime/Interfaces/IPrime.sol\\\";\\n\\nimport { ComptrollerInterface, Action } from \\\"./ComptrollerInterface.sol\\\";\\nimport { ComptrollerStorage } from \\\"./ComptrollerStorage.sol\\\";\\nimport { ExponentialNoError } from \\\"./ExponentialNoError.sol\\\";\\nimport { VToken } from \\\"./VToken.sol\\\";\\nimport { RewardsDistributor } from \\\"./Rewards/RewardsDistributor.sol\\\";\\nimport { MaxLoopsLimitHelper } from \\\"./MaxLoopsLimitHelper.sol\\\";\\nimport { ensureNonzeroAddress } from \\\"./lib/validators.sol\\\";\\n\\n/**\\n * @title Comptroller\\n * @author Venus\\n * @notice The Comptroller is designed to provide checks for all minting, redeeming, transferring, borrowing, lending, repaying, liquidating,\\n * and seizing done by the `vToken` contract. Each pool has one `Comptroller` checking these interactions across markets. When a user interacts\\n * with a given market by one of these main actions, a call is made to a corresponding hook in the associated `Comptroller`, which either allows\\n * or reverts the transaction. These hooks also update supply and borrow rewards as they are called. The comptroller holds the logic for assessing\\n * liquidity snapshots of an account via the collateral factor and liquidation threshold. This check determines the collateral needed for a borrow,\\n * as well as how much of a borrow may be liquidated. A user may borrow a portion of their collateral with the maximum amount determined by the\\n * markets collateral factor. However, if their borrowed amount exceeds an amount calculated using the market\\u2019s corresponding liquidation threshold,\\n * the borrow is eligible for liquidation.\\n *\\n * The `Comptroller` also includes two functions `liquidateAccount()` and `healAccount()`, which are meant to handle accounts that do not exceed\\n * the `minLiquidatableCollateral` for the `Comptroller`:\\n *\\n * - `healAccount()`: This function is called to seize all of a given user\\u2019s collateral, requiring the `msg.sender` repay a certain percentage\\n * of the debt calculated by `collateral/(borrows*liquidationIncentive)`. The function can only be called if the calculated percentage does not exceed\\n * 100%, because otherwise no `badDebt` would be created and `liquidateAccount()` should be used instead. The difference in the actual amount of debt\\n * and debt paid off is recorded as `badDebt` for each market, which can then be auctioned off for the risk reserves of the associated pool.\\n * - `liquidateAccount()`: This function can only be called if the collateral seized will cover all borrows of an account, as well as the liquidation\\n * incentive. Otherwise, the pool will incur bad debt, in which case the function `healAccount()` should be used instead. This function skips the logic\\n * verifying that the repay amount does not exceed the close factor.\\n */\\ncontract Comptroller is\\n Ownable2StepUpgradeable,\\n AccessControlledV8,\\n ComptrollerStorage,\\n ComptrollerInterface,\\n ExponentialNoError,\\n MaxLoopsLimitHelper\\n{\\n // PoolRegistry, immutable to save on gas\\n /// @custom:oz-upgrades-unsafe-allow state-variable-immutable\\n address public immutable poolRegistry;\\n\\n /// @notice Emitted when an account enters a market\\n event MarketEntered(VToken indexed vToken, address indexed account);\\n\\n /// @notice Emitted when an account exits a market\\n event MarketExited(VToken indexed vToken, address indexed account);\\n\\n /// @notice Emitted when close factor is changed by admin\\n event NewCloseFactor(uint256 oldCloseFactorMantissa, uint256 newCloseFactorMantissa);\\n\\n /// @notice Emitted when a collateral factor is changed by admin\\n event NewCollateralFactor(VToken vToken, uint256 oldCollateralFactorMantissa, uint256 newCollateralFactorMantissa);\\n\\n /// @notice Emitted when liquidation threshold is changed by admin\\n event NewLiquidationThreshold(\\n VToken vToken,\\n uint256 oldLiquidationThresholdMantissa,\\n uint256 newLiquidationThresholdMantissa\\n );\\n\\n /// @notice Emitted when liquidation incentive is changed by admin\\n event NewLiquidationIncentive(uint256 oldLiquidationIncentiveMantissa, uint256 newLiquidationIncentiveMantissa);\\n\\n /// @notice Emitted when price oracle is changed\\n event NewPriceOracle(ResilientOracleInterface oldPriceOracle, ResilientOracleInterface newPriceOracle);\\n\\n /// @notice Emitted when an action is paused on a market\\n event ActionPausedMarket(VToken vToken, Action action, bool pauseState);\\n\\n /// @notice Emitted when borrow cap for a vToken is changed\\n event NewBorrowCap(VToken indexed vToken, uint256 newBorrowCap);\\n\\n /// @notice Emitted when the collateral threshold (in USD) for non-batch liquidations is changed\\n event NewMinLiquidatableCollateral(uint256 oldMinLiquidatableCollateral, uint256 newMinLiquidatableCollateral);\\n\\n /// @notice Emitted when supply cap for a vToken is changed\\n event NewSupplyCap(VToken indexed vToken, uint256 newSupplyCap);\\n\\n /// @notice Emitted when a rewards distributor is added\\n event NewRewardsDistributor(address indexed rewardsDistributor, address indexed rewardToken);\\n\\n /// @notice Emitted when a market is supported\\n event MarketSupported(VToken vToken);\\n\\n /// @notice Emitted when prime token contract address is changed\\n event NewPrimeToken(IPrime oldPrimeToken, IPrime newPrimeToken);\\n\\n /// @notice Emitted when forced liquidation is enabled or disabled for a market\\n event IsForcedLiquidationEnabledUpdated(address indexed vToken, bool enable);\\n\\n /// @notice Emitted when a market is unlisted\\n event MarketUnlisted(address indexed vToken);\\n /// @notice Emitted when the borrowing or redeeming delegate rights are updated for an account\\n event DelegateUpdated(address indexed approver, address indexed delegate, bool approved);\\n\\n /// @notice Thrown when collateral factor exceeds the upper bound\\n error InvalidCollateralFactor();\\n\\n /// @notice Thrown when liquidation threshold exceeds the collateral factor\\n error InvalidLiquidationThreshold();\\n\\n /// @notice Thrown when the action is only available to specific sender, but the real sender was different\\n error UnexpectedSender(address expectedSender, address actualSender);\\n\\n /// @notice Thrown when the oracle returns an invalid price for some asset\\n error PriceError(address vToken);\\n\\n /// @notice Thrown if VToken unexpectedly returned a nonzero error code while trying to get account snapshot\\n error SnapshotError(address vToken, address user);\\n\\n /// @notice Thrown when the market is not listed\\n error MarketNotListed(address market);\\n\\n /// @notice Thrown when a market has an unexpected comptroller\\n error ComptrollerMismatch();\\n\\n /// @notice Thrown when user is not member of market\\n error MarketNotCollateral(address vToken, address user);\\n\\n /// @notice Thrown when borrow action is not paused\\n error BorrowActionNotPaused();\\n\\n /// @notice Thrown when mint action is not paused\\n error MintActionNotPaused();\\n\\n /// @notice Thrown when redeem action is not paused\\n error RedeemActionNotPaused();\\n\\n /// @notice Thrown when repay action is not paused\\n error RepayActionNotPaused();\\n\\n /// @notice Thrown when seize action is not paused\\n error SeizeActionNotPaused();\\n\\n /// @notice Thrown when exit market action is not paused\\n error ExitMarketActionNotPaused();\\n\\n /// @notice Thrown when transfer action is not paused\\n error TransferActionNotPaused();\\n\\n /// @notice Thrown when enter market action is not paused\\n error EnterMarketActionNotPaused();\\n\\n /// @notice Thrown when liquidate action is not paused\\n error LiquidateActionNotPaused();\\n\\n /// @notice Thrown when borrow cap is not zero\\n error BorrowCapIsNotZero();\\n\\n /// @notice Thrown when supply cap is not zero\\n error SupplyCapIsNotZero();\\n\\n /// @notice Thrown when collateral factor is not zero\\n error CollateralFactorIsNotZero();\\n\\n /**\\n * @notice Thrown during the liquidation if user's total collateral amount is lower than\\n * a predefined threshold. In this case only batch liquidations (either liquidateAccount\\n * or healAccount) are available.\\n */\\n error MinimalCollateralViolated(uint256 expectedGreaterThan, uint256 actual);\\n error CollateralExceedsThreshold(uint256 expectedLessThanOrEqualTo, uint256 actual);\\n error InsufficientCollateral(uint256 collateralToSeize, uint256 availableCollateral);\\n\\n /// @notice Thrown when the account doesn't have enough liquidity to redeem or borrow\\n error InsufficientLiquidity();\\n\\n /// @notice Thrown when trying to liquidate a healthy account\\n error InsufficientShortfall();\\n\\n /// @notice Thrown when trying to repay more than allowed by close factor\\n error TooMuchRepay();\\n\\n /// @notice Thrown if the user is trying to exit a market in which they have an outstanding debt\\n error NonzeroBorrowBalance();\\n\\n /// @notice Thrown when trying to perform an action that is paused\\n error ActionPaused(address market, Action action);\\n\\n /// @notice Thrown when trying to add a market that is already listed\\n error MarketAlreadyListed(address market);\\n\\n /// @notice Thrown if the supply cap is exceeded\\n error SupplyCapExceeded(address market, uint256 cap);\\n\\n /// @notice Thrown if the borrow cap is exceeded\\n error BorrowCapExceeded(address market, uint256 cap);\\n\\n /// @notice Thrown if delegate approval status is already set to the requested value\\n error DelegationStatusUnchanged();\\n\\n /// @param poolRegistry_ Pool registry address\\n /// @custom:oz-upgrades-unsafe-allow constructor\\n /// @custom:error ZeroAddressNotAllowed is thrown when pool registry address is zero\\n constructor(address poolRegistry_) {\\n ensureNonzeroAddress(poolRegistry_);\\n\\n poolRegistry = poolRegistry_;\\n _disableInitializers();\\n }\\n\\n /**\\n * @param loopLimit Limit for the loops can iterate to avoid the DOS\\n * @param accessControlManager Access control manager contract address\\n */\\n function initialize(uint256 loopLimit, address accessControlManager) external initializer {\\n __Ownable2Step_init();\\n __AccessControlled_init_unchained(accessControlManager);\\n\\n _setMaxLoopsLimit(loopLimit);\\n }\\n\\n /**\\n * @notice Add assets to be included in account liquidity calculation; enabling them to be used as collateral\\n * @param vTokens The list of addresses of the vToken markets to be enabled\\n * @return errors An array of NO_ERROR for compatibility with Venus core tooling\\n * @custom:event MarketEntered is emitted for each market on success\\n * @custom:error ActionPaused error is thrown if entering any of the markets is paused\\n * @custom:error MarketNotListed error is thrown if any of the markets is not listed\\n * @custom:access Not restricted\\n */\\n function enterMarkets(address[] memory vTokens) external override returns (uint256[] memory) {\\n uint256 len = vTokens.length;\\n\\n uint256[] memory results = new uint256[](len);\\n for (uint256 i; i < len; ++i) {\\n VToken vToken = VToken(vTokens[i]);\\n\\n _addToMarket(vToken, msg.sender);\\n results[i] = NO_ERROR;\\n }\\n\\n return results;\\n }\\n\\n /**\\n * @notice Unlist a market by setting isListed to false\\n * @dev Checks if all actions are paused, borrow/supply caps is set to 0 and collateral factor is to 0.\\n * @param market The address of the market (token) to unlist\\n * @return uint256 Always NO_ERROR for compatibility with Venus core tooling\\n * @custom:event MarketUnlisted is emitted on success\\n * @custom:error MarketNotListed error is thrown when the market is not listed\\n * @custom:error BorrowActionNotPaused error is thrown if borrow action is not paused\\n * @custom:error MintActionNotPaused error is thrown if mint action is not paused\\n * @custom:error RedeemActionNotPaused error is thrown if redeem action is not paused\\n * @custom:error RepayActionNotPaused error is thrown if repay action is not paused\\n * @custom:error EnterMarketActionNotPaused error is thrown if enter market action is not paused\\n * @custom:error LiquidateActionNotPaused error is thrown if liquidate action is not paused\\n * @custom:error BorrowCapIsNotZero error is thrown if borrow cap is not zero\\n * @custom:error SupplyCapIsNotZero error is thrown if supply cap is not zero\\n * @custom:error CollateralFactorIsNotZero error is thrown if collateral factor is not zero\\n */\\n function unlistMarket(address market) external returns (uint256) {\\n _checkAccessAllowed(\\\"unlistMarket(address)\\\");\\n\\n Market storage _market = markets[market];\\n\\n if (!_market.isListed) {\\n revert MarketNotListed(market);\\n }\\n\\n if (!actionPaused(market, Action.BORROW)) {\\n revert BorrowActionNotPaused();\\n }\\n\\n if (!actionPaused(market, Action.MINT)) {\\n revert MintActionNotPaused();\\n }\\n\\n if (!actionPaused(market, Action.REDEEM)) {\\n revert RedeemActionNotPaused();\\n }\\n\\n if (!actionPaused(market, Action.REPAY)) {\\n revert RepayActionNotPaused();\\n }\\n\\n if (!actionPaused(market, Action.SEIZE)) {\\n revert SeizeActionNotPaused();\\n }\\n\\n if (!actionPaused(market, Action.ENTER_MARKET)) {\\n revert EnterMarketActionNotPaused();\\n }\\n\\n if (!actionPaused(market, Action.LIQUIDATE)) {\\n revert LiquidateActionNotPaused();\\n }\\n\\n if (!actionPaused(market, Action.TRANSFER)) {\\n revert TransferActionNotPaused();\\n }\\n\\n if (!actionPaused(market, Action.EXIT_MARKET)) {\\n revert ExitMarketActionNotPaused();\\n }\\n\\n if (borrowCaps[market] != 0) {\\n revert BorrowCapIsNotZero();\\n }\\n\\n if (supplyCaps[market] != 0) {\\n revert SupplyCapIsNotZero();\\n }\\n\\n if (_market.collateralFactorMantissa != 0) {\\n revert CollateralFactorIsNotZero();\\n }\\n\\n _market.isListed = false;\\n emit MarketUnlisted(market);\\n\\n return NO_ERROR;\\n }\\n\\n /**\\n * @notice Grants or revokes the borrowing or redeeming delegate rights to / from an account\\n * If allowed, the delegate will be able to borrow funds on behalf of the sender\\n * Upon a delegated borrow, the delegate will receive the funds, and the borrower\\n * will see the debt on their account\\n * Upon a delegated redeem, the delegate will receive the redeemed amount and the approver\\n * will see a deduction in his vToken balance\\n * @param delegate The address to update the rights for\\n * @param approved Whether to grant (true) or revoke (false) the borrowing or redeeming rights\\n * @custom:event DelegateUpdated emits on success\\n * @custom:error ZeroAddressNotAllowed is thrown when delegate address is zero\\n * @custom:error DelegationStatusUnchanged is thrown if approval status is already set to the requested value\\n * @custom:access Not restricted\\n */\\n function updateDelegate(address delegate, bool approved) external {\\n ensureNonzeroAddress(delegate);\\n if (approvedDelegates[msg.sender][delegate] == approved) {\\n revert DelegationStatusUnchanged();\\n }\\n\\n approvedDelegates[msg.sender][delegate] = approved;\\n emit DelegateUpdated(msg.sender, delegate, approved);\\n }\\n\\n /**\\n * @notice Removes asset from sender's account liquidity calculation; disabling them as collateral\\n * @dev Sender must not have an outstanding borrow balance in the asset,\\n * or be providing necessary collateral for an outstanding borrow.\\n * @param vTokenAddress The address of the asset to be removed\\n * @return error Always NO_ERROR for compatibility with Venus core tooling\\n * @custom:event MarketExited is emitted on success\\n * @custom:error ActionPaused error is thrown if exiting the market is paused\\n * @custom:error NonzeroBorrowBalance error is thrown if the user has an outstanding borrow in this market\\n * @custom:error MarketNotListed error is thrown when the market is not listed\\n * @custom:error InsufficientLiquidity error is thrown if exiting the market would lead to user's insolvency\\n * @custom:error SnapshotError is thrown if some vToken fails to return the account's supply and borrows\\n * @custom:error PriceError is thrown if the oracle returns an incorrect price for some asset\\n * @custom:access Not restricted\\n */\\n function exitMarket(address vTokenAddress) external override returns (uint256) {\\n _checkActionPauseState(vTokenAddress, Action.EXIT_MARKET);\\n VToken vToken = VToken(vTokenAddress);\\n /* Get sender tokensHeld and amountOwed underlying from the vToken */\\n (uint256 tokensHeld, uint256 amountOwed, ) = _safeGetAccountSnapshot(vToken, msg.sender);\\n\\n /* Fail if the sender has a borrow balance */\\n if (amountOwed != 0) {\\n revert NonzeroBorrowBalance();\\n }\\n\\n /* Fail if the sender is not permitted to redeem all of their tokens */\\n _checkRedeemAllowed(vTokenAddress, msg.sender, tokensHeld);\\n\\n Market storage marketToExit = markets[address(vToken)];\\n\\n /* Return true if the sender is not already \\u2018in\\u2019 the market */\\n if (!marketToExit.accountMembership[msg.sender]) {\\n return NO_ERROR;\\n }\\n\\n /* Set vToken account membership to false */\\n delete marketToExit.accountMembership[msg.sender];\\n\\n /* Delete vToken from the account\\u2019s list of assets */\\n // load into memory for faster iteration\\n VToken[] memory userAssetList = accountAssets[msg.sender];\\n uint256 len = userAssetList.length;\\n\\n uint256 assetIndex = len;\\n for (uint256 i; i < len; ++i) {\\n if (userAssetList[i] == vToken) {\\n assetIndex = i;\\n break;\\n }\\n }\\n\\n // We *must* have found the asset in the list or our redundant data structure is broken\\n assert(assetIndex < len);\\n\\n // copy last item in list to location of item to be removed, reduce length by 1\\n VToken[] storage storedList = accountAssets[msg.sender];\\n storedList[assetIndex] = storedList[storedList.length - 1];\\n storedList.pop();\\n\\n emit MarketExited(vToken, msg.sender);\\n\\n return NO_ERROR;\\n }\\n\\n /*** Policy Hooks ***/\\n\\n /**\\n * @notice Checks if the account should be allowed to mint tokens in the given market\\n * @param vToken The market to verify the mint against\\n * @param minter The account which would get the minted tokens\\n * @param mintAmount The amount of underlying being supplied to the market in exchange for tokens\\n * @custom:error ActionPaused error is thrown if supplying to this market is paused\\n * @custom:error MarketNotListed error is thrown when the market is not listed\\n * @custom:error SupplyCapExceeded error is thrown if the total supply exceeds the cap after minting\\n * @custom:access Not restricted\\n */\\n function preMintHook(address vToken, address minter, uint256 mintAmount) external override {\\n _checkActionPauseState(vToken, Action.MINT);\\n\\n if (!markets[vToken].isListed) {\\n revert MarketNotListed(address(vToken));\\n }\\n\\n uint256 supplyCap = supplyCaps[vToken];\\n // Skipping the cap check for uncapped coins to save some gas\\n if (supplyCap != type(uint256).max) {\\n uint256 vTokenSupply = VToken(vToken).totalSupply();\\n Exp memory exchangeRate = Exp({ mantissa: VToken(vToken).exchangeRateStored() });\\n uint256 nextTotalSupply = mul_ScalarTruncateAddUInt(exchangeRate, vTokenSupply, mintAmount);\\n if (nextTotalSupply > supplyCap) {\\n revert SupplyCapExceeded(vToken, supplyCap);\\n }\\n }\\n\\n // Keep the flywheel moving\\n uint256 rewardDistributorsCount = rewardsDistributors.length;\\n\\n for (uint256 i; i < rewardDistributorsCount; ++i) {\\n RewardsDistributor rewardsDistributor = rewardsDistributors[i];\\n rewardsDistributor.updateRewardTokenSupplyIndex(vToken);\\n rewardsDistributor.distributeSupplierRewardToken(vToken, minter);\\n }\\n }\\n\\n /**\\n * @notice Validates mint, accrues interest and updates score in prime. Reverts on rejection. May emit logs.\\n * @param vToken Asset being minted\\n * @param minter The address minting the tokens\\n * @param actualMintAmount The amount of the underlying asset being minted\\n * @param mintTokens The number of tokens being minted\\n */\\n // solhint-disable-next-line no-unused-vars\\n function mintVerify(address vToken, address minter, uint256 actualMintAmount, uint256 mintTokens) external {\\n if (address(prime) != address(0)) {\\n prime.accrueInterestAndUpdateScore(minter, vToken);\\n }\\n }\\n\\n /**\\n * @notice Checks if the account should be allowed to redeem tokens in the given market\\n * @param vToken The market to verify the redeem against\\n * @param redeemer The account which would redeem the tokens\\n * @param redeemTokens The number of vTokens to exchange for the underlying asset in the market\\n * @custom:error ActionPaused error is thrown if withdrawals are paused in this market\\n * @custom:error MarketNotListed error is thrown when the market is not listed\\n * @custom:error InsufficientLiquidity error is thrown if the withdrawal would lead to user's insolvency\\n * @custom:error SnapshotError is thrown if some vToken fails to return the account's supply and borrows\\n * @custom:error PriceError is thrown if the oracle returns an incorrect price for some asset\\n * @custom:access Not restricted\\n */\\n function preRedeemHook(address vToken, address redeemer, uint256 redeemTokens) external override {\\n _checkActionPauseState(vToken, Action.REDEEM);\\n\\n _checkRedeemAllowed(vToken, redeemer, redeemTokens);\\n\\n // Keep the flywheel moving\\n uint256 rewardDistributorsCount = rewardsDistributors.length;\\n\\n for (uint256 i; i < rewardDistributorsCount; ++i) {\\n RewardsDistributor rewardsDistributor = rewardsDistributors[i];\\n rewardsDistributor.updateRewardTokenSupplyIndex(vToken);\\n rewardsDistributor.distributeSupplierRewardToken(vToken, redeemer);\\n }\\n }\\n\\n /**\\n * @notice Validates redeem, accrues interest and updates score in prime. Reverts on rejection. May emit logs.\\n * @param vToken Asset being redeemed\\n * @param redeemer The address redeeming the tokens\\n * @param redeemAmount The amount of the underlying asset being redeemed\\n * @param redeemTokens The number of tokens being redeemed\\n */\\n function redeemVerify(address vToken, address redeemer, uint256 redeemAmount, uint256 redeemTokens) external {\\n if (address(prime) != address(0)) {\\n prime.accrueInterestAndUpdateScore(redeemer, vToken);\\n }\\n }\\n\\n /**\\n * @notice Validates repayBorrow, accrues interest and updates score in prime. Reverts on rejection. May emit logs.\\n * @param vToken Asset being repaid\\n * @param payer The address repaying the borrow\\n * @param borrower The address of the borrower\\n * @param actualRepayAmount The amount of underlying being repaid\\n */\\n function repayBorrowVerify(\\n address vToken,\\n address payer, // solhint-disable-line no-unused-vars\\n address borrower,\\n uint256 actualRepayAmount, // solhint-disable-line no-unused-vars\\n uint256 borrowerIndex // solhint-disable-line no-unused-vars\\n ) external {\\n if (address(prime) != address(0)) {\\n prime.accrueInterestAndUpdateScore(borrower, vToken);\\n }\\n }\\n\\n /**\\n * @notice Validates liquidateBorrow, accrues interest and updates score in prime. Reverts on rejection. May emit logs.\\n * @param vTokenBorrowed Asset which was borrowed by the borrower\\n * @param vTokenCollateral Asset which was used as collateral and will be seized\\n * @param liquidator The address repaying the borrow and seizing the collateral\\n * @param borrower The address of the borrower\\n * @param actualRepayAmount The amount of underlying being repaid\\n * @param seizeTokens The amount of collateral token that will be seized\\n */\\n function liquidateBorrowVerify(\\n address vTokenBorrowed,\\n address vTokenCollateral, // solhint-disable-line no-unused-vars\\n address liquidator,\\n address borrower,\\n uint256 actualRepayAmount, // solhint-disable-line no-unused-vars\\n uint256 seizeTokens // solhint-disable-line no-unused-vars\\n ) external {\\n if (address(prime) != address(0)) {\\n prime.accrueInterestAndUpdateScore(borrower, vTokenBorrowed);\\n prime.accrueInterestAndUpdateScore(liquidator, vTokenBorrowed);\\n }\\n }\\n\\n /**\\n * @notice Validates seize, accrues interest and updates score in prime. Reverts on rejection. May emit logs.\\n * @param vTokenCollateral Asset which was used as collateral and will be seized\\n * @param vTokenBorrowed Asset which was borrowed by the borrower\\n * @param liquidator The address repaying the borrow and seizing the collateral\\n * @param borrower The address of the borrower\\n * @param seizeTokens The number of collateral tokens to seize\\n */\\n function seizeVerify(\\n address vTokenCollateral,\\n address vTokenBorrowed, // solhint-disable-line no-unused-vars\\n address liquidator,\\n address borrower,\\n uint256 seizeTokens // solhint-disable-line no-unused-vars\\n ) external {\\n if (address(prime) != address(0)) {\\n prime.accrueInterestAndUpdateScore(borrower, vTokenCollateral);\\n prime.accrueInterestAndUpdateScore(liquidator, vTokenCollateral);\\n }\\n }\\n\\n /**\\n * @notice Validates transfer, accrues interest and updates score in prime. Reverts on rejection. May emit logs.\\n * @param vToken Asset being transferred\\n * @param src The account which sources the tokens\\n * @param dst The account which receives the tokens\\n * @param transferTokens The number of vTokens to transfer\\n */\\n // solhint-disable-next-line no-unused-vars\\n function transferVerify(address vToken, address src, address dst, uint256 transferTokens) external {\\n if (address(prime) != address(0)) {\\n prime.accrueInterestAndUpdateScore(src, vToken);\\n prime.accrueInterestAndUpdateScore(dst, vToken);\\n }\\n }\\n\\n /**\\n * @notice Checks if the account should be allowed to borrow the underlying asset of the given market\\n * @param vToken The market to verify the borrow against\\n * @param borrower The account which would borrow the asset\\n * @param borrowAmount The amount of underlying the account would borrow\\n * @custom:error ActionPaused error is thrown if borrowing is paused in this market\\n * @custom:error MarketNotListed error is thrown when the market is not listed\\n * @custom:error InsufficientLiquidity error is thrown if there is not enough collateral to borrow\\n * @custom:error BorrowCapExceeded is thrown if the borrow cap will be exceeded should this borrow succeed\\n * @custom:error SnapshotError is thrown if some vToken fails to return the account's supply and borrows\\n * @custom:error PriceError is thrown if the oracle returns an incorrect price for some asset\\n * @custom:access Not restricted if vToken is enabled as collateral, otherwise only vToken\\n */\\n /// disable-eslint\\n function preBorrowHook(address vToken, address borrower, uint256 borrowAmount) external override {\\n _checkActionPauseState(vToken, Action.BORROW);\\n\\n if (!markets[vToken].isListed) {\\n revert MarketNotListed(address(vToken));\\n }\\n\\n if (!markets[vToken].accountMembership[borrower]) {\\n // only vTokens may call borrowAllowed if borrower not in market\\n _checkSenderIs(vToken);\\n\\n // attempt to add borrower to the market or revert\\n _addToMarket(VToken(msg.sender), borrower);\\n }\\n\\n // Update the prices of tokens\\n updatePrices(borrower);\\n\\n if (oracle.getUnderlyingPrice(vToken) == 0) {\\n revert PriceError(address(vToken));\\n }\\n\\n uint256 borrowCap = borrowCaps[vToken];\\n // Skipping the cap check for uncapped coins to save some gas\\n if (borrowCap != type(uint256).max) {\\n uint256 totalBorrows = VToken(vToken).totalBorrows();\\n uint256 badDebt = VToken(vToken).badDebt();\\n uint256 nextTotalBorrows = totalBorrows + borrowAmount + badDebt;\\n if (nextTotalBorrows > borrowCap) {\\n revert BorrowCapExceeded(vToken, borrowCap);\\n }\\n }\\n\\n AccountLiquiditySnapshot memory snapshot = _getHypotheticalLiquiditySnapshot(\\n borrower,\\n VToken(vToken),\\n 0,\\n borrowAmount,\\n _getCollateralFactor\\n );\\n\\n if (snapshot.shortfall > 0) {\\n revert InsufficientLiquidity();\\n }\\n\\n Exp memory borrowIndex = Exp({ mantissa: VToken(vToken).borrowIndex() });\\n\\n // Keep the flywheel moving\\n uint256 rewardDistributorsCount = rewardsDistributors.length;\\n\\n for (uint256 i; i < rewardDistributorsCount; ++i) {\\n RewardsDistributor rewardsDistributor = rewardsDistributors[i];\\n rewardsDistributor.updateRewardTokenBorrowIndex(vToken, borrowIndex);\\n rewardsDistributor.distributeBorrowerRewardToken(vToken, borrower, borrowIndex);\\n }\\n }\\n\\n /**\\n * @notice Validates borrow, accrues interest and updates score in prime. Reverts on rejection. May emit logs.\\n * @param vToken Asset whose underlying is being borrowed\\n * @param borrower The address borrowing the underlying\\n * @param borrowAmount The amount of the underlying asset requested to borrow\\n */\\n // solhint-disable-next-line no-unused-vars\\n function borrowVerify(address vToken, address borrower, uint256 borrowAmount) external {\\n if (address(prime) != address(0)) {\\n prime.accrueInterestAndUpdateScore(borrower, vToken);\\n }\\n }\\n\\n /**\\n * @notice Checks if the account should be allowed to repay a borrow in the given market\\n * @param vToken The market to verify the repay against\\n * @param borrower The account which would borrowed the asset\\n * @custom:error ActionPaused error is thrown if repayments are paused in this market\\n * @custom:error MarketNotListed error is thrown when the market is not listed\\n * @custom:access Not restricted\\n */\\n function preRepayHook(address vToken, address borrower) external override {\\n _checkActionPauseState(vToken, Action.REPAY);\\n\\n oracle.updatePrice(vToken);\\n\\n if (!markets[vToken].isListed) {\\n revert MarketNotListed(address(vToken));\\n }\\n\\n // Keep the flywheel moving\\n uint256 rewardDistributorsCount = rewardsDistributors.length;\\n\\n for (uint256 i; i < rewardDistributorsCount; ++i) {\\n Exp memory borrowIndex = Exp({ mantissa: VToken(vToken).borrowIndex() });\\n RewardsDistributor rewardsDistributor = rewardsDistributors[i];\\n rewardsDistributor.updateRewardTokenBorrowIndex(vToken, borrowIndex);\\n rewardsDistributor.distributeBorrowerRewardToken(vToken, borrower, borrowIndex);\\n }\\n }\\n\\n /**\\n * @notice Checks if the liquidation should be allowed to occur\\n * @param vTokenBorrowed Asset which was borrowed by the borrower\\n * @param vTokenCollateral Asset which was used as collateral and will be seized\\n * @param borrower The address of the borrower\\n * @param repayAmount The amount of underlying being repaid\\n * @param skipLiquidityCheck Allows the borrow to be liquidated regardless of the account liquidity\\n * @custom:error ActionPaused error is thrown if liquidations are paused in this market\\n * @custom:error MarketNotListed error is thrown if either collateral or borrowed token is not listed\\n * @custom:error TooMuchRepay error is thrown if the liquidator is trying to repay more than allowed by close factor\\n * @custom:error MinimalCollateralViolated is thrown if the users' total collateral is lower than the threshold for non-batch liquidations\\n * @custom:error InsufficientShortfall is thrown when trying to liquidate a healthy account\\n * @custom:error SnapshotError is thrown if some vToken fails to return the account's supply and borrows\\n * @custom:error PriceError is thrown if the oracle returns an incorrect price for some asset\\n */\\n function preLiquidateHook(\\n address vTokenBorrowed,\\n address vTokenCollateral,\\n address borrower,\\n uint256 repayAmount,\\n bool skipLiquidityCheck\\n ) external override {\\n // Pause Action.LIQUIDATE on BORROWED TOKEN to prevent liquidating it.\\n // If we want to pause liquidating to vTokenCollateral, we should pause\\n // Action.SEIZE on it\\n _checkActionPauseState(vTokenBorrowed, Action.LIQUIDATE);\\n\\n // Update the prices of tokens\\n updatePrices(borrower);\\n\\n if (!markets[vTokenBorrowed].isListed) {\\n revert MarketNotListed(address(vTokenBorrowed));\\n }\\n if (!markets[vTokenCollateral].isListed) {\\n revert MarketNotListed(address(vTokenCollateral));\\n }\\n\\n uint256 borrowBalance = VToken(vTokenBorrowed).borrowBalanceStored(borrower);\\n\\n /* Allow accounts to be liquidated if it is a forced liquidation */\\n if (skipLiquidityCheck || isForcedLiquidationEnabled[vTokenBorrowed]) {\\n if (repayAmount > borrowBalance) {\\n revert TooMuchRepay();\\n }\\n return;\\n }\\n\\n /* The borrower must have shortfall and collateral > threshold in order to be liquidatable */\\n AccountLiquiditySnapshot memory snapshot = _getCurrentLiquiditySnapshot(borrower, _getLiquidationThreshold);\\n\\n if (snapshot.totalCollateral <= minLiquidatableCollateral) {\\n /* The liquidator should use either liquidateAccount or healAccount */\\n revert MinimalCollateralViolated(minLiquidatableCollateral, snapshot.totalCollateral);\\n }\\n\\n if (snapshot.shortfall == 0) {\\n revert InsufficientShortfall();\\n }\\n\\n /* The liquidator may not repay more than what is allowed by the closeFactor */\\n uint256 maxClose = mul_ScalarTruncate(Exp({ mantissa: closeFactorMantissa }), borrowBalance);\\n if (repayAmount > maxClose) {\\n revert TooMuchRepay();\\n }\\n }\\n\\n /**\\n * @notice Checks if the seizing of assets should be allowed to occur\\n * @param vTokenCollateral Asset which was used as collateral and will be seized\\n * @param seizerContract Contract that tries to seize the asset (either borrowed vToken or Comptroller)\\n * @param liquidator The address repaying the borrow and seizing the collateral\\n * @param borrower The address of the borrower\\n * @custom:error ActionPaused error is thrown if seizing this type of collateral is paused\\n * @custom:error MarketNotListed error is thrown if either collateral or borrowed token is not listed\\n * @custom:error ComptrollerMismatch error is when seizer contract or seized asset belong to different pools\\n * @custom:access Not restricted\\n */\\n function preSeizeHook(\\n address vTokenCollateral,\\n address seizerContract,\\n address liquidator,\\n address borrower\\n ) external override {\\n // Pause Action.SEIZE on COLLATERAL to prevent seizing it.\\n // If we want to pause liquidating vTokenBorrowed, we should pause\\n // Action.LIQUIDATE on it\\n _checkActionPauseState(vTokenCollateral, Action.SEIZE);\\n\\n Market storage market = markets[vTokenCollateral];\\n\\n if (!market.isListed) {\\n revert MarketNotListed(vTokenCollateral);\\n }\\n\\n if (seizerContract == address(this)) {\\n // If Comptroller is the seizer, just check if collateral's comptroller\\n // is equal to the current address\\n if (address(VToken(vTokenCollateral).comptroller()) != address(this)) {\\n revert ComptrollerMismatch();\\n }\\n } else {\\n // If the seizer is not the Comptroller, check that the seizer is a\\n // listed market, and that the markets' comptrollers match\\n if (!markets[seizerContract].isListed) {\\n revert MarketNotListed(seizerContract);\\n }\\n if (VToken(vTokenCollateral).comptroller() != VToken(seizerContract).comptroller()) {\\n revert ComptrollerMismatch();\\n }\\n }\\n\\n if (!market.accountMembership[borrower]) {\\n revert MarketNotCollateral(vTokenCollateral, borrower);\\n }\\n\\n // Keep the flywheel moving\\n uint256 rewardDistributorsCount = rewardsDistributors.length;\\n\\n for (uint256 i; i < rewardDistributorsCount; ++i) {\\n RewardsDistributor rewardsDistributor = rewardsDistributors[i];\\n rewardsDistributor.updateRewardTokenSupplyIndex(vTokenCollateral);\\n rewardsDistributor.distributeSupplierRewardToken(vTokenCollateral, borrower);\\n rewardsDistributor.distributeSupplierRewardToken(vTokenCollateral, liquidator);\\n }\\n }\\n\\n /**\\n * @notice Checks if the account should be allowed to transfer tokens in the given market\\n * @param vToken The market to verify the transfer against\\n * @param src The account which sources the tokens\\n * @param dst The account which receives the tokens\\n * @param transferTokens The number of vTokens to transfer\\n * @custom:error ActionPaused error is thrown if withdrawals are paused in this market\\n * @custom:error MarketNotListed error is thrown when the market is not listed\\n * @custom:error InsufficientLiquidity error is thrown if the withdrawal would lead to user's insolvency\\n * @custom:error SnapshotError is thrown if some vToken fails to return the account's supply and borrows\\n * @custom:error PriceError is thrown if the oracle returns an incorrect price for some asset\\n * @custom:access Not restricted\\n */\\n function preTransferHook(address vToken, address src, address dst, uint256 transferTokens) external override {\\n _checkActionPauseState(vToken, Action.TRANSFER);\\n\\n // Currently the only consideration is whether or not\\n // the src is allowed to redeem this many tokens\\n _checkRedeemAllowed(vToken, src, transferTokens);\\n\\n // Keep the flywheel moving\\n uint256 rewardDistributorsCount = rewardsDistributors.length;\\n\\n for (uint256 i; i < rewardDistributorsCount; ++i) {\\n RewardsDistributor rewardsDistributor = rewardsDistributors[i];\\n rewardsDistributor.updateRewardTokenSupplyIndex(vToken);\\n rewardsDistributor.distributeSupplierRewardToken(vToken, src);\\n rewardsDistributor.distributeSupplierRewardToken(vToken, dst);\\n }\\n }\\n\\n /*** Pool-level operations ***/\\n\\n /**\\n * @notice Seizes all the remaining collateral, makes msg.sender repay the existing\\n * borrows, and treats the rest of the debt as bad debt (for each market).\\n * The sender has to repay a certain percentage of the debt, computed as\\n * collateral / (borrows * liquidationIncentive).\\n * @param user account to heal\\n * @custom:error CollateralExceedsThreshold error is thrown when the collateral is too big for healing\\n * @custom:error SnapshotError is thrown if some vToken fails to return the account's supply and borrows\\n * @custom:error PriceError is thrown if the oracle returns an incorrect price for some asset\\n * @custom:access Not restricted\\n */\\n function healAccount(address user) external {\\n VToken[] memory userAssets = getAssetsIn(user);\\n uint256 userAssetsCount = userAssets.length;\\n\\n address liquidator = msg.sender;\\n {\\n ResilientOracleInterface oracle_ = oracle;\\n // We need all user's markets to be fresh for the computations to be correct\\n for (uint256 i; i < userAssetsCount; ++i) {\\n userAssets[i].accrueInterest();\\n oracle_.updatePrice(address(userAssets[i]));\\n }\\n }\\n\\n AccountLiquiditySnapshot memory snapshot = _getCurrentLiquiditySnapshot(user, _getLiquidationThreshold);\\n\\n if (snapshot.totalCollateral > minLiquidatableCollateral) {\\n revert CollateralExceedsThreshold(minLiquidatableCollateral, snapshot.totalCollateral);\\n }\\n\\n if (snapshot.shortfall == 0) {\\n revert InsufficientShortfall();\\n }\\n\\n // percentage = collateral / (borrows * liquidation incentive)\\n Exp memory collateral = Exp({ mantissa: snapshot.totalCollateral });\\n Exp memory scaledBorrows = mul_(\\n Exp({ mantissa: snapshot.borrows }),\\n Exp({ mantissa: liquidationIncentiveMantissa })\\n );\\n\\n Exp memory percentage = div_(collateral, scaledBorrows);\\n if (lessThanExp(Exp({ mantissa: MANTISSA_ONE }), percentage)) {\\n revert CollateralExceedsThreshold(scaledBorrows.mantissa, collateral.mantissa);\\n }\\n\\n for (uint256 i; i < userAssetsCount; ++i) {\\n VToken market = userAssets[i];\\n\\n (uint256 tokens, uint256 borrowBalance, ) = _safeGetAccountSnapshot(market, user);\\n uint256 repaymentAmount = mul_ScalarTruncate(percentage, borrowBalance);\\n\\n // Seize the entire collateral\\n if (tokens != 0) {\\n market.seize(liquidator, user, tokens);\\n }\\n // Repay a certain percentage of the borrow, forgive the rest\\n if (borrowBalance != 0) {\\n market.healBorrow(liquidator, user, repaymentAmount);\\n }\\n }\\n }\\n\\n /**\\n * @notice Liquidates all borrows of the borrower. Callable only if the collateral is less than\\n * a predefined threshold, and the account collateral can be seized to cover all borrows. If\\n * the collateral is higher than the threshold, use regular liquidations. If the collateral is\\n * below the threshold, and the account is insolvent, use healAccount.\\n * @param borrower the borrower address\\n * @param orders an array of liquidation orders\\n * @custom:error CollateralExceedsThreshold error is thrown when the collateral is too big for a batch liquidation\\n * @custom:error InsufficientCollateral error is thrown when there is not enough collateral to cover the debt\\n * @custom:error SnapshotError is thrown if some vToken fails to return the account's supply and borrows\\n * @custom:error PriceError is thrown if the oracle returns an incorrect price for some asset\\n * @custom:access Not restricted\\n */\\n function liquidateAccount(address borrower, LiquidationOrder[] calldata orders) external {\\n // We will accrue interest and update the oracle prices later during the liquidation\\n\\n AccountLiquiditySnapshot memory snapshot = _getCurrentLiquiditySnapshot(borrower, _getLiquidationThreshold);\\n\\n if (snapshot.totalCollateral > minLiquidatableCollateral) {\\n // You should use the regular vToken.liquidateBorrow(...) call\\n revert CollateralExceedsThreshold(minLiquidatableCollateral, snapshot.totalCollateral);\\n }\\n\\n uint256 collateralToSeize = mul_ScalarTruncate(\\n Exp({ mantissa: liquidationIncentiveMantissa }),\\n snapshot.borrows\\n );\\n if (collateralToSeize >= snapshot.totalCollateral) {\\n // There is not enough collateral to seize. Use healBorrow to repay some part of the borrow\\n // and record bad debt.\\n revert InsufficientCollateral(collateralToSeize, snapshot.totalCollateral);\\n }\\n\\n if (snapshot.shortfall == 0) {\\n revert InsufficientShortfall();\\n }\\n\\n uint256 ordersCount = orders.length;\\n\\n _ensureMaxLoops(ordersCount / 2);\\n\\n for (uint256 i; i < ordersCount; ++i) {\\n if (!markets[address(orders[i].vTokenBorrowed)].isListed) {\\n revert MarketNotListed(address(orders[i].vTokenBorrowed));\\n }\\n if (!markets[address(orders[i].vTokenCollateral)].isListed) {\\n revert MarketNotListed(address(orders[i].vTokenCollateral));\\n }\\n\\n LiquidationOrder calldata order = orders[i];\\n order.vTokenBorrowed.forceLiquidateBorrow(\\n msg.sender,\\n borrower,\\n order.repayAmount,\\n order.vTokenCollateral,\\n true\\n );\\n }\\n\\n VToken[] memory borrowMarkets = getAssetsIn(borrower);\\n uint256 marketsCount = borrowMarkets.length;\\n\\n for (uint256 i; i < marketsCount; ++i) {\\n (, uint256 borrowBalance, ) = _safeGetAccountSnapshot(borrowMarkets[i], borrower);\\n require(borrowBalance == 0, \\\"Nonzero borrow balance after liquidation\\\");\\n }\\n }\\n\\n /**\\n * @notice Sets the closeFactor to use when liquidating borrows\\n * @param newCloseFactorMantissa New close factor, scaled by 1e18\\n * @custom:event Emits NewCloseFactor on success\\n * @custom:access Controlled by AccessControlManager\\n */\\n function setCloseFactor(uint256 newCloseFactorMantissa) external {\\n _checkAccessAllowed(\\\"setCloseFactor(uint256)\\\");\\n require(MAX_CLOSE_FACTOR_MANTISSA >= newCloseFactorMantissa, \\\"Close factor greater than maximum close factor\\\");\\n require(MIN_CLOSE_FACTOR_MANTISSA <= newCloseFactorMantissa, \\\"Close factor smaller than minimum close factor\\\");\\n\\n uint256 oldCloseFactorMantissa = closeFactorMantissa;\\n closeFactorMantissa = newCloseFactorMantissa;\\n emit NewCloseFactor(oldCloseFactorMantissa, newCloseFactorMantissa);\\n }\\n\\n /**\\n * @notice Sets the collateralFactor for a market\\n * @dev This function is restricted by the AccessControlManager\\n * @param vToken The market to set the factor on\\n * @param newCollateralFactorMantissa The new collateral factor, scaled by 1e18\\n * @param newLiquidationThresholdMantissa The new liquidation threshold, scaled by 1e18\\n * @custom:event Emits NewCollateralFactor when collateral factor is updated\\n * and NewLiquidationThreshold when liquidation threshold is updated\\n * @custom:error MarketNotListed error is thrown when the market is not listed\\n * @custom:error InvalidCollateralFactor error is thrown when collateral factor is too high\\n * @custom:error InvalidLiquidationThreshold error is thrown when liquidation threshold is lower than collateral factor\\n * @custom:error PriceError is thrown when the oracle returns an invalid price for the asset\\n * @custom:access Controlled by AccessControlManager\\n */\\n function setCollateralFactor(\\n VToken vToken,\\n uint256 newCollateralFactorMantissa,\\n uint256 newLiquidationThresholdMantissa\\n ) external {\\n _checkAccessAllowed(\\\"setCollateralFactor(address,uint256,uint256)\\\");\\n\\n // Verify market is listed\\n Market storage market = markets[address(vToken)];\\n if (!market.isListed) {\\n revert MarketNotListed(address(vToken));\\n }\\n\\n // Check collateral factor <= 0.9\\n if (newCollateralFactorMantissa > MAX_COLLATERAL_FACTOR_MANTISSA) {\\n revert InvalidCollateralFactor();\\n }\\n\\n // Ensure that liquidation threshold <= 1\\n if (newLiquidationThresholdMantissa > MANTISSA_ONE) {\\n revert InvalidLiquidationThreshold();\\n }\\n\\n // Ensure that liquidation threshold >= CF\\n if (newLiquidationThresholdMantissa < newCollateralFactorMantissa) {\\n revert InvalidLiquidationThreshold();\\n }\\n\\n // If collateral factor != 0, fail if price == 0\\n if (newCollateralFactorMantissa != 0 && oracle.getUnderlyingPrice(address(vToken)) == 0) {\\n revert PriceError(address(vToken));\\n }\\n\\n uint256 oldCollateralFactorMantissa = market.collateralFactorMantissa;\\n if (newCollateralFactorMantissa != oldCollateralFactorMantissa) {\\n market.collateralFactorMantissa = newCollateralFactorMantissa;\\n emit NewCollateralFactor(vToken, oldCollateralFactorMantissa, newCollateralFactorMantissa);\\n }\\n\\n uint256 oldLiquidationThresholdMantissa = market.liquidationThresholdMantissa;\\n if (newLiquidationThresholdMantissa != oldLiquidationThresholdMantissa) {\\n market.liquidationThresholdMantissa = newLiquidationThresholdMantissa;\\n emit NewLiquidationThreshold(vToken, oldLiquidationThresholdMantissa, newLiquidationThresholdMantissa);\\n }\\n }\\n\\n /**\\n * @notice Sets liquidationIncentive\\n * @dev This function is restricted by the AccessControlManager\\n * @param newLiquidationIncentiveMantissa New liquidationIncentive scaled by 1e18\\n * @custom:event Emits NewLiquidationIncentive on success\\n * @custom:access Controlled by AccessControlManager\\n */\\n function setLiquidationIncentive(uint256 newLiquidationIncentiveMantissa) external {\\n require(newLiquidationIncentiveMantissa >= MANTISSA_ONE, \\\"liquidation incentive should be greater than 1e18\\\");\\n\\n _checkAccessAllowed(\\\"setLiquidationIncentive(uint256)\\\");\\n\\n // Save current value for use in log\\n uint256 oldLiquidationIncentiveMantissa = liquidationIncentiveMantissa;\\n\\n // Set liquidation incentive to new incentive\\n liquidationIncentiveMantissa = newLiquidationIncentiveMantissa;\\n\\n // Emit event with old incentive, new incentive\\n emit NewLiquidationIncentive(oldLiquidationIncentiveMantissa, newLiquidationIncentiveMantissa);\\n }\\n\\n /**\\n * @notice Add the market to the markets mapping and set it as listed\\n * @dev Only callable by the PoolRegistry\\n * @param vToken The address of the market (token) to list\\n * @custom:error MarketAlreadyListed is thrown if the market is already listed in this pool\\n * @custom:access Only PoolRegistry\\n */\\n function supportMarket(VToken vToken) external {\\n _checkSenderIs(poolRegistry);\\n\\n if (markets[address(vToken)].isListed) {\\n revert MarketAlreadyListed(address(vToken));\\n }\\n\\n require(vToken.isVToken(), \\\"Comptroller: Invalid vToken\\\"); // Sanity check to make sure its really a VToken\\n\\n Market storage newMarket = markets[address(vToken)];\\n newMarket.isListed = true;\\n newMarket.collateralFactorMantissa = 0;\\n newMarket.liquidationThresholdMantissa = 0;\\n\\n _addMarket(address(vToken));\\n\\n uint256 rewardDistributorsCount = rewardsDistributors.length;\\n\\n for (uint256 i; i < rewardDistributorsCount; ++i) {\\n rewardsDistributors[i].initializeMarket(address(vToken));\\n }\\n\\n emit MarketSupported(vToken);\\n }\\n\\n /**\\n * @notice Set the given borrow caps for the given vToken markets. Borrowing that brings total borrows to or above borrow cap will revert.\\n * @dev This function is restricted by the AccessControlManager\\n * @dev A borrow cap of type(uint256).max corresponds to unlimited borrowing.\\n * @dev Borrow caps smaller than the current total borrows are accepted. This way, new borrows will not be allowed\\n until the total borrows amount goes below the new borrow cap\\n * @param vTokens The addresses of the markets (tokens) to change the borrow caps for\\n * @param newBorrowCaps The new borrow cap values in underlying to be set. A value of type(uint256).max corresponds to unlimited borrowing.\\n * @custom:access Controlled by AccessControlManager\\n */\\n function setMarketBorrowCaps(VToken[] calldata vTokens, uint256[] calldata newBorrowCaps) external {\\n _checkAccessAllowed(\\\"setMarketBorrowCaps(address[],uint256[])\\\");\\n\\n uint256 numMarkets = vTokens.length;\\n uint256 numBorrowCaps = newBorrowCaps.length;\\n\\n require(numMarkets != 0 && numMarkets == numBorrowCaps, \\\"invalid input\\\");\\n\\n _ensureMaxLoops(numMarkets);\\n\\n for (uint256 i; i < numMarkets; ++i) {\\n borrowCaps[address(vTokens[i])] = newBorrowCaps[i];\\n emit NewBorrowCap(vTokens[i], newBorrowCaps[i]);\\n }\\n }\\n\\n /**\\n * @notice Set the given supply caps for the given vToken markets. Supply that brings total Supply to or above supply cap will revert.\\n * @dev This function is restricted by the AccessControlManager\\n * @dev A supply cap of type(uint256).max corresponds to unlimited supply.\\n * @dev Supply caps smaller than the current total supplies are accepted. This way, new supplies will not be allowed\\n until the total supplies amount goes below the new supply cap\\n * @param vTokens The addresses of the markets (tokens) to change the supply caps for\\n * @param newSupplyCaps The new supply cap values in underlying to be set. A value of type(uint256).max corresponds to unlimited supply.\\n * @custom:access Controlled by AccessControlManager\\n */\\n function setMarketSupplyCaps(VToken[] calldata vTokens, uint256[] calldata newSupplyCaps) external {\\n _checkAccessAllowed(\\\"setMarketSupplyCaps(address[],uint256[])\\\");\\n uint256 vTokensCount = vTokens.length;\\n\\n require(vTokensCount != 0, \\\"invalid number of markets\\\");\\n require(vTokensCount == newSupplyCaps.length, \\\"invalid number of markets\\\");\\n\\n _ensureMaxLoops(vTokensCount);\\n\\n for (uint256 i; i < vTokensCount; ++i) {\\n supplyCaps[address(vTokens[i])] = newSupplyCaps[i];\\n emit NewSupplyCap(vTokens[i], newSupplyCaps[i]);\\n }\\n }\\n\\n /**\\n * @notice Pause/unpause specified actions\\n * @dev This function is restricted by the AccessControlManager\\n * @param marketsList Markets to pause/unpause the actions on\\n * @param actionsList List of action ids to pause/unpause\\n * @param paused The new paused state (true=paused, false=unpaused)\\n * @custom:access Controlled by AccessControlManager\\n */\\n function setActionsPaused(VToken[] calldata marketsList, Action[] calldata actionsList, bool paused) external {\\n _checkAccessAllowed(\\\"setActionsPaused(address[],uint256[],bool)\\\");\\n\\n uint256 marketsCount = marketsList.length;\\n uint256 actionsCount = actionsList.length;\\n\\n _ensureMaxLoops(marketsCount * actionsCount);\\n\\n for (uint256 marketIdx; marketIdx < marketsCount; ++marketIdx) {\\n for (uint256 actionIdx; actionIdx < actionsCount; ++actionIdx) {\\n _setActionPaused(address(marketsList[marketIdx]), actionsList[actionIdx], paused);\\n }\\n }\\n }\\n\\n /**\\n * @notice Set the given collateral threshold for non-batch liquidations. Regular liquidations\\n * will fail if the collateral amount is less than this threshold. Liquidators should use batch\\n * operations like liquidateAccount or healAccount.\\n * @dev This function is restricted by the AccessControlManager\\n * @param newMinLiquidatableCollateral The new min liquidatable collateral (in USD).\\n * @custom:access Controlled by AccessControlManager\\n */\\n function setMinLiquidatableCollateral(uint256 newMinLiquidatableCollateral) external {\\n _checkAccessAllowed(\\\"setMinLiquidatableCollateral(uint256)\\\");\\n\\n uint256 oldMinLiquidatableCollateral = minLiquidatableCollateral;\\n minLiquidatableCollateral = newMinLiquidatableCollateral;\\n emit NewMinLiquidatableCollateral(oldMinLiquidatableCollateral, newMinLiquidatableCollateral);\\n }\\n\\n /**\\n * @notice Add a new RewardsDistributor and initialize it with all markets. We can add several RewardsDistributor\\n * contracts with the same rewardToken, and there could be overlaping among them considering the last reward slot (block or second)\\n * @dev Only callable by the admin\\n * @param _rewardsDistributor Address of the RewardDistributor contract to add\\n * @custom:access Only Governance\\n * @custom:event Emits NewRewardsDistributor with distributor address\\n */\\n function addRewardsDistributor(RewardsDistributor _rewardsDistributor) external onlyOwner {\\n require(!rewardsDistributorExists[address(_rewardsDistributor)], \\\"already exists\\\");\\n\\n uint256 rewardsDistributorsLen = rewardsDistributors.length;\\n _ensureMaxLoops(rewardsDistributorsLen + 1);\\n\\n rewardsDistributors.push(_rewardsDistributor);\\n rewardsDistributorExists[address(_rewardsDistributor)] = true;\\n\\n uint256 marketsCount = allMarkets.length;\\n\\n for (uint256 i; i < marketsCount; ++i) {\\n _rewardsDistributor.initializeMarket(address(allMarkets[i]));\\n }\\n\\n emit NewRewardsDistributor(address(_rewardsDistributor), address(_rewardsDistributor.rewardToken()));\\n }\\n\\n /**\\n * @notice Sets a new price oracle for the Comptroller\\n * @dev Only callable by the admin\\n * @param newOracle Address of the new price oracle to set\\n * @custom:event Emits NewPriceOracle on success\\n * @custom:error ZeroAddressNotAllowed is thrown when the new oracle address is zero\\n */\\n function setPriceOracle(ResilientOracleInterface newOracle) external onlyOwner {\\n ensureNonzeroAddress(address(newOracle));\\n\\n ResilientOracleInterface oldOracle = oracle;\\n oracle = newOracle;\\n emit NewPriceOracle(oldOracle, newOracle);\\n }\\n\\n /**\\n * @notice Set the for loop iteration limit to avoid DOS\\n * @param limit Limit for the max loops can execute at a time\\n */\\n function setMaxLoopsLimit(uint256 limit) external onlyOwner {\\n _setMaxLoopsLimit(limit);\\n }\\n\\n /**\\n * @notice Sets the prime token contract for the comptroller\\n * @param _prime Address of the Prime contract\\n */\\n function setPrimeToken(IPrime _prime) external onlyOwner {\\n ensureNonzeroAddress(address(_prime));\\n\\n emit NewPrimeToken(prime, _prime);\\n prime = _prime;\\n }\\n\\n /**\\n * @notice Enables forced liquidations for a market. If forced liquidation is enabled,\\n * borrows in the market may be liquidated regardless of the account liquidity\\n * @param vTokenBorrowed Borrowed vToken\\n * @param enable Whether to enable forced liquidations\\n */\\n function setForcedLiquidation(address vTokenBorrowed, bool enable) external {\\n _checkAccessAllowed(\\\"setForcedLiquidation(address,bool)\\\");\\n ensureNonzeroAddress(vTokenBorrowed);\\n\\n if (!markets[vTokenBorrowed].isListed) {\\n revert MarketNotListed(vTokenBorrowed);\\n }\\n\\n isForcedLiquidationEnabled[vTokenBorrowed] = enable;\\n emit IsForcedLiquidationEnabledUpdated(vTokenBorrowed, enable);\\n }\\n\\n /**\\n * @notice Determine the current account liquidity with respect to liquidation threshold requirements\\n * @dev The interface of this function is intentionally kept compatible with Compound and Venus Core\\n * @param account The account get liquidity for\\n * @return error Always NO_ERROR for compatibility with Venus core tooling\\n * @return liquidity Account liquidity in excess of liquidation threshold requirements,\\n * @return shortfall Account shortfall below liquidation threshold requirements\\n */\\n function getAccountLiquidity(\\n address account\\n ) external view returns (uint256 error, uint256 liquidity, uint256 shortfall) {\\n AccountLiquiditySnapshot memory snapshot = _getCurrentLiquiditySnapshot(account, _getLiquidationThreshold);\\n return (NO_ERROR, snapshot.liquidity, snapshot.shortfall);\\n }\\n\\n /**\\n * @notice Determine the current account liquidity with respect to collateral requirements\\n * @dev The interface of this function is intentionally kept compatible with Compound and Venus Core\\n * @param account The account get liquidity for\\n * @return error Always NO_ERROR for compatibility with Venus core tooling\\n * @return liquidity Account liquidity in excess of collateral requirements,\\n * @return shortfall Account shortfall below collateral requirements\\n */\\n function getBorrowingPower(\\n address account\\n ) external view returns (uint256 error, uint256 liquidity, uint256 shortfall) {\\n AccountLiquiditySnapshot memory snapshot = _getCurrentLiquiditySnapshot(account, _getCollateralFactor);\\n return (NO_ERROR, snapshot.liquidity, snapshot.shortfall);\\n }\\n\\n /**\\n * @notice Determine what the account liquidity would be if the given amounts were redeemed/borrowed\\n * @dev The interface of this function is intentionally kept compatible with Compound and Venus Core\\n * @param vTokenModify The market to hypothetically redeem/borrow in\\n * @param account The account to determine liquidity for\\n * @param redeemTokens The number of tokens to hypothetically redeem\\n * @param borrowAmount The amount of underlying to hypothetically borrow\\n * @return error Always NO_ERROR for compatibility with Venus core tooling\\n * @return liquidity Hypothetical account liquidity in excess of collateral requirements,\\n * @return shortfall Hypothetical account shortfall below collateral requirements\\n */\\n function getHypotheticalAccountLiquidity(\\n address account,\\n address vTokenModify,\\n uint256 redeemTokens,\\n uint256 borrowAmount\\n ) external view returns (uint256 error, uint256 liquidity, uint256 shortfall) {\\n AccountLiquiditySnapshot memory snapshot = _getHypotheticalLiquiditySnapshot(\\n account,\\n VToken(vTokenModify),\\n redeemTokens,\\n borrowAmount,\\n _getCollateralFactor\\n );\\n return (NO_ERROR, snapshot.liquidity, snapshot.shortfall);\\n }\\n\\n /**\\n * @notice Return all of the markets\\n * @dev The automatic getter may be used to access an individual market.\\n * @return markets The list of market addresses\\n */\\n function getAllMarkets() external view override returns (VToken[] memory) {\\n return allMarkets;\\n }\\n\\n /**\\n * @notice Check if a market is marked as listed (active)\\n * @param vToken vToken Address for the market to check\\n * @return listed True if listed otherwise false\\n */\\n function isMarketListed(VToken vToken) external view returns (bool) {\\n return markets[address(vToken)].isListed;\\n }\\n\\n /*** Assets You Are In ***/\\n\\n /**\\n * @notice Returns whether the given account is entered in a given market\\n * @param account The address of the account to check\\n * @param vToken The vToken to check\\n * @return True if the account is in the market specified, otherwise false.\\n */\\n function checkMembership(address account, VToken vToken) external view returns (bool) {\\n return markets[address(vToken)].accountMembership[account];\\n }\\n\\n /**\\n * @notice Calculate number of tokens of collateral asset to seize given an underlying amount\\n * @dev Used in liquidation (called in vToken.liquidateBorrowFresh)\\n * @param vTokenBorrowed The address of the borrowed vToken\\n * @param vTokenCollateral The address of the collateral vToken\\n * @param actualRepayAmount The amount of vTokenBorrowed underlying to convert into vTokenCollateral tokens\\n * @return error Always NO_ERROR for compatibility with Venus core tooling\\n * @return tokensToSeize Number of vTokenCollateral tokens to be seized in a liquidation\\n * @custom:error PriceError if the oracle returns an invalid price\\n */\\n function liquidateCalculateSeizeTokens(\\n address vTokenBorrowed,\\n address vTokenCollateral,\\n uint256 actualRepayAmount\\n ) external view override returns (uint256 error, uint256 tokensToSeize) {\\n /* Read oracle prices for borrowed and collateral markets */\\n uint256 priceBorrowedMantissa = _safeGetUnderlyingPrice(VToken(vTokenBorrowed));\\n uint256 priceCollateralMantissa = _safeGetUnderlyingPrice(VToken(vTokenCollateral));\\n\\n /*\\n * Get the exchange rate and calculate the number of collateral tokens to seize:\\n * seizeAmount = actualRepayAmount * liquidationIncentive * priceBorrowed / priceCollateral\\n * seizeTokens = seizeAmount / exchangeRate\\n * = actualRepayAmount * (liquidationIncentive * priceBorrowed) / (priceCollateral * exchangeRate)\\n */\\n uint256 exchangeRateMantissa = VToken(vTokenCollateral).exchangeRateStored(); // Note: reverts on error\\n uint256 seizeTokens;\\n Exp memory numerator;\\n Exp memory denominator;\\n Exp memory ratio;\\n\\n numerator = mul_(Exp({ mantissa: liquidationIncentiveMantissa }), Exp({ mantissa: priceBorrowedMantissa }));\\n denominator = mul_(Exp({ mantissa: priceCollateralMantissa }), Exp({ mantissa: exchangeRateMantissa }));\\n ratio = div_(numerator, denominator);\\n\\n seizeTokens = mul_ScalarTruncate(ratio, actualRepayAmount);\\n\\n return (NO_ERROR, seizeTokens);\\n }\\n\\n /**\\n * @notice Returns reward speed given a vToken\\n * @param vToken The vToken to get the reward speeds for\\n * @return rewardSpeeds Array of total supply and borrow speeds and reward token for all reward distributors\\n */\\n function getRewardsByMarket(address vToken) external view returns (RewardSpeeds[] memory rewardSpeeds) {\\n uint256 rewardsDistributorsLength = rewardsDistributors.length;\\n rewardSpeeds = new RewardSpeeds[](rewardsDistributorsLength);\\n for (uint256 i; i < rewardsDistributorsLength; ++i) {\\n RewardsDistributor rewardsDistributor = rewardsDistributors[i];\\n address rewardToken = address(rewardsDistributor.rewardToken());\\n rewardSpeeds[i] = RewardSpeeds({\\n rewardToken: rewardToken,\\n supplySpeed: rewardsDistributor.rewardTokenSupplySpeeds(vToken),\\n borrowSpeed: rewardsDistributor.rewardTokenBorrowSpeeds(vToken)\\n });\\n }\\n return rewardSpeeds;\\n }\\n\\n /**\\n * @notice Return all reward distributors for this pool\\n * @return Array of RewardDistributor addresses\\n */\\n function getRewardDistributors() external view returns (RewardsDistributor[] memory) {\\n return rewardsDistributors;\\n }\\n\\n /**\\n * @notice A marker method that returns true for a valid Comptroller contract\\n * @return Always true\\n */\\n function isComptroller() external pure override returns (bool) {\\n return true;\\n }\\n\\n /**\\n * @notice Update the prices of all the tokens associated with the provided account\\n * @param account Address of the account to get associated tokens with\\n */\\n function updatePrices(address account) public {\\n VToken[] memory vTokens = getAssetsIn(account);\\n uint256 vTokensCount = vTokens.length;\\n\\n ResilientOracleInterface oracle_ = oracle;\\n\\n for (uint256 i; i < vTokensCount; ++i) {\\n oracle_.updatePrice(address(vTokens[i]));\\n }\\n }\\n\\n /**\\n * @notice Checks if a certain action is paused on a market\\n * @param market vToken address\\n * @param action Action to check\\n * @return paused True if the action is paused otherwise false\\n */\\n function actionPaused(address market, Action action) public view returns (bool) {\\n return _actionPaused[market][action];\\n }\\n\\n /**\\n * @notice Returns the assets an account has entered\\n * @param account The address of the account to pull assets for\\n * @return A list with the assets the account has entered\\n */\\n function getAssetsIn(address account) public view returns (VToken[] memory) {\\n uint256 len;\\n VToken[] memory _accountAssets = accountAssets[account];\\n uint256 _accountAssetsLength = _accountAssets.length;\\n\\n VToken[] memory assetsIn = new VToken[](_accountAssetsLength);\\n\\n for (uint256 i; i < _accountAssetsLength; ++i) {\\n Market storage market = markets[address(_accountAssets[i])];\\n if (market.isListed) {\\n assetsIn[len] = _accountAssets[i];\\n ++len;\\n }\\n }\\n\\n assembly {\\n mstore(assetsIn, len)\\n }\\n\\n return assetsIn;\\n }\\n\\n /**\\n * @notice Add the market to the borrower's \\\"assets in\\\" for liquidity calculations\\n * @param vToken The market to enter\\n * @param borrower The address of the account to modify\\n */\\n function _addToMarket(VToken vToken, address borrower) internal {\\n _checkActionPauseState(address(vToken), Action.ENTER_MARKET);\\n Market storage marketToJoin = markets[address(vToken)];\\n\\n if (!marketToJoin.isListed) {\\n revert MarketNotListed(address(vToken));\\n }\\n\\n if (marketToJoin.accountMembership[borrower]) {\\n // already joined\\n return;\\n }\\n\\n // survived the gauntlet, add to list\\n // NOTE: we store these somewhat redundantly as a significant optimization\\n // this avoids having to iterate through the list for the most common use cases\\n // that is, only when we need to perform liquidity checks\\n // and not whenever we want to check if an account is in a particular market\\n marketToJoin.accountMembership[borrower] = true;\\n accountAssets[borrower].push(vToken);\\n\\n emit MarketEntered(vToken, borrower);\\n }\\n\\n /**\\n * @notice Internal function to validate that a market hasn't already been added\\n * and if it hasn't adds it\\n * @param vToken The market to support\\n */\\n function _addMarket(address vToken) internal {\\n uint256 marketsCount = allMarkets.length;\\n\\n for (uint256 i; i < marketsCount; ++i) {\\n if (allMarkets[i] == VToken(vToken)) {\\n revert MarketAlreadyListed(vToken);\\n }\\n }\\n allMarkets.push(VToken(vToken));\\n marketsCount = allMarkets.length;\\n _ensureMaxLoops(marketsCount);\\n }\\n\\n /**\\n * @dev Pause/unpause an action on a market\\n * @param market Market to pause/unpause the action on\\n * @param action Action id to pause/unpause\\n * @param paused The new paused state (true=paused, false=unpaused)\\n */\\n function _setActionPaused(address market, Action action, bool paused) internal {\\n require(markets[market].isListed, \\\"cannot pause a market that is not listed\\\");\\n _actionPaused[market][action] = paused;\\n emit ActionPausedMarket(VToken(market), action, paused);\\n }\\n\\n /**\\n * @dev Internal function to check that vTokens can be safely redeemed for the underlying asset.\\n * @param vToken Address of the vTokens to redeem\\n * @param redeemer Account redeeming the tokens\\n * @param redeemTokens The number of tokens to redeem\\n */\\n function _checkRedeemAllowed(address vToken, address redeemer, uint256 redeemTokens) internal {\\n Market storage market = markets[vToken];\\n\\n if (!market.isListed) {\\n revert MarketNotListed(address(vToken));\\n }\\n\\n /* If the redeemer is not 'in' the market, then we can bypass the liquidity check */\\n if (!market.accountMembership[redeemer]) {\\n return;\\n }\\n\\n // Update the prices of tokens\\n updatePrices(redeemer);\\n\\n /* Otherwise, perform a hypothetical liquidity check to guard against shortfall */\\n AccountLiquiditySnapshot memory snapshot = _getHypotheticalLiquiditySnapshot(\\n redeemer,\\n VToken(vToken),\\n redeemTokens,\\n 0,\\n _getCollateralFactor\\n );\\n if (snapshot.shortfall > 0) {\\n revert InsufficientLiquidity();\\n }\\n }\\n\\n /**\\n * @notice Get the total collateral, weighted collateral, borrow balance, liquidity, shortfall\\n * @param account The account to get the snapshot for\\n * @param weight The function to compute the weight of the collateral \\u2013\\u00a0either collateral factor or\\n * liquidation threshold. Accepts the address of the vToken and returns the weight as Exp.\\n * @dev Note that we calculate the exchangeRateStored for each collateral vToken using stored data,\\n * without calculating accumulated interest.\\n * @return snapshot Account liquidity snapshot\\n */\\n function _getCurrentLiquiditySnapshot(\\n address account,\\n function(VToken) internal view returns (Exp memory) weight\\n ) internal view returns (AccountLiquiditySnapshot memory snapshot) {\\n return _getHypotheticalLiquiditySnapshot(account, VToken(address(0)), 0, 0, weight);\\n }\\n\\n /**\\n * @notice Determine what the supply/borrow balances would be if the given amounts were redeemed/borrowed\\n * @param vTokenModify The market to hypothetically redeem/borrow in\\n * @param account The account to determine liquidity for\\n * @param redeemTokens The number of tokens to hypothetically redeem\\n * @param borrowAmount The amount of underlying to hypothetically borrow\\n * @param weight The function to compute the weight of the collateral \\u2013\\u00a0either collateral factor or\\n liquidation threshold. Accepts the address of the VToken and returns the weight\\n * @dev Note that we calculate the exchangeRateStored for each collateral vToken using stored data,\\n * without calculating accumulated interest.\\n * @return snapshot Account liquidity snapshot\\n */\\n function _getHypotheticalLiquiditySnapshot(\\n address account,\\n VToken vTokenModify,\\n uint256 redeemTokens,\\n uint256 borrowAmount,\\n function(VToken) internal view returns (Exp memory) weight\\n ) internal view returns (AccountLiquiditySnapshot memory snapshot) {\\n // For each asset the account is in\\n VToken[] memory assets = getAssetsIn(account);\\n uint256 assetsCount = assets.length;\\n\\n for (uint256 i; i < assetsCount; ++i) {\\n VToken asset = assets[i];\\n\\n // Read the balances and exchange rate from the vToken\\n (uint256 vTokenBalance, uint256 borrowBalance, uint256 exchangeRateMantissa) = _safeGetAccountSnapshot(\\n asset,\\n account\\n );\\n\\n // Get the normalized price of the asset\\n Exp memory oraclePrice = Exp({ mantissa: _safeGetUnderlyingPrice(asset) });\\n\\n // Pre-compute conversion factors from vTokens -> usd\\n Exp memory vTokenPrice = mul_(Exp({ mantissa: exchangeRateMantissa }), oraclePrice);\\n Exp memory weightedVTokenPrice = mul_(weight(asset), vTokenPrice);\\n\\n // weightedCollateral += weightedVTokenPrice * vTokenBalance\\n snapshot.weightedCollateral = mul_ScalarTruncateAddUInt(\\n weightedVTokenPrice,\\n vTokenBalance,\\n snapshot.weightedCollateral\\n );\\n\\n // totalCollateral += vTokenPrice * vTokenBalance\\n snapshot.totalCollateral = mul_ScalarTruncateAddUInt(vTokenPrice, vTokenBalance, snapshot.totalCollateral);\\n\\n // borrows += oraclePrice * borrowBalance\\n snapshot.borrows = mul_ScalarTruncateAddUInt(oraclePrice, borrowBalance, snapshot.borrows);\\n\\n // Calculate effects of interacting with vTokenModify\\n if (asset == vTokenModify) {\\n // redeem effect\\n // effects += tokensToDenom * redeemTokens\\n snapshot.effects = mul_ScalarTruncateAddUInt(weightedVTokenPrice, redeemTokens, snapshot.effects);\\n\\n // borrow effect\\n // effects += oraclePrice * borrowAmount\\n snapshot.effects = mul_ScalarTruncateAddUInt(oraclePrice, borrowAmount, snapshot.effects);\\n }\\n }\\n\\n uint256 borrowPlusEffects = snapshot.borrows + snapshot.effects;\\n // These are safe, as the underflow condition is checked first\\n unchecked {\\n if (snapshot.weightedCollateral > borrowPlusEffects) {\\n snapshot.liquidity = snapshot.weightedCollateral - borrowPlusEffects;\\n snapshot.shortfall = 0;\\n } else {\\n snapshot.liquidity = 0;\\n snapshot.shortfall = borrowPlusEffects - snapshot.weightedCollateral;\\n }\\n }\\n\\n return snapshot;\\n }\\n\\n /**\\n * @dev Retrieves price from oracle for an asset and checks it is nonzero\\n * @param asset Address for asset to query price\\n * @return Underlying price\\n */\\n function _safeGetUnderlyingPrice(VToken asset) internal view returns (uint256) {\\n uint256 oraclePriceMantissa = oracle.getUnderlyingPrice(address(asset));\\n if (oraclePriceMantissa == 0) {\\n revert PriceError(address(asset));\\n }\\n return oraclePriceMantissa;\\n }\\n\\n /**\\n * @dev Return collateral factor for a market\\n * @param asset Address for asset\\n * @return Collateral factor as exponential\\n */\\n function _getCollateralFactor(VToken asset) internal view returns (Exp memory) {\\n return Exp({ mantissa: markets[address(asset)].collateralFactorMantissa });\\n }\\n\\n /**\\n * @dev Retrieves liquidation threshold for a market as an exponential\\n * @param asset Address for asset to liquidation threshold\\n * @return Liquidation threshold as exponential\\n */\\n function _getLiquidationThreshold(VToken asset) internal view returns (Exp memory) {\\n return Exp({ mantissa: markets[address(asset)].liquidationThresholdMantissa });\\n }\\n\\n /**\\n * @dev Returns supply and borrow balances of user in vToken, reverts on failure\\n * @param vToken Market to query\\n * @param user Account address\\n * @return vTokenBalance Balance of vTokens, the same as vToken.balanceOf(user)\\n * @return borrowBalance Borrowed amount, including the interest\\n * @return exchangeRateMantissa Stored exchange rate\\n */\\n function _safeGetAccountSnapshot(\\n VToken vToken,\\n address user\\n ) internal view returns (uint256 vTokenBalance, uint256 borrowBalance, uint256 exchangeRateMantissa) {\\n uint256 err;\\n (err, vTokenBalance, borrowBalance, exchangeRateMantissa) = vToken.getAccountSnapshot(user);\\n if (err != 0) {\\n revert SnapshotError(address(vToken), user);\\n }\\n return (vTokenBalance, borrowBalance, exchangeRateMantissa);\\n }\\n\\n /// @notice Reverts if the call is not from expectedSender\\n /// @param expectedSender Expected transaction sender\\n function _checkSenderIs(address expectedSender) internal view {\\n if (msg.sender != expectedSender) {\\n revert UnexpectedSender(expectedSender, msg.sender);\\n }\\n }\\n\\n /// @notice Reverts if a certain action is paused on a market\\n /// @param market Market to check\\n /// @param action Action to check\\n function _checkActionPauseState(address market, Action action) private view {\\n if (actionPaused(market, action)) {\\n revert ActionPaused(market, action);\\n }\\n }\\n}\\n\",\"keccak256\":\"0x7c6e1c6264e4681f82a9ac1bcd9155197a930033291ee5561ad97a56006f5e9c\",\"license\":\"BSD-3-Clause\"},\"contracts/ComptrollerInterface.sol\":{\"content\":\"// SPDX-License-Identifier: BSD-3-Clause\\npragma solidity ^0.8.25;\\n\\nimport { ResilientOracleInterface } from \\\"@venusprotocol/oracle/contracts/interfaces/OracleInterface.sol\\\";\\n\\nimport { VToken } from \\\"./VToken.sol\\\";\\nimport { RewardsDistributor } from \\\"./Rewards/RewardsDistributor.sol\\\";\\n\\nenum Action {\\n MINT,\\n REDEEM,\\n BORROW,\\n REPAY,\\n SEIZE,\\n LIQUIDATE,\\n TRANSFER,\\n ENTER_MARKET,\\n EXIT_MARKET\\n}\\n\\n/**\\n * @title ComptrollerInterface\\n * @author Venus\\n * @notice Interface implemented by the `Comptroller` contract.\\n */\\ninterface ComptrollerInterface {\\n /*** Assets You Are In ***/\\n\\n function enterMarkets(address[] calldata vTokens) external returns (uint256[] memory);\\n\\n function exitMarket(address vToken) external returns (uint256);\\n\\n /*** Policy Hooks ***/\\n\\n function preMintHook(address vToken, address minter, uint256 mintAmount) external;\\n\\n function preRedeemHook(address vToken, address redeemer, uint256 redeemTokens) external;\\n\\n function preBorrowHook(address vToken, address borrower, uint256 borrowAmount) external;\\n\\n function preRepayHook(address vToken, address borrower) external;\\n\\n function preLiquidateHook(\\n address vTokenBorrowed,\\n address vTokenCollateral,\\n address borrower,\\n uint256 repayAmount,\\n bool skipLiquidityCheck\\n ) external;\\n\\n function preSeizeHook(\\n address vTokenCollateral,\\n address vTokenBorrowed,\\n address liquidator,\\n address borrower\\n ) external;\\n\\n function borrowVerify(address vToken, address borrower, uint borrowAmount) external;\\n\\n function mintVerify(address vToken, address minter, uint mintAmount, uint mintTokens) external;\\n\\n function redeemVerify(address vToken, address redeemer, uint redeemAmount, uint redeemTokens) external;\\n\\n function repayBorrowVerify(\\n address vToken,\\n address payer,\\n address borrower,\\n uint repayAmount,\\n uint borrowerIndex\\n ) external;\\n\\n function liquidateBorrowVerify(\\n address vTokenBorrowed,\\n address vTokenCollateral,\\n address liquidator,\\n address borrower,\\n uint repayAmount,\\n uint seizeTokens\\n ) external;\\n\\n function seizeVerify(\\n address vTokenCollateral,\\n address vTokenBorrowed,\\n address liquidator,\\n address borrower,\\n uint seizeTokens\\n ) external;\\n\\n function transferVerify(address vToken, address src, address dst, uint transferTokens) external;\\n\\n function preTransferHook(address vToken, address src, address dst, uint256 transferTokens) external;\\n\\n function isComptroller() external view returns (bool);\\n\\n /*** Liquidity/Liquidation Calculations ***/\\n\\n function liquidateCalculateSeizeTokens(\\n address vTokenBorrowed,\\n address vTokenCollateral,\\n uint256 repayAmount\\n ) external view returns (uint256, uint256);\\n\\n function getAllMarkets() external view returns (VToken[] memory);\\n\\n function actionPaused(address market, Action action) external view returns (bool);\\n}\\n\\n/**\\n * @title ComptrollerViewInterface\\n * @author Venus\\n * @notice Interface implemented by the `Comptroller` contract, including only some util view functions.\\n */\\ninterface ComptrollerViewInterface {\\n function markets(address) external view returns (bool, uint256);\\n\\n function oracle() external view returns (ResilientOracleInterface);\\n\\n function getAssetsIn(address) external view returns (VToken[] memory);\\n\\n function closeFactorMantissa() external view returns (uint256);\\n\\n function liquidationIncentiveMantissa() external view returns (uint256);\\n\\n function minLiquidatableCollateral() external view returns (uint256);\\n\\n function getRewardDistributors() external view returns (RewardsDistributor[] memory);\\n\\n function getAllMarkets() external view returns (VToken[] memory);\\n\\n function borrowCaps(address) external view returns (uint256);\\n\\n function supplyCaps(address) external view returns (uint256);\\n\\n function approvedDelegates(address user, address delegate) external view returns (bool);\\n}\\n\",\"keccak256\":\"0xcbf7f9977472650c61345b7cbde23e81f626e1f8863bab4c6ce3dacfc7604919\",\"license\":\"BSD-3-Clause\"},\"contracts/ComptrollerStorage.sol\":{\"content\":\"// SPDX-License-Identifier: BSD-3-Clause\\npragma solidity 0.8.25;\\n\\nimport { ResilientOracleInterface } from \\\"@venusprotocol/oracle/contracts/interfaces/OracleInterface.sol\\\";\\n\\nimport { VToken } from \\\"./VToken.sol\\\";\\nimport { RewardsDistributor } from \\\"./Rewards/RewardsDistributor.sol\\\";\\nimport { IPrime } from \\\"@venusprotocol/venus-protocol/contracts/Tokens/Prime/Interfaces/IPrime.sol\\\";\\nimport { Action } from \\\"./ComptrollerInterface.sol\\\";\\n\\n/**\\n * @title ComptrollerStorage\\n * @author Venus\\n * @notice Storage layout for the `Comptroller` contract.\\n */\\ncontract ComptrollerStorage {\\n struct LiquidationOrder {\\n VToken vTokenCollateral;\\n VToken vTokenBorrowed;\\n uint256 repayAmount;\\n }\\n\\n struct AccountLiquiditySnapshot {\\n uint256 totalCollateral;\\n uint256 weightedCollateral;\\n uint256 borrows;\\n uint256 effects;\\n uint256 liquidity;\\n uint256 shortfall;\\n }\\n\\n struct RewardSpeeds {\\n address rewardToken;\\n uint256 supplySpeed;\\n uint256 borrowSpeed;\\n }\\n\\n struct Market {\\n // Whether or not this market is listed\\n bool isListed;\\n // Multiplier representing the most one can borrow against their collateral in this market.\\n // For instance, 0.9 to allow borrowing 90% of collateral value.\\n // Must be between 0 and 1, and stored as a mantissa.\\n uint256 collateralFactorMantissa;\\n // Multiplier representing the collateralization after which the borrow is eligible\\n // for liquidation. For instance, 0.8 liquidate when the borrow is 80% of collateral\\n // value. Must be between 0 and collateral factor, stored as a mantissa.\\n uint256 liquidationThresholdMantissa;\\n // Per-market mapping of \\\"accounts in this asset\\\"\\n mapping(address => bool) accountMembership;\\n }\\n\\n /**\\n * @notice Oracle which gives the price of any given asset\\n */\\n ResilientOracleInterface public oracle;\\n\\n /**\\n * @notice Multiplier used to calculate the maximum repayAmount when liquidating a borrow\\n */\\n uint256 public closeFactorMantissa;\\n\\n /**\\n * @notice Multiplier representing the discount on collateral that a liquidator receives\\n */\\n uint256 public liquidationIncentiveMantissa;\\n\\n /**\\n * @notice Per-account mapping of \\\"assets you are in\\\"\\n */\\n mapping(address => VToken[]) public accountAssets;\\n\\n /**\\n * @notice Official mapping of vTokens -> Market metadata\\n * @dev Used e.g. to determine if a market is supported\\n */\\n mapping(address => Market) public markets;\\n\\n /// @notice A list of all markets\\n VToken[] public allMarkets;\\n\\n /// @notice Borrow caps enforced by borrowAllowed for each vToken address. Defaults to zero which restricts borrowing.\\n mapping(address => uint256) public borrowCaps;\\n\\n /// @notice Minimal collateral required for regular (non-batch) liquidations\\n uint256 public minLiquidatableCollateral;\\n\\n /// @notice Supply caps enforced by mintAllowed for each vToken address. Defaults to zero which corresponds to minting not allowed\\n mapping(address => uint256) public supplyCaps;\\n\\n /// @notice True if a certain action is paused on a certain market\\n mapping(address => mapping(Action => bool)) internal _actionPaused;\\n\\n // List of Reward Distributors added\\n RewardsDistributor[] internal rewardsDistributors;\\n\\n // Used to check if rewards distributor is added\\n mapping(address => bool) internal rewardsDistributorExists;\\n\\n /// @notice Flag indicating whether forced liquidation enabled for a market\\n mapping(address => bool) public isForcedLiquidationEnabled;\\n\\n uint256 internal constant NO_ERROR = 0;\\n\\n // closeFactorMantissa must be strictly greater than this value\\n uint256 internal constant MIN_CLOSE_FACTOR_MANTISSA = 0.05e18; // 0.05\\n\\n // closeFactorMantissa must not exceed this value\\n uint256 internal constant MAX_CLOSE_FACTOR_MANTISSA = 0.9e18; // 0.9\\n\\n // No collateralFactorMantissa may exceed this value\\n uint256 internal constant MAX_COLLATERAL_FACTOR_MANTISSA = 0.95e18; // 0.95\\n\\n /// Prime token address\\n IPrime public prime;\\n\\n /// @notice Whether the delegate is allowed to borrow or redeem on behalf of the user\\n //mapping(address user => mapping (address delegate => bool approved)) public approvedDelegates;\\n mapping(address => mapping(address => bool)) public approvedDelegates;\\n\\n /**\\n * @dev This empty reserved space is put in place to allow future versions to add new\\n * variables without shifting down storage in the inheritance chain.\\n * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\\n */\\n uint256[47] private __gap;\\n}\\n\",\"keccak256\":\"0x4df44cb65ac152bac2be4b4545430e703005a74a55b72e144100b16421bd8bfd\",\"license\":\"BSD-3-Clause\"},\"contracts/ErrorReporter.sol\":{\"content\":\"// SPDX-License-Identifier: BSD-3-Clause\\npragma solidity 0.8.25;\\n\\n/**\\n * @title TokenErrorReporter\\n * @author Venus\\n * @notice Errors that can be thrown by the `VToken` contract.\\n */\\ncontract TokenErrorReporter {\\n uint256 public constant NO_ERROR = 0; // support legacy return codes\\n\\n error TransferNotAllowed();\\n\\n error MintFreshnessCheck();\\n\\n error RedeemFreshnessCheck();\\n error RedeemTransferOutNotPossible();\\n\\n error BorrowFreshnessCheck();\\n error BorrowCashNotAvailable();\\n error DelegateNotApproved();\\n\\n error RepayBorrowFreshnessCheck();\\n\\n error HealBorrowUnauthorized();\\n error ForceLiquidateBorrowUnauthorized();\\n\\n error LiquidateFreshnessCheck();\\n error LiquidateCollateralFreshnessCheck();\\n error LiquidateAccrueCollateralInterestFailed(uint256 errorCode);\\n error LiquidateLiquidatorIsBorrower();\\n error LiquidateCloseAmountIsZero();\\n error LiquidateCloseAmountIsUintMax();\\n\\n error LiquidateSeizeLiquidatorIsBorrower();\\n\\n error ProtocolSeizeShareTooBig();\\n\\n error SetReserveFactorFreshCheck();\\n error SetReserveFactorBoundsCheck();\\n\\n error AddReservesFactorFreshCheck(uint256 actualAddAmount);\\n\\n error ReduceReservesFreshCheck();\\n error ReduceReservesCashNotAvailable();\\n error ReduceReservesCashValidation();\\n\\n error SetInterestRateModelFreshCheck();\\n}\\n\",\"keccak256\":\"0x7a7ced1caaac2d9242659ebd50b99f308c725ce958ac9e11f7ada404b1d97a7b\",\"license\":\"BSD-3-Clause\"},\"contracts/ExponentialNoError.sol\":{\"content\":\"// SPDX-License-Identifier: BSD-3-Clause\\npragma solidity 0.8.25;\\n\\nimport { EXP_SCALE as EXP_SCALE_, MANTISSA_ONE as MANTISSA_ONE_ } from \\\"./lib/constants.sol\\\";\\n\\n/**\\n * @title Exponential module for storing fixed-precision decimals\\n * @author Compound\\n * @notice Exp is a struct which stores decimals with a fixed precision of 18 decimal places.\\n * Thus, if we wanted to store the 5.1, mantissa would store 5.1e18. That is:\\n * `Exp({mantissa: 5100000000000000000})`.\\n */\\ncontract ExponentialNoError {\\n struct Exp {\\n uint256 mantissa;\\n }\\n\\n struct Double {\\n uint256 mantissa;\\n }\\n\\n uint256 internal constant EXP_SCALE = EXP_SCALE_;\\n uint256 internal constant DOUBLE_SCALE = 1e36;\\n uint256 internal constant HALF_EXP_SCALE = EXP_SCALE / 2;\\n uint256 internal constant MANTISSA_ONE = MANTISSA_ONE_;\\n\\n /**\\n * @dev Truncates the given exp to a whole number value.\\n * For example, truncate(Exp{mantissa: 15 * EXP_SCALE}) = 15\\n */\\n function truncate(Exp memory exp) internal pure returns (uint256) {\\n // Note: We are not using careful math here as we're performing a division that cannot fail\\n return exp.mantissa / EXP_SCALE;\\n }\\n\\n /**\\n * @dev Multiply an Exp by a scalar, then truncate to return an unsigned integer.\\n */\\n // solhint-disable-next-line func-name-mixedcase\\n function mul_ScalarTruncate(Exp memory a, uint256 scalar) internal pure returns (uint256) {\\n Exp memory product = mul_(a, scalar);\\n return truncate(product);\\n }\\n\\n /**\\n * @dev Multiply an Exp by a scalar, truncate, then add an to an unsigned integer, returning an unsigned integer.\\n */\\n // solhint-disable-next-line func-name-mixedcase\\n function mul_ScalarTruncateAddUInt(Exp memory a, uint256 scalar, uint256 addend) internal pure returns (uint256) {\\n Exp memory product = mul_(a, scalar);\\n return add_(truncate(product), addend);\\n }\\n\\n /**\\n * @dev Checks if first Exp is less than second Exp.\\n */\\n function lessThanExp(Exp memory left, Exp memory right) internal pure returns (bool) {\\n return left.mantissa < right.mantissa;\\n }\\n\\n function safe224(uint256 n, string memory errorMessage) internal pure returns (uint224) {\\n require(n <= type(uint224).max, errorMessage);\\n return uint224(n);\\n }\\n\\n function safe32(uint256 n, string memory errorMessage) internal pure returns (uint32) {\\n require(n <= type(uint32).max, errorMessage);\\n return uint32(n);\\n }\\n\\n function add_(Exp memory a, Exp memory b) internal pure returns (Exp memory) {\\n return Exp({ mantissa: add_(a.mantissa, b.mantissa) });\\n }\\n\\n function add_(Double memory a, Double memory b) internal pure returns (Double memory) {\\n return Double({ mantissa: add_(a.mantissa, b.mantissa) });\\n }\\n\\n function add_(uint256 a, uint256 b) internal pure returns (uint256) {\\n return a + b;\\n }\\n\\n function sub_(Exp memory a, Exp memory b) internal pure returns (Exp memory) {\\n return Exp({ mantissa: sub_(a.mantissa, b.mantissa) });\\n }\\n\\n function sub_(Double memory a, Double memory b) internal pure returns (Double memory) {\\n return Double({ mantissa: sub_(a.mantissa, b.mantissa) });\\n }\\n\\n function sub_(uint256 a, uint256 b) internal pure returns (uint256) {\\n return a - b;\\n }\\n\\n function mul_(Exp memory a, Exp memory b) internal pure returns (Exp memory) {\\n return Exp({ mantissa: mul_(a.mantissa, b.mantissa) / EXP_SCALE });\\n }\\n\\n function mul_(Exp memory a, uint256 b) internal pure returns (Exp memory) {\\n return Exp({ mantissa: mul_(a.mantissa, b) });\\n }\\n\\n function mul_(uint256 a, Exp memory b) internal pure returns (uint256) {\\n return mul_(a, b.mantissa) / EXP_SCALE;\\n }\\n\\n function mul_(Double memory a, Double memory b) internal pure returns (Double memory) {\\n return Double({ mantissa: mul_(a.mantissa, b.mantissa) / DOUBLE_SCALE });\\n }\\n\\n function mul_(Double memory a, uint256 b) internal pure returns (Double memory) {\\n return Double({ mantissa: mul_(a.mantissa, b) });\\n }\\n\\n function mul_(uint256 a, Double memory b) internal pure returns (uint256) {\\n return mul_(a, b.mantissa) / DOUBLE_SCALE;\\n }\\n\\n function mul_(uint256 a, uint256 b) internal pure returns (uint256) {\\n return a * b;\\n }\\n\\n function div_(Exp memory a, Exp memory b) internal pure returns (Exp memory) {\\n return Exp({ mantissa: div_(mul_(a.mantissa, EXP_SCALE), b.mantissa) });\\n }\\n\\n function div_(Exp memory a, uint256 b) internal pure returns (Exp memory) {\\n return Exp({ mantissa: div_(a.mantissa, b) });\\n }\\n\\n function div_(uint256 a, Exp memory b) internal pure returns (uint256) {\\n return div_(mul_(a, EXP_SCALE), b.mantissa);\\n }\\n\\n function div_(Double memory a, Double memory b) internal pure returns (Double memory) {\\n return Double({ mantissa: div_(mul_(a.mantissa, DOUBLE_SCALE), b.mantissa) });\\n }\\n\\n function div_(Double memory a, uint256 b) internal pure returns (Double memory) {\\n return Double({ mantissa: div_(a.mantissa, b) });\\n }\\n\\n function div_(uint256 a, Double memory b) internal pure returns (uint256) {\\n return div_(mul_(a, DOUBLE_SCALE), b.mantissa);\\n }\\n\\n function div_(uint256 a, uint256 b) internal pure returns (uint256) {\\n return a / b;\\n }\\n\\n function fraction(uint256 a, uint256 b) internal pure returns (Double memory) {\\n return Double({ mantissa: div_(mul_(a, DOUBLE_SCALE), b) });\\n }\\n}\\n\",\"keccak256\":\"0x8afbe8a24fe3539c124e718681ed3dcebd24c40dd53095786c0155998213b9b0\",\"license\":\"BSD-3-Clause\"},\"contracts/InterestRateModel.sol\":{\"content\":\"// SPDX-License-Identifier: BSD-3-Clause\\npragma solidity 0.8.25;\\n\\n/**\\n * @title Compound's InterestRateModel Interface\\n * @author Compound\\n */\\nabstract contract InterestRateModel {\\n /**\\n * @notice Calculates the current borrow interest rate per slot (block or second)\\n * @param cash The total amount of cash the market has\\n * @param borrows The total amount of borrows the market has outstanding\\n * @param reserves The total amount of reserves the market has\\n * @param badDebt The amount of badDebt in the market\\n * @return The borrow rate percentage per slot (block or second) as a mantissa (scaled by EXP_SCALE)\\n */\\n function getBorrowRate(\\n uint256 cash,\\n uint256 borrows,\\n uint256 reserves,\\n uint256 badDebt\\n ) external view virtual returns (uint256);\\n\\n /**\\n * @notice Calculates the current supply interest rate per slot (block or second)\\n * @param cash The total amount of cash the market has\\n * @param borrows The total amount of borrows the market has outstanding\\n * @param reserves The total amount of reserves the market has\\n * @param reserveFactorMantissa The current reserve factor the market has\\n * @param badDebt The amount of badDebt in the market\\n * @return The supply rate percentage per slot (block or second) as a mantissa (scaled by EXP_SCALE)\\n */\\n function getSupplyRate(\\n uint256 cash,\\n uint256 borrows,\\n uint256 reserves,\\n uint256 reserveFactorMantissa,\\n uint256 badDebt\\n ) external view virtual returns (uint256);\\n\\n /**\\n * @notice Indicator that this is an InterestRateModel contract (for inspection)\\n * @return Always true\\n */\\n function isInterestRateModel() external pure virtual returns (bool) {\\n return true;\\n }\\n}\\n\",\"keccak256\":\"0xc4fda1ab75ebe4b187b707c4f10c58780f343cf343c537f641dc75d3cd28ab51\",\"license\":\"BSD-3-Clause\"},\"contracts/Lens/SpokePoolLens.sol\":{\"content\":\"// SPDX-License-Identifier: BSD-3-Clause\\npragma solidity 0.8.25;\\n\\nimport { IERC20 } from \\\"@openzeppelin/contracts/token/ERC20/IERC20.sol\\\";\\nimport { IERC20Metadata } from \\\"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol\\\";\\nimport { ResilientOracleInterface } from \\\"@venusprotocol/oracle/contracts/interfaces/OracleInterface.sol\\\";\\nimport { TimeManagerV8 } from \\\"@venusprotocol/solidity-utilities/contracts/TimeManagerV8.sol\\\";\\n\\nimport { ExponentialNoError } from \\\"../ExponentialNoError.sol\\\";\\nimport { VToken } from \\\"../VToken.sol\\\";\\nimport { Action, ComptrollerInterface, ComptrollerViewInterface } from \\\"../ComptrollerInterface.sol\\\";\\nimport { PoolRegistryInterface } from \\\"../Pool/PoolRegistryInterface.sol\\\";\\nimport { PoolRegistry } from \\\"../Pool/PoolRegistry.sol\\\";\\nimport { RewardsDistributor } from \\\"../Rewards/RewardsDistributor.sol\\\";\\nimport { SpokeComptrollerViewInterface } from \\\"../Spoke/SpokeComptrollerInterface.sol\\\";\\n\\n/**\\n * @title SpokePoolLens\\n * @author Venus\\n * @notice Reads pool and market state specific to a spoke pool\\n *\\n * @dev Spoke pools expose additional state that is not included in the shared PoolLens data, including the\\n * deviation-bounded oracle, the supply and liquidation allowlists, liquidation thresholds and liquidation incentives.\\n * Those reads are named `spoke*` or `getSpokePool*` and return spoke-shaped structs.\\n *\\n * The rest of the surface mirrors `PoolLens` under the same names and struct shapes, so that reading a spoke pool\\n * takes one address rather than two. Those reads go through getters both kinds of pool share.\\n *\\n * Access-controlled call permissions are deliberately not reported: `AccessControlManager` derives the role from its\\n * own caller, so asked from a lens it answers `false` for an account that can in fact call.\\n *\\n * This contract holds no state and is versioned by redeployment.\\n */\\ncontract SpokePoolLens is ExponentialNoError, TimeManagerV8 {\\n /**\\n * @dev Mirrors `PoolLens.PoolData` with spoke-pool-specific fields.\\n */\\n struct SpokePoolData {\\n string name;\\n address creator;\\n address comptroller;\\n uint256 blockPosted;\\n uint256 timestampPosted;\\n string category;\\n string logoURL;\\n string description;\\n address priceOracle;\\n uint256 closeFactor;\\n /// @notice The pool-wide liquidation incentive, scaled by 1e18\\n uint256 poolLiquidationIncentiveMantissa;\\n uint256 minLiquidatableCollateral;\\n /// @notice Oracle used to bound collateral prices for borrowing and redeeming. Zero until governance sets it,\\n /// and while it is zero both actions revert\\n address deviationBoundedOracle;\\n /// @notice Whether liquidation is restricted to allowlisted accounts\\n bool liquidationAllowlistEnabled;\\n SpokeVTokenMetadata[] vTokens;\\n }\\n\\n /**\\n * @dev Mirrors `PoolLens.VTokenMetadata` with spoke-pool-specific fields.\\n */\\n struct SpokeVTokenMetadata {\\n address vToken;\\n uint256 exchangeRateCurrent;\\n uint256 supplyRatePerBlockOrTimestamp;\\n uint256 borrowRatePerBlockOrTimestamp;\\n uint256 reserveFactorMantissa;\\n uint256 supplyCaps;\\n uint256 borrowCaps;\\n uint256 totalBorrows;\\n uint256 totalReserves;\\n uint256 totalSupply;\\n uint256 totalCash;\\n bool isListed;\\n uint256 collateralFactorMantissa;\\n /// @notice The collateral weight above which a position becomes liquidatable, scaled by 1e18\\n uint256 liquidationThresholdMantissa;\\n address underlyingAssetAddress;\\n uint256 vTokenDecimals;\\n uint256 underlyingDecimals;\\n uint256 pausedActions;\\n /// @notice The liquidation incentive effective for this market, scaled by 1e18\\n uint256 effectiveLiquidationIncentiveMantissa;\\n /// @notice The market-specific liquidation incentive, or zero when using the pool-wide value\\n uint256 ownLiquidationIncentiveMantissa;\\n /// @notice Whether supply is restricted to allowlisted accounts\\n bool supplyAllowlistEnabled;\\n /// @notice Whether the market allows full liquidation without a shortfall\\n bool forcedLiquidationEnabled;\\n }\\n\\n /**\\n * @dev Returns both the market's allowlist status and whether the account is allowlisted.\\n */\\n struct SpokeSupplyPermission {\\n address vToken;\\n /// @notice Whether supply is restricted to allowlisted accounts\\n bool allowlistEnabled;\\n /// @notice Whether the account is allowlisted for the market\\n bool accountAllowlisted;\\n }\\n\\n /**\\n * @dev Struct for VTokenBalance.\\n */\\n struct VTokenBalances {\\n address vToken;\\n uint256 balanceOf;\\n uint256 borrowBalanceCurrent;\\n uint256 balanceOfUnderlying;\\n uint256 tokenBalance;\\n uint256 tokenAllowance;\\n }\\n\\n /**\\n * @dev Struct for underlyingPrice of VToken.\\n */\\n struct VTokenUnderlyingPrice {\\n address vToken;\\n uint256 underlyingPrice;\\n }\\n\\n /**\\n * @dev Struct with pending reward info for a market.\\n */\\n struct PendingReward {\\n address vTokenAddress;\\n uint256 amount;\\n }\\n\\n /**\\n * @dev Struct with reward distribution totals for a single reward token and distributor.\\n */\\n struct RewardSummary {\\n address distributorAddress;\\n address rewardTokenAddress;\\n uint256 totalRewards;\\n PendingReward[] pendingRewards;\\n }\\n\\n /**\\n * @dev Struct used in RewardDistributor to save last updated market state.\\n */\\n struct RewardTokenState {\\n // The market's last updated rewardTokenBorrowIndex or rewardTokenSupplyIndex\\n uint224 index;\\n // The block number or timestamp the index was last updated at\\n uint256 blockOrTimestamp;\\n // The block number or timestamp at which to stop rewards\\n uint256 lastRewardingBlockOrTimestamp;\\n }\\n\\n /**\\n * @dev Struct with bad debt of a market denominated\\n */\\n struct BadDebt {\\n address vTokenAddress;\\n uint256 badDebtUsd;\\n }\\n\\n /**\\n * @dev Struct with bad debt total denominated in usd for a pool and an array of BadDebt structs for each market\\n */\\n struct BadDebtSummary {\\n address comptroller;\\n uint256 totalBadDebtUsd;\\n BadDebt[] badDebts;\\n }\\n\\n /**\\n * @param timeBased_ A boolean indicating whether the contract is based on time or block\\n * @param blocksPerYear_ The number of blocks per year\\n * @custom:oz-upgrades-unsafe-allow constructor\\n */\\n constructor(bool timeBased_, uint256 blocksPerYear_) TimeManagerV8(timeBased_, blocksPerYear_) {}\\n\\n /**\\n * @notice Queries the user's supply/borrow balances in vTokens\\n * @param vTokens The list of vToken addresses\\n * @param account The user Account\\n * @return A list of structs containing balances data\\n */\\n function vTokenBalancesAll(VToken[] calldata vTokens, address account) external returns (VTokenBalances[] memory) {\\n uint256 vTokenCount = vTokens.length;\\n VTokenBalances[] memory res = new VTokenBalances[](vTokenCount);\\n for (uint256 i; i < vTokenCount; ++i) {\\n res[i] = vTokenBalances(vTokens[i], account);\\n }\\n return res;\\n }\\n\\n /**\\n * @notice Queries every pool and market in a spoke pool registry\\n * @dev Not intended to be called in a transaction due to its gas cost. The registry must contain only spoke pools.\\n * @param poolRegistryAddress The registry to enumerate\\n * @return The data of every pool in the registry\\n */\\n function getAllSpokePools(address poolRegistryAddress) external view returns (SpokePoolData[] memory) {\\n PoolRegistry.VenusPool[] memory pools = PoolRegistryInterface(poolRegistryAddress).getAllPools();\\n uint256 poolLength = pools.length;\\n\\n SpokePoolData[] memory poolDataItems = new SpokePoolData[](poolLength);\\n\\n for (uint256 i; i < poolLength; ++i) {\\n poolDataItems[i] = getSpokePoolData(poolRegistryAddress, pools[i]);\\n }\\n\\n return poolDataItems;\\n }\\n\\n /**\\n * @notice Queries a spoke pool by its comptroller address\\n * @param poolRegistryAddress The registry containing the pool\\n * @param comptroller The pool's comptroller\\n * @return The pool's data\\n */\\n function getSpokePoolByComptroller(\\n address poolRegistryAddress,\\n address comptroller\\n ) external view returns (SpokePoolData memory) {\\n PoolRegistryInterface poolRegistry = PoolRegistryInterface(poolRegistryAddress);\\n return getSpokePoolData(poolRegistryAddress, poolRegistry.getPoolByComptroller(comptroller));\\n }\\n\\n /**\\n * @notice Returns whether an account may supply to each specified market\\n * @dev The account checked is the one receiving the minted vTokens, which can differ from the payer when using\\n * `mintBehalf`.\\n * @param comptroller The spoke pool's comptroller\\n * @param vTokens The markets to test\\n * @param account The account to test\\n * @return One entry per market, in the order given\\n */\\n function spokeSupplyPermissions(\\n address comptroller,\\n VToken[] memory vTokens,\\n address account\\n ) external view returns (SpokeSupplyPermission[] memory) {\\n SpokeComptrollerViewInterface spoke = SpokeComptrollerViewInterface(comptroller);\\n uint256 len = vTokens.length;\\n\\n SpokeSupplyPermission[] memory permissions = new SpokeSupplyPermission[](len);\\n\\n for (uint256 i; i < len; ++i) {\\n address vToken = address(vTokens[i]);\\n permissions[i] = SpokeSupplyPermission({\\n vToken: vToken,\\n allowlistEnabled: spoke.isSupplyAllowlistEnabled(vToken),\\n accountAllowlisted: spoke.isAllowedSupplier(vToken, account)\\n });\\n }\\n\\n return permissions;\\n }\\n\\n /**\\n * @notice Returns whether an account may seize collateral in a spoke pool\\n * @param comptroller The spoke pool's comptroller\\n * @param liquidator The account to test\\n * @return allowlistEnabled Whether liquidation is restricted to allowlisted accounts\\n * @return accountAllowlisted Whether the account is allowlisted\\n */\\n function spokeLiquidationPermission(\\n address comptroller,\\n address liquidator\\n ) external view returns (bool allowlistEnabled, bool accountAllowlisted) {\\n SpokeComptrollerViewInterface spoke = SpokeComptrollerViewInterface(comptroller);\\n return (spoke.isLiquidationAllowlistEnabled(), spoke.isAllowedLiquidator(liquidator));\\n }\\n\\n /**\\n * @notice Returns the price data for the underlying assets of the specified vTokens\\n * @param vTokens The list of vToken addresses\\n * @return An array containing the price data for each asset\\n */\\n function vTokenUnderlyingPriceAll(\\n VToken[] calldata vTokens\\n ) external view returns (VTokenUnderlyingPrice[] memory) {\\n uint256 vTokenCount = vTokens.length;\\n VTokenUnderlyingPrice[] memory res = new VTokenUnderlyingPrice[](vTokenCount);\\n for (uint256 i; i < vTokenCount; ++i) {\\n res[i] = vTokenUnderlyingPrice(vTokens[i]);\\n }\\n return res;\\n }\\n\\n /**\\n * @notice Returns the pending rewards for a user for a given pool.\\n * @param account The user account.\\n * @param comptrollerAddress address\\n * @return Pending rewards array\\n */\\n function getPendingRewards(\\n address account,\\n address comptrollerAddress\\n ) external view returns (RewardSummary[] memory) {\\n VToken[] memory markets = ComptrollerInterface(comptrollerAddress).getAllMarkets();\\n RewardsDistributor[] memory rewardsDistributors = ComptrollerViewInterface(comptrollerAddress)\\n .getRewardDistributors();\\n RewardSummary[] memory rewardSummary = new RewardSummary[](rewardsDistributors.length);\\n for (uint256 i; i < rewardsDistributors.length; ++i) {\\n RewardSummary memory reward;\\n reward.distributorAddress = address(rewardsDistributors[i]);\\n reward.rewardTokenAddress = address(rewardsDistributors[i].rewardToken());\\n reward.totalRewards = rewardsDistributors[i].rewardTokenAccrued(account);\\n reward.pendingRewards = _calculateNotDistributedAwards(account, markets, rewardsDistributors[i]);\\n rewardSummary[i] = reward;\\n }\\n return rewardSummary;\\n }\\n\\n /**\\n * @notice Returns a summary of a pool's bad debt broken down by market\\n *\\n * @param comptrollerAddress Address of the comptroller\\n *\\n * @return badDebtSummary A struct with comptroller address, total bad debut denominated in usd, and\\n * a break down of bad debt by market\\n */\\n function getPoolBadDebt(address comptrollerAddress) external view returns (BadDebtSummary memory) {\\n uint256 totalBadDebtUsd;\\n\\n // Get every market in the pool\\n ComptrollerViewInterface comptroller = ComptrollerViewInterface(comptrollerAddress);\\n VToken[] memory markets = comptroller.getAllMarkets();\\n ResilientOracleInterface priceOracle = comptroller.oracle();\\n\\n BadDebt[] memory badDebts = new BadDebt[](markets.length);\\n\\n BadDebtSummary memory badDebtSummary;\\n badDebtSummary.comptroller = comptrollerAddress;\\n badDebtSummary.badDebts = badDebts;\\n\\n // // Calculate the bad debt is USD per market\\n for (uint256 i; i < markets.length; ++i) {\\n BadDebt memory badDebt;\\n badDebt.vTokenAddress = address(markets[i]);\\n badDebt.badDebtUsd =\\n (VToken(address(markets[i])).badDebt() * priceOracle.getUnderlyingPrice(address(markets[i]))) /\\n EXP_SCALE;\\n badDebtSummary.badDebts[i] = badDebt;\\n totalBadDebtUsd = totalBadDebtUsd + badDebt.badDebtUsd;\\n }\\n\\n badDebtSummary.totalBadDebtUsd = totalBadDebtUsd;\\n\\n return badDebtSummary;\\n }\\n\\n /**\\n * @notice Returns vToken holding the specified underlying asset in the specified pool\\n * @param poolRegistryAddress The address of the PoolRegistry contract\\n * @param comptroller The pool comptroller\\n * @param asset The underlyingAsset of VToken\\n * @return Address of the vToken\\n */\\n function getVTokenForAsset(\\n address poolRegistryAddress,\\n address comptroller,\\n address asset\\n ) external view returns (address) {\\n PoolRegistryInterface poolRegistryInterface = PoolRegistryInterface(poolRegistryAddress);\\n return poolRegistryInterface.getVTokenForAsset(comptroller, asset);\\n }\\n\\n /**\\n * @notice Returns all pools that support the specified underlying asset\\n * @param poolRegistryAddress The address of the PoolRegistry contract\\n * @param asset The underlying asset of vToken\\n * @return A list of Comptroller contracts\\n */\\n function getPoolsSupportedByAsset(\\n address poolRegistryAddress,\\n address asset\\n ) external view returns (address[] memory) {\\n PoolRegistryInterface poolRegistryInterface = PoolRegistryInterface(poolRegistryAddress);\\n return poolRegistryInterface.getPoolsSupportedByAsset(asset);\\n }\\n\\n /**\\n * @notice Queries the user's supply/borrow balances in the specified vToken\\n * @param vToken vToken address\\n * @param account The user Account\\n * @return A struct containing the balances data\\n */\\n function vTokenBalances(VToken vToken, address account) public returns (VTokenBalances memory) {\\n uint256 balanceOf = vToken.balanceOf(account);\\n uint256 borrowBalanceCurrent = vToken.borrowBalanceCurrent(account);\\n uint256 balanceOfUnderlying = vToken.balanceOfUnderlying(account);\\n uint256 tokenBalance;\\n uint256 tokenAllowance;\\n\\n IERC20 underlying = IERC20(vToken.underlying());\\n tokenBalance = underlying.balanceOf(account);\\n tokenAllowance = underlying.allowance(account, address(vToken));\\n\\n return\\n VTokenBalances({\\n vToken: address(vToken),\\n balanceOf: balanceOf,\\n borrowBalanceCurrent: borrowBalanceCurrent,\\n balanceOfUnderlying: balanceOfUnderlying,\\n tokenBalance: tokenBalance,\\n tokenAllowance: tokenAllowance\\n });\\n }\\n\\n /**\\n * @notice Queries a spoke pool from its registry entry\\n * @param poolRegistryAddress The registry containing the pool\\n * @param venusPool The pool's registry entry\\n * @return The pool's data, including its markets\\n */\\n function getSpokePoolData(\\n address poolRegistryAddress,\\n PoolRegistry.VenusPool memory venusPool\\n ) public view returns (SpokePoolData memory) {\\n address comptroller = venusPool.comptroller;\\n\\n SpokeVTokenMetadata[] memory vTokenMetadataItems = spokeVTokenMetadataAll(\\n ComptrollerInterface(comptroller).getAllMarkets()\\n );\\n\\n PoolRegistry.VenusPoolMetaData memory metaData = PoolRegistryInterface(poolRegistryAddress)\\n .getVenusPoolMetadata(comptroller);\\n\\n ComptrollerViewInterface pooledView = ComptrollerViewInterface(comptroller);\\n SpokeComptrollerViewInterface spokeView = SpokeComptrollerViewInterface(comptroller);\\n\\n SpokePoolData memory poolData;\\n poolData.name = venusPool.name;\\n poolData.creator = venusPool.creator;\\n poolData.comptroller = comptroller;\\n poolData.blockPosted = venusPool.blockPosted;\\n poolData.timestampPosted = venusPool.timestampPosted;\\n poolData.category = metaData.category;\\n poolData.logoURL = metaData.logoURL;\\n poolData.description = metaData.description;\\n poolData.priceOracle = address(pooledView.oracle());\\n poolData.closeFactor = pooledView.closeFactorMantissa();\\n // This call is made from the lens, so the comptroller returns the pool-wide incentive.\\n poolData.poolLiquidationIncentiveMantissa = pooledView.liquidationIncentiveMantissa();\\n poolData.minLiquidatableCollateral = pooledView.minLiquidatableCollateral();\\n poolData.deviationBoundedOracle = address(spokeView.deviationBoundedOracle());\\n poolData.liquidationAllowlistEnabled = spokeView.isLiquidationAllowlistEnabled();\\n poolData.vTokens = vTokenMetadataItems;\\n\\n return poolData;\\n }\\n\\n /**\\n * @notice Queries the metadata of every given market of a spoke pool\\n * @param vTokens The markets to read\\n * @return One entry per market, in the order given\\n */\\n function spokeVTokenMetadataAll(VToken[] memory vTokens) public view returns (SpokeVTokenMetadata[] memory) {\\n uint256 len = vTokens.length;\\n\\n SpokeVTokenMetadata[] memory metadataItems = new SpokeVTokenMetadata[](len);\\n\\n for (uint256 i; i < len; ++i) {\\n metadataItems[i] = spokeVTokenMetadata(vTokens[i]);\\n }\\n\\n return metadataItems;\\n }\\n\\n /**\\n * @notice Queries the metadata of one market of a spoke pool\\n * @param vToken The market to read\\n * @return The market's metadata\\n */\\n function spokeVTokenMetadata(VToken vToken) public view returns (SpokeVTokenMetadata memory) {\\n address vTokenAddress = address(vToken);\\n address comptroller = address(vToken.comptroller());\\n\\n ComptrollerViewInterface pooledView = ComptrollerViewInterface(comptroller);\\n SpokeComptrollerViewInterface spokeView = SpokeComptrollerViewInterface(comptroller);\\n\\n // Read through the spoke interface, which declares all three returns. `ComptrollerViewInterface` declares\\n // the first two, so reading it there drops the liquidation threshold with no error.\\n (bool isListed, uint256 collateralFactorMantissa, uint256 liquidationThresholdMantissa) = spokeView.markets(\\n vTokenAddress\\n );\\n\\n address underlying = vToken.underlying();\\n\\n return\\n SpokeVTokenMetadata({\\n vToken: vTokenAddress,\\n exchangeRateCurrent: vToken.exchangeRateStored(),\\n // Zero for an empty market, as `PoolLens` reports: the rate model divides by the market's supply.\\n supplyRatePerBlockOrTimestamp: vToken.totalSupply() > 0 ? vToken.supplyRatePerBlock() : 0,\\n borrowRatePerBlockOrTimestamp: vToken.borrowRatePerBlock(),\\n reserveFactorMantissa: vToken.reserveFactorMantissa(),\\n supplyCaps: pooledView.supplyCaps(vTokenAddress),\\n borrowCaps: pooledView.borrowCaps(vTokenAddress),\\n totalBorrows: vToken.totalBorrows(),\\n totalReserves: vToken.totalReserves(),\\n totalSupply: vToken.totalSupply(),\\n totalCash: vToken.getCash(),\\n isListed: isListed,\\n collateralFactorMantissa: collateralFactorMantissa,\\n liquidationThresholdMantissa: liquidationThresholdMantissa,\\n underlyingAssetAddress: underlying,\\n vTokenDecimals: vToken.decimals(),\\n underlyingDecimals: IERC20Metadata(underlying).decimals(),\\n pausedActions: _pausedActions(comptroller, vTokenAddress),\\n effectiveLiquidationIncentiveMantissa: spokeView.effectiveLiquidationIncentive(vTokenAddress),\\n ownLiquidationIncentiveMantissa: spokeView.liquidationIncentives(vTokenAddress),\\n supplyAllowlistEnabled: spokeView.isSupplyAllowlistEnabled(vTokenAddress),\\n forcedLiquidationEnabled: spokeView.isForcedLiquidationEnabled(vTokenAddress)\\n });\\n }\\n\\n /**\\n * @notice Returns the price data for the underlying asset of the specified vToken\\n * @param vToken vToken address\\n * @return The price data for each asset\\n */\\n function vTokenUnderlyingPrice(VToken vToken) public view returns (VTokenUnderlyingPrice memory) {\\n ComptrollerViewInterface comptroller = ComptrollerViewInterface(address(vToken.comptroller()));\\n ResilientOracleInterface priceOracle = comptroller.oracle();\\n\\n return\\n VTokenUnderlyingPrice({\\n vToken: address(vToken),\\n underlyingPrice: priceOracle.getUnderlyingPrice(address(vToken))\\n });\\n }\\n\\n function _calculateNotDistributedAwards(\\n address account,\\n VToken[] memory markets,\\n RewardsDistributor rewardsDistributor\\n ) internal view returns (PendingReward[] memory) {\\n PendingReward[] memory pendingRewards = new PendingReward[](markets.length);\\n\\n for (uint256 i; i < markets.length; ++i) {\\n // Market borrow and supply state we will modify update in-memory, in order to not modify storage\\n RewardTokenState memory borrowState;\\n RewardTokenState memory supplyState;\\n\\n if (isTimeBased) {\\n (\\n borrowState.index,\\n borrowState.blockOrTimestamp,\\n borrowState.lastRewardingBlockOrTimestamp\\n ) = rewardsDistributor.rewardTokenBorrowStateTimeBased(address(markets[i]));\\n (\\n supplyState.index,\\n supplyState.blockOrTimestamp,\\n supplyState.lastRewardingBlockOrTimestamp\\n ) = rewardsDistributor.rewardTokenSupplyStateTimeBased(address(markets[i]));\\n } else {\\n (\\n borrowState.index,\\n borrowState.blockOrTimestamp,\\n borrowState.lastRewardingBlockOrTimestamp\\n ) = rewardsDistributor.rewardTokenBorrowState(address(markets[i]));\\n (\\n supplyState.index,\\n supplyState.blockOrTimestamp,\\n supplyState.lastRewardingBlockOrTimestamp\\n ) = rewardsDistributor.rewardTokenSupplyState(address(markets[i]));\\n }\\n\\n Exp memory marketBorrowIndex = Exp({ mantissa: markets[i].borrowIndex() });\\n\\n // Update market supply and borrow index in-memory\\n updateMarketBorrowIndex(address(markets[i]), rewardsDistributor, borrowState, marketBorrowIndex);\\n updateMarketSupplyIndex(address(markets[i]), rewardsDistributor, supplyState);\\n\\n // Calculate pending rewards\\n uint256 borrowReward = calculateBorrowerReward(\\n address(markets[i]),\\n rewardsDistributor,\\n account,\\n borrowState,\\n marketBorrowIndex\\n );\\n uint256 supplyReward = calculateSupplierReward(\\n address(markets[i]),\\n rewardsDistributor,\\n account,\\n supplyState\\n );\\n\\n PendingReward memory pendingReward;\\n pendingReward.vTokenAddress = address(markets[i]);\\n pendingReward.amount = borrowReward + supplyReward;\\n pendingRewards[i] = pendingReward;\\n }\\n return pendingRewards;\\n }\\n\\n function updateMarketBorrowIndex(\\n address vToken,\\n RewardsDistributor rewardsDistributor,\\n RewardTokenState memory borrowState,\\n Exp memory marketBorrowIndex\\n ) internal view {\\n uint256 borrowSpeed = rewardsDistributor.rewardTokenBorrowSpeeds(vToken);\\n uint256 blockNumberOrTimestamp = getBlockNumberOrTimestamp();\\n\\n if (\\n borrowState.lastRewardingBlockOrTimestamp > 0 &&\\n blockNumberOrTimestamp > borrowState.lastRewardingBlockOrTimestamp\\n ) {\\n blockNumberOrTimestamp = borrowState.lastRewardingBlockOrTimestamp;\\n }\\n\\n uint256 deltaBlocksOrTimestamp = sub_(blockNumberOrTimestamp, borrowState.blockOrTimestamp);\\n if (deltaBlocksOrTimestamp > 0 && borrowSpeed > 0) {\\n // Remove the total earned interest rate since the opening of the market from total borrows\\n uint256 borrowAmount = div_(VToken(vToken).totalBorrows(), marketBorrowIndex);\\n uint256 tokensAccrued = mul_(deltaBlocksOrTimestamp, borrowSpeed);\\n Double memory ratio = borrowAmount > 0 ? fraction(tokensAccrued, borrowAmount) : Double({ mantissa: 0 });\\n Double memory index = add_(Double({ mantissa: borrowState.index }), ratio);\\n borrowState.index = safe224(index.mantissa, \\\"new index overflows\\\");\\n borrowState.blockOrTimestamp = blockNumberOrTimestamp;\\n } else if (deltaBlocksOrTimestamp > 0) {\\n borrowState.blockOrTimestamp = blockNumberOrTimestamp;\\n }\\n }\\n\\n function updateMarketSupplyIndex(\\n address vToken,\\n RewardsDistributor rewardsDistributor,\\n RewardTokenState memory supplyState\\n ) internal view {\\n uint256 supplySpeed = rewardsDistributor.rewardTokenSupplySpeeds(vToken);\\n uint256 blockNumberOrTimestamp = getBlockNumberOrTimestamp();\\n\\n if (\\n supplyState.lastRewardingBlockOrTimestamp > 0 &&\\n blockNumberOrTimestamp > supplyState.lastRewardingBlockOrTimestamp\\n ) {\\n blockNumberOrTimestamp = supplyState.lastRewardingBlockOrTimestamp;\\n }\\n\\n uint256 deltaBlocksOrTimestamp = sub_(blockNumberOrTimestamp, supplyState.blockOrTimestamp);\\n if (deltaBlocksOrTimestamp > 0 && supplySpeed > 0) {\\n uint256 supplyTokens = VToken(vToken).totalSupply();\\n uint256 tokensAccrued = mul_(deltaBlocksOrTimestamp, supplySpeed);\\n Double memory ratio = supplyTokens > 0 ? fraction(tokensAccrued, supplyTokens) : Double({ mantissa: 0 });\\n Double memory index = add_(Double({ mantissa: supplyState.index }), ratio);\\n supplyState.index = safe224(index.mantissa, \\\"new index overflows\\\");\\n supplyState.blockOrTimestamp = blockNumberOrTimestamp;\\n } else if (deltaBlocksOrTimestamp > 0) {\\n supplyState.blockOrTimestamp = blockNumberOrTimestamp;\\n }\\n }\\n\\n function calculateBorrowerReward(\\n address vToken,\\n RewardsDistributor rewardsDistributor,\\n address borrower,\\n RewardTokenState memory borrowState,\\n Exp memory marketBorrowIndex\\n ) internal view returns (uint256) {\\n Double memory borrowIndex = Double({ mantissa: borrowState.index });\\n Double memory borrowerIndex = Double({\\n mantissa: rewardsDistributor.rewardTokenBorrowerIndex(vToken, borrower)\\n });\\n if (borrowerIndex.mantissa == 0 && borrowIndex.mantissa >= rewardsDistributor.INITIAL_INDEX()) {\\n // Covers the case where users borrowed tokens before the market's borrow state index was set\\n borrowerIndex.mantissa = rewardsDistributor.INITIAL_INDEX();\\n }\\n Double memory deltaIndex = sub_(borrowIndex, borrowerIndex);\\n uint256 borrowerAmount = div_(VToken(vToken).borrowBalanceStored(borrower), marketBorrowIndex);\\n uint256 borrowerDelta = mul_(borrowerAmount, deltaIndex);\\n return borrowerDelta;\\n }\\n\\n function calculateSupplierReward(\\n address vToken,\\n RewardsDistributor rewardsDistributor,\\n address supplier,\\n RewardTokenState memory supplyState\\n ) internal view returns (uint256) {\\n Double memory supplyIndex = Double({ mantissa: supplyState.index });\\n Double memory supplierIndex = Double({\\n mantissa: rewardsDistributor.rewardTokenSupplierIndex(vToken, supplier)\\n });\\n if (supplierIndex.mantissa == 0 && supplyIndex.mantissa >= rewardsDistributor.INITIAL_INDEX()) {\\n // Covers the case where users supplied tokens before the market's supply state index was set\\n supplierIndex.mantissa = rewardsDistributor.INITIAL_INDEX();\\n }\\n Double memory deltaIndex = sub_(supplyIndex, supplierIndex);\\n uint256 supplierTokens = VToken(vToken).balanceOf(supplier);\\n uint256 supplierDelta = mul_(supplierTokens, deltaIndex);\\n return supplierDelta;\\n }\\n\\n /**\\n * @dev Encodes paused actions using the same bit positions as `PoolLens`.\\n * @param comptroller The market's comptroller\\n * @param vToken The market to read\\n * @return A bitmask of the paused actions\\n */\\n function _pausedActions(address comptroller, address vToken) private view returns (uint256) {\\n uint256 pausedActions;\\n\\n for (uint8 i; i <= uint8(type(Action).max); ++i) {\\n uint256 paused = ComptrollerInterface(comptroller).actionPaused(vToken, Action(i)) ? 1 : 0;\\n pausedActions |= paused << i;\\n }\\n\\n return pausedActions;\\n }\\n}\\n\",\"keccak256\":\"0x3c3eb512000d70cc42bd69ce144f882620c2ac285965b3a5938eb482df3614fc\",\"license\":\"BSD-3-Clause\"},\"contracts/MaxLoopsLimitHelper.sol\":{\"content\":\"// SPDX-License-Identifier: BSD-3-Clause\\npragma solidity 0.8.25;\\n\\n/**\\n * @title MaxLoopsLimitHelper\\n * @author Venus\\n * @notice Abstract contract used to avoid collection with too many items that would generate gas errors and DoS.\\n */\\nabstract contract MaxLoopsLimitHelper {\\n // Limit for the loops to avoid the DOS\\n uint256 public maxLoopsLimit;\\n\\n /**\\n * @dev This empty reserved space is put in place to allow future versions to add new\\n * variables without shifting down storage in the inheritance chain.\\n * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\\n */\\n uint256[49] private __gap;\\n\\n /// @notice Emitted when max loops limit is set\\n event MaxLoopsLimitUpdated(uint256 oldMaxLoopsLimit, uint256 newmaxLoopsLimit);\\n\\n /// @notice Thrown an error on maxLoopsLimit exceeds for any loop\\n error MaxLoopsLimitExceeded(uint256 loopsLimit, uint256 requiredLoops);\\n\\n /**\\n * @notice Set the limit for the loops can iterate to avoid the DOS\\n * @param limit Limit for the max loops can execute at a time\\n */\\n function _setMaxLoopsLimit(uint256 limit) internal {\\n require(limit > maxLoopsLimit, \\\"Comptroller: Invalid maxLoopsLimit\\\");\\n\\n uint256 oldMaxLoopsLimit = maxLoopsLimit;\\n maxLoopsLimit = limit;\\n\\n emit MaxLoopsLimitUpdated(oldMaxLoopsLimit, limit);\\n }\\n\\n /**\\n * @notice Compare the maxLoopsLimit with number of the times loop iterate\\n * @param len Length of the loops iterate\\n * @custom:error MaxLoopsLimitExceeded error is thrown when loops length exceeds maxLoopsLimit\\n */\\n function _ensureMaxLoops(uint256 len) internal view {\\n if (len > maxLoopsLimit) {\\n revert MaxLoopsLimitExceeded(maxLoopsLimit, len);\\n }\\n }\\n}\\n\",\"keccak256\":\"0x4c25e30635485d162177effa384eee51768b0141a567a0da16ff6ad673274166\",\"license\":\"BSD-3-Clause\"},\"contracts/Pool/PoolRegistry.sol\":{\"content\":\"// SPDX-License-Identifier: BSD-3-Clause\\npragma solidity 0.8.25;\\n\\nimport { Ownable2StepUpgradeable } from \\\"@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol\\\";\\nimport { SafeERC20Upgradeable } from \\\"@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol\\\";\\nimport { IERC20Upgradeable } from \\\"@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol\\\";\\nimport { AccessControlledV8 } from \\\"@venusprotocol/governance-contracts/contracts/Governance/AccessControlledV8.sol\\\";\\n\\nimport { PoolRegistryInterface } from \\\"./PoolRegistryInterface.sol\\\";\\nimport { Comptroller } from \\\"../Comptroller.sol\\\";\\nimport { VToken } from \\\"../VToken.sol\\\";\\nimport { ensureNonzeroAddress } from \\\"../lib/validators.sol\\\";\\n\\n/**\\n * @title PoolRegistry\\n * @author Venus\\n * @notice The Isolated Pools architecture centers around the `PoolRegistry` contract. The `PoolRegistry` maintains a directory of isolated lending\\n * pools and can perform actions like creating and registering new pools, adding new markets to existing pools, setting and updating the pool's required\\n * metadata, and providing the getter methods to get information on the pools.\\n *\\n * Isolated lending has three main components: PoolRegistry, pools, and markets. The PoolRegistry is responsible for managing pools.\\n * It can create new pools, update pool metadata and manage markets within pools. PoolRegistry contains getter methods to get the details of\\n * any existing pool like `getVTokenForAsset` and `getPoolsSupportedByAsset`. It also contains methods for updating pool metadata (`updatePoolMetadata`)\\n * and setting pool name (`setPoolName`).\\n *\\n * The directory of pools is managed through two mappings: `_poolByComptroller` which is a hashmap with the comptroller address as the key and `VenusPool` as\\n * the value and `_poolsByID` which is an array of comptroller addresses. Individual pools can be accessed by calling `getPoolByComptroller` with the pool's\\n * comptroller address. `_poolsByID` is used to iterate through all of the pools.\\n *\\n * PoolRegistry also contains a map of asset addresses called `_supportedPools` that maps to an array of assets suppored by each pool. This array of pools by\\n * asset is retrieved by calling `getPoolsSupportedByAsset`.\\n *\\n * PoolRegistry registers new isolated pools in the directory with the `createRegistryPool` method. Isolated pools are composed of independent markets with\\n * specific assets and custom risk management configurations according to their markets.\\n */\\ncontract PoolRegistry is Ownable2StepUpgradeable, AccessControlledV8, PoolRegistryInterface {\\n using SafeERC20Upgradeable for IERC20Upgradeable;\\n\\n struct AddMarketInput {\\n VToken vToken;\\n uint256 collateralFactor;\\n uint256 liquidationThreshold;\\n uint256 initialSupply;\\n address vTokenReceiver;\\n uint256 supplyCap;\\n uint256 borrowCap;\\n }\\n\\n uint256 internal constant MAX_POOL_NAME_LENGTH = 100;\\n\\n /**\\n * @notice Maps pool's comptroller address to metadata.\\n */\\n mapping(address => VenusPoolMetaData) public metadata;\\n\\n /**\\n * @dev Maps pool ID to pool's comptroller address\\n */\\n mapping(uint256 => address) private _poolsByID;\\n\\n /**\\n * @dev Total number of pools created.\\n */\\n uint256 private _numberOfPools;\\n\\n /**\\n * @dev Maps comptroller address to Venus pool Index.\\n */\\n mapping(address => VenusPool) private _poolByComptroller;\\n\\n /**\\n * @dev Maps pool's comptroller address to asset to vToken.\\n */\\n mapping(address => mapping(address => address)) private _vTokens;\\n\\n /**\\n * @dev Maps asset to list of supported pools.\\n */\\n mapping(address => address[]) private _supportedPools;\\n\\n /**\\n * @notice Emitted when a new Venus pool is added to the directory.\\n */\\n event PoolRegistered(address indexed comptroller, VenusPool pool);\\n\\n /**\\n * @notice Emitted when a pool name is set.\\n */\\n event PoolNameSet(address indexed comptroller, string oldName, string newName);\\n\\n /**\\n * @notice Emitted when a pool metadata is updated.\\n */\\n event PoolMetadataUpdated(\\n address indexed comptroller,\\n VenusPoolMetaData oldMetadata,\\n VenusPoolMetaData newMetadata\\n );\\n\\n /**\\n * @notice Emitted when a Market is added to the pool.\\n */\\n event MarketAdded(address indexed comptroller, address indexed vTokenAddress);\\n\\n /// @custom:oz-upgrades-unsafe-allow constructor\\n constructor() {\\n // Note that the contract is upgradeable. Use initialize() or reinitializers\\n // to set the state variables.\\n _disableInitializers();\\n }\\n\\n /**\\n * @notice Initializes the deployer to owner\\n * @param accessControlManager_ AccessControlManager contract address\\n */\\n function initialize(address accessControlManager_) external initializer {\\n __Ownable2Step_init();\\n __AccessControlled_init_unchained(accessControlManager_);\\n }\\n\\n /**\\n * @notice Adds a new Venus pool to the directory\\n * @dev Price oracle must be configured before adding a pool\\n * @param name The name of the pool\\n * @param comptroller Pool's Comptroller contract\\n * @param closeFactor The pool's close factor (scaled by 1e18)\\n * @param liquidationIncentive The pool's liquidation incentive (scaled by 1e18)\\n * @param minLiquidatableCollateral Minimal collateral for regular (non-batch) liquidations flow\\n * @return index The index of the registered Venus pool\\n * @custom:error ZeroAddressNotAllowed is thrown when Comptroller address is zero\\n * @custom:error ZeroAddressNotAllowed is thrown when price oracle address is zero\\n */\\n function addPool(\\n string calldata name,\\n Comptroller comptroller,\\n uint256 closeFactor,\\n uint256 liquidationIncentive,\\n uint256 minLiquidatableCollateral\\n ) external virtual returns (uint256 index) {\\n _checkAccessAllowed(\\\"addPool(string,address,uint256,uint256,uint256)\\\");\\n // Input validation\\n ensureNonzeroAddress(address(comptroller));\\n ensureNonzeroAddress(address(comptroller.oracle()));\\n\\n uint256 poolId = _registerPool(name, address(comptroller));\\n\\n // Set Venus pool parameters\\n comptroller.setCloseFactor(closeFactor);\\n comptroller.setLiquidationIncentive(liquidationIncentive);\\n comptroller.setMinLiquidatableCollateral(minLiquidatableCollateral);\\n\\n return poolId;\\n }\\n\\n /**\\n * @notice Add a market to an existing pool and then mint to provide initial supply\\n * @param input The structure describing the parameters for adding a market to a pool\\n * @custom:error ZeroAddressNotAllowed is thrown when vToken address is zero\\n * @custom:error ZeroAddressNotAllowed is thrown when vTokenReceiver address is zero\\n */\\n function addMarket(AddMarketInput memory input) external {\\n _checkAccessAllowed(\\\"addMarket(AddMarketInput)\\\");\\n ensureNonzeroAddress(address(input.vToken));\\n ensureNonzeroAddress(input.vTokenReceiver);\\n require(input.initialSupply > 0, \\\"PoolRegistry: initialSupply is zero\\\");\\n\\n VToken vToken = input.vToken;\\n address vTokenAddress = address(vToken);\\n address comptrollerAddress = address(vToken.comptroller());\\n Comptroller comptroller = Comptroller(comptrollerAddress);\\n address underlyingAddress = vToken.underlying();\\n IERC20Upgradeable underlying = IERC20Upgradeable(underlyingAddress);\\n\\n require(_poolByComptroller[comptrollerAddress].creator != address(0), \\\"PoolRegistry: Pool not registered\\\");\\n // solhint-disable-next-line reason-string\\n require(\\n _vTokens[comptrollerAddress][underlyingAddress] == address(0),\\n \\\"PoolRegistry: Market already added for asset comptroller combination\\\"\\n );\\n\\n comptroller.supportMarket(vToken);\\n comptroller.setCollateralFactor(vToken, input.collateralFactor, input.liquidationThreshold);\\n\\n uint256[] memory newSupplyCaps = new uint256[](1);\\n uint256[] memory newBorrowCaps = new uint256[](1);\\n VToken[] memory vTokens = new VToken[](1);\\n\\n newSupplyCaps[0] = input.supplyCap;\\n newBorrowCaps[0] = input.borrowCap;\\n vTokens[0] = vToken;\\n\\n comptroller.setMarketSupplyCaps(vTokens, newSupplyCaps);\\n comptroller.setMarketBorrowCaps(vTokens, newBorrowCaps);\\n\\n _vTokens[comptrollerAddress][underlyingAddress] = vTokenAddress;\\n _supportedPools[underlyingAddress].push(comptrollerAddress);\\n\\n uint256 amountToSupply = _transferIn(underlying, msg.sender, input.initialSupply);\\n underlying.forceApprove(vTokenAddress, 0);\\n underlying.forceApprove(vTokenAddress, amountToSupply);\\n vToken.mintBehalf(input.vTokenReceiver, amountToSupply);\\n\\n emit MarketAdded(comptrollerAddress, vTokenAddress);\\n }\\n\\n /**\\n * @notice Modify existing Venus pool name\\n * @param comptroller Pool's Comptroller\\n * @param name New pool name\\n */\\n function setPoolName(address comptroller, string calldata name) external {\\n _checkAccessAllowed(\\\"setPoolName(address,string)\\\");\\n _ensureValidName(name);\\n VenusPool storage pool = _poolByComptroller[comptroller];\\n string memory oldName = pool.name;\\n pool.name = name;\\n emit PoolNameSet(comptroller, oldName, name);\\n }\\n\\n /**\\n * @notice Update metadata of an existing pool\\n * @param comptroller Pool's Comptroller\\n * @param metadata_ New pool metadata\\n */\\n function updatePoolMetadata(address comptroller, VenusPoolMetaData calldata metadata_) external {\\n _checkAccessAllowed(\\\"updatePoolMetadata(address,VenusPoolMetaData)\\\");\\n VenusPoolMetaData memory oldMetadata = metadata[comptroller];\\n metadata[comptroller] = metadata_;\\n emit PoolMetadataUpdated(comptroller, oldMetadata, metadata_);\\n }\\n\\n /**\\n * @notice Returns arrays of all Venus pools' data\\n * @dev This function is not designed to be called in a transaction: it is too gas-intensive\\n * @return A list of all pools within PoolRegistry, with details for each pool\\n */\\n function getAllPools() external view override returns (VenusPool[] memory) {\\n uint256 numberOfPools_ = _numberOfPools; // storage load to save gas\\n VenusPool[] memory _pools = new VenusPool[](numberOfPools_);\\n for (uint256 i = 1; i <= numberOfPools_; ++i) {\\n address comptroller = _poolsByID[i];\\n _pools[i - 1] = (_poolByComptroller[comptroller]);\\n }\\n return _pools;\\n }\\n\\n /**\\n * @param comptroller The comptroller proxy address associated to the pool\\n * @return Returns Venus pool\\n */\\n function getPoolByComptroller(address comptroller) external view override returns (VenusPool memory) {\\n return _poolByComptroller[comptroller];\\n }\\n\\n /**\\n * @param comptroller comptroller of Venus pool\\n * @return Returns Metadata of Venus pool\\n */\\n function getVenusPoolMetadata(address comptroller) external view override returns (VenusPoolMetaData memory) {\\n return metadata[comptroller];\\n }\\n\\n function getVTokenForAsset(address comptroller, address asset) external view override returns (address) {\\n return _vTokens[comptroller][asset];\\n }\\n\\n function getPoolsSupportedByAsset(address asset) external view override returns (address[] memory) {\\n return _supportedPools[asset];\\n }\\n\\n /**\\n * @dev Adds a new Venus pool to the directory (without checking msg.sender).\\n * @param name The name of the pool\\n * @param comptroller The pool's Comptroller proxy contract address\\n * @return The index of the registered Venus pool\\n */\\n function _registerPool(string calldata name, address comptroller) internal returns (uint256) {\\n VenusPool storage storedPool = _poolByComptroller[comptroller];\\n\\n require(storedPool.creator == address(0), \\\"PoolRegistry: Pool already exists in the directory.\\\");\\n _ensureValidName(name);\\n\\n ++_numberOfPools;\\n uint256 numberOfPools_ = _numberOfPools; // cache on stack to save storage read gas\\n\\n VenusPool memory pool = VenusPool(name, msg.sender, comptroller, block.number, block.timestamp);\\n\\n _poolsByID[numberOfPools_] = comptroller;\\n _poolByComptroller[comptroller] = pool;\\n\\n emit PoolRegistered(comptroller, pool);\\n return numberOfPools_;\\n }\\n\\n function _transferIn(IERC20Upgradeable token, address from, uint256 amount) internal returns (uint256) {\\n uint256 balanceBefore = token.balanceOf(address(this));\\n token.safeTransferFrom(from, address(this), amount);\\n uint256 balanceAfter = token.balanceOf(address(this));\\n return balanceAfter - balanceBefore;\\n }\\n\\n function _ensureValidName(string calldata name) internal pure {\\n require(bytes(name).length <= MAX_POOL_NAME_LENGTH, \\\"Pool's name is too large\\\");\\n }\\n}\\n\",\"keccak256\":\"0xafbb871f7b4db3ef439d846baa5f18a136192f9b43ea695c81262a77b06c4b25\",\"license\":\"BSD-3-Clause\"},\"contracts/Pool/PoolRegistryInterface.sol\":{\"content\":\"// SPDX-License-Identifier: BSD-3-Clause\\npragma solidity ^0.8.25;\\n\\n/**\\n * @title PoolRegistryInterface\\n * @author Venus\\n * @notice Interface implemented by `PoolRegistry`.\\n */\\ninterface PoolRegistryInterface {\\n /**\\n * @notice Struct for a Venus interest rate pool.\\n */\\n struct VenusPool {\\n string name;\\n address creator;\\n address comptroller;\\n uint256 blockPosted;\\n uint256 timestampPosted;\\n }\\n\\n /**\\n * @notice Struct for a Venus interest rate pool metadata.\\n */\\n struct VenusPoolMetaData {\\n string category;\\n string logoURL;\\n string description;\\n }\\n\\n /// @notice Get all pools in PoolRegistry\\n function getAllPools() external view returns (VenusPool[] memory);\\n\\n /// @notice Get a pool by comptroller address\\n function getPoolByComptroller(address comptroller) external view returns (VenusPool memory);\\n\\n /// @notice Get the address of the VToken contract in the Pool where the underlying token is the provided asset\\n function getVTokenForAsset(address comptroller, address asset) external view returns (address);\\n\\n /// @notice Get the addresss of the Pools supported that include a market for the provided asset\\n function getPoolsSupportedByAsset(address asset) external view returns (address[] memory);\\n\\n /// @notice Get the metadata of a Pool by comptroller address\\n function getVenusPoolMetadata(address comptroller) external view returns (VenusPoolMetaData memory);\\n}\\n\",\"keccak256\":\"0x7b39cda3b372a686501ce3c2aad288c6af410148110318249d75d4516729c92c\",\"license\":\"BSD-3-Clause\"},\"contracts/Rewards/RewardsDistributor.sol\":{\"content\":\"// SPDX-License-Identifier: BSD-3-Clause\\npragma solidity 0.8.25;\\n\\nimport { Ownable2StepUpgradeable } from \\\"@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol\\\";\\nimport { IERC20Upgradeable } from \\\"@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol\\\";\\nimport { SafeERC20Upgradeable } from \\\"@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol\\\";\\nimport { AccessControlledV8 } from \\\"@venusprotocol/governance-contracts/contracts/Governance/AccessControlledV8.sol\\\";\\nimport { TimeManagerV8 } from \\\"@venusprotocol/solidity-utilities/contracts/TimeManagerV8.sol\\\";\\n\\nimport { ExponentialNoError } from \\\"../ExponentialNoError.sol\\\";\\nimport { VToken } from \\\"../VToken.sol\\\";\\nimport { Comptroller } from \\\"../Comptroller.sol\\\";\\nimport { MaxLoopsLimitHelper } from \\\"../MaxLoopsLimitHelper.sol\\\";\\nimport { RewardsDistributorStorage } from \\\"./RewardsDistributorStorage.sol\\\";\\n\\n/**\\n * @title `RewardsDistributor`\\n * @author Venus\\n * @notice Contract used to configure, track and distribute rewards to users based on their actions (borrows and supplies) in the protocol.\\n * Users can receive additional rewards through a `RewardsDistributor`. Each `RewardsDistributor` proxy is initialized with a specific reward\\n * token and `Comptroller`, which can then distribute the reward token to users that supply or borrow in the associated pool.\\n * Authorized users can set the reward token borrow and supply speeds for each market in the pool. This sets a fixed amount of reward\\n * token to be released each slot (block or second) for borrowers and suppliers, which is distributed based on a user\\u2019s percentage of the borrows or supplies\\n * respectively. The owner can also set up reward distributions to contributor addresses (distinct from suppliers and borrowers) by setting\\n * their contributor reward token speed, which similarly allocates a fixed amount of reward token per slot (block or second).\\n *\\n * The owner has the ability to transfer any amount of reward tokens held by the contract to any other address. Rewards are not distributed\\n * automatically and must be claimed by a user calling `claimRewardToken()`. Users should be aware that it is up to the owner and other centralized\\n * entities to ensure that the `RewardsDistributor` holds enough tokens to distribute the accumulated rewards of users and contributors.\\n */\\ncontract RewardsDistributor is\\n ExponentialNoError,\\n Ownable2StepUpgradeable,\\n AccessControlledV8,\\n MaxLoopsLimitHelper,\\n RewardsDistributorStorage,\\n TimeManagerV8\\n{\\n using SafeERC20Upgradeable for IERC20Upgradeable;\\n\\n /// @notice The initial REWARD TOKEN index for a market\\n uint224 public constant INITIAL_INDEX = 1e36;\\n\\n /// @notice Emitted when REWARD TOKEN is distributed to a supplier\\n event DistributedSupplierRewardToken(\\n VToken indexed vToken,\\n address indexed supplier,\\n uint256 rewardTokenDelta,\\n uint256 rewardTokenTotal,\\n uint256 rewardTokenSupplyIndex\\n );\\n\\n /// @notice Emitted when REWARD TOKEN is distributed to a borrower\\n event DistributedBorrowerRewardToken(\\n VToken indexed vToken,\\n address indexed borrower,\\n uint256 rewardTokenDelta,\\n uint256 rewardTokenTotal,\\n uint256 rewardTokenBorrowIndex\\n );\\n\\n /// @notice Emitted when a new supply-side REWARD TOKEN speed is calculated for a market\\n event RewardTokenSupplySpeedUpdated(VToken indexed vToken, uint256 newSpeed);\\n\\n /// @notice Emitted when a new borrow-side REWARD TOKEN speed is calculated for a market\\n event RewardTokenBorrowSpeedUpdated(VToken indexed vToken, uint256 newSpeed);\\n\\n /// @notice Emitted when REWARD TOKEN is granted by admin\\n event RewardTokenGranted(address indexed recipient, uint256 amount);\\n\\n /// @notice Emitted when a new REWARD TOKEN speed is set for a contributor\\n event ContributorRewardTokenSpeedUpdated(address indexed contributor, uint256 newSpeed);\\n\\n /// @notice Emitted when a market is initialized\\n event MarketInitialized(address indexed vToken);\\n\\n /// @notice Emitted when a reward token supply index is updated\\n event RewardTokenSupplyIndexUpdated(address indexed vToken);\\n\\n /// @notice Emitted when a reward token borrow index is updated\\n event RewardTokenBorrowIndexUpdated(address indexed vToken, Exp marketBorrowIndex);\\n\\n /// @notice Emitted when a reward for contributor is updated\\n event ContributorRewardsUpdated(address indexed contributor, uint256 rewardAccrued);\\n\\n /// @notice Emitted when a reward token last rewarding block for supply is updated\\n event SupplyLastRewardingBlockUpdated(address indexed vToken, uint32 newBlock);\\n\\n /// @notice Emitted when a reward token last rewarding block for borrow is updated\\n event BorrowLastRewardingBlockUpdated(address indexed vToken, uint32 newBlock);\\n\\n /// @notice Emitted when a reward token last rewarding timestamp for supply is updated\\n event SupplyLastRewardingBlockTimestampUpdated(address indexed vToken, uint256 newTimestamp);\\n\\n /// @notice Emitted when a reward token last rewarding timestamp for borrow is updated\\n event BorrowLastRewardingBlockTimestampUpdated(address indexed vToken, uint256 newTimestamp);\\n\\n modifier onlyComptroller() {\\n require(address(comptroller) == msg.sender, \\\"Only comptroller can call this function\\\");\\n _;\\n }\\n\\n /**\\n * @param timeBased_ A boolean indicating whether the contract is based on time or block.\\n * @param blocksPerYear_ The number of blocks per year\\n * @custom:oz-upgrades-unsafe-allow constructor\\n */\\n constructor(bool timeBased_, uint256 blocksPerYear_) TimeManagerV8(timeBased_, blocksPerYear_) {\\n // Note that the contract is upgradeable. Use initialize() or reinitializers\\n // to set the state variables.\\n _disableInitializers();\\n }\\n\\n /**\\n * @notice RewardsDistributor initializer\\n * @dev Initializes the deployer to owner\\n * @param comptroller_ Comptroller to attach the reward distributor to\\n * @param rewardToken_ Reward token to distribute\\n * @param loopsLimit_ Maximum number of iterations for the loops in this contract\\n * @param accessControlManager_ AccessControlManager contract address\\n */\\n function initialize(\\n Comptroller comptroller_,\\n IERC20Upgradeable rewardToken_,\\n uint256 loopsLimit_,\\n address accessControlManager_\\n ) external initializer {\\n comptroller = comptroller_;\\n rewardToken = rewardToken_;\\n __Ownable2Step_init();\\n __AccessControlled_init_unchained(accessControlManager_);\\n\\n _setMaxLoopsLimit(loopsLimit_);\\n }\\n\\n /**\\n * @notice Initializes the market state for a specific vToken\\n * @param vToken The address of the vToken to be initialized\\n * @custom:event MarketInitialized emits on success\\n * @custom:access Only Comptroller\\n */\\n function initializeMarket(address vToken) external onlyComptroller {\\n uint256 blockNumberOrTimestamp = getBlockNumberOrTimestamp();\\n\\n isTimeBased\\n ? _initializeMarketTimestampBased(vToken, blockNumberOrTimestamp)\\n : _initializeMarketBlockBased(vToken, safe32(blockNumberOrTimestamp, \\\"block number exceeds 32 bits\\\"));\\n\\n emit MarketInitialized(vToken);\\n }\\n\\n /*** Reward Token Distribution ***/\\n\\n /**\\n * @notice Calculate reward token accrued by a borrower and possibly transfer it to them\\n * Borrowers will begin to accrue after the first interaction with the protocol.\\n * @dev This function should only be called when the user has a borrow position in the market\\n * (e.g. Comptroller.preBorrowHook, and Comptroller.preRepayHook)\\n * We avoid an external call to check if they are in the market to save gas because this function is called in many places\\n * @param vToken The market in which the borrower is interacting\\n * @param borrower The address of the borrower to distribute REWARD TOKEN to\\n * @param marketBorrowIndex The current global borrow index of vToken\\n */\\n function distributeBorrowerRewardToken(\\n address vToken,\\n address borrower,\\n Exp memory marketBorrowIndex\\n ) external onlyComptroller {\\n _distributeBorrowerRewardToken(vToken, borrower, marketBorrowIndex);\\n }\\n\\n function updateRewardTokenSupplyIndex(address vToken) external onlyComptroller {\\n _updateRewardTokenSupplyIndex(vToken);\\n }\\n\\n /**\\n * @notice Transfer REWARD TOKEN to the recipient\\n * @dev Note: If there is not enough REWARD TOKEN, we do not perform the transfer all\\n * @param recipient The address of the recipient to transfer REWARD TOKEN to\\n * @param amount The amount of REWARD TOKEN to (possibly) transfer\\n */\\n function grantRewardToken(address recipient, uint256 amount) external onlyOwner {\\n uint256 amountLeft = _grantRewardToken(recipient, amount);\\n require(amountLeft == 0, \\\"insufficient rewardToken for grant\\\");\\n emit RewardTokenGranted(recipient, amount);\\n }\\n\\n function updateRewardTokenBorrowIndex(address vToken, Exp memory marketBorrowIndex) external onlyComptroller {\\n _updateRewardTokenBorrowIndex(vToken, marketBorrowIndex);\\n }\\n\\n /**\\n * @notice Set REWARD TOKEN borrow and supply speeds for the specified markets\\n * @param vTokens The markets whose REWARD TOKEN speed to update\\n * @param supplySpeeds New supply-side REWARD TOKEN speed for the corresponding market\\n * @param borrowSpeeds New borrow-side REWARD TOKEN speed for the corresponding market\\n */\\n function setRewardTokenSpeeds(\\n VToken[] memory vTokens,\\n uint256[] memory supplySpeeds,\\n uint256[] memory borrowSpeeds\\n ) external {\\n _checkAccessAllowed(\\\"setRewardTokenSpeeds(address[],uint256[],uint256[])\\\");\\n uint256 numTokens = vTokens.length;\\n require(numTokens == supplySpeeds.length && numTokens == borrowSpeeds.length, \\\"invalid setRewardTokenSpeeds\\\");\\n\\n for (uint256 i; i < numTokens; ++i) {\\n _setRewardTokenSpeed(vTokens[i], supplySpeeds[i], borrowSpeeds[i]);\\n }\\n }\\n\\n /**\\n * @notice Set REWARD TOKEN last rewarding block for the specified markets, used when contract is block based\\n * @param vTokens The markets whose REWARD TOKEN last rewarding block to update\\n * @param supplyLastRewardingBlocks New supply-side REWARD TOKEN last rewarding block for the corresponding market\\n * @param borrowLastRewardingBlocks New borrow-side REWARD TOKEN last rewarding block for the corresponding market\\n */\\n function setLastRewardingBlocks(\\n VToken[] calldata vTokens,\\n uint32[] calldata supplyLastRewardingBlocks,\\n uint32[] calldata borrowLastRewardingBlocks\\n ) external {\\n _checkAccessAllowed(\\\"setLastRewardingBlocks(address[],uint32[],uint32[])\\\");\\n require(!isTimeBased, \\\"Block-based operation only\\\");\\n\\n uint256 numTokens = vTokens.length;\\n require(\\n numTokens == supplyLastRewardingBlocks.length && numTokens == borrowLastRewardingBlocks.length,\\n \\\"RewardsDistributor::setLastRewardingBlocks invalid input\\\"\\n );\\n\\n for (uint256 i; i < numTokens; ) {\\n _setLastRewardingBlock(vTokens[i], supplyLastRewardingBlocks[i], borrowLastRewardingBlocks[i]);\\n unchecked {\\n ++i;\\n }\\n }\\n }\\n\\n /**\\n * @notice Set REWARD TOKEN last rewarding block timestamp for the specified markets, used when contract is time based\\n * @param vTokens The markets whose REWARD TOKEN last rewarding block to update\\n * @param supplyLastRewardingBlockTimestamps New supply-side REWARD TOKEN last rewarding block timestamp for the corresponding market\\n * @param borrowLastRewardingBlockTimestamps New borrow-side REWARD TOKEN last rewarding block timestamp for the corresponding market\\n */\\n function setLastRewardingBlockTimestamps(\\n VToken[] calldata vTokens,\\n uint256[] calldata supplyLastRewardingBlockTimestamps,\\n uint256[] calldata borrowLastRewardingBlockTimestamps\\n ) external {\\n _checkAccessAllowed(\\\"setLastRewardingBlockTimestamps(address[],uint256[],uint256[])\\\");\\n require(isTimeBased, \\\"Time-based operation only\\\");\\n\\n uint256 numTokens = vTokens.length;\\n require(\\n numTokens == supplyLastRewardingBlockTimestamps.length &&\\n numTokens == borrowLastRewardingBlockTimestamps.length,\\n \\\"RewardsDistributor::setLastRewardingBlockTimestamps invalid input\\\"\\n );\\n\\n for (uint256 i; i < numTokens; ) {\\n _setLastRewardingBlockTimestamp(\\n vTokens[i],\\n supplyLastRewardingBlockTimestamps[i],\\n borrowLastRewardingBlockTimestamps[i]\\n );\\n unchecked {\\n ++i;\\n }\\n }\\n }\\n\\n /**\\n * @notice Set REWARD TOKEN speed for a single contributor\\n * @param contributor The contributor whose REWARD TOKEN speed to update\\n * @param rewardTokenSpeed New REWARD TOKEN speed for contributor\\n */\\n function setContributorRewardTokenSpeed(address contributor, uint256 rewardTokenSpeed) external onlyOwner {\\n // note that REWARD TOKEN speed could be set to 0 to halt liquidity rewards for a contributor\\n updateContributorRewards(contributor);\\n if (rewardTokenSpeed == 0) {\\n // release storage\\n delete lastContributorBlock[contributor];\\n } else {\\n lastContributorBlock[contributor] = getBlockNumberOrTimestamp();\\n }\\n rewardTokenContributorSpeeds[contributor] = rewardTokenSpeed;\\n\\n emit ContributorRewardTokenSpeedUpdated(contributor, rewardTokenSpeed);\\n }\\n\\n function distributeSupplierRewardToken(address vToken, address supplier) external onlyComptroller {\\n _distributeSupplierRewardToken(vToken, supplier);\\n }\\n\\n /**\\n * @notice Claim all the rewardToken accrued by holder in all markets\\n * @param holder The address to claim REWARD TOKEN for\\n */\\n function claimRewardToken(address holder) external {\\n return claimRewardToken(holder, comptroller.getAllMarkets());\\n }\\n\\n /**\\n * @notice Set the limit for the loops can iterate to avoid the DOS\\n * @param limit Limit for the max loops can execute at a time\\n */\\n function setMaxLoopsLimit(uint256 limit) external onlyOwner {\\n _setMaxLoopsLimit(limit);\\n }\\n\\n /**\\n * @notice Calculate additional accrued REWARD TOKEN for a contributor since last accrual\\n * @param contributor The address to calculate contributor rewards for\\n */\\n function updateContributorRewards(address contributor) public {\\n uint256 rewardTokenSpeed = rewardTokenContributorSpeeds[contributor];\\n uint256 blockNumberOrTimestamp = getBlockNumberOrTimestamp();\\n uint256 deltaBlocksOrTimestamp = sub_(blockNumberOrTimestamp, lastContributorBlock[contributor]);\\n if (deltaBlocksOrTimestamp > 0 && rewardTokenSpeed > 0) {\\n uint256 newAccrued = mul_(deltaBlocksOrTimestamp, rewardTokenSpeed);\\n uint256 contributorAccrued = add_(rewardTokenAccrued[contributor], newAccrued);\\n\\n rewardTokenAccrued[contributor] = contributorAccrued;\\n lastContributorBlock[contributor] = blockNumberOrTimestamp;\\n\\n emit ContributorRewardsUpdated(contributor, rewardTokenAccrued[contributor]);\\n }\\n }\\n\\n /**\\n * @notice Claim all the rewardToken accrued by holder in the specified markets\\n * @param holder The address to claim REWARD TOKEN for\\n * @param vTokens The list of markets to claim REWARD TOKEN in\\n */\\n function claimRewardToken(address holder, VToken[] memory vTokens) public {\\n uint256 vTokensCount = vTokens.length;\\n\\n _ensureMaxLoops(vTokensCount);\\n\\n for (uint256 i; i < vTokensCount; ++i) {\\n VToken vToken = vTokens[i];\\n require(comptroller.isMarketListed(vToken), \\\"market must be listed\\\");\\n Exp memory borrowIndex = Exp({ mantissa: vToken.borrowIndex() });\\n _updateRewardTokenBorrowIndex(address(vToken), borrowIndex);\\n _distributeBorrowerRewardToken(address(vToken), holder, borrowIndex);\\n _updateRewardTokenSupplyIndex(address(vToken));\\n _distributeSupplierRewardToken(address(vToken), holder);\\n }\\n rewardTokenAccrued[holder] = _grantRewardToken(holder, rewardTokenAccrued[holder]);\\n }\\n\\n /**\\n * @notice Set REWARD TOKEN last rewarding block for a single market.\\n * @param vToken market's whose reward token last rewarding block to be updated\\n * @param supplyLastRewardingBlock New supply-side REWARD TOKEN last rewarding block for market\\n * @param borrowLastRewardingBlock New borrow-side REWARD TOKEN last rewarding block for market\\n */\\n function _setLastRewardingBlock(\\n VToken vToken,\\n uint32 supplyLastRewardingBlock,\\n uint32 borrowLastRewardingBlock\\n ) internal {\\n require(comptroller.isMarketListed(vToken), \\\"rewardToken market is not listed\\\");\\n\\n uint256 blockNumber = getBlockNumberOrTimestamp();\\n\\n require(supplyLastRewardingBlock > blockNumber, \\\"setting last rewarding block in the past is not allowed\\\");\\n require(borrowLastRewardingBlock > blockNumber, \\\"setting last rewarding block in the past is not allowed\\\");\\n\\n uint32 currentSupplyLastRewardingBlock = rewardTokenSupplyState[address(vToken)].lastRewardingBlock;\\n uint32 currentBorrowLastRewardingBlock = rewardTokenBorrowState[address(vToken)].lastRewardingBlock;\\n\\n require(\\n currentSupplyLastRewardingBlock == 0 || currentSupplyLastRewardingBlock > blockNumber,\\n \\\"this RewardsDistributor is already locked\\\"\\n );\\n require(\\n currentBorrowLastRewardingBlock == 0 || currentBorrowLastRewardingBlock > blockNumber,\\n \\\"this RewardsDistributor is already locked\\\"\\n );\\n\\n if (currentSupplyLastRewardingBlock != supplyLastRewardingBlock) {\\n rewardTokenSupplyState[address(vToken)].lastRewardingBlock = supplyLastRewardingBlock;\\n emit SupplyLastRewardingBlockUpdated(address(vToken), supplyLastRewardingBlock);\\n }\\n\\n if (currentBorrowLastRewardingBlock != borrowLastRewardingBlock) {\\n rewardTokenBorrowState[address(vToken)].lastRewardingBlock = borrowLastRewardingBlock;\\n emit BorrowLastRewardingBlockUpdated(address(vToken), borrowLastRewardingBlock);\\n }\\n }\\n\\n /**\\n * @notice Set REWARD TOKEN last rewarding timestamp for a single market.\\n * @param vToken market's whose reward token last rewarding timestamp to be updated\\n * @param supplyLastRewardingBlockTimestamp New supply-side REWARD TOKEN last rewarding timestamp for market\\n * @param borrowLastRewardingBlockTimestamp New borrow-side REWARD TOKEN last rewarding timestamp for market\\n */\\n function _setLastRewardingBlockTimestamp(\\n VToken vToken,\\n uint256 supplyLastRewardingBlockTimestamp,\\n uint256 borrowLastRewardingBlockTimestamp\\n ) internal {\\n require(comptroller.isMarketListed(vToken), \\\"rewardToken market is not listed\\\");\\n\\n uint256 blockTimestamp = getBlockNumberOrTimestamp();\\n\\n require(\\n supplyLastRewardingBlockTimestamp > blockTimestamp,\\n \\\"setting last rewarding timestamp in the past is not allowed\\\"\\n );\\n require(\\n borrowLastRewardingBlockTimestamp > blockTimestamp,\\n \\\"setting last rewarding timestamp in the past is not allowed\\\"\\n );\\n\\n uint256 currentSupplyLastRewardingBlockTimestamp = rewardTokenSupplyStateTimeBased[address(vToken)]\\n .lastRewardingTimestamp;\\n uint256 currentBorrowLastRewardingBlockTimestamp = rewardTokenBorrowStateTimeBased[address(vToken)]\\n .lastRewardingTimestamp;\\n\\n require(\\n currentSupplyLastRewardingBlockTimestamp == 0 || currentSupplyLastRewardingBlockTimestamp > blockTimestamp,\\n \\\"this RewardsDistributor is already locked\\\"\\n );\\n require(\\n currentBorrowLastRewardingBlockTimestamp == 0 || currentBorrowLastRewardingBlockTimestamp > blockTimestamp,\\n \\\"this RewardsDistributor is already locked\\\"\\n );\\n\\n if (currentSupplyLastRewardingBlockTimestamp != supplyLastRewardingBlockTimestamp) {\\n rewardTokenSupplyStateTimeBased[address(vToken)].lastRewardingTimestamp = supplyLastRewardingBlockTimestamp;\\n emit SupplyLastRewardingBlockTimestampUpdated(address(vToken), supplyLastRewardingBlockTimestamp);\\n }\\n\\n if (currentBorrowLastRewardingBlockTimestamp != borrowLastRewardingBlockTimestamp) {\\n rewardTokenBorrowStateTimeBased[address(vToken)].lastRewardingTimestamp = borrowLastRewardingBlockTimestamp;\\n emit BorrowLastRewardingBlockTimestampUpdated(address(vToken), borrowLastRewardingBlockTimestamp);\\n }\\n }\\n\\n /**\\n * @notice Set REWARD TOKEN speed for a single market.\\n * @param vToken market's whose reward token rate to be updated\\n * @param supplySpeed New supply-side REWARD TOKEN speed for market\\n * @param borrowSpeed New borrow-side REWARD TOKEN speed for market\\n */\\n function _setRewardTokenSpeed(VToken vToken, uint256 supplySpeed, uint256 borrowSpeed) internal {\\n require(comptroller.isMarketListed(vToken), \\\"rewardToken market is not listed\\\");\\n\\n if (rewardTokenSupplySpeeds[address(vToken)] != supplySpeed) {\\n // Supply speed updated so let's update supply state to ensure that\\n // 1. REWARD TOKEN accrued properly for the old speed, and\\n // 2. REWARD TOKEN accrued at the new speed starts after this block.\\n _updateRewardTokenSupplyIndex(address(vToken));\\n\\n // Update speed and emit event\\n rewardTokenSupplySpeeds[address(vToken)] = supplySpeed;\\n emit RewardTokenSupplySpeedUpdated(vToken, supplySpeed);\\n }\\n\\n if (rewardTokenBorrowSpeeds[address(vToken)] != borrowSpeed) {\\n // Borrow speed updated so let's update borrow state to ensure that\\n // 1. REWARD TOKEN accrued properly for the old speed, and\\n // 2. REWARD TOKEN accrued at the new speed starts after this block.\\n Exp memory borrowIndex = Exp({ mantissa: vToken.borrowIndex() });\\n _updateRewardTokenBorrowIndex(address(vToken), borrowIndex);\\n\\n // Update speed and emit event\\n rewardTokenBorrowSpeeds[address(vToken)] = borrowSpeed;\\n emit RewardTokenBorrowSpeedUpdated(vToken, borrowSpeed);\\n }\\n }\\n\\n /**\\n * @notice Calculate REWARD TOKEN accrued by a supplier and possibly transfer it to them.\\n * @param vToken The market in which the supplier is interacting\\n * @param supplier The address of the supplier to distribute REWARD TOKEN to\\n */\\n function _distributeSupplierRewardToken(address vToken, address supplier) internal {\\n RewardToken storage supplyState = rewardTokenSupplyState[vToken];\\n TimeBasedRewardToken storage supplyStateTimeBased = rewardTokenSupplyStateTimeBased[vToken];\\n\\n uint256 supplyIndex = isTimeBased ? supplyStateTimeBased.index : supplyState.index;\\n uint256 supplierIndex = rewardTokenSupplierIndex[vToken][supplier];\\n\\n // Update supplier's index to the current index since we are distributing accrued REWARD TOKEN\\n rewardTokenSupplierIndex[vToken][supplier] = supplyIndex;\\n\\n if (supplierIndex == 0 && supplyIndex >= INITIAL_INDEX) {\\n // Covers the case where users supplied tokens before the market's supply state index was set.\\n // Rewards the user with REWARD TOKEN accrued from the start of when supplier rewards were first\\n // set for the market.\\n supplierIndex = INITIAL_INDEX;\\n }\\n\\n // Calculate change in the cumulative sum of the REWARD TOKEN per vToken accrued\\n Double memory deltaIndex = Double({ mantissa: sub_(supplyIndex, supplierIndex) });\\n\\n uint256 supplierTokens = VToken(vToken).balanceOf(supplier);\\n\\n // Calculate REWARD TOKEN accrued: vTokenAmount * accruedPerVToken\\n uint256 supplierDelta = mul_(supplierTokens, deltaIndex);\\n\\n uint256 supplierAccrued = add_(rewardTokenAccrued[supplier], supplierDelta);\\n rewardTokenAccrued[supplier] = supplierAccrued;\\n\\n emit DistributedSupplierRewardToken(VToken(vToken), supplier, supplierDelta, supplierAccrued, supplyIndex);\\n }\\n\\n /**\\n * @notice Calculate reward token accrued by a borrower and possibly transfer it to them.\\n * @param vToken The market in which the borrower is interacting\\n * @param borrower The address of the borrower to distribute REWARD TOKEN to\\n * @param marketBorrowIndex The current global borrow index of vToken\\n */\\n function _distributeBorrowerRewardToken(address vToken, address borrower, Exp memory marketBorrowIndex) internal {\\n RewardToken storage borrowState = rewardTokenBorrowState[vToken];\\n TimeBasedRewardToken storage borrowStateTimeBased = rewardTokenBorrowStateTimeBased[vToken];\\n\\n uint256 borrowIndex = isTimeBased ? borrowStateTimeBased.index : borrowState.index;\\n uint256 borrowerIndex = rewardTokenBorrowerIndex[vToken][borrower];\\n\\n // Update borrowers's index to the current index since we are distributing accrued REWARD TOKEN\\n rewardTokenBorrowerIndex[vToken][borrower] = borrowIndex;\\n\\n if (borrowerIndex == 0 && borrowIndex >= INITIAL_INDEX) {\\n // Covers the case where users borrowed tokens before the market's borrow state index was set.\\n // Rewards the user with REWARD TOKEN accrued from the start of when borrower rewards were first\\n // set for the market.\\n borrowerIndex = INITIAL_INDEX;\\n }\\n\\n // Calculate change in the cumulative sum of the REWARD TOKEN per borrowed unit accrued\\n Double memory deltaIndex = Double({ mantissa: sub_(borrowIndex, borrowerIndex) });\\n\\n uint256 borrowerAmount = div_(VToken(vToken).borrowBalanceStored(borrower), marketBorrowIndex);\\n\\n // Calculate REWARD TOKEN accrued: vTokenAmount * accruedPerBorrowedUnit\\n if (borrowerAmount != 0) {\\n uint256 borrowerDelta = mul_(borrowerAmount, deltaIndex);\\n\\n uint256 borrowerAccrued = add_(rewardTokenAccrued[borrower], borrowerDelta);\\n rewardTokenAccrued[borrower] = borrowerAccrued;\\n\\n emit DistributedBorrowerRewardToken(VToken(vToken), borrower, borrowerDelta, borrowerAccrued, borrowIndex);\\n }\\n }\\n\\n /**\\n * @notice Transfer REWARD TOKEN to the user.\\n * @dev Note: If there is not enough REWARD TOKEN, we do not perform the transfer all.\\n * @param user The address of the user to transfer REWARD TOKEN to\\n * @param amount The amount of REWARD TOKEN to (possibly) transfer\\n * @return The amount of REWARD TOKEN which was NOT transferred to the user\\n */\\n function _grantRewardToken(address user, uint256 amount) internal returns (uint256) {\\n uint256 rewardTokenRemaining = rewardToken.balanceOf(address(this));\\n if (amount > 0 && amount <= rewardTokenRemaining) {\\n rewardToken.safeTransfer(user, amount);\\n return 0;\\n }\\n return amount;\\n }\\n\\n /**\\n * @notice Accrue REWARD TOKEN to the market by updating the supply index\\n * @param vToken The market whose supply index to update\\n * @dev Index is a cumulative sum of the REWARD TOKEN per vToken accrued\\n */\\n function _updateRewardTokenSupplyIndex(address vToken) internal {\\n RewardToken storage supplyState = rewardTokenSupplyState[vToken];\\n TimeBasedRewardToken storage supplyStateTimeBased = rewardTokenSupplyStateTimeBased[vToken];\\n\\n uint256 supplySpeed = rewardTokenSupplySpeeds[vToken];\\n uint256 blockNumberOrTimestamp = getBlockNumberOrTimestamp();\\n\\n if (!isTimeBased) {\\n safe32(blockNumberOrTimestamp, \\\"block number exceeds 32 bits\\\");\\n }\\n\\n uint256 lastRewardingBlockOrTimestamp = isTimeBased\\n ? supplyStateTimeBased.lastRewardingTimestamp\\n : uint256(supplyState.lastRewardingBlock);\\n\\n if (lastRewardingBlockOrTimestamp > 0 && blockNumberOrTimestamp > lastRewardingBlockOrTimestamp) {\\n blockNumberOrTimestamp = lastRewardingBlockOrTimestamp;\\n }\\n\\n uint256 deltaBlocksOrTimestamp = sub_(\\n blockNumberOrTimestamp,\\n (isTimeBased ? supplyStateTimeBased.timestamp : uint256(supplyState.block))\\n );\\n if (deltaBlocksOrTimestamp > 0 && supplySpeed > 0) {\\n uint256 supplyTokens = VToken(vToken).totalSupply();\\n uint256 accruedSinceUpdate = mul_(deltaBlocksOrTimestamp, supplySpeed);\\n Double memory ratio = supplyTokens > 0\\n ? fraction(accruedSinceUpdate, supplyTokens)\\n : Double({ mantissa: 0 });\\n uint224 supplyIndex = isTimeBased ? supplyStateTimeBased.index : supplyState.index;\\n uint224 index = safe224(\\n add_(Double({ mantissa: supplyIndex }), ratio).mantissa,\\n \\\"new index exceeds 224 bits\\\"\\n );\\n\\n if (isTimeBased) {\\n supplyStateTimeBased.index = index;\\n supplyStateTimeBased.timestamp = blockNumberOrTimestamp;\\n } else {\\n supplyState.index = index;\\n supplyState.block = uint32(blockNumberOrTimestamp);\\n }\\n } else if (deltaBlocksOrTimestamp > 0) {\\n isTimeBased ? supplyStateTimeBased.timestamp = blockNumberOrTimestamp : supplyState.block = uint32(\\n blockNumberOrTimestamp\\n );\\n }\\n\\n emit RewardTokenSupplyIndexUpdated(vToken);\\n }\\n\\n /**\\n * @notice Accrue REWARD TOKEN to the market by updating the borrow index\\n * @param vToken The market whose borrow index to update\\n * @param marketBorrowIndex The current global borrow index of vToken\\n * @dev Index is a cumulative sum of the REWARD TOKEN per vToken accrued\\n */\\n function _updateRewardTokenBorrowIndex(address vToken, Exp memory marketBorrowIndex) internal {\\n RewardToken storage borrowState = rewardTokenBorrowState[vToken];\\n TimeBasedRewardToken storage borrowStateTimeBased = rewardTokenBorrowStateTimeBased[vToken];\\n\\n uint256 borrowSpeed = rewardTokenBorrowSpeeds[vToken];\\n uint256 blockNumberOrTimestamp = getBlockNumberOrTimestamp();\\n\\n if (!isTimeBased) {\\n safe32(blockNumberOrTimestamp, \\\"block number exceeds 32 bits\\\");\\n }\\n\\n uint256 lastRewardingBlockOrTimestamp = isTimeBased\\n ? borrowStateTimeBased.lastRewardingTimestamp\\n : uint256(borrowState.lastRewardingBlock);\\n\\n if (lastRewardingBlockOrTimestamp > 0 && blockNumberOrTimestamp > lastRewardingBlockOrTimestamp) {\\n blockNumberOrTimestamp = lastRewardingBlockOrTimestamp;\\n }\\n\\n uint256 deltaBlocksOrTimestamp = sub_(\\n blockNumberOrTimestamp,\\n (isTimeBased ? borrowStateTimeBased.timestamp : uint256(borrowState.block))\\n );\\n if (deltaBlocksOrTimestamp > 0 && borrowSpeed > 0) {\\n uint256 borrowAmount = div_(VToken(vToken).totalBorrows(), marketBorrowIndex);\\n uint256 accruedSinceUpdate = mul_(deltaBlocksOrTimestamp, borrowSpeed);\\n Double memory ratio = borrowAmount > 0\\n ? fraction(accruedSinceUpdate, borrowAmount)\\n : Double({ mantissa: 0 });\\n uint224 borrowIndex = isTimeBased ? borrowStateTimeBased.index : borrowState.index;\\n uint224 index = safe224(\\n add_(Double({ mantissa: borrowIndex }), ratio).mantissa,\\n \\\"new index exceeds 224 bits\\\"\\n );\\n\\n if (isTimeBased) {\\n borrowStateTimeBased.index = index;\\n borrowStateTimeBased.timestamp = blockNumberOrTimestamp;\\n } else {\\n borrowState.index = index;\\n borrowState.block = uint32(blockNumberOrTimestamp);\\n }\\n } else if (deltaBlocksOrTimestamp > 0) {\\n if (isTimeBased) {\\n borrowStateTimeBased.timestamp = blockNumberOrTimestamp;\\n } else {\\n borrowState.block = uint32(blockNumberOrTimestamp);\\n }\\n }\\n\\n emit RewardTokenBorrowIndexUpdated(vToken, marketBorrowIndex);\\n }\\n\\n /**\\n * @notice Initializes the market state for a specific vToken called when contract is block-based\\n * @param vToken The address of the vToken to be initialized\\n * @param blockNumber current block number\\n */\\n function _initializeMarketBlockBased(address vToken, uint32 blockNumber) internal {\\n RewardToken storage supplyState = rewardTokenSupplyState[vToken];\\n RewardToken storage borrowState = rewardTokenBorrowState[vToken];\\n\\n /*\\n * Update market state indices\\n */\\n if (supplyState.index == 0) {\\n // Initialize supply state index with default value\\n supplyState.index = INITIAL_INDEX;\\n }\\n\\n if (borrowState.index == 0) {\\n // Initialize borrow state index with default value\\n borrowState.index = INITIAL_INDEX;\\n }\\n\\n /*\\n * Update market state block numbers\\n */\\n supplyState.block = borrowState.block = blockNumber;\\n }\\n\\n /**\\n * @notice Initializes the market state for a specific vToken called when contract is time-based\\n * @param vToken The address of the vToken to be initialized\\n * @param blockTimestamp current block timestamp\\n */\\n function _initializeMarketTimestampBased(address vToken, uint256 blockTimestamp) internal {\\n TimeBasedRewardToken storage supplyState = rewardTokenSupplyStateTimeBased[vToken];\\n TimeBasedRewardToken storage borrowState = rewardTokenBorrowStateTimeBased[vToken];\\n\\n /*\\n * Update market state indices\\n */\\n if (supplyState.index == 0) {\\n // Initialize supply state index with default value\\n supplyState.index = INITIAL_INDEX;\\n }\\n\\n if (borrowState.index == 0) {\\n // Initialize borrow state index with default value\\n borrowState.index = INITIAL_INDEX;\\n }\\n\\n /*\\n * Update market state block timestamp\\n */\\n supplyState.timestamp = borrowState.timestamp = blockTimestamp;\\n }\\n}\\n\",\"keccak256\":\"0x3cc824e59c923cfe25c4f1a875959b7e9410cde8e73cd405de13f301298c697f\",\"license\":\"BSD-3-Clause\"},\"contracts/Rewards/RewardsDistributorStorage.sol\":{\"content\":\"// SPDX-License-Identifier: BSD-3-Clause\\npragma solidity 0.8.25;\\n\\nimport { IERC20Upgradeable } from \\\"@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol\\\";\\n\\nimport { Comptroller } from \\\"../Comptroller.sol\\\";\\n\\n/**\\n * @title RewardsDistributorStorage\\n * @author Venus\\n * @dev Storage for RewardsDistributor\\n */\\ncontract RewardsDistributorStorage {\\n struct RewardToken {\\n // The market's last updated rewardTokenBorrowIndex or rewardTokenSupplyIndex\\n uint224 index;\\n // The block number the index was last updated at\\n uint32 block;\\n // The block number at which to stop rewards\\n uint32 lastRewardingBlock;\\n }\\n\\n struct TimeBasedRewardToken {\\n // The market's last updated rewardTokenBorrowIndex or rewardTokenSupplyIndex\\n uint224 index;\\n // The block timestamp the index was last updated at\\n uint256 timestamp;\\n // The block timestamp at which to stop rewards\\n uint256 lastRewardingTimestamp;\\n }\\n\\n /// @notice The REWARD TOKEN market supply state for each market\\n mapping(address => RewardToken) public rewardTokenSupplyState;\\n\\n /// @notice The REWARD TOKEN borrow index for each market for each supplier as of the last time they accrued REWARD TOKEN\\n mapping(address => mapping(address => uint256)) public rewardTokenSupplierIndex;\\n\\n /// @notice The REWARD TOKEN accrued but not yet transferred to each user\\n mapping(address => uint256) public rewardTokenAccrued;\\n\\n /// @notice The rate at which rewardToken is distributed to the corresponding borrow market per slot (block or second)\\n mapping(address => uint256) public rewardTokenBorrowSpeeds;\\n\\n /// @notice The rate at which rewardToken is distributed to the corresponding supply market per slot (block or second)\\n mapping(address => uint256) public rewardTokenSupplySpeeds;\\n\\n /// @notice The REWARD TOKEN market borrow state for each market\\n mapping(address => RewardToken) public rewardTokenBorrowState;\\n\\n /// @notice The portion of REWARD TOKEN that each contributor receives per slot (block or second)\\n mapping(address => uint256) public rewardTokenContributorSpeeds;\\n\\n /// @notice Last slot (block or second) at which a contributor's REWARD TOKEN rewards have been allocated\\n mapping(address => uint256) public lastContributorBlock;\\n\\n /// @notice The REWARD TOKEN borrow index for each market for each borrower as of the last time they accrued REWARD TOKEN\\n mapping(address => mapping(address => uint256)) public rewardTokenBorrowerIndex;\\n\\n Comptroller internal comptroller;\\n\\n IERC20Upgradeable public rewardToken;\\n\\n /// @notice The REWARD TOKEN market supply state for each market\\n mapping(address => TimeBasedRewardToken) public rewardTokenSupplyStateTimeBased;\\n\\n /// @notice The REWARD TOKEN market borrow state for each market\\n mapping(address => TimeBasedRewardToken) public rewardTokenBorrowStateTimeBased;\\n\\n /**\\n * @dev This empty reserved space is put in place to allow future versions to add new\\n * variables without shifting down storage in the inheritance chain\\n * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\\n */\\n uint256[37] private __gap;\\n}\\n\",\"keccak256\":\"0x70e5015a78a2acf95277949c8b4796e10b9ac194130be006d5e5217e158070c0\",\"license\":\"BSD-3-Clause\"},\"contracts/Spoke/SpokeComptrollerInterface.sol\":{\"content\":\"// SPDX-License-Identifier: BSD-3-Clause\\npragma solidity ^0.8.25;\\n\\nimport { ResilientOracleInterface } from \\\"@venusprotocol/oracle/contracts/interfaces/OracleInterface.sol\\\";\\nimport { IDeviationBoundedOracle } from \\\"@venusprotocol/oracle/contracts/interfaces/IDeviationBoundedOracle.sol\\\";\\n\\nimport { ComptrollerInterface, Action } from \\\"../ComptrollerInterface.sol\\\";\\nimport { VToken } from \\\"../VToken.sol\\\";\\n\\n/**\\n * @title SpokeComptrollerInterface\\n * @author Venus\\n * @notice Interface implemented by the `SpokeComptroller` contract. It declares the events and errors that make up the\\n * contract's observable surface, so integrators and off-chain consumers can decode them without depending on the\\n * implementation.\\n * @dev The getters this fork adds are declared separately, in `SpokeComptrollerViewInterface` below. They cannot live\\n * here: `SpokeComptroller` implements this interface, and it serves those getters from public variables declared in\\n * `SpokeComptrollerStorage`, which is a sibling base rather than a derived one. Solidity will not let a public\\n * variable in one base satisfy a function declared in another, so declaring them here would force\\n * `SpokeComptrollerStorage` to inherit this interface and mark six variables `override` - noise in the file that has\\n * to be diffed by hand against `ComptrollerStorage`, for no gain over a standalone view interface.\\n */\\ninterface SpokeComptrollerInterface is ComptrollerInterface {\\n /// @notice Emitted when an account enters a market\\n event MarketEntered(VToken indexed vToken, address indexed account);\\n\\n /// @notice Emitted when an account exits a market\\n event MarketExited(VToken indexed vToken, address indexed account);\\n\\n /// @notice Emitted when close factor is changed by admin\\n event NewCloseFactor(uint256 oldCloseFactorMantissa, uint256 newCloseFactorMantissa);\\n\\n /// @notice Emitted when a collateral factor is changed by admin\\n event NewCollateralFactor(VToken vToken, uint256 oldCollateralFactorMantissa, uint256 newCollateralFactorMantissa);\\n\\n /// @notice Emitted when liquidation threshold is changed by admin\\n event NewLiquidationThreshold(\\n VToken vToken,\\n uint256 oldLiquidationThresholdMantissa,\\n uint256 newLiquidationThresholdMantissa\\n );\\n\\n /// @notice Emitted when liquidation incentive is changed by admin\\n event NewLiquidationIncentive(uint256 oldLiquidationIncentiveMantissa, uint256 newLiquidationIncentiveMantissa);\\n\\n /// @notice Emitted when the liquidation incentive of a single market is changed by admin\\n event NewMarketLiquidationIncentive(\\n address indexed vToken,\\n uint256 oldLiquidationIncentiveMantissa,\\n uint256 newLiquidationIncentiveMantissa\\n );\\n\\n /// @notice Emitted when price oracle is changed\\n event NewPriceOracle(ResilientOracleInterface oldPriceOracle, ResilientOracleInterface newPriceOracle);\\n\\n /// @notice Emitted when the deviation-bounded oracle is changed\\n event NewDeviationBoundedOracle(IDeviationBoundedOracle oldBoundedOracle, IDeviationBoundedOracle newBoundedOracle);\\n\\n /// @notice Emitted when an action is paused on a market\\n event ActionPausedMarket(VToken vToken, Action action, bool pauseState);\\n\\n /// @notice Emitted when borrow cap for a vToken is changed\\n event NewBorrowCap(VToken indexed vToken, uint256 newBorrowCap);\\n\\n /// @notice Emitted when the collateral threshold (in USD) for non-batch liquidations is changed\\n event NewMinLiquidatableCollateral(uint256 oldMinLiquidatableCollateral, uint256 newMinLiquidatableCollateral);\\n\\n /// @notice Emitted when supply cap for a vToken is changed\\n event NewSupplyCap(VToken indexed vToken, uint256 newSupplyCap);\\n\\n /// @notice Emitted when a rewards distributor is added\\n event NewRewardsDistributor(address indexed rewardsDistributor, address indexed rewardToken);\\n\\n /// @notice Emitted when a market is supported\\n event MarketSupported(VToken vToken);\\n\\n /// @notice Emitted when forced liquidation is enabled or disabled for a market\\n event IsForcedLiquidationEnabledUpdated(address indexed vToken, bool enable);\\n\\n /// @notice Emitted when a market is unlisted\\n event MarketUnlisted(address indexed vToken);\\n\\n /// @notice Emitted when the borrowing or redeeming delegate rights are updated for an account\\n event DelegateUpdated(address indexed approver, address indexed delegate, bool approved);\\n\\n /// @notice Emitted when a market's supply allowlist is enabled or disabled\\n event SupplyAllowlistEnabledUpdated(address indexed vToken, bool enabled);\\n\\n /// @notice Emitted when an account is added to or removed from a market's supply allowlist\\n event AllowedSupplierUpdated(address indexed vToken, address indexed supplier, bool allowed);\\n\\n /// @notice Emitted when the pool's liquidation allowlist is enabled or disabled\\n event LiquidationAllowlistEnabledUpdated(bool enabled);\\n\\n /// @notice Emitted when an account is added to or removed from the pool's liquidation allowlist\\n event AllowedLiquidatorUpdated(address indexed liquidator, bool allowed);\\n\\n /// @notice Thrown when the close factor is outside the bounds set by `MIN_CLOSE_FACTOR_MANTISSA` and\\n /// `MAX_CLOSE_FACTOR_MANTISSA`\\n error InvalidCloseFactor();\\n\\n /// @notice Thrown when collateral factor exceeds the upper bound\\n error InvalidCollateralFactor();\\n\\n /// @notice Thrown when liquidation threshold exceeds the collateral factor\\n error InvalidLiquidationThreshold();\\n\\n /// @notice Thrown when a liquidation incentive is low enough that a liquidator would seize less value than it\\n /// repaid: below `1e18 + protocolSeizeShareMantissa` for a single market, below\\n /// `MIN_POOL_LIQUIDATION_INCENTIVE_MANTISSA` for the pool-wide fallback\\n error InvalidLiquidationIncentive();\\n\\n /// @notice Thrown when the action is only available to specific sender, but the real sender was different\\n error UnexpectedSender(address expectedSender, address actualSender);\\n\\n /// @notice Thrown when the oracle returns an invalid price for some asset\\n error PriceError(address vToken);\\n\\n /// @notice Thrown if VToken unexpectedly returned a nonzero error code while trying to get account snapshot\\n error SnapshotError(address vToken, address user);\\n\\n /// @notice Thrown when the market is not listed\\n error MarketNotListed(address market);\\n\\n /// @notice Thrown when a market has an unexpected comptroller\\n error ComptrollerMismatch();\\n\\n /// @notice Thrown when user is not member of market\\n error MarketNotCollateral(address vToken, address user);\\n\\n /// @notice Thrown when borrow action is not paused\\n error BorrowActionNotPaused();\\n\\n /// @notice Thrown when mint action is not paused\\n error MintActionNotPaused();\\n\\n /// @notice Thrown when redeem action is not paused\\n error RedeemActionNotPaused();\\n\\n /// @notice Thrown when repay action is not paused\\n error RepayActionNotPaused();\\n\\n /// @notice Thrown when seize action is not paused\\n error SeizeActionNotPaused();\\n\\n /// @notice Thrown when exit market action is not paused\\n error ExitMarketActionNotPaused();\\n\\n /// @notice Thrown when transfer action is not paused\\n error TransferActionNotPaused();\\n\\n /// @notice Thrown when enter market action is not paused\\n error EnterMarketActionNotPaused();\\n\\n /// @notice Thrown when liquidate action is not paused\\n error LiquidateActionNotPaused();\\n\\n /// @notice Thrown when borrow cap is not zero\\n error BorrowCapIsNotZero();\\n\\n /// @notice Thrown when supply cap is not zero\\n error SupplyCapIsNotZero();\\n\\n /// @notice Thrown when collateral factor is not zero\\n error CollateralFactorIsNotZero();\\n\\n /**\\n * @notice Thrown during the liquidation if user's total collateral amount is lower than\\n * a predefined threshold. In this case only batch liquidations (either liquidateAccount\\n * or healAccount) are available.\\n */\\n error MinimalCollateralViolated(uint256 expectedGreaterThan, uint256 actual);\\n\\n /**\\n * @notice Thrown by the batch operations when the account's total collateral is above\\n * `minLiquidatableCollateral`, which means it is large enough for a regular `VToken.liquidateBorrow`\\n * @dev Same name, arguments and meaning as the shared `Comptroller`, so a decoder written against either one\\n * reads this correctly.\\n */\\n error CollateralExceedsThreshold(uint256 expectedLessThanOrEqualTo, uint256 actual);\\n\\n /// @notice Thrown by `healAccount` when the collateral can clear the whole debt at each market's own liquidation\\n /// incentive, so healing would forgive nothing and `liquidateAccount` has to be used instead\\n error CollateralCoversDebt(uint256 borrows, uint256 maxClearableDebt);\\n\\n /// @notice Thrown by `liquidateAccount` when the debt is too large for the collateral to clear, so `healAccount`\\n /// has to be used instead. Reports the debt and the largest debt the collateral could have cleared.\\n /// @dev Deliberately not the shared `Comptroller`'s `InsufficientCollateral`: both arguments here are debt values\\n /// rather than collateral values, and reusing that name would leave the two versions sharing one selector.\\n error DebtExceedsClearableAmount(uint256 borrows, uint256 maxClearableDebt);\\n\\n /// @notice Thrown when the account doesn't have enough liquidity to redeem or borrow\\n error InsufficientLiquidity();\\n\\n /// @notice Thrown when trying to liquidate a healthy account\\n error InsufficientShortfall();\\n\\n /// @notice Thrown if the liquidation allowlist is enabled and the account liquidating is not on it\\n error LiquidationNotAllowed(address liquidator);\\n\\n /// @notice Thrown when trying to repay more than allowed by close factor\\n error TooMuchRepay();\\n\\n /// @notice Thrown if the user is trying to exit a market in which they have an outstanding debt\\n error NonzeroBorrowBalance();\\n\\n /// @notice Thrown if a debt remains in any of the borrower's markets once every liquidation order has been\\n /// executed, which means the orders passed to `liquidateAccount` did not cover the whole position\\n error NonzeroBorrowBalanceAfterLiquidation();\\n\\n /// @notice Thrown when trying to perform an action that is paused\\n error ActionPaused(address market, Action action);\\n\\n /// @notice Thrown when trying to add a market that is already listed\\n error MarketAlreadyListed(address market);\\n\\n /// @notice Thrown when the market being listed does not identify itself as a VToken\\n error InvalidVToken();\\n\\n /// @notice Thrown when an array argument is empty, or when two array arguments have different lengths\\n error InvalidArrayLength();\\n\\n /// @notice Thrown if the supply cap is exceeded\\n error SupplyCapExceeded(address market, uint256 cap);\\n\\n /// @notice Thrown if the account being credited with the minted vTokens is not on the market's supply allowlist\\n error SupplyNotAllowed(address market, address supplier);\\n\\n /// @notice Thrown if the borrow cap is exceeded\\n error BorrowCapExceeded(address market, uint256 cap);\\n\\n /// @notice Thrown if delegate approval status is already set to the requested value\\n error DelegationStatusUnchanged();\\n\\n /// @notice Thrown when adding a rewards distributor that this pool already has\\n error RewardsDistributorAlreadyExists();\\n}\\n\\n/**\\n * @title SpokeComptrollerViewInterface\\n * @author Venus\\n * @notice The getters `SpokeComptroller` adds on top of the pooled `Comptroller`, for integrators that read this pool\\n * rather than implement it - the Liquidity Hub's spoke adapter, lenses, keepers and VIP tooling.\\n * @dev Standalone and not implemented by `SpokeComptroller`, matching how `ComptrollerViewInterface` exposes the\\n * shared pool's getters.\\n *\\n * Scope is the supply side: what an integrator that funds this pool has to read before and while it supplies, plus\\n * the two allowlists and the per-market discount that only exist on this fork. `supplyCaps` and `actionPaused` are\\n * repeated from `ComptrollerViewInterface` and `ComptrollerInterface` rather than inherited, so that a consumer of a\\n * spoke pool needs one import and not three. `actionPaused` also has to be repeated on its own terms: the shared\\n * declaration types its second argument as the `Action` enum, and a consumer that does not want this repo's enum in\\n * its build needs the ABI-equivalent `uint8` form. The two share a selector, so they cannot both be declared in one\\n * inheritance chain - which is the reason this interface inherits nothing.\\n *\\n * `markets` is repeated for a different reason: the shared declaration returns two of the three words the getter\\n * actually produces, so a consumer reading it there silently loses `liquidationThresholdMantissa`. The form declared\\n * here returns all three.\\n *\\n * Anything else the fork shares with the pooled `Comptroller` - `borrowCaps`, `oracle`, `closeFactorMantissa`,\\n * `minLiquidatableCollateral` - is read through `ComptrollerViewInterface` as usual.\\n */\\ninterface SpokeComptrollerViewInterface {\\n /**\\n * @notice The listing state and both collateral weights of a market\\n * @dev The auto-generated getter over the `markets` mapping. It returns three words, one per non-mapping member\\n * of `Market`. `ComptrollerViewInterface` declares only the first two, so a caller reading the getter through\\n * that declaration drops `liquidationThresholdMantissa` without any error: the extra word is simply trailing\\n * return data the decoder ignores. Read it here to get all three.\\n * @param vToken The market to query\\n * @return isListed True once the market has been added to the pool\\n * @return collateralFactorMantissa The most an account may borrow against this collateral, scaled by 1e18\\n * @return liquidationThresholdMantissa The weighting past which the position becomes liquidatable, scaled by 1e18\\n */\\n function markets(\\n address vToken\\n ) external view returns (bool isListed, uint256 collateralFactorMantissa, uint256 liquidationThresholdMantissa);\\n\\n /**\\n * @notice Whether a market may be liquidated while the borrower is still above the liquidation threshold\\n * @dev Read in `preLiquidateHook`. While this is set the close factor no longer bounds the repayment, so a\\n * position in the market can be closed in full in one call.\\n * @param vToken The market to read the setting of\\n * @return enabled True if this market allows liquidation without a shortfall\\n */\\n function isForcedLiquidationEnabled(address vToken) external view returns (bool enabled);\\n\\n /**\\n * @notice The most underlying a market will hold before it stops accepting supply\\n * @dev `type(uint256).max` disables the check. Zero is a real cap of zero rather than an \\\"unset\\\" sentinel:\\n * `preMintHook` compares `nextTotalSupply > supplyCap`, so a market left at zero rejects every mint.\\n * @param vToken The market to query\\n * @return cap The market's supply cap, in underlying units\\n */\\n function supplyCaps(address vToken) external view returns (uint256 cap);\\n\\n /**\\n * @notice Whether a market accepts supply only from the accounts on its supply allowlist\\n * @dev Enforced in `preMintHook` alone. Redeeming is never restricted, and a market that has never had this\\n * enabled accepts supply from anyone.\\n * @param vToken The market to read the setting of\\n * @return enabled True if the market's supply allowlist is armed\\n */\\n function isSupplyAllowlistEnabled(address vToken) external view returns (bool enabled);\\n\\n /**\\n * @notice Whether an account may supply to a market while that market's supply allowlist is enabled\\n * @dev The account this meters is the one CREDITED with the newly minted vTokens, not the one paying for them:\\n * `mintBehalf` lets a third party fund a mint attributed to someone else, and metering the recipient is what\\n * bounds the market's supply.\\n * @param vToken The market whose allowlist to read\\n * @param supplier The account to test\\n * @return allowed True if `supplier` may be credited with this market's vTokens\\n */\\n function isAllowedSupplier(address vToken, address supplier) external view returns (bool allowed);\\n\\n /**\\n * @notice Whether seizing collateral in this pool is restricted to the accounts on the liquidation allowlist\\n * @dev Pool-wide rather than per market, because `healAccount` seizes across every market the borrower is in and\\n * so cannot attribute a seizure to a single one of them.\\n * @return enabled True if the pool's liquidation allowlist is armed\\n */\\n function isLiquidationAllowlistEnabled() external view returns (bool enabled);\\n\\n /**\\n * @notice Whether an account may seize collateral in this pool while the liquidation allowlist is enabled\\n * @param liquidator The account to test\\n * @return allowed True if `liquidator` may seize collateral in this pool\\n */\\n function isAllowedLiquidator(address liquidator) external view returns (bool allowed);\\n\\n /**\\n * @notice The discount a market has of its own, or zero when it takes the pool-wide one\\n * @dev Zero is the \\\"unset\\\" sentinel rather than a real discount. Read `effectiveLiquidationIncentive` to get the\\n * value that actually applies to a market.\\n * @param vToken The collateral market to read the discount of\\n * @return incentiveMantissa The market's own discount scaled by 1e18, or zero if it has none\\n */\\n function liquidationIncentives(address vToken) external view returns (uint256 incentiveMantissa);\\n\\n /**\\n * @notice The discount that applies to a market: its own if it has one, otherwise the pool-wide value\\n * @param vToken The collateral market to read the discount of\\n * @return incentiveMantissa The discount that prices this market's collateral, scaled by 1e18\\n */\\n function effectiveLiquidationIncentive(address vToken) external view returns (uint256 incentiveMantissa);\\n\\n /**\\n * @notice The oracle that bounds an asset's price against a recent window\\n * @dev Read only where the collateral factor weights a position. The liquidation-threshold paths stay on the\\n * `oracle`, because they route liquidations. Zero until governance sets it, and while it is zero borrowing and\\n * redeeming fail closed.\\n * @return boundedOracle The deviation-bounded oracle this pool prices borrowing capacity through\\n */\\n function deviationBoundedOracle() external view returns (IDeviationBoundedOracle boundedOracle);\\n\\n /**\\n * @notice Whether an action is paused on a market\\n * @dev Typed as `uint8` rather than `Action` so that a consumer can read this without importing the enum. The\\n * encoding is identical; the deployed ordering is\\n * `{ MINT, REDEEM, BORROW, REPAY, SEIZE, LIQUIDATE, TRANSFER, ENTER_MARKET, EXIT_MARKET }`.\\n * @param market The market to query\\n * @param action The action, as its position in `Action`\\n * @return paused True if the action is paused on this market\\n */\\n function actionPaused(address market, uint8 action) external view returns (bool paused);\\n}\\n\",\"keccak256\":\"0x5e5c11b9d7f23436a40aa453fc6a56c34d73d22399f9adb605fc1b2a87afc6b7\",\"license\":\"BSD-3-Clause\"},\"contracts/VToken.sol\":{\"content\":\"// SPDX-License-Identifier: BSD-3-Clause\\npragma solidity 0.8.25;\\n\\nimport { Ownable2StepUpgradeable } from \\\"@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol\\\";\\nimport { IERC20Upgradeable } from \\\"@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol\\\";\\nimport { SafeERC20Upgradeable } from \\\"@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol\\\";\\nimport { AccessControlledV8 } from \\\"@venusprotocol/governance-contracts/contracts/Governance/AccessControlledV8.sol\\\";\\nimport { IProtocolShareReserve } from \\\"@venusprotocol/protocol-reserve/contracts/Interfaces/IProtocolShareReserve.sol\\\";\\n\\nimport { VTokenInterface } from \\\"./VTokenInterfaces.sol\\\";\\nimport { ComptrollerInterface, ComptrollerViewInterface } from \\\"./ComptrollerInterface.sol\\\";\\nimport { TokenErrorReporter } from \\\"./ErrorReporter.sol\\\";\\nimport { InterestRateModel } from \\\"./InterestRateModel.sol\\\";\\nimport { ExponentialNoError } from \\\"./ExponentialNoError.sol\\\";\\nimport { TimeManagerV8 } from \\\"@venusprotocol/solidity-utilities/contracts/TimeManagerV8.sol\\\";\\nimport { ensureNonzeroAddress } from \\\"./lib/validators.sol\\\";\\n\\n/**\\n * @title VToken\\n * @author Venus\\n * @notice Each asset that is supported by a pool is integrated through an instance of the `VToken` contract. As outlined in the protocol overview,\\n * each isolated pool creates its own `vToken` corresponding to an asset. Within a given pool, each included `vToken` is referred to as a market of\\n * the pool. The main actions a user regularly interacts with in a market are:\\n\\n- mint/redeem of vTokens;\\n- transfer of vTokens;\\n- borrow/repay a loan on an underlying asset;\\n- liquidate a borrow or liquidate/heal an account.\\n\\n * A user supplies the underlying asset to a pool by minting `vTokens`, where the corresponding `vToken` amount is determined by the `exchangeRate`.\\n * The `exchangeRate` will change over time, dependent on a number of factors, some of which accrue interest. Additionally, once users have minted\\n * `vToken` in a pool, they can borrow any asset in the isolated pool by using their `vToken` as collateral. In order to borrow an asset or use a `vToken`\\n * as collateral, the user must be entered into each corresponding market (else, the `vToken` will not be considered collateral for a borrow). Note that\\n * a user may borrow up to a portion of their collateral determined by the market\\u2019s collateral factor. However, if their borrowed amount exceeds an amount\\n * calculated using the market\\u2019s corresponding liquidation threshold, the borrow is eligible for liquidation. When a user repays a borrow, they must also\\n * pay off interest accrued on the borrow.\\n * \\n * The Venus protocol includes unique mechanisms for healing an account and liquidating an account. These actions are performed in the `Comptroller`\\n * and consider all borrows and collateral for which a given account is entered within a market. These functions may only be called on an account with a\\n * total collateral amount that is no larger than a universal `minLiquidatableCollateral` value, which is used for all markets within a `Comptroller`.\\n * Both functions settle all of an account\\u2019s borrows, but `healAccount()` may add `badDebt` to a vToken. For more detail, see the description of\\n * `healAccount()` and `liquidateAccount()` in the `Comptroller` summary section below.\\n */\\ncontract VToken is\\n Ownable2StepUpgradeable,\\n AccessControlledV8,\\n VTokenInterface,\\n ExponentialNoError,\\n TokenErrorReporter,\\n TimeManagerV8\\n{\\n using SafeERC20Upgradeable for IERC20Upgradeable;\\n\\n uint256 internal constant DEFAULT_PROTOCOL_SEIZE_SHARE_MANTISSA = 5e16; // 5%\\n\\n // Maximum fraction of interest that can be set aside for reserves\\n uint256 internal constant MAX_RESERVE_FACTOR_MANTISSA = 1e18;\\n\\n // Maximum borrow rate that can ever be applied per slot(block or second)\\n /// @custom:oz-upgrades-unsafe-allow state-variable-immutable\\n uint256 internal immutable MAX_BORROW_RATE_MANTISSA;\\n\\n /**\\n * Reentrancy Guard **\\n */\\n\\n /**\\n * @dev Prevents a contract from calling itself, directly or indirectly.\\n */\\n modifier nonReentrant() {\\n require(_notEntered, \\\"re-entered\\\");\\n _notEntered = false;\\n _;\\n _notEntered = true; // get a gas-refund post-Istanbul\\n }\\n\\n /**\\n * @param timeBased_ A boolean indicating whether the contract is based on time or block.\\n * @param blocksPerYear_ The number of blocks per year\\n * @param maxBorrowRateMantissa_ The maximum value of borrowing rate mantissa\\n * @custom:oz-upgrades-unsafe-allow constructor\\n */\\n constructor(\\n bool timeBased_,\\n uint256 blocksPerYear_,\\n uint256 maxBorrowRateMantissa_\\n ) TimeManagerV8(timeBased_, blocksPerYear_) {\\n // Note that the contract is upgradeable. Use initialize() or reinitializers\\n // to set the state variables.\\n require(maxBorrowRateMantissa_ <= 1e18, \\\"Max borrow rate must be <= 1e18\\\");\\n\\n MAX_BORROW_RATE_MANTISSA = maxBorrowRateMantissa_;\\n _disableInitializers();\\n }\\n\\n /**\\n * @notice Construct a new money market\\n * @param underlying_ The address of the underlying asset\\n * @param comptroller_ The address of the Comptroller\\n * @param interestRateModel_ The address of the interest rate model\\n * @param initialExchangeRateMantissa_ The initial exchange rate, scaled by 1e18\\n * @param name_ ERC-20 name of this token\\n * @param symbol_ ERC-20 symbol of this token\\n * @param decimals_ ERC-20 decimal precision of this token\\n * @param admin_ Address of the administrator of this token\\n * @param accessControlManager_ AccessControlManager contract address\\n * @param riskManagement Addresses of risk & income related contracts\\n * @param reserveFactorMantissa_ Percentage of borrow interest that goes to reserves (from 0 to 1e18)\\n * @custom:error ZeroAddressNotAllowed is thrown when admin address is zero\\n * @custom:error ZeroAddressNotAllowed is thrown when shortfall contract address is zero\\n * @custom:error ZeroAddressNotAllowed is thrown when protocol share reserve address is zero\\n */\\n function initialize(\\n address underlying_,\\n ComptrollerInterface comptroller_,\\n InterestRateModel interestRateModel_,\\n uint256 initialExchangeRateMantissa_,\\n string memory name_,\\n string memory symbol_,\\n uint8 decimals_,\\n address admin_,\\n address accessControlManager_,\\n RiskManagementInit memory riskManagement,\\n uint256 reserveFactorMantissa_\\n ) external initializer {\\n ensureNonzeroAddress(admin_);\\n\\n // Initialize the market\\n _initialize(\\n underlying_,\\n comptroller_,\\n interestRateModel_,\\n initialExchangeRateMantissa_,\\n name_,\\n symbol_,\\n decimals_,\\n admin_,\\n accessControlManager_,\\n riskManagement,\\n reserveFactorMantissa_\\n );\\n }\\n\\n /**\\n * @notice Transfer `amount` tokens from `msg.sender` to `dst`\\n * @param dst The address of the destination account\\n * @param amount The number of tokens to transfer\\n * @return success True if the transfer succeeded, reverts otherwise\\n * @custom:event Emits Transfer event on success\\n * @custom:error TransferNotAllowed is thrown if trying to transfer to self\\n * @custom:access Not restricted\\n */\\n function transfer(address dst, uint256 amount) external override nonReentrant returns (bool) {\\n _transferTokens(msg.sender, msg.sender, dst, amount);\\n return true;\\n }\\n\\n /**\\n * @notice Transfer `amount` tokens from `src` to `dst`\\n * @param src The address of the source account\\n * @param dst The address of the destination account\\n * @param amount The number of tokens to transfer\\n * @return success True if the transfer succeeded, reverts otherwise\\n * @custom:event Emits Transfer event on success\\n * @custom:error TransferNotAllowed is thrown if trying to transfer to self\\n * @custom:access Not restricted\\n */\\n function transferFrom(address src, address dst, uint256 amount) external override nonReentrant returns (bool) {\\n _transferTokens(msg.sender, src, dst, amount);\\n return true;\\n }\\n\\n /**\\n * @notice Approve `spender` to transfer up to `amount` from `src`\\n * @dev This will overwrite the approval amount for `spender`\\n * and is subject to issues noted [here](https://eips.ethereum.org/EIPS/eip-20#approve)\\n * @param spender The address of the account which may transfer tokens\\n * @param amount The number of tokens that are approved (uint256.max means infinite)\\n * @return success Whether or not the approval succeeded\\n * @custom:event Emits Approval event\\n * @custom:access Not restricted\\n * @custom:error ZeroAddressNotAllowed is thrown when spender address is zero\\n */\\n function approve(address spender, uint256 amount) external override returns (bool) {\\n ensureNonzeroAddress(spender);\\n\\n address src = msg.sender;\\n transferAllowances[src][spender] = amount;\\n emit Approval(src, spender, amount);\\n return true;\\n }\\n\\n /**\\n * @notice Increase approval for `spender`\\n * @param spender The address of the account which may transfer tokens\\n * @param addedValue The number of additional tokens spender can transfer\\n * @return success Whether or not the approval succeeded\\n * @custom:event Emits Approval event\\n * @custom:access Not restricted\\n * @custom:error ZeroAddressNotAllowed is thrown when spender address is zero\\n */\\n function increaseAllowance(address spender, uint256 addedValue) external override returns (bool) {\\n ensureNonzeroAddress(spender);\\n\\n address src = msg.sender;\\n uint256 newAllowance = transferAllowances[src][spender];\\n newAllowance += addedValue;\\n transferAllowances[src][spender] = newAllowance;\\n\\n emit Approval(src, spender, newAllowance);\\n return true;\\n }\\n\\n /**\\n * @notice Decreases approval for `spender`\\n * @param spender The address of the account which may transfer tokens\\n * @param subtractedValue The number of tokens to remove from total approval\\n * @return success Whether or not the approval succeeded\\n * @custom:event Emits Approval event\\n * @custom:access Not restricted\\n * @custom:error ZeroAddressNotAllowed is thrown when spender address is zero\\n */\\n function decreaseAllowance(address spender, uint256 subtractedValue) external override returns (bool) {\\n ensureNonzeroAddress(spender);\\n\\n address src = msg.sender;\\n uint256 currentAllowance = transferAllowances[src][spender];\\n require(currentAllowance >= subtractedValue, \\\"decreased allowance below zero\\\");\\n unchecked {\\n currentAllowance -= subtractedValue;\\n }\\n\\n transferAllowances[src][spender] = currentAllowance;\\n\\n emit Approval(src, spender, currentAllowance);\\n return true;\\n }\\n\\n /**\\n * @notice Get the underlying balance of the `owner`\\n * @dev This also accrues interest in a transaction\\n * @param owner The address of the account to query\\n * @return amount The amount of underlying owned by `owner`\\n */\\n function balanceOfUnderlying(address owner) external override returns (uint256) {\\n Exp memory exchangeRate = Exp({ mantissa: exchangeRateCurrent() });\\n return mul_ScalarTruncate(exchangeRate, accountTokens[owner]);\\n }\\n\\n /**\\n * @notice Returns the current total borrows plus accrued interest\\n * @return totalBorrows The total borrows with interest\\n */\\n function totalBorrowsCurrent() external override nonReentrant returns (uint256) {\\n accrueInterest();\\n return totalBorrows;\\n }\\n\\n /**\\n * @notice Accrue interest to updated borrowIndex and then calculate account's borrow balance using the updated borrowIndex\\n * @param account The address whose balance should be calculated after updating borrowIndex\\n * @return borrowBalance The calculated balance\\n */\\n function borrowBalanceCurrent(address account) external override nonReentrant returns (uint256) {\\n accrueInterest();\\n return _borrowBalanceStored(account);\\n }\\n\\n /**\\n * @notice Sender supplies assets into the market and receives vTokens in exchange\\n * @dev Accrues interest whether or not the operation succeeds, unless reverted\\n * @param mintAmount The amount of the underlying asset to supply\\n * @return error Always NO_ERROR for compatibility with Venus core tooling\\n * @custom:event Emits Mint and Transfer events; may emit AccrueInterest\\n * @custom:access Not restricted\\n */\\n function mint(uint256 mintAmount) external override nonReentrant returns (uint256) {\\n accrueInterest();\\n\\n _mintFresh(msg.sender, msg.sender, mintAmount);\\n return NO_ERROR;\\n }\\n\\n /**\\n * @notice Sender calls on-behalf of minter. minter supplies assets into the market and receives vTokens in exchange\\n * @dev Accrues interest whether or not the operation succeeds, unless reverted\\n * @param minter User whom the supply will be attributed to\\n * @param mintAmount The amount of the underlying asset to supply\\n * @return error Always NO_ERROR for compatibility with Venus core tooling\\n * @custom:event Emits Mint and Transfer events; may emit AccrueInterest\\n * @custom:access Not restricted\\n * @custom:error ZeroAddressNotAllowed is thrown when minter address is zero\\n */\\n function mintBehalf(address minter, uint256 mintAmount) external override nonReentrant returns (uint256) {\\n ensureNonzeroAddress(minter);\\n\\n accrueInterest();\\n\\n _mintFresh(msg.sender, minter, mintAmount);\\n return NO_ERROR;\\n }\\n\\n /**\\n * @notice Sender redeems vTokens in exchange for the underlying asset\\n * @dev Accrues interest whether or not the operation succeeds, unless reverted\\n * @param redeemTokens The number of vTokens to redeem into underlying\\n * @return error Always NO_ERROR for compatibility with Venus core tooling\\n * @custom:event Emits Redeem and Transfer events; may emit AccrueInterest\\n * @custom:error RedeemTransferOutNotPossible is thrown when the protocol has insufficient cash\\n * @custom:access Not restricted\\n */\\n function redeem(uint256 redeemTokens) external override nonReentrant returns (uint256) {\\n accrueInterest();\\n\\n _redeemFresh(msg.sender, msg.sender, redeemTokens, 0);\\n return NO_ERROR;\\n }\\n\\n /**\\n * @notice Sender redeems assets on behalf of some other address. This function is only available\\n * for senders, explicitly marked as delegates of the supplier using `comptroller.updateDelegate`\\n * @dev Accrues interest whether or not the operation succeeds, unless reverted\\n * @param redeemer The user on behalf of whom to redeem\\n * @param redeemTokens The number of vTokens to redeem into underlying\\n * @return error Always NO_ERROR for compatibility with Venus core tooling\\n * @custom:error InsufficientRedeemApproval is thrown when sender is not approved by the redeemer for the given amount\\n * @custom:error RedeemTransferOutNotPossible is thrown when the protocol has insufficient cash\\n * @custom:event Emits Redeem and Transfer events; may emit AccrueInterest\\n * @custom:access Not restricted\\n */\\n function redeemBehalf(address redeemer, uint256 redeemTokens) external override nonReentrant returns (uint256) {\\n _ensureSenderIsDelegateOf(redeemer);\\n\\n accrueInterest();\\n\\n _redeemFresh(redeemer, msg.sender, redeemTokens, 0);\\n return NO_ERROR;\\n }\\n\\n /**\\n * @notice Sender redeems vTokens in exchange for a specified amount of underlying asset\\n * @dev Accrues interest whether or not the operation succeeds, unless reverted\\n * @param redeemAmount The amount of underlying to receive from redeeming vTokens\\n * @return error Always NO_ERROR for compatibility with Venus core tooling\\n */\\n function redeemUnderlying(uint256 redeemAmount) external override nonReentrant returns (uint256) {\\n accrueInterest();\\n\\n _redeemFresh(msg.sender, msg.sender, 0, redeemAmount);\\n return NO_ERROR;\\n }\\n\\n /**\\n * @notice Sender redeems underlying assets on behalf of some other address. This function is only available\\n * for senders, explicitly marked as delegates of the supplier using `comptroller.updateDelegate`\\n * @dev Accrues interest whether or not the operation succeeds, unless reverted\\n * @param redeemer, on behalf of whom to redeem\\n * @param redeemAmount The amount of underlying to receive from redeeming vTokens\\n * @return error Always NO_ERROR for compatibility with Venus core tooling\\n * @custom:error InsufficientRedeemApproval is thrown when sender is not approved by the redeemer for the given amount\\n * @custom:event Emits Redeem and Transfer events; may emit AccrueInterest\\n * @custom:access Not restricted\\n */\\n function redeemUnderlyingBehalf(\\n address redeemer,\\n uint256 redeemAmount\\n ) external override nonReentrant returns (uint256) {\\n _ensureSenderIsDelegateOf(redeemer);\\n\\n accrueInterest();\\n\\n _redeemFresh(redeemer, msg.sender, 0, redeemAmount);\\n return NO_ERROR;\\n }\\n\\n /**\\n * @notice Sender borrows assets from the protocol to their own address\\n * @param borrowAmount The amount of the underlying asset to borrow\\n * @return error Always NO_ERROR for compatibility with Venus core tooling\\n * @custom:event Emits Borrow event; may emit AccrueInterest\\n * @custom:error BorrowCashNotAvailable is thrown when the protocol has insufficient cash\\n * @custom:access Not restricted\\n */\\n function borrow(uint256 borrowAmount) external override nonReentrant returns (uint256) {\\n accrueInterest();\\n\\n _borrowFresh(msg.sender, msg.sender, borrowAmount);\\n return NO_ERROR;\\n }\\n\\n /**\\n * @notice Sender borrows assets on behalf of some other address. This function is only available\\n * for senders, explicitly marked as delegates of the borrower using `comptroller.updateDelegate`\\n * @param borrower The borrower, on behalf of whom to borrow\\n * @param borrowAmount The amount of the underlying asset to borrow\\n * @return error Always NO_ERROR for compatibility with Venus core tooling\\n * @custom:error DelegateNotApproved is thrown if caller is not approved delegate\\n * @custom:error BorrowCashNotAvailable is thrown when the protocol has insufficient cash\\n * @custom:event Emits Borrow event; may emit AccrueInterest\\n * @custom:access Not restricted\\n */\\n function borrowBehalf(address borrower, uint256 borrowAmount) external override returns (uint256) {\\n _ensureSenderIsDelegateOf(borrower);\\n accrueInterest();\\n\\n _borrowFresh(borrower, msg.sender, borrowAmount);\\n return NO_ERROR;\\n }\\n\\n /**\\n * @notice Sender repays their own borrow\\n * @param repayAmount The amount to repay, or type(uint256).max for the full outstanding amount\\n * @return error Always NO_ERROR for compatibility with Venus core tooling\\n * @custom:event Emits RepayBorrow event; may emit AccrueInterest\\n * @custom:access Not restricted\\n */\\n function repayBorrow(uint256 repayAmount) external override nonReentrant returns (uint256) {\\n accrueInterest();\\n\\n _repayBorrowFresh(msg.sender, msg.sender, repayAmount);\\n return NO_ERROR;\\n }\\n\\n /**\\n * @notice Sender repays a borrow belonging to borrower\\n * @param borrower the account with the debt being payed off\\n * @param repayAmount The amount to repay, or type(uint256).max for the full outstanding amount\\n * @return error Always NO_ERROR for compatibility with Venus core tooling\\n * @custom:event Emits RepayBorrow event; may emit AccrueInterest\\n * @custom:access Not restricted\\n */\\n function repayBorrowBehalf(address borrower, uint256 repayAmount) external override nonReentrant returns (uint256) {\\n accrueInterest();\\n\\n _repayBorrowFresh(msg.sender, borrower, repayAmount);\\n return NO_ERROR;\\n }\\n\\n /**\\n * @notice The sender liquidates the borrowers collateral.\\n * The collateral seized is transferred to the liquidator.\\n * @param borrower The borrower of this vToken to be liquidated\\n * @param repayAmount The amount of the underlying borrowed asset to repay\\n * @param vTokenCollateral The market in which to seize collateral from the borrower\\n * @return error Always NO_ERROR for compatibility with Venus core tooling\\n * @custom:event Emits LiquidateBorrow event; may emit AccrueInterest\\n * @custom:error LiquidateAccrueCollateralInterestFailed is thrown when it is not possible to accrue interest on the collateral vToken\\n * @custom:error LiquidateCollateralFreshnessCheck is thrown when interest has not been accrued on the collateral vToken\\n * @custom:error LiquidateLiquidatorIsBorrower is thrown when trying to liquidate self\\n * @custom:error LiquidateCloseAmountIsZero is thrown when repayment amount is zero\\n * @custom:error LiquidateCloseAmountIsUintMax is thrown when repayment amount is UINT_MAX\\n * @custom:access Not restricted\\n */\\n function liquidateBorrow(\\n address borrower,\\n uint256 repayAmount,\\n VTokenInterface vTokenCollateral\\n ) external override returns (uint256) {\\n _liquidateBorrow(msg.sender, borrower, repayAmount, vTokenCollateral, false);\\n return NO_ERROR;\\n }\\n\\n /**\\n * @notice sets protocol share accumulated from liquidations\\n * @dev must be equal or less than liquidation incentive - 1\\n * @param newProtocolSeizeShareMantissa_ new protocol share mantissa\\n * @custom:event Emits NewProtocolSeizeShare event on success\\n * @custom:error Unauthorized error is thrown when the call is not authorized by AccessControlManager\\n * @custom:error ProtocolSeizeShareTooBig is thrown when the new seize share is too high\\n * @custom:access Controlled by AccessControlManager\\n */\\n function setProtocolSeizeShare(uint256 newProtocolSeizeShareMantissa_) external {\\n _checkAccessAllowed(\\\"setProtocolSeizeShare(uint256)\\\");\\n uint256 liquidationIncentive = ComptrollerViewInterface(address(comptroller)).liquidationIncentiveMantissa();\\n if (newProtocolSeizeShareMantissa_ + MANTISSA_ONE > liquidationIncentive) {\\n revert ProtocolSeizeShareTooBig();\\n }\\n\\n uint256 oldProtocolSeizeShareMantissa = protocolSeizeShareMantissa;\\n protocolSeizeShareMantissa = newProtocolSeizeShareMantissa_;\\n emit NewProtocolSeizeShare(oldProtocolSeizeShareMantissa, newProtocolSeizeShareMantissa_);\\n }\\n\\n /**\\n * @notice accrues interest and sets a new reserve factor for the protocol using _setReserveFactorFresh\\n * @dev Admin function to accrue interest and set a new reserve factor\\n * @param newReserveFactorMantissa New reserve factor (from 0 to 1e18)\\n * @custom:event Emits NewReserveFactor event; may emit AccrueInterest\\n * @custom:error Unauthorized error is thrown when the call is not authorized by AccessControlManager\\n * @custom:error SetReserveFactorBoundsCheck is thrown when the new reserve factor is too high\\n * @custom:access Controlled by AccessControlManager\\n */\\n function setReserveFactor(uint256 newReserveFactorMantissa) external override nonReentrant {\\n _checkAccessAllowed(\\\"setReserveFactor(uint256)\\\");\\n\\n accrueInterest();\\n _setReserveFactorFresh(newReserveFactorMantissa);\\n }\\n\\n /**\\n * @notice Accrues interest and reduces reserves by transferring to the protocol reserve contract\\n * @dev Gracefully return if reserves already reduced in accrueInterest\\n * @param reduceAmount Amount of reduction to reserves\\n * @custom:event Emits ReservesReduced event; may emit AccrueInterest\\n * @custom:error ReduceReservesCashNotAvailable is thrown when the vToken does not have sufficient cash\\n * @custom:error ReduceReservesCashValidation is thrown when trying to withdraw more cash than the reserves have\\n * @custom:access Not restricted\\n */\\n function reduceReserves(uint256 reduceAmount) external override nonReentrant {\\n accrueInterest();\\n if (reduceReservesBlockNumber == getBlockNumberOrTimestamp()) return;\\n _reduceReservesFresh(reduceAmount);\\n }\\n\\n /**\\n * @notice The sender adds to reserves.\\n * @param addAmount The amount of underlying token to add as reserves\\n * @custom:event Emits ReservesAdded event; may emit AccrueInterest\\n * @custom:access Not restricted\\n */\\n function addReserves(uint256 addAmount) external override nonReentrant {\\n accrueInterest();\\n _addReservesFresh(addAmount);\\n }\\n\\n /**\\n * @notice accrues interest and updates the interest rate model using _setInterestRateModelFresh\\n * @dev Admin function to accrue interest and update the interest rate model\\n * @param newInterestRateModel the new interest rate model to use\\n * @custom:event Emits NewMarketInterestRateModel event; may emit AccrueInterest\\n * @custom:error Unauthorized error is thrown when the call is not authorized by AccessControlManager\\n * @custom:access Controlled by AccessControlManager\\n */\\n function setInterestRateModel(InterestRateModel newInterestRateModel) external override {\\n _checkAccessAllowed(\\\"setInterestRateModel(address)\\\");\\n\\n accrueInterest();\\n _setInterestRateModelFresh(newInterestRateModel);\\n }\\n\\n /**\\n * @notice Repays a certain amount of debt, treats the rest of the borrow as bad debt, essentially\\n * \\\"forgiving\\\" the borrower. Healing is a situation that should rarely happen. However, some pools\\n * may list risky assets or be configured improperly \\u2013 we want to still handle such cases gracefully.\\n * We assume that Comptroller does the seizing, so this function is only available to Comptroller.\\n * @dev This function does not call any Comptroller hooks (like \\\"healAllowed\\\"), because we assume\\n * the Comptroller does all the necessary checks before calling this function.\\n * @param payer account who repays the debt\\n * @param borrower account to heal\\n * @param repayAmount amount to repay\\n * @custom:event Emits RepayBorrow, BadDebtIncreased events; may emit AccrueInterest\\n * @custom:error HealBorrowUnauthorized is thrown when the request does not come from Comptroller\\n * @custom:access Only Comptroller\\n */\\n function healBorrow(address payer, address borrower, uint256 repayAmount) external override nonReentrant {\\n if (repayAmount != 0) {\\n comptroller.preRepayHook(address(this), borrower);\\n }\\n\\n if (msg.sender != address(comptroller)) {\\n revert HealBorrowUnauthorized();\\n }\\n\\n uint256 accountBorrowsPrev = _borrowBalanceStored(borrower);\\n uint256 totalBorrowsNew = totalBorrows;\\n\\n uint256 actualRepayAmount;\\n if (repayAmount != 0) {\\n // _doTransferIn reverts if anything goes wrong, since we can't be sure if side effects occurred.\\n // We violate checks-effects-interactions here to account for tokens that take transfer fees\\n actualRepayAmount = _doTransferIn(payer, repayAmount);\\n totalBorrowsNew = totalBorrowsNew - actualRepayAmount;\\n emit RepayBorrow(\\n payer,\\n borrower,\\n actualRepayAmount,\\n accountBorrowsPrev - actualRepayAmount,\\n totalBorrowsNew\\n );\\n }\\n\\n // The transaction will fail if trying to repay too much\\n uint256 badDebtDelta = accountBorrowsPrev - actualRepayAmount;\\n if (badDebtDelta != 0) {\\n uint256 badDebtOld = badDebt;\\n uint256 badDebtNew = badDebtOld + badDebtDelta;\\n totalBorrowsNew = totalBorrowsNew - badDebtDelta;\\n badDebt = badDebtNew;\\n\\n // We treat healing as \\\"repayment\\\", where vToken is the payer\\n emit RepayBorrow(address(this), borrower, badDebtDelta, 0, totalBorrowsNew);\\n emit BadDebtIncreased(borrower, badDebtDelta, badDebtOld, badDebtNew);\\n }\\n\\n accountBorrows[borrower].principal = 0;\\n accountBorrows[borrower].interestIndex = borrowIndex;\\n totalBorrows = totalBorrowsNew;\\n\\n emit HealBorrow(payer, borrower, repayAmount);\\n }\\n\\n /**\\n * @notice The extended version of liquidations, callable only by Comptroller. May skip\\n * the close factor check. The collateral seized is transferred to the liquidator.\\n * @param liquidator The address repaying the borrow and seizing collateral\\n * @param borrower The borrower of this vToken to be liquidated\\n * @param repayAmount The amount of the underlying borrowed asset to repay\\n * @param vTokenCollateral The market in which to seize collateral from the borrower\\n * @param skipLiquidityCheck If set to true, allows to liquidate up to 100% of the borrow\\n * regardless of the account liquidity\\n * @custom:event Emits LiquidateBorrow event; may emit AccrueInterest\\n * @custom:error ForceLiquidateBorrowUnauthorized is thrown when the request does not come from Comptroller\\n * @custom:error LiquidateAccrueCollateralInterestFailed is thrown when it is not possible to accrue interest on the collateral vToken\\n * @custom:error LiquidateCollateralFreshnessCheck is thrown when interest has not been accrued on the collateral vToken\\n * @custom:error LiquidateLiquidatorIsBorrower is thrown when trying to liquidate self\\n * @custom:error LiquidateCloseAmountIsZero is thrown when repayment amount is zero\\n * @custom:error LiquidateCloseAmountIsUintMax is thrown when repayment amount is UINT_MAX\\n * @custom:access Only Comptroller\\n */\\n function forceLiquidateBorrow(\\n address liquidator,\\n address borrower,\\n uint256 repayAmount,\\n VTokenInterface vTokenCollateral,\\n bool skipLiquidityCheck\\n ) external override {\\n if (msg.sender != address(comptroller)) {\\n revert ForceLiquidateBorrowUnauthorized();\\n }\\n _liquidateBorrow(liquidator, borrower, repayAmount, vTokenCollateral, skipLiquidityCheck);\\n }\\n\\n /**\\n * @notice Transfers collateral tokens (this market) to the liquidator.\\n * @dev Will fail unless called by another vToken during the process of liquidation.\\n * It's absolutely critical to use msg.sender as the borrowed vToken and not a parameter.\\n * @param liquidator The account receiving seized collateral\\n * @param borrower The account having collateral seized\\n * @param seizeTokens The number of vTokens to seize\\n * @custom:event Emits Transfer, ReservesAdded events\\n * @custom:error LiquidateSeizeLiquidatorIsBorrower is thrown when trying to liquidate self\\n * @custom:access Not restricted\\n */\\n function seize(address liquidator, address borrower, uint256 seizeTokens) external override nonReentrant {\\n _seize(msg.sender, liquidator, borrower, seizeTokens);\\n }\\n\\n /**\\n * @notice Updates bad debt\\n * @dev Called only when bad debt is recovered from auction. Updates internal cash balance.\\n * @param recoveredAmount_ The amount of bad debt recovered\\n * @custom:event Emits BadDebtRecovered event\\n * @custom:access Only Shortfall contract\\n */\\n function badDebtRecovered(uint256 recoveredAmount_) external {\\n require(msg.sender == shortfall, \\\"only shortfall contract can update bad debt\\\");\\n require(recoveredAmount_ <= badDebt, \\\"more than bad debt recovered from auction\\\");\\n\\n uint256 badDebtOld = badDebt;\\n uint256 badDebtNew = badDebtOld - recoveredAmount_;\\n badDebt = badDebtNew;\\n internalCash += recoveredAmount_;\\n\\n emit BadDebtRecovered(badDebtOld, badDebtNew);\\n }\\n\\n /**\\n * @notice Sets protocol share reserve contract address\\n * @param protocolShareReserve_ The address of the protocol share reserve contract\\n * @custom:error ZeroAddressNotAllowed is thrown when protocol share reserve address is zero\\n * @custom:access Only Governance\\n */\\n function setProtocolShareReserve(address payable protocolShareReserve_) external onlyOwner {\\n _setProtocolShareReserve(protocolShareReserve_);\\n }\\n\\n /**\\n * @notice Sets shortfall contract address\\n * @param shortfall_ The address of the shortfall contract\\n * @custom:error ZeroAddressNotAllowed is thrown when shortfall contract address is zero\\n * @custom:access Only Governance\\n */\\n function setShortfallContract(address shortfall_) external onlyOwner {\\n _setShortfallContract(shortfall_);\\n }\\n\\n /**\\n * @notice A public function to sweep accidental ERC-20 transfers to this contract. Tokens are sent to admin (timelock)\\n * @param token The address of the ERC-20 token to sweep\\n * @custom:access Only Governance\\n */\\n function sweepToken(IERC20Upgradeable token) external override {\\n require(msg.sender == owner(), \\\"VToken::sweepToken: only admin can sweep tokens\\\");\\n require(address(token) != underlying, \\\"VToken::sweepToken: can not sweep underlying token\\\");\\n uint256 balance = token.balanceOf(address(this));\\n token.safeTransfer(owner(), balance);\\n\\n emit SweepToken(address(token));\\n }\\n\\n /**\\n * @notice Sync internalCash with the actual underlying token balance\\n * @dev Used for one-time migration after upgrade to initialize internalCash\\n * @custom:event Emits CashSynced\\n * @custom:access Controlled by AccessControlManager\\n */\\n function syncCash() external override nonReentrant {\\n _checkAccessAllowed(\\\"syncCash()\\\");\\n uint256 oldInternalCash = internalCash;\\n uint256 actualCash = IERC20Upgradeable(underlying).balanceOf(address(this));\\n internalCash = actualCash;\\n emit CashSynced(oldInternalCash, actualCash);\\n }\\n\\n /**\\n * @notice A public function to set new threshold of slot(block or second) difference after which funds will be sent to the protocol share reserve\\n * @param _newReduceReservesBlockOrTimestampDelta slot(block or second) difference value\\n * @custom:access Only Governance\\n */\\n function setReduceReservesBlockDelta(uint256 _newReduceReservesBlockOrTimestampDelta) external {\\n _checkAccessAllowed(\\\"setReduceReservesBlockDelta(uint256)\\\");\\n require(_newReduceReservesBlockOrTimestampDelta > 0, \\\"Invalid Input\\\");\\n emit NewReduceReservesBlockDelta(reduceReservesBlockDelta, _newReduceReservesBlockOrTimestampDelta);\\n reduceReservesBlockDelta = _newReduceReservesBlockOrTimestampDelta;\\n }\\n\\n /**\\n * @notice Get the current allowance from `owner` for `spender`\\n * @param owner The address of the account which owns the tokens to be spent\\n * @param spender The address of the account which may transfer tokens\\n * @return amount The number of tokens allowed to be spent (type(uint256).max means infinite)\\n */\\n function allowance(address owner, address spender) external view override returns (uint256) {\\n return transferAllowances[owner][spender];\\n }\\n\\n /**\\n * @notice Get the token balance of the `owner`\\n * @param owner The address of the account to query\\n * @return amount The number of tokens owned by `owner`\\n */\\n function balanceOf(address owner) external view override returns (uint256) {\\n return accountTokens[owner];\\n }\\n\\n /**\\n * @notice Get a snapshot of the account's balances, and the cached exchange rate\\n * @dev This is used by comptroller to more efficiently perform liquidity checks.\\n * @param account Address of the account to snapshot\\n * @return error Always NO_ERROR for compatibility with Venus core tooling\\n * @return vTokenBalance User's balance of vTokens\\n * @return borrowBalance Amount owed in terms of underlying\\n * @return exchangeRate Stored exchange rate\\n */\\n function getAccountSnapshot(\\n address account\\n )\\n external\\n view\\n override\\n returns (uint256 error, uint256 vTokenBalance, uint256 borrowBalance, uint256 exchangeRate)\\n {\\n return (NO_ERROR, accountTokens[account], _borrowBalanceStored(account), _exchangeRateStored());\\n }\\n\\n /**\\n * @notice Get the internally tracked cash balance of this vToken in the underlying asset\\n * @return cash The quantity of underlying asset tracked internally by this contract\\n */\\n function getCash() external view override returns (uint256) {\\n return _getCashPrior();\\n }\\n\\n /**\\n * @notice Returns the current per slot(block or second) borrow interest rate for this vToken\\n * @return rate The borrow interest rate per slot(block or second), scaled by 1e18\\n */\\n function borrowRatePerBlock() external view override returns (uint256) {\\n return interestRateModel.getBorrowRate(_getCashPrior(), totalBorrows, totalReserves, badDebt);\\n }\\n\\n /**\\n * @notice Returns the current per-slot(block or second) supply interest rate for this v\\n * @return rate The supply interest rate per slot(block or second), scaled by 1e18\\n */\\n function supplyRatePerBlock() external view override returns (uint256) {\\n return\\n interestRateModel.getSupplyRate(\\n _getCashPrior(),\\n totalBorrows,\\n totalReserves,\\n reserveFactorMantissa,\\n badDebt\\n );\\n }\\n\\n /**\\n * @notice Return the borrow balance of account based on stored data\\n * @param account The address whose balance should be calculated\\n * @return borrowBalance The calculated balance\\n */\\n function borrowBalanceStored(address account) external view override returns (uint256) {\\n return _borrowBalanceStored(account);\\n }\\n\\n /**\\n * @notice Calculates the exchange rate from the underlying to the VToken\\n * @dev This function does not accrue interest before calculating the exchange rate\\n * @return exchangeRate Calculated exchange rate scaled by 1e18\\n */\\n function exchangeRateStored() external view override returns (uint256) {\\n return _exchangeRateStored();\\n }\\n\\n /**\\n * @notice Accrue interest then return the up-to-date exchange rate\\n * @return exchangeRate Calculated exchange rate scaled by 1e18\\n */\\n function exchangeRateCurrent() public override nonReentrant returns (uint256) {\\n accrueInterest();\\n return _exchangeRateStored();\\n }\\n\\n /**\\n * @notice Applies accrued interest to total borrows and reserves\\n * @dev This calculates interest accrued from the last checkpointed slot(block or second)\\n * up to the current slot(block or second) and writes new checkpoint to storage and\\n * reduce spread reserves to protocol share reserve\\n * if currentSlot - reduceReservesBlockNumber >= slotDelta\\n * @return Always NO_ERROR\\n * @custom:event Emits AccrueInterest event on success\\n * @custom:access Not restricted\\n */\\n function accrueInterest() public virtual override returns (uint256) {\\n /* Remember the initial block number or timestamp */\\n uint256 currentSlotNumber = getBlockNumberOrTimestamp();\\n uint256 accrualSlotNumberPrior = accrualBlockNumber;\\n\\n /* Short-circuit accumulating 0 interest */\\n if (accrualSlotNumberPrior == currentSlotNumber) {\\n return NO_ERROR;\\n }\\n\\n /* Read the previous values out of storage */\\n uint256 cashPrior = _getCashPrior();\\n uint256 borrowsPrior = totalBorrows;\\n uint256 reservesPrior = totalReserves;\\n uint256 borrowIndexPrior = borrowIndex;\\n\\n /* Calculate the current borrow interest rate */\\n uint256 borrowRateMantissa = interestRateModel.getBorrowRate(cashPrior, borrowsPrior, reservesPrior, badDebt);\\n require(borrowRateMantissa <= MAX_BORROW_RATE_MANTISSA, \\\"borrow rate is absurdly high\\\");\\n\\n /* Calculate the number of slots elapsed since the last accrual */\\n uint256 slotDelta = currentSlotNumber - accrualSlotNumberPrior;\\n\\n /*\\n * Calculate the interest accumulated into borrows and reserves and the new index:\\n * simpleInterestFactor = borrowRate * slotDelta\\n * interestAccumulated = simpleInterestFactor * totalBorrows\\n * totalBorrowsNew = interestAccumulated + totalBorrows\\n * totalReservesNew = interestAccumulated * reserveFactor + totalReserves\\n * borrowIndexNew = simpleInterestFactor * borrowIndex + borrowIndex\\n */\\n\\n Exp memory simpleInterestFactor = mul_(Exp({ mantissa: borrowRateMantissa }), slotDelta);\\n uint256 interestAccumulated = mul_ScalarTruncate(simpleInterestFactor, borrowsPrior);\\n uint256 totalBorrowsNew = interestAccumulated + borrowsPrior;\\n uint256 totalReservesNew = mul_ScalarTruncateAddUInt(\\n Exp({ mantissa: reserveFactorMantissa }),\\n interestAccumulated,\\n reservesPrior\\n );\\n uint256 borrowIndexNew = mul_ScalarTruncateAddUInt(simpleInterestFactor, borrowIndexPrior, borrowIndexPrior);\\n\\n /////////////////////////\\n // EFFECTS & INTERACTIONS\\n // (No safe failures beyond this point)\\n\\n /* We write the previously calculated values into storage */\\n accrualBlockNumber = currentSlotNumber;\\n borrowIndex = borrowIndexNew;\\n totalBorrows = totalBorrowsNew;\\n totalReserves = totalReservesNew;\\n\\n if (currentSlotNumber - reduceReservesBlockNumber >= reduceReservesBlockDelta) {\\n reduceReservesBlockNumber = currentSlotNumber;\\n if (cashPrior < totalReservesNew) {\\n _reduceReservesFresh(cashPrior);\\n } else {\\n _reduceReservesFresh(totalReservesNew);\\n }\\n }\\n\\n /* We emit an AccrueInterest event */\\n emit AccrueInterest(cashPrior, interestAccumulated, borrowIndexNew, totalBorrowsNew);\\n\\n return NO_ERROR;\\n }\\n\\n /**\\n * @notice User supplies assets into the market and receives vTokens in exchange\\n * @dev Assumes interest has already been accrued up to the current block or timestamp\\n * @param payer The address of the account which is sending the assets for supply\\n * @param minter The address of the account which is supplying the assets\\n * @param mintAmount The amount of the underlying asset to supply\\n */\\n function _mintFresh(address payer, address minter, uint256 mintAmount) internal {\\n /* Fail if mint not allowed */\\n comptroller.preMintHook(address(this), minter, mintAmount);\\n\\n /* Verify market's slot(block or second) number equals current slot(block or second) number */\\n if (accrualBlockNumber != getBlockNumberOrTimestamp()) {\\n revert MintFreshnessCheck();\\n }\\n\\n Exp memory exchangeRate = Exp({ mantissa: _exchangeRateStored() });\\n\\n /////////////////////////\\n // EFFECTS & INTERACTIONS\\n // (No safe failures beyond this point)\\n\\n /*\\n * We call `_doTransferIn` for the minter and the mintAmount.\\n * `_doTransferIn` reverts if anything goes wrong, since we can't be sure if\\n * side-effects occurred. The function returns the amount actually transferred,\\n * in case of a fee. On success, the vToken holds an additional `actualMintAmount`\\n * of cash.\\n */\\n uint256 actualMintAmount = _doTransferIn(payer, mintAmount);\\n\\n /*\\n * We get the current exchange rate and calculate the number of vTokens to be minted:\\n * mintTokens = actualMintAmount / exchangeRate\\n */\\n\\n uint256 mintTokens = div_(actualMintAmount, exchangeRate);\\n\\n /*\\n * We calculate the new total supply of vTokens and minter token balance, checking for overflow:\\n * totalSupplyNew = totalSupply + mintTokens\\n * accountTokensNew = accountTokens[minter] + mintTokens\\n * And write them into storage\\n */\\n totalSupply = totalSupply + mintTokens;\\n uint256 balanceAfter = accountTokens[minter] + mintTokens;\\n accountTokens[minter] = balanceAfter;\\n\\n /* We emit a Mint event, and a Transfer event */\\n emit Mint(minter, actualMintAmount, mintTokens, balanceAfter);\\n emit Transfer(address(0), minter, mintTokens);\\n\\n /* We call the defense and prime accrue interest hook */\\n comptroller.mintVerify(address(this), minter, actualMintAmount, mintTokens);\\n }\\n\\n /**\\n * @notice Redeemer redeems vTokens in exchange for the underlying assets, transferred to the receiver. Redeemer and receiver can be the same\\n * address, or different addresses if the receiver was previously approved by the redeemer as a valid delegate (see Comptroller.updateDelegate)\\n * @dev Assumes interest has already been accrued up to the current slot(block or second)\\n * @param redeemer The address of the account which is redeeming the tokens\\n * @param receiver The receiver of the underlying tokens\\n * @param redeemTokensIn The number of vTokens to redeem into underlying (only one of redeemTokensIn or redeemAmountIn may be non-zero)\\n * @param redeemAmountIn The number of underlying tokens to receive from redeeming vTokens (only one of redeemTokensIn or redeemAmountIn may be non-zero)\\n */\\n function _redeemFresh(address redeemer, address receiver, uint256 redeemTokensIn, uint256 redeemAmountIn) internal {\\n require(redeemTokensIn == 0 || redeemAmountIn == 0, \\\"one of redeemTokensIn or redeemAmountIn must be zero\\\");\\n\\n /* Verify market's slot(block or second) number equals current slot(block or second) number */\\n if (accrualBlockNumber != getBlockNumberOrTimestamp()) {\\n revert RedeemFreshnessCheck();\\n }\\n\\n /* exchangeRate = invoke Exchange Rate Stored() */\\n Exp memory exchangeRate = Exp({ mantissa: _exchangeRateStored() });\\n\\n uint256 redeemTokens;\\n uint256 redeemAmount;\\n\\n /* If redeemTokensIn > 0: */\\n if (redeemTokensIn > 0) {\\n /*\\n * We calculate the exchange rate and the amount of underlying to be redeemed:\\n * redeemTokens = redeemTokensIn\\n */\\n redeemTokens = redeemTokensIn;\\n } else {\\n /*\\n * We get the current exchange rate and calculate the amount to be redeemed:\\n * redeemTokens = redeemAmountIn / exchangeRate\\n */\\n redeemTokens = div_(redeemAmountIn, exchangeRate);\\n\\n uint256 _redeemAmount = mul_(redeemTokens, exchangeRate);\\n if (_redeemAmount != 0 && _redeemAmount != redeemAmountIn) redeemTokens++; // round up\\n }\\n\\n // redeemAmount = exchangeRate * redeemTokens\\n redeemAmount = mul_ScalarTruncate(exchangeRate, redeemTokens);\\n\\n // Revert if amount is zero\\n if (redeemAmount == 0) {\\n revert(\\\"redeemAmount is zero\\\");\\n }\\n\\n /* Fail if redeem not allowed */\\n comptroller.preRedeemHook(address(this), redeemer, redeemTokens);\\n\\n /* Fail gracefully if protocol has insufficient cash */\\n if (_getCashPrior() - totalReserves < redeemAmount) {\\n revert RedeemTransferOutNotPossible();\\n }\\n\\n /////////////////////////\\n // EFFECTS & INTERACTIONS\\n // (No safe failures beyond this point)\\n\\n /*\\n * We write the previously calculated values into storage.\\n * Note: Avoid token reentrancy attacks by writing reduced supply before external transfer.\\n */\\n totalSupply = totalSupply - redeemTokens;\\n uint256 balanceAfter = accountTokens[redeemer] - redeemTokens;\\n accountTokens[redeemer] = balanceAfter;\\n\\n /*\\n * We invoke _doTransferOut for the receiver and the redeemAmount.\\n * On success, the vToken has redeemAmount less of cash.\\n * _doTransferOut reverts if anything goes wrong, since we can't be sure if side effects occurred.\\n */\\n _doTransferOut(receiver, redeemAmount);\\n\\n /* We emit a Transfer event, and a Redeem event */\\n emit Transfer(redeemer, address(this), redeemTokens);\\n emit Redeem(redeemer, redeemAmount, redeemTokens, balanceAfter);\\n\\n /* We call the defense and prime accrue interest hook */\\n comptroller.redeemVerify(address(this), redeemer, redeemAmount, redeemTokens);\\n }\\n\\n /**\\n * @notice Users or their delegates borrow assets from the protocol\\n * @param borrower User who borrows the assets\\n * @param receiver The receiver of the tokens, if called by a delegate\\n * @param borrowAmount The amount of the underlying asset to borrow\\n */\\n function _borrowFresh(address borrower, address receiver, uint256 borrowAmount) internal {\\n /* Fail if borrow not allowed */\\n comptroller.preBorrowHook(address(this), borrower, borrowAmount);\\n\\n /* Verify market's slot(block or second) number equals current slot(block or second) number */\\n if (accrualBlockNumber != getBlockNumberOrTimestamp()) {\\n revert BorrowFreshnessCheck();\\n }\\n\\n /* Fail gracefully if protocol has insufficient underlying cash */\\n if (_getCashPrior() - totalReserves < borrowAmount) {\\n revert BorrowCashNotAvailable();\\n }\\n\\n /*\\n * We calculate the new borrower and total borrow balances, failing on overflow:\\n * accountBorrowNew = accountBorrow + borrowAmount\\n * totalBorrowsNew = totalBorrows + borrowAmount\\n */\\n uint256 accountBorrowsPrev = _borrowBalanceStored(borrower);\\n uint256 accountBorrowsNew = accountBorrowsPrev + borrowAmount;\\n uint256 totalBorrowsNew = totalBorrows + borrowAmount;\\n\\n /////////////////////////\\n // EFFECTS & INTERACTIONS\\n // (No safe failures beyond this point)\\n\\n /*\\n * We write the previously calculated values into storage.\\n * Note: Avoid token reentrancy attacks by writing increased borrow before external transfer.\\n `*/\\n accountBorrows[borrower].principal = accountBorrowsNew;\\n accountBorrows[borrower].interestIndex = borrowIndex;\\n totalBorrows = totalBorrowsNew;\\n\\n /*\\n * We invoke _doTransferOut for the receiver and the borrowAmount.\\n * On success, the vToken borrowAmount less of cash.\\n * _doTransferOut reverts if anything goes wrong, since we can't be sure if side effects occurred.\\n */\\n _doTransferOut(receiver, borrowAmount);\\n\\n /* We emit a Borrow event */\\n emit Borrow(borrower, borrowAmount, accountBorrowsNew, totalBorrowsNew);\\n\\n /* We call the defense and prime accrue interest hook */\\n comptroller.borrowVerify(address(this), borrower, borrowAmount);\\n }\\n\\n /**\\n * @notice Borrows are repaid by another user (possibly the borrower).\\n * @param payer the account paying off the borrow\\n * @param borrower the account with the debt being payed off\\n * @param repayAmount the amount of underlying tokens being returned, or type(uint256).max for the full outstanding amount\\n * @return (uint) the actual repayment amount.\\n */\\n function _repayBorrowFresh(address payer, address borrower, uint256 repayAmount) internal returns (uint256) {\\n /* Fail if repayBorrow not allowed */\\n comptroller.preRepayHook(address(this), borrower);\\n\\n /* Verify market's slot(block or second) number equals current slot(block or second) number */\\n if (accrualBlockNumber != getBlockNumberOrTimestamp()) {\\n revert RepayBorrowFreshnessCheck();\\n }\\n\\n /* We fetch the amount the borrower owes, with accumulated interest */\\n uint256 accountBorrowsPrev = _borrowBalanceStored(borrower);\\n\\n uint256 repayAmountFinal = repayAmount >= accountBorrowsPrev ? accountBorrowsPrev : repayAmount;\\n\\n /////////////////////////\\n // EFFECTS & INTERACTIONS\\n // (No safe failures beyond this point)\\n\\n /*\\n * We call _doTransferIn for the payer and the repayAmount\\n * On success, the vToken holds an additional repayAmount of cash.\\n * _doTransferIn reverts if anything goes wrong, since we can't be sure if side effects occurred.\\n * it returns the amount actually transferred, in case of a fee.\\n */\\n uint256 actualRepayAmount = _doTransferIn(payer, repayAmountFinal);\\n\\n /*\\n * We calculate the new borrower and total borrow balances, failing on underflow:\\n * accountBorrowsNew = accountBorrows - actualRepayAmount\\n * totalBorrowsNew = totalBorrows - actualRepayAmount\\n */\\n uint256 accountBorrowsNew = accountBorrowsPrev - actualRepayAmount;\\n uint256 totalBorrowsNew = totalBorrows - actualRepayAmount;\\n\\n /* We write the previously calculated values into storage */\\n accountBorrows[borrower].principal = accountBorrowsNew;\\n accountBorrows[borrower].interestIndex = borrowIndex;\\n totalBorrows = totalBorrowsNew;\\n\\n /* We emit a RepayBorrow event */\\n emit RepayBorrow(payer, borrower, actualRepayAmount, accountBorrowsNew, totalBorrowsNew);\\n\\n /* We call the defense and prime accrue interest hook */\\n comptroller.repayBorrowVerify(address(this), payer, borrower, actualRepayAmount, borrowIndex);\\n\\n return actualRepayAmount;\\n }\\n\\n /**\\n * @notice The sender liquidates the borrowers collateral.\\n * The collateral seized is transferred to the liquidator.\\n * @param liquidator The address repaying the borrow and seizing collateral\\n * @param borrower The borrower of this vToken to be liquidated\\n * @param repayAmount The amount of the underlying borrowed asset to repay\\n * @param vTokenCollateral The market in which to seize collateral from the borrower\\n * @param skipLiquidityCheck If set to true, allows to liquidate up to 100% of the borrow\\n * regardless of the account liquidity\\n */\\n function _liquidateBorrow(\\n address liquidator,\\n address borrower,\\n uint256 repayAmount,\\n VTokenInterface vTokenCollateral,\\n bool skipLiquidityCheck\\n ) internal nonReentrant {\\n accrueInterest();\\n\\n uint256 error = vTokenCollateral.accrueInterest();\\n if (error != NO_ERROR) {\\n // accrueInterest emits logs on errors, but we still want to log the fact that an attempted liquidation failed\\n revert LiquidateAccrueCollateralInterestFailed(error);\\n }\\n\\n _liquidateBorrowFresh(liquidator, borrower, repayAmount, vTokenCollateral, skipLiquidityCheck);\\n }\\n\\n /**\\n * @notice The liquidator liquidates the borrowers collateral.\\n * The collateral seized is transferred to the liquidator.\\n * @param liquidator The address repaying the borrow and seizing collateral\\n * @param borrower The borrower of this vToken to be liquidated\\n * @param repayAmount The amount of the underlying borrowed asset to repay\\n * @param vTokenCollateral The market in which to seize collateral from the borrower\\n * @param skipLiquidityCheck If set to true, allows to liquidate up to 100% of the borrow\\n * regardless of the account liquidity\\n */\\n function _liquidateBorrowFresh(\\n address liquidator,\\n address borrower,\\n uint256 repayAmount,\\n VTokenInterface vTokenCollateral,\\n bool skipLiquidityCheck\\n ) internal {\\n /* Fail if liquidate not allowed */\\n comptroller.preLiquidateHook(\\n address(this),\\n address(vTokenCollateral),\\n borrower,\\n repayAmount,\\n skipLiquidityCheck\\n );\\n\\n /* Verify market's slot(block or second) number equals current slot(block or second) number */\\n if (accrualBlockNumber != getBlockNumberOrTimestamp()) {\\n revert LiquidateFreshnessCheck();\\n }\\n\\n /* Verify vTokenCollateral market's slot(block or second) number equals current slot(block or second) number */\\n if (vTokenCollateral.accrualBlockNumber() != getBlockNumberOrTimestamp()) {\\n revert LiquidateCollateralFreshnessCheck();\\n }\\n\\n /* Fail if borrower = liquidator */\\n if (borrower == liquidator) {\\n revert LiquidateLiquidatorIsBorrower();\\n }\\n\\n /* Fail if repayAmount = 0 */\\n if (repayAmount == 0) {\\n revert LiquidateCloseAmountIsZero();\\n }\\n\\n /* Fail if repayAmount = type(uint256).max */\\n if (repayAmount == type(uint256).max) {\\n revert LiquidateCloseAmountIsUintMax();\\n }\\n\\n /* Fail if repayBorrow fails */\\n uint256 actualRepayAmount = _repayBorrowFresh(liquidator, borrower, repayAmount);\\n\\n /////////////////////////\\n // EFFECTS & INTERACTIONS\\n // (No safe failures beyond this point)\\n\\n /* We calculate the number of collateral tokens that will be seized */\\n (uint256 amountSeizeError, uint256 seizeTokens) = comptroller.liquidateCalculateSeizeTokens(\\n address(this),\\n address(vTokenCollateral),\\n actualRepayAmount\\n );\\n require(amountSeizeError == NO_ERROR, \\\"LIQUIDATE_COMPTROLLER_CALCULATE_AMOUNT_SEIZE_FAILED\\\");\\n\\n /* Revert if borrower collateral token balance < seizeTokens */\\n require(vTokenCollateral.balanceOf(borrower) >= seizeTokens, \\\"LIQUIDATE_SEIZE_TOO_MUCH\\\");\\n\\n // If this is also the collateral, call _seize internally to avoid re-entrancy, otherwise make an external call\\n if (address(vTokenCollateral) == address(this)) {\\n _seize(address(this), liquidator, borrower, seizeTokens);\\n } else {\\n vTokenCollateral.seize(liquidator, borrower, seizeTokens);\\n }\\n\\n /* We emit a LiquidateBorrow event */\\n emit LiquidateBorrow(liquidator, borrower, actualRepayAmount, address(vTokenCollateral), seizeTokens);\\n\\n /* We call the defense and prime accrue interest hook */\\n comptroller.liquidateBorrowVerify(\\n address(this),\\n address(vTokenCollateral),\\n liquidator,\\n borrower,\\n actualRepayAmount,\\n seizeTokens\\n );\\n }\\n\\n /**\\n * @notice Transfers collateral tokens (this market) to the liquidator.\\n * @dev Called only during an in-kind liquidation, or by liquidateBorrow during the liquidation of another VToken.\\n * It's absolutely critical to use msg.sender as the seizer vToken and not a parameter.\\n * @param seizerContract The contract seizing the collateral (either borrowed vToken or Comptroller)\\n * @param liquidator The account receiving seized collateral\\n * @param borrower The account having collateral seized\\n * @param seizeTokens The number of vTokens to seize\\n */\\n function _seize(address seizerContract, address liquidator, address borrower, uint256 seizeTokens) internal {\\n /* Fail if seize not allowed */\\n comptroller.preSeizeHook(address(this), seizerContract, liquidator, borrower);\\n\\n /* Fail if borrower = liquidator */\\n if (borrower == liquidator) {\\n revert LiquidateSeizeLiquidatorIsBorrower();\\n }\\n\\n /*\\n * We calculate the new borrower and liquidator token balances, failing on underflow/overflow:\\n * borrowerTokensNew = accountTokens[borrower] - seizeTokens\\n * liquidatorTokensNew = accountTokens[liquidator] + seizeTokens\\n */\\n uint256 liquidationIncentiveMantissa = ComptrollerViewInterface(address(comptroller))\\n .liquidationIncentiveMantissa();\\n uint256 numerator = mul_(seizeTokens, Exp({ mantissa: protocolSeizeShareMantissa }));\\n uint256 protocolSeizeTokens = div_(numerator, Exp({ mantissa: liquidationIncentiveMantissa }));\\n uint256 liquidatorSeizeTokens = seizeTokens - protocolSeizeTokens;\\n Exp memory exchangeRate = Exp({ mantissa: _exchangeRateStored() });\\n uint256 protocolSeizeAmount = mul_ScalarTruncate(exchangeRate, protocolSeizeTokens);\\n\\n /////////////////////////\\n // EFFECTS & INTERACTIONS\\n // (No safe failures beyond this point)\\n\\n /* We write the calculated values into storage */\\n totalSupply = totalSupply - protocolSeizeTokens;\\n accountTokens[borrower] = accountTokens[borrower] - seizeTokens;\\n accountTokens[liquidator] = accountTokens[liquidator] + liquidatorSeizeTokens;\\n\\n // _doTransferOut reverts if anything goes wrong, since we can't be sure if side effects occurred.\\n // Transferring an underlying asset to the protocolShareReserve contract to channel the funds for different use.\\n _doTransferOut(protocolShareReserve, protocolSeizeAmount);\\n\\n // Update the pool asset's state in the protocol share reserve for the above transfer.\\n IProtocolShareReserve(protocolShareReserve).updateAssetsState(\\n address(comptroller),\\n underlying,\\n IProtocolShareReserve.IncomeType.LIQUIDATION\\n );\\n\\n /* Emit a Transfer event */\\n emit Transfer(borrower, liquidator, liquidatorSeizeTokens);\\n emit ProtocolSeize(borrower, protocolShareReserve, protocolSeizeAmount);\\n\\n /* We call the defense and prime accrue interest hook */\\n comptroller.seizeVerify(address(this), seizerContract, liquidator, borrower, seizeTokens);\\n }\\n\\n function _setComptroller(ComptrollerInterface newComptroller) internal {\\n ComptrollerInterface oldComptroller = comptroller;\\n // Ensure invoke comptroller.isComptroller() returns true\\n require(newComptroller.isComptroller(), \\\"marker method returned false\\\");\\n\\n // Set market's comptroller to newComptroller\\n comptroller = newComptroller;\\n\\n // Emit NewComptroller(oldComptroller, newComptroller)\\n emit NewComptroller(oldComptroller, newComptroller);\\n }\\n\\n /**\\n * @notice Sets a new reserve factor for the protocol (*requires fresh interest accrual)\\n * @dev Admin function to set a new reserve factor\\n * @param newReserveFactorMantissa New reserve factor (from 0 to 1e18)\\n */\\n function _setReserveFactorFresh(uint256 newReserveFactorMantissa) internal {\\n // Verify market's slot(block or second) number equals current slot(block or second) number\\n if (accrualBlockNumber != getBlockNumberOrTimestamp()) {\\n revert SetReserveFactorFreshCheck();\\n }\\n\\n // Check newReserveFactor \\u2264 maxReserveFactor\\n if (newReserveFactorMantissa > MAX_RESERVE_FACTOR_MANTISSA) {\\n revert SetReserveFactorBoundsCheck();\\n }\\n\\n uint256 oldReserveFactorMantissa = reserveFactorMantissa;\\n reserveFactorMantissa = newReserveFactorMantissa;\\n\\n emit NewReserveFactor(oldReserveFactorMantissa, newReserveFactorMantissa);\\n }\\n\\n /**\\n * @notice Add reserves by transferring from caller\\n * @dev Requires fresh interest accrual\\n * @param addAmount Amount of addition to reserves\\n * @return actualAddAmount The actual amount added, excluding the potential token fees\\n */\\n function _addReservesFresh(uint256 addAmount) internal returns (uint256) {\\n // totalReserves + actualAddAmount\\n uint256 totalReservesNew;\\n uint256 actualAddAmount;\\n\\n // We fail gracefully unless market's slot(block or second) number equals current slot(block or second) number\\n if (accrualBlockNumber != getBlockNumberOrTimestamp()) {\\n revert AddReservesFactorFreshCheck(actualAddAmount);\\n }\\n\\n actualAddAmount = _doTransferIn(msg.sender, addAmount);\\n totalReservesNew = totalReserves + actualAddAmount;\\n totalReserves = totalReservesNew;\\n emit ReservesAdded(msg.sender, actualAddAmount, totalReservesNew);\\n\\n return actualAddAmount;\\n }\\n\\n /**\\n * @notice Reduces reserves by transferring to the protocol reserve contract\\n * @dev Requires fresh interest accrual\\n * @param reduceAmount Amount of reduction to reserves\\n */\\n function _reduceReservesFresh(uint256 reduceAmount) internal {\\n if (reduceAmount == 0) {\\n return;\\n }\\n // totalReserves - reduceAmount\\n uint256 totalReservesNew;\\n\\n // We fail gracefully unless market's slot(block or second) number equals current slot(block or second) number\\n if (accrualBlockNumber != getBlockNumberOrTimestamp()) {\\n revert ReduceReservesFreshCheck();\\n }\\n\\n // Fail gracefully if protocol has insufficient underlying cash\\n if (_getCashPrior() < reduceAmount) {\\n revert ReduceReservesCashNotAvailable();\\n }\\n\\n // Check reduceAmount \\u2264 reserves[n] (totalReserves)\\n if (reduceAmount > totalReserves) {\\n revert ReduceReservesCashValidation();\\n }\\n\\n /////////////////////////\\n // EFFECTS & INTERACTIONS\\n // (No safe failures beyond this point)\\n\\n totalReservesNew = totalReserves - reduceAmount;\\n\\n // Store reserves[n+1] = reserves[n] - reduceAmount\\n totalReserves = totalReservesNew;\\n\\n // _doTransferOut reverts if anything goes wrong, since we can't be sure if side effects occurred.\\n // Transferring an underlying asset to the protocolShareReserve contract to channel the funds for different use.\\n _doTransferOut(protocolShareReserve, reduceAmount);\\n\\n // Update the pool asset's state in the protocol share reserve for the above transfer.\\n IProtocolShareReserve(protocolShareReserve).updateAssetsState(\\n address(comptroller),\\n underlying,\\n IProtocolShareReserve.IncomeType.SPREAD\\n );\\n\\n emit SpreadReservesReduced(protocolShareReserve, reduceAmount, totalReservesNew);\\n }\\n\\n /**\\n * @notice updates the interest rate model (*requires fresh interest accrual)\\n * @dev Admin function to update the interest rate model\\n * @param newInterestRateModel the new interest rate model to use\\n */\\n function _setInterestRateModelFresh(InterestRateModel newInterestRateModel) internal {\\n // Used to store old model for use in the event that is emitted on success\\n InterestRateModel oldInterestRateModel;\\n\\n // We fail gracefully unless market's slot(block or second) number equals current slot(block or second) number\\n if (accrualBlockNumber != getBlockNumberOrTimestamp()) {\\n revert SetInterestRateModelFreshCheck();\\n }\\n\\n // Track the market's current interest rate model\\n oldInterestRateModel = interestRateModel;\\n\\n // Ensure invoke newInterestRateModel.isInterestRateModel() returns true\\n require(newInterestRateModel.isInterestRateModel(), \\\"marker method returned false\\\");\\n\\n // Set the interest rate model to newInterestRateModel\\n interestRateModel = newInterestRateModel;\\n\\n // Emit NewMarketInterestRateModel(oldInterestRateModel, newInterestRateModel)\\n emit NewMarketInterestRateModel(oldInterestRateModel, newInterestRateModel);\\n }\\n\\n /**\\n * Safe Token **\\n */\\n\\n /**\\n * @dev Similar to ERC-20 transfer, but handles tokens that have transfer fees.\\n * This function returns the actual amount received,\\n * which may be less than `amount` if there is a fee attached to the transfer.\\n * @param from Sender of the underlying tokens\\n * @param amount Amount of underlying to transfer\\n * @return Actual amount received\\n */\\n function _doTransferIn(address from, uint256 amount) internal virtual returns (uint256) {\\n IERC20Upgradeable token = IERC20Upgradeable(underlying);\\n uint256 balanceBefore = token.balanceOf(address(this));\\n token.safeTransferFrom(from, address(this), amount);\\n uint256 balanceAfter = token.balanceOf(address(this));\\n uint256 actualAmount = balanceAfter - balanceBefore;\\n internalCash += actualAmount;\\n // Return the amount that was *actually* transferred\\n return actualAmount;\\n }\\n\\n /**\\n * @dev Just a regular ERC-20 transfer, reverts on failure\\n * @param to Receiver of the underlying tokens\\n * @param amount Amount of underlying to transfer\\n */\\n function _doTransferOut(address to, uint256 amount) internal virtual {\\n IERC20Upgradeable token = IERC20Upgradeable(underlying);\\n internalCash -= amount;\\n token.safeTransfer(to, amount);\\n }\\n\\n /**\\n * @notice Transfer `tokens` tokens from `src` to `dst` by `spender`\\n * @dev Called by both `transfer` and `transferFrom` internally\\n * @param spender The address of the account performing the transfer\\n * @param src The address of the source account\\n * @param dst The address of the destination account\\n * @param tokens The number of tokens to transfer\\n */\\n function _transferTokens(address spender, address src, address dst, uint256 tokens) internal {\\n /* Fail if transfer not allowed */\\n comptroller.preTransferHook(address(this), src, dst, tokens);\\n\\n /* Do not allow self-transfers */\\n if (src == dst) {\\n revert TransferNotAllowed();\\n }\\n\\n /* Get the allowance, infinite for the account owner */\\n uint256 startingAllowance;\\n if (spender == src) {\\n startingAllowance = type(uint256).max;\\n } else {\\n startingAllowance = transferAllowances[src][spender];\\n }\\n\\n /* Do the calculations, checking for {under,over}flow */\\n uint256 allowanceNew = startingAllowance - tokens;\\n uint256 srcTokensNew = accountTokens[src] - tokens;\\n uint256 dstTokensNew = accountTokens[dst] + tokens;\\n\\n /////////////////////////\\n // EFFECTS & INTERACTIONS\\n\\n accountTokens[src] = srcTokensNew;\\n accountTokens[dst] = dstTokensNew;\\n\\n /* Eat some of the allowance (if necessary) */\\n if (startingAllowance != type(uint256).max) {\\n transferAllowances[src][spender] = allowanceNew;\\n }\\n\\n /* We emit a Transfer event */\\n emit Transfer(src, dst, tokens);\\n\\n comptroller.transferVerify(address(this), src, dst, tokens);\\n }\\n\\n /**\\n * @notice Initialize the money market\\n * @param underlying_ The address of the underlying asset\\n * @param comptroller_ The address of the Comptroller\\n * @param interestRateModel_ The address of the interest rate model\\n * @param initialExchangeRateMantissa_ The initial exchange rate, scaled by 1e18\\n * @param name_ ERC-20 name of this token\\n * @param symbol_ ERC-20 symbol of this token\\n * @param decimals_ ERC-20 decimal precision of this token\\n * @param admin_ Address of the administrator of this token\\n * @param accessControlManager_ AccessControlManager contract address\\n * @param riskManagement Addresses of risk & income related contracts\\n * @param reserveFactorMantissa_ Percentage of borrow interest that goes to reserves (from 0 to 1e18)\\n */\\n function _initialize(\\n address underlying_,\\n ComptrollerInterface comptroller_,\\n InterestRateModel interestRateModel_,\\n uint256 initialExchangeRateMantissa_,\\n string memory name_,\\n string memory symbol_,\\n uint8 decimals_,\\n address admin_,\\n address accessControlManager_,\\n RiskManagementInit memory riskManagement,\\n uint256 reserveFactorMantissa_\\n ) internal onlyInitializing {\\n __Ownable2Step_init();\\n __AccessControlled_init_unchained(accessControlManager_);\\n require(accrualBlockNumber == 0 && borrowIndex == 0, \\\"market may only be initialized once\\\");\\n\\n // Set initial exchange rate\\n initialExchangeRateMantissa = initialExchangeRateMantissa_;\\n require(initialExchangeRateMantissa > 0, \\\"initial exchange rate must be greater than zero.\\\");\\n\\n _setComptroller(comptroller_);\\n\\n // Initialize slot(block or second) number and borrow index (slot(block or second) number mocks depend on comptroller being set)\\n accrualBlockNumber = getBlockNumberOrTimestamp();\\n borrowIndex = MANTISSA_ONE;\\n\\n // Set the interest rate model (depends on slot(block or second) number / borrow index)\\n _setInterestRateModelFresh(interestRateModel_);\\n\\n _setReserveFactorFresh(reserveFactorMantissa_);\\n\\n name = name_;\\n symbol = symbol_;\\n decimals = decimals_;\\n _setShortfallContract(riskManagement.shortfall);\\n _setProtocolShareReserve(riskManagement.protocolShareReserve);\\n protocolSeizeShareMantissa = DEFAULT_PROTOCOL_SEIZE_SHARE_MANTISSA;\\n\\n // Set underlying and sanity check it\\n underlying = underlying_;\\n IERC20Upgradeable(underlying).totalSupply();\\n\\n // The counter starts true to prevent changing it from zero to non-zero (i.e. smaller cost/refund)\\n _notEntered = true;\\n _transferOwnership(admin_);\\n }\\n\\n function _setShortfallContract(address shortfall_) internal {\\n ensureNonzeroAddress(shortfall_);\\n address oldShortfall = shortfall;\\n shortfall = shortfall_;\\n emit NewShortfallContract(oldShortfall, shortfall_);\\n }\\n\\n function _setProtocolShareReserve(address payable protocolShareReserve_) internal {\\n ensureNonzeroAddress(protocolShareReserve_);\\n address oldProtocolShareReserve = address(protocolShareReserve);\\n protocolShareReserve = protocolShareReserve_;\\n emit NewProtocolShareReserve(oldProtocolShareReserve, address(protocolShareReserve_));\\n }\\n\\n function _ensureSenderIsDelegateOf(address user) internal view {\\n if (!ComptrollerViewInterface(address(comptroller)).approvedDelegates(user, msg.sender)) {\\n revert DelegateNotApproved();\\n }\\n }\\n\\n /**\\n * @notice Gets the internally tracked cash balance of this market\\n * @dev Returns the internalCash state variable, which is updated on transfers in/out\\n * @return The quantity of underlying tokens tracked internally by this contract\\n */\\n function _getCashPrior() internal view virtual returns (uint256) {\\n return internalCash;\\n }\\n\\n /**\\n * @notice Return the borrow balance of account based on stored data\\n * @param account The address whose balance should be calculated\\n * @return borrowBalance the calculated balance\\n */\\n function _borrowBalanceStored(address account) internal view returns (uint256) {\\n /* Get borrowBalance and borrowIndex */\\n BorrowSnapshot memory borrowSnapshot = accountBorrows[account];\\n\\n /* If borrowBalance = 0 then borrowIndex is likely also 0.\\n * Rather than failing the calculation with a division by 0, we immediately return 0 in this case.\\n */\\n if (borrowSnapshot.principal == 0) {\\n return 0;\\n }\\n\\n /* Calculate new borrow balance using the interest index:\\n * recentBorrowBalance = borrower.borrowBalance * market.borrowIndex / borrower.borrowIndex\\n */\\n uint256 principalTimesIndex = borrowSnapshot.principal * borrowIndex;\\n\\n return principalTimesIndex / borrowSnapshot.interestIndex;\\n }\\n\\n /**\\n * @notice Calculates the exchange rate from the underlying to the VToken\\n * @dev This function does not accrue interest before calculating the exchange rate\\n * @return exchangeRate Calculated exchange rate scaled by 1e18\\n */\\n function _exchangeRateStored() internal view virtual returns (uint256) {\\n uint256 _totalSupply = totalSupply;\\n if (_totalSupply == 0) {\\n /*\\n * If there are no tokens minted:\\n * exchangeRate = initialExchangeRate\\n */\\n return initialExchangeRateMantissa;\\n }\\n /*\\n * Otherwise:\\n * exchangeRate = (totalCash + totalBorrows + badDebt - totalReserves) / totalSupply\\n */\\n uint256 totalCash = _getCashPrior();\\n uint256 cashPlusBorrowsMinusReserves = totalCash + totalBorrows + badDebt - totalReserves;\\n uint256 exchangeRate = (cashPlusBorrowsMinusReserves * EXP_SCALE) / _totalSupply;\\n\\n return exchangeRate;\\n }\\n}\\n\",\"keccak256\":\"0x7267f5337062ad01a5011f7725a86cc2ad0351cca0aaceadc167341f168cdef2\",\"license\":\"BSD-3-Clause\"},\"contracts/VTokenInterfaces.sol\":{\"content\":\"// SPDX-License-Identifier: BSD-3-Clause\\npragma solidity 0.8.25;\\n\\nimport { IERC20Upgradeable } from \\\"@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol\\\";\\nimport { ResilientOracleInterface } from \\\"@venusprotocol/oracle/contracts/interfaces/OracleInterface.sol\\\";\\n\\nimport { ComptrollerInterface } from \\\"./ComptrollerInterface.sol\\\";\\nimport { InterestRateModel } from \\\"./InterestRateModel.sol\\\";\\n\\n/**\\n * @title VTokenStorage\\n * @author Venus\\n * @notice Storage layout used by the `VToken` contract\\n */\\n// solhint-disable-next-line max-states-count\\ncontract VTokenStorage {\\n /**\\n * @notice Container for borrow balance information\\n * @member principal Total balance (with accrued interest), after applying the most recent balance-changing action\\n * @member interestIndex Global borrowIndex as of the most recent balance-changing action\\n */\\n struct BorrowSnapshot {\\n uint256 principal;\\n uint256 interestIndex;\\n }\\n\\n /**\\n * @dev Guard variable for re-entrancy checks\\n */\\n bool internal _notEntered;\\n\\n /**\\n * @notice Underlying asset for this VToken\\n */\\n address public underlying;\\n\\n /**\\n * @notice EIP-20 token name for this token\\n */\\n string public name;\\n\\n /**\\n * @notice EIP-20 token symbol for this token\\n */\\n string public symbol;\\n\\n /**\\n * @notice EIP-20 token decimals for this token\\n */\\n uint8 public decimals;\\n\\n /**\\n * @notice Protocol share Reserve contract address\\n */\\n address payable public protocolShareReserve;\\n\\n /**\\n * @notice Contract which oversees inter-vToken operations\\n */\\n ComptrollerInterface public comptroller;\\n\\n /**\\n * @notice Model which tells what the current interest rate should be\\n */\\n InterestRateModel public interestRateModel;\\n\\n // Initial exchange rate used when minting the first VTokens (used when totalSupply = 0)\\n uint256 internal initialExchangeRateMantissa;\\n\\n /**\\n * @notice Fraction of interest currently set aside for reserves\\n */\\n uint256 public reserveFactorMantissa;\\n\\n /**\\n * @notice Slot(block or second) number that interest was last accrued at\\n */\\n uint256 public accrualBlockNumber;\\n\\n /**\\n * @notice Accumulator of the total earned interest rate since the opening of the market\\n */\\n uint256 public borrowIndex;\\n\\n /**\\n * @notice Total amount of outstanding borrows of the underlying in this market\\n */\\n uint256 public totalBorrows;\\n\\n /**\\n * @notice Total amount of reserves of the underlying held in this market\\n */\\n uint256 public totalReserves;\\n\\n /**\\n * @notice Total number of tokens in circulation\\n */\\n uint256 public totalSupply;\\n\\n /**\\n * @notice Total bad debt of the market\\n */\\n uint256 public badDebt;\\n\\n // Official record of token balances for each account\\n mapping(address => uint256) internal accountTokens;\\n\\n // Approved token transfer amounts on behalf of others\\n mapping(address => mapping(address => uint256)) internal transferAllowances;\\n\\n // Mapping of account addresses to outstanding borrow balances\\n mapping(address => BorrowSnapshot) internal accountBorrows;\\n\\n /**\\n * @notice Share of seized collateral that is added to reserves\\n */\\n uint256 public protocolSeizeShareMantissa;\\n\\n /**\\n * @notice Storage of Shortfall contract address\\n */\\n address public shortfall;\\n\\n /**\\n * @notice delta slot (block or second) after which reserves will be reduced\\n */\\n uint256 public reduceReservesBlockDelta;\\n\\n /**\\n * @notice last slot (block or second) number at which reserves were reduced\\n */\\n uint256 public reduceReservesBlockNumber;\\n\\n /**\\n * @notice Tracked internal cash balance, immune to direct token transfers (donation attacks)\\n * @dev Updated via _doTransferIn(), _doTransferOut(), syncCash(), and badDebtRecovered(). Must be initialized via syncCash() after upgrade.\\n */\\n uint256 public internalCash;\\n\\n /**\\n * @dev This empty reserved space is put in place to allow future versions to add new\\n * variables without shifting down storage in the inheritance chain.\\n * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\\n */\\n uint256[47] private __gap;\\n}\\n\\n/**\\n * @title VTokenInterface\\n * @author Venus\\n * @notice Interface implemented by the `VToken` contract\\n */\\nabstract contract VTokenInterface is VTokenStorage {\\n struct RiskManagementInit {\\n address shortfall;\\n address payable protocolShareReserve;\\n }\\n\\n /*** Market Events ***/\\n\\n /**\\n * @notice Event emitted when interest is accrued\\n */\\n event AccrueInterest(uint256 cashPrior, uint256 interestAccumulated, uint256 borrowIndex, uint256 totalBorrows);\\n\\n /**\\n * @notice Event emitted when tokens are minted\\n */\\n event Mint(address indexed minter, uint256 mintAmount, uint256 mintTokens, uint256 accountBalance);\\n\\n /**\\n * @notice Event emitted when tokens are redeemed\\n */\\n event Redeem(address indexed redeemer, uint256 redeemAmount, uint256 redeemTokens, uint256 accountBalance);\\n\\n /**\\n * @notice Event emitted when underlying is borrowed\\n */\\n event Borrow(address indexed borrower, uint256 borrowAmount, uint256 accountBorrows, uint256 totalBorrows);\\n\\n /**\\n * @notice Event emitted when a borrow is repaid\\n */\\n event RepayBorrow(\\n address indexed payer,\\n address indexed borrower,\\n uint256 repayAmount,\\n uint256 accountBorrows,\\n uint256 totalBorrows\\n );\\n\\n /**\\n * @notice Event emitted when bad debt is accumulated on a market\\n * @param borrower borrower to \\\"forgive\\\"\\n * @param badDebtDelta amount of new bad debt recorded\\n * @param badDebtOld previous bad debt value\\n * @param badDebtNew new bad debt value\\n */\\n event BadDebtIncreased(address indexed borrower, uint256 badDebtDelta, uint256 badDebtOld, uint256 badDebtNew);\\n\\n /**\\n * @notice Event emitted when bad debt is recovered via an auction\\n * @param badDebtOld previous bad debt value\\n * @param badDebtNew new bad debt value\\n */\\n event BadDebtRecovered(uint256 badDebtOld, uint256 badDebtNew);\\n\\n /**\\n * @notice Event emitted when a borrow is liquidated\\n */\\n event LiquidateBorrow(\\n address indexed liquidator,\\n address indexed borrower,\\n uint256 repayAmount,\\n address indexed vTokenCollateral,\\n uint256 seizeTokens\\n );\\n\\n /*** Admin Events ***/\\n\\n /**\\n * @notice Event emitted when comptroller is changed\\n */\\n event NewComptroller(ComptrollerInterface indexed oldComptroller, ComptrollerInterface indexed newComptroller);\\n\\n /**\\n * @notice Event emitted when shortfall contract address is changed\\n */\\n event NewShortfallContract(address indexed oldShortfall, address indexed newShortfall);\\n\\n /**\\n * @notice Event emitted when protocol share reserve contract address is changed\\n */\\n event NewProtocolShareReserve(address indexed oldProtocolShareReserve, address indexed newProtocolShareReserve);\\n\\n /**\\n * @notice Event emitted when interestRateModel is changed\\n */\\n event NewMarketInterestRateModel(\\n InterestRateModel indexed oldInterestRateModel,\\n InterestRateModel indexed newInterestRateModel\\n );\\n\\n /**\\n * @notice Event emitted when protocol seize share is changed\\n */\\n event NewProtocolSeizeShare(uint256 oldProtocolSeizeShareMantissa, uint256 newProtocolSeizeShareMantissa);\\n\\n /**\\n * @notice Event emitted when the reserve factor is changed\\n */\\n event NewReserveFactor(uint256 oldReserveFactorMantissa, uint256 newReserveFactorMantissa);\\n\\n /**\\n * @notice Event emitted when the reserves are added\\n */\\n event ReservesAdded(address indexed benefactor, uint256 addAmount, uint256 newTotalReserves);\\n\\n /**\\n * @notice Event emitted when the spread reserves are reduced\\n */\\n event SpreadReservesReduced(address indexed protocolShareReserve, uint256 reduceAmount, uint256 newTotalReserves);\\n\\n /**\\n * @notice EIP20 Transfer event\\n */\\n event Transfer(address indexed from, address indexed to, uint256 amount);\\n\\n /**\\n * @notice EIP20 Approval event\\n */\\n event Approval(address indexed owner, address indexed spender, uint256 amount);\\n\\n /**\\n * @notice Event emitted when healing the borrow\\n */\\n event HealBorrow(address indexed payer, address indexed borrower, uint256 repayAmount);\\n\\n /**\\n * @notice Event emitted when tokens are swept\\n */\\n event SweepToken(address indexed token);\\n\\n /**\\n * @notice Event emitted when reduce reserves slot (block or second) delta is changed\\n */\\n event NewReduceReservesBlockDelta(\\n uint256 oldReduceReservesBlockOrTimestampDelta,\\n uint256 newReduceReservesBlockOrTimestampDelta\\n );\\n\\n /**\\n * @notice Event emitted when liquidation reserves are reduced\\n */\\n event ProtocolSeize(address indexed from, address indexed to, uint256 amount);\\n\\n /**\\n * @notice Event emitted when internalCash is synced with actual token balance\\n */\\n event CashSynced(uint256 oldInternalCash, uint256 newInternalCash);\\n\\n /*** User Interface ***/\\n\\n function mint(uint256 mintAmount) external virtual returns (uint256);\\n\\n function mintBehalf(address minter, uint256 mintAllowed) external virtual returns (uint256);\\n\\n function redeem(uint256 redeemTokens) external virtual returns (uint256);\\n\\n function redeemBehalf(address redeemer, uint256 redeemTokens) external virtual returns (uint256);\\n\\n function redeemUnderlying(uint256 redeemAmount) external virtual returns (uint256);\\n\\n function redeemUnderlyingBehalf(address redeemer, uint256 redeemAmount) external virtual returns (uint256);\\n\\n function borrow(uint256 borrowAmount) external virtual returns (uint256);\\n\\n function borrowBehalf(address borrwwer, uint256 borrowAmount) external virtual returns (uint256);\\n\\n function repayBorrow(uint256 repayAmount) external virtual returns (uint256);\\n\\n function repayBorrowBehalf(address borrower, uint256 repayAmount) external virtual returns (uint256);\\n\\n function liquidateBorrow(\\n address borrower,\\n uint256 repayAmount,\\n VTokenInterface vTokenCollateral\\n ) external virtual returns (uint256);\\n\\n function healBorrow(address payer, address borrower, uint256 repayAmount) external virtual;\\n\\n function forceLiquidateBorrow(\\n address liquidator,\\n address borrower,\\n uint256 repayAmount,\\n VTokenInterface vTokenCollateral,\\n bool skipCloseFactorCheck\\n ) external virtual;\\n\\n function seize(address liquidator, address borrower, uint256 seizeTokens) external virtual;\\n\\n function transfer(address dst, uint256 amount) external virtual returns (bool);\\n\\n function transferFrom(address src, address dst, uint256 amount) external virtual returns (bool);\\n\\n function accrueInterest() external virtual returns (uint256);\\n\\n function sweepToken(IERC20Upgradeable token) external virtual;\\n\\n /*** Admin Functions ***/\\n\\n function setReserveFactor(uint256 newReserveFactorMantissa) external virtual;\\n\\n function reduceReserves(uint256 reduceAmount) external virtual;\\n\\n function exchangeRateCurrent() external virtual returns (uint256);\\n\\n function borrowBalanceCurrent(address account) external virtual returns (uint256);\\n\\n function setInterestRateModel(InterestRateModel newInterestRateModel) external virtual;\\n\\n function addReserves(uint256 addAmount) external virtual;\\n\\n function syncCash() external virtual;\\n\\n function totalBorrowsCurrent() external virtual returns (uint256);\\n\\n function balanceOfUnderlying(address owner) external virtual returns (uint256);\\n\\n function approve(address spender, uint256 amount) external virtual returns (bool);\\n\\n function increaseAllowance(address spender, uint256 addedValue) external virtual returns (bool);\\n\\n function decreaseAllowance(address spender, uint256 subtractedValue) external virtual returns (bool);\\n\\n function allowance(address owner, address spender) external view virtual returns (uint256);\\n\\n function balanceOf(address owner) external view virtual returns (uint256);\\n\\n function getAccountSnapshot(address account) external view virtual returns (uint256, uint256, uint256, uint256);\\n\\n function borrowRatePerBlock() external view virtual returns (uint256);\\n\\n function supplyRatePerBlock() external view virtual returns (uint256);\\n\\n function borrowBalanceStored(address account) external view virtual returns (uint256);\\n\\n function exchangeRateStored() external view virtual returns (uint256);\\n\\n function getCash() external view virtual returns (uint256);\\n\\n /**\\n * @notice Indicator that this is a VToken contract (for inspection)\\n * @return Always true\\n */\\n function isVToken() external pure virtual returns (bool) {\\n return true;\\n }\\n}\\n\",\"keccak256\":\"0x155684e9cb12cdd8be63d2314c9452b68ee44ff98a00e0d65cd1fd7d0bdd4391\",\"license\":\"BSD-3-Clause\"},\"contracts/lib/constants.sol\":{\"content\":\"// SPDX-License-Identifier: BSD-3-Clause\\npragma solidity ^0.8.25;\\n\\n/// @dev The approximate number of seconds per year\\nuint256 constant SECONDS_PER_YEAR = 31_536_000;\\n\\n/// @dev Base unit for computations, usually used in scaling (multiplications, divisions)\\nuint256 constant EXP_SCALE = 1e18;\\n\\n/// @dev A unit (literal one) in EXP_SCALE, usually used in additions/subtractions\\nuint256 constant MANTISSA_ONE = EXP_SCALE;\\n\",\"keccak256\":\"0x54ab3a6f3bc87569ed12370f3470a1ec84cea9796d4d0ccf3d07dd4280c044aa\",\"license\":\"BSD-3-Clause\"},\"contracts/lib/validators.sol\":{\"content\":\"// SPDX-License-Identifier: BSD-3-Clause\\npragma solidity 0.8.25;\\n\\n/// @notice Thrown if the supplied address is a zero address where it is not allowed\\nerror ZeroAddressNotAllowed();\\n\\n/// @notice Checks if the provided address is nonzero, reverts otherwise\\n/// @param address_ Address to check\\n/// @custom:error ZeroAddressNotAllowed is thrown if the provided address is a zero address\\nfunction ensureNonzeroAddress(address address_) pure {\\n if (address_ == address(0)) {\\n revert ZeroAddressNotAllowed();\\n }\\n}\\n\",\"keccak256\":\"0x93bdd5cfb100f0f9a1d446857e23c3633df6ab12c266333c978428edd96b1367\",\"license\":\"BSD-3-Clause\"}},\"version\":1}", + "bytecode": "0x60e060405234801561001057600080fd5b5060405161478038038061478083398101604081905261002f916100dc565b81818115801561003d575080155b1561005b576040516302723dfb60e21b815260040160405180910390fd5b81801561006757508015155b156100855760405163ae0fcab360e01b815260040160405180910390fd5b81151560a05281610096578061009c565b6301e133805b608052816100b3576100d460201b612304176100be565b6100d860201b612308175b6001600160401b031660c0525061010f92505050565b4390565b4290565b600080604083850312156100ef57600080fd5b825180151581146100ff57600080fd5b6020939093015192949293505050565b60805160a05160c05161463b6101456000396000611fd70152600081816102e6015261249c015260006101d4015261463b6000f3fe608060405234801561001057600080fd5b50600436106101165760003560e01c80637f1a7828116100a2578063c7ad089511610071578063c7ad0895146102e1578063d77ebf9614610318578063e1d146fb14610338578063f1ef0eb014610340578063f246e34f1461036057600080fd5b80637f1a7828146102645780639d823c0214610284578063a035b0ae14610297578063b3124239146102c157600080fd5b806355dd9515116100e957806355dd9515146101a45780636857249c146101cf5780637a27db57146102045780637c51b642146102245780637c84e3b31461024457600080fd5b80631f884fdf1461011b57806325777efe146101445780633e3e399c14610164578063421368b014610184575b600080fd5b61012e6101293660046134b1565b610380565b60405161013b91906134f2565b60405180910390f35b61015761015236600461368a565b61044a565b60405161013b91906137c7565b610177610172366004613826565b6104ff565b60405161013b9190613843565b610197610192366004613826565b610871565b60405161013b91906138cd565b6101b76101b23660046138dc565b61114a565b6040516001600160a01b03909116815260200161013b565b6101f67f000000000000000000000000000000000000000000000000000000000000000081565b60405190815260200161013b565b610217610212366004613927565b6111ca565b60405161013b9190613960565b610237610232366004613a3c565b6114e1565b60405161013b9190613ac7565b610257610252366004613826565b61159a565b60405161013b9190613b09565b610277610272366004613b50565b611704565b60405161013b9190613e0b565b610277610292366004613927565b611b0e565b6102aa6102a5366004613927565b611b95565b60408051921515835290151560208301520161013b565b6102d46102cf366004613927565b611c76565b60405161013b9190613e1e565b6103087f000000000000000000000000000000000000000000000000000000000000000081565b604051901515815260200161013b565b61032b610326366004613927565b611f5d565b60405161013b9190613e2c565b6101f6611fd0565b61035361034e366004613e6d565b612003565b60405161013b9190613ec5565b61037361036e366004613826565b6121e3565b60405161013b9190613f1e565b6060816000816001600160401b0381111561039d5761039d613552565b6040519080825280602002602001820160405280156103e257816020015b60408051808201909152600080825260208201528152602001906001900390816103bb5790505b50905060005b8281101561043f5761041a86868381811061040557610405613f82565b90506020020160208101906102529190613826565b82828151811061042c5761042c613f82565b60209081029190910101526001016103e8565b509150505b92915050565b80516060906000816001600160401b0381111561046957610469613552565b6040519080825280602002602001820160405280156104a257816020015b61048f6132d3565b8152602001906001900390816104875790505b50905060005b828110156104f7576104d28582815181106104c5576104c5613f82565b6020026020010151610871565b8282815181106104e4576104e4613f82565b60209081029190910101526001016104a8565b509392505050565b604080516060808201835260008083526020830152918101919091526000808390506000816001600160a01b031663b0772d0b6040518163ffffffff1660e01b8152600401600060405180830381865afa158015610561573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526105899190810190613f98565b90506000826001600160a01b0316637dc0d1d06040518163ffffffff1660e01b8152600401602060405180830381865afa1580156105cb573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105ef9190614031565b9050600082516001600160401b0381111561060c5761060c613552565b60405190808252806020026020018201604052801561065157816020015b604080518082019091526000808252602082015281526020019060019003908161062a5790505b509050610681604051806060016040528060006001600160a01b0316815260200160008152602001606081525090565b6001600160a01b03881681526040810182905260005b845181101561085d5760408051808201909152600080825260208201528582815181106106c6576106c6613f82565b602002602001015181600001906001600160a01b031690816001600160a01b031681525050670de0b6b3a7640000856001600160a01b031663fc57d4df88858151811061071557610715613f82565b60200260200101516040518263ffffffff1660e01b815260040161074891906001600160a01b0391909116815260200190565b602060405180830381865afa158015610765573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610789919061404e565b87848151811061079b5761079b613f82565b60200260200101516001600160a01b031663bbcac5576040518163ffffffff1660e01b8152600401602060405180830381865afa1580156107e0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610804919061404e565b61080e919061407d565b6108189190614094565b6020820152604083015180518291908490811061083757610837613f82565b602002602001018190525080602001518861085291906140b6565b975050600101610697565b506020810195909552509295945050505050565b6108796132d3565b60008290506000836001600160a01b0316635fe3b5676040518163ffffffff1660e01b8152600401602060405180830381865afa1580156108be573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108e29190614031565b604051638e8f294b60e01b81526001600160a01b0384811660048301529192508291829160009182918291851690638e8f294b90602401606060405180830381865afa158015610936573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061095a91906140d9565b9250925092506000896001600160a01b0316636f307dc36040518163ffffffff1660e01b8152600401602060405180830381865afa1580156109a0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109c49190614031565b9050604051806102c00160405280896001600160a01b031681526020018b6001600160a01b031663182df0f56040518163ffffffff1660e01b8152600401602060405180830381865afa158015610a1f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a43919061404e565b815260200160008c6001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610a88573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610aac919061404e565b11610ab8576000610b1a565b8b6001600160a01b031663ae9d70b06040518163ffffffff1660e01b8152600401602060405180830381865afa158015610af6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b1a919061404e565b81526020018b6001600160a01b031663f8f9da286040518163ffffffff1660e01b8152600401602060405180830381865afa158015610b5d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b81919061404e565b81526020018b6001600160a01b031663173b99046040518163ffffffff1660e01b8152600401602060405180830381865afa158015610bc4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610be8919061404e565b81526040516302c3bcbb60e01b81526001600160a01b038b811660048301526020909201918916906302c3bcbb90602401602060405180830381865afa158015610c36573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c5a919061404e565b815260405163252c221960e11b81526001600160a01b038b81166004830152602090920191891690634a58443290602401602060405180830381865afa158015610ca8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ccc919061404e565b81526020018b6001600160a01b03166347bd37186040518163ffffffff1660e01b8152600401602060405180830381865afa158015610d0f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d33919061404e565b81526020018b6001600160a01b0316638f840ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610d76573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d9a919061404e565b81526020018b6001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610ddd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e01919061404e565b81526020018b6001600160a01b0316633b1d21a26040518163ffffffff1660e01b8152600401602060405180830381865afa158015610e44573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e68919061404e565b81526020018515158152602001848152602001838152602001826001600160a01b031681526020018b6001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015610ece573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ef2919061410e565b60ff168152602001826001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015610f38573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f5c919061410e565b60ff168152602001610f6e898b61230c565b8152604051637db94b9560e01b81526001600160a01b038b81166004830152602090920191881690637db94b9590602401602060405180830381865afa158015610fbc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fe0919061404e565b815260405163c5c0542560e01b81526001600160a01b038b8116600483015260209092019188169063c5c0542590602401602060405180830381865afa15801561102e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611052919061404e565b815260405163f45fe69f60e01b81526001600160a01b038b8116600483015260209092019188169063f45fe69f90602401602060405180830381865afa1580156110a0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110c49190614131565b1515815260405163460d60c560e11b81526001600160a01b038b81166004830152602090920191881690638c1ac18a90602401602060405180830381865afa158015611114573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111389190614131565b151590529a9950505050505050505050565b60405163266e0a7f60e01b81526001600160a01b0383811660048301528281166024830152600091859182169063266e0a7f90604401602060405180830381865afa15801561119d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111c19190614031565b95945050505050565b60606000826001600160a01b031663b0772d0b6040518163ffffffff1660e01b8152600401600060405180830381865afa15801561120c573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526112349190810190613f98565b90506000836001600160a01b03166361252fd16040518163ffffffff1660e01b8152600401600060405180830381865afa158015611276573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261129e919081019061414c565b9050600081516001600160401b038111156112bb576112bb613552565b60405190808252806020026020018201604052801561130c57816020015b60408051608081018252600080825260208083018290529282015260608082015282526000199092019101816112d95790505b50905060005b82518110156114d757604080516080810182526000808252602082018190529181019190915260608082015283828151811061135057611350613f82565b60209081029190910101516001600160a01b03168152835184908390811061137a5761137a613f82565b60200260200101516001600160a01b031663f7c618c16040518163ffffffff1660e01b8152600401602060405180830381865afa1580156113bf573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113e39190614031565b6001600160a01b03166020820152835184908390811061140557611405613f82565b6020908102919091010151604051631627ee8960e01b81526001600160a01b038a8116600483015290911690631627ee8990602401602060405180830381865afa158015611457573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061147b919061404e565b8160400181815250506114a8888686858151811061149b5761149b613f82565b60200260200101516123cf565b8160600181905250808383815181106114c3576114c3613f82565b602090810291909101015250600101611312565b5095945050505050565b6060826000816001600160401b038111156114fe576114fe613552565b60405190808252806020026020018201604052801561153757816020015b611524613392565b81526020019060019003908161151c5790505b50905060005b828110156114d75761157587878381811061155a5761155a613f82565b905060200201602081019061156f9190613826565b86611c76565b82828151811061158757611587613f82565b602090810291909101015260010161153d565b60408051808201909152600080825260208201526000826001600160a01b0316635fe3b5676040518163ffffffff1660e01b8152600401602060405180830381865afa1580156115ee573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116129190614031565b90506000816001600160a01b0316637dc0d1d06040518163ffffffff1660e01b8152600401602060405180830381865afa158015611654573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116789190614031565b6040805180820182526001600160a01b03808816808352925163fc57d4df60e01b815260048101939093529293509160208301919084169063fc57d4df90602401602060405180830381865afa1580156116d6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116fa919061404e565b9052949350505050565b61170c6133d1565b6000826040015190506000611780826001600160a01b031663b0772d0b6040518163ffffffff1660e01b8152600401600060405180830381865afa158015611758573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526101529190810190613f98565b6040516328ebbe8b60e21b81526001600160a01b03848116600483015291925060009187169063a3aefa2c90602401600060405180830381865afa1580156117cc573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526117f4919081019061421f565b905082806118006133d1565b875181526020808901516001600160a01b03908116828401528781166040808501919091526060808c0151908501526080808c015190850152865160a08501528683015160c08501528681015160e085015280516307dc0d1d60e41b8152905191861692637dc0d1d0926004808401938290030181865afa158015611889573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118ad9190614031565b8161010001906001600160a01b031690816001600160a01b031681525050826001600160a01b031663e87554466040518163ffffffff1660e01b8152600401602060405180830381865afa158015611909573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061192d919061404e565b81610120018181525050826001600160a01b0316634ada90af6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611975573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611999919061404e565b81610140018181525050826001600160a01b031663db5c65de6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156119e1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a05919061404e565b81610160018181525050816001600160a01b031663d7c46d2d6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611a4d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a719190614031565b8161018001906001600160a01b031690816001600160a01b031681525050816001600160a01b031663f582cffd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611acd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611af19190614131565b15156101a08201526101c081019490945250919695505050505050565b611b166133d1565b604051637aee632d60e01b81526001600160a01b0383811660048301528491611b8d91839190821690637aee632d90602401600060405180830381865afa158015611b65573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526102729190810190614356565b949350505050565b6000806000849050806001600160a01b031663f582cffd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611bdb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611bff9190614131565b604051639dcd31fd60e01b81526001600160a01b038681166004830152831690639dcd31fd90602401602060405180830381865afa158015611c45573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c699190614131565b92509250505b9250929050565b611c7e613392565b6040516370a0823160e01b81526001600160a01b038381166004830152600091908516906370a0823190602401602060405180830381865afa158015611cc8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611cec919061404e565b6040516305eff7ef60e21b81526001600160a01b0385811660048301529192506000918616906317bfdfbc906024016020604051808303816000875af1158015611d3a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d5e919061404e565b604051633af9e66960e01b81526001600160a01b038681166004830152919250600091871690633af9e669906024016020604051808303816000875af1158015611dac573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611dd0919061404e565b90506000806000886001600160a01b0316636f307dc36040518163ffffffff1660e01b8152600401602060405180830381865afa158015611e15573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e399190614031565b6040516370a0823160e01b81526001600160a01b038a81166004830152919250908216906370a0823190602401602060405180830381865afa158015611e83573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ea7919061404e565b604051636eb1769f60e11b81526001600160a01b038a811660048301528b811660248301529194509082169063dd62ed3e90604401602060405180830381865afa158015611ef9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f1d919061404e565b6040805160c0810182526001600160a01b038c168152602081019890985287019590955250506060840191909152608083015260a0820152905092915050565b604051631e6db74760e31b81526001600160a01b038281166004830152606091849182169063f36dba3890602401600060405180830381865afa158015611fa8573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611b8d919081019061438a565b6000611ffe7f000000000000000000000000000000000000000000000000000000000000000063ffffffff16565b905090565b815160609084906000816001600160401b0381111561202457612024613552565b60405190808252806020026020018201604052801561206f57816020015b60408051606081018252600080825260208083018290529282015282526000199092019101816120425790505b50905060005b828110156121d857600087828151811061209157612091613f82565b602002602001015190506040518060600160405280826001600160a01b03168152602001866001600160a01b031663f45fe69f846040518263ffffffff1660e01b81526004016120f091906001600160a01b0391909116815260200190565b602060405180830381865afa15801561210d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121319190614131565b151581526040516327cccc8b60e21b81526001600160a01b0384811660048301528a81166024830152602090920191881690639f33322c90604401602060405180830381865afa158015612189573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121ad9190614131565b15158152508383815181106121c4576121c4613f82565b602090810291909101015250600101612075565b509695505050505050565b60606000826001600160a01b031663d88ff1f46040518163ffffffff1660e01b8152600401600060405180830381865afa158015612225573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261224d9190810190614418565b80519091506000816001600160401b0381111561226c5761226c613552565b6040519080825280602002602001820160405280156122a557816020015b6122926133d1565b81526020019060019003908161228a5790505b50905060005b828110156122fb576122d6868583815181106122c9576122c9613f82565b6020026020010151611704565b8282815181106122e8576122e8613f82565b60209081029190910101526001016122ab565b50949350505050565b4390565b4290565b60008060005b600860ff8216116104f7576000856001600160a01b031663e85a2960868460ff166008811115612344576123446144c8565b6040518363ffffffff1660e01b81526004016123619291906144de565b602060405180830381865afa15801561237e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123a29190614131565b6123ad5760006123b0565b60015b60ff9081169083161b9290921791506123c881614519565b9050612312565b6060600083516001600160401b038111156123ec576123ec613552565b60405190808252806020026020018201604052801561243157816020015b604080518082019091526000808252602082015281526020019060019003908161240a5790505b50905060005b84518110156122fb5761246d604051806060016040528060006001600160e01b0316815260200160008152602001600081525090565b61249a604051806060016040528060006001600160e01b0316815260200160008152602001600081525090565b7f00000000000000000000000000000000000000000000000000000000000000001561261d57856001600160a01b0316638f693ec78885815181106124e1576124e1613f82565b60200260200101516040518263ffffffff1660e01b815260040161251491906001600160a01b0391909116815260200190565b606060405180830381865afa158015612531573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612555919061454f565b604085015260208401526001600160e01b0316825286516001600160a01b0387169063818149459089908690811061258f5761258f613f82565b60200260200101516040518263ffffffff1660e01b81526004016125c291906001600160a01b0391909116815260200190565b606060405180830381865afa1580156125df573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612603919061454f565b604084015260208301526001600160e01b03168152612788565b856001600160a01b0316632c427b5788858151811061263e5761263e613f82565b60200260200101516040518263ffffffff1660e01b815260040161267191906001600160a01b0391909116815260200190565b606060405180830381865afa15801561268e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126b29190614581565b63ffffffff90811660408601521660208401526001600160e01b0316825286516001600160a01b038716906392a18235908990869081106126f5576126f5613f82565b60200260200101516040518263ffffffff1660e01b815260040161272891906001600160a01b0391909116815260200190565b606060405180830381865afa158015612745573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127699190614581565b63ffffffff90811660408501521660208301526001600160e01b031681525b600060405180602001604052808986815181106127a7576127a7613f82565b60200260200101516001600160a01b031663aa5af0fd6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156127ec573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612810919061404e565b815250905061283a88858151811061282a5761282a613f82565b602002602001015188858461292f565b61285e88858151811061284f5761284f613f82565b60200260200101518884612b30565b600061288689868151811061287557612875613f82565b6020026020010151898c8786612d27565b905060006128af8a878151811061289f5761289f613f82565b60200260200101518a8d87612f57565b60408051808201909152600080825260208201529091508a87815181106128d8576128d8613f82565b60209081029190910101516001600160a01b031681526128f882846140b6565b60208201528751819089908990811061291357612913613f82565b6020026020010181905250505050505050806001019050612437565b604051637c05a7c560e01b81526001600160a01b03858116600483015260009190851690637c05a7c590602401602060405180830381865afa158015612979573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061299d919061404e565b905060006129a9611fd0565b9050600084604001511180156129c25750836040015181115b156129ce575060408301515b60006129de82866020015161317b565b90506000811180156129f05750600083115b15612b19576000612a62886001600160a01b03166347bd37186040518163ffffffff1660e01b8152600401602060405180830381865afa158015612a38573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a5c919061404e565b8661318e565b90506000612a7083866131ac565b90506000808311612a905760405180602001604052806000815250612a9a565b612a9a82846131b8565b90506000612ac360405180602001604052808b600001516001600160e01b0316815250836131fd565b9050612afe8160000151604051806040016040528060138152602001726e657720696e646578206f766572666c6f777360681b815250613229565b6001600160e01b031689525050505060208501829052612b27565b8015612b2757602085018290525b50505050505050565b604051631d31307360e21b81526001600160a01b038481166004830152600091908416906374c4c1cc90602401602060405180830381865afa158015612b7a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b9e919061404e565b90506000612baa611fd0565b905060008360400151118015612bc35750826040015181115b15612bcf575060408201515b6000612bdf82856020015161317b565b9050600081118015612bf15750600083115b15612d11576000866001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612c36573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c5a919061404e565b90506000612c6883866131ac565b90506000808311612c885760405180602001604052806000815250612c92565b612c9282846131b8565b90506000612cbb60405180602001604052808a600001516001600160e01b0316815250836131fd565b9050612cf68160000151604051806040016040528060138152602001726e657720696e646578206f766572666c6f777360681b815250613229565b6001600160e01b031688525050505060208401829052612d1f565b8015612d1f57602084018290525b505050505050565b604080516020808201835284516001600160e01b031682528251908101928390526336fe846560e11b9092526001600160a01b0387811660248401528581166044840152600092839181908916636dfd08ca60648301602060405180830381865afa158015612d9a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612dbe919061404e565b90528051909150158015612e405750866001600160a01b031663160c3a036040518163ffffffff1660e01b8152600401602060405180830381865afa158015612e0b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e2f91906145c4565b6001600160e01b0316826000015110155b15612eb357866001600160a01b031663160c3a036040518163ffffffff1660e01b8152600401602060405180830381865afa158015612e83573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ea791906145c4565b6001600160e01b031681525b6000612ebf8383613265565b6040516395dd919360e01b81526001600160a01b038981166004830152919250600091612f3a91908c16906395dd919390602401602060405180830381865afa158015612f10573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f34919061404e565b8761318e565b90506000612f488284613291565b9b9a5050505050505050505050565b604080516020808201835283516001600160e01b0316825282519081019283905263552c097160e01b9092526001600160a01b038681166024840152848116604484015260009283918190881663552c097160648301602060405180830381865afa158015612fca573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612fee919061404e565b905280519091501580156130705750856001600160a01b031663160c3a036040518163ffffffff1660e01b8152600401602060405180830381865afa15801561303b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061305f91906145c4565b6001600160e01b0316826000015110155b156130e357856001600160a01b031663160c3a036040518163ffffffff1660e01b8152600401602060405180830381865afa1580156130b3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130d791906145c4565b6001600160e01b031681525b60006130ef8383613265565b6040516370a0823160e01b81526001600160a01b0388811660048301529192506000918a16906370a0823190602401602060405180830381865afa15801561313b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061315f919061404e565b9050600061316d8284613291565b9a9950505050505050505050565b600061318782846145df565b9392505050565b60006131876131a584670de0b6b3a76400006131ac565b83516132bb565b6000613187828461407d565b60408051602081019091526000815260405180602001604052806131f46131ee866ec097ce7bc90715b34b9f10000000006131ac565b856132bb565b90529392505050565b60408051602081019091526000815260405180602001604052806131f4856000015185600001516132c7565b6000816001600160e01b0384111561325d5760405162461bcd60e51b815260040161325491906145f2565b60405180910390fd5b509192915050565b60408051602081019091526000815260405180602001604052806131f48560000151856000015161317b565b60006ec097ce7bc90715b34b9f10000000006132b18484600001516131ac565b6131879190614094565b60006131878284614094565b600061318782846140b6565b604051806102c0016040528060006001600160a01b0316815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600015158152602001600081526020016000815260200160006001600160a01b0316815260200160008152602001600081526020016000815260200160008152602001600081526020016000151581526020016000151581525090565b6040518060c0016040528060006001600160a01b0316815260200160008152602001600081526020016000815260200160008152602001600081525090565b604051806101e001604052806060815260200160006001600160a01b0316815260200160006001600160a01b03168152602001600081526020016000815260200160608152602001606081526020016060815260200160006001600160a01b0316815260200160008152602001600081526020016000815260200160006001600160a01b03168152602001600015158152602001606081525090565b60008083601f84011261347f57600080fd5b5081356001600160401b0381111561349657600080fd5b6020830191508360208260051b8501011115611c6f57600080fd5b600080602083850312156134c457600080fd5b82356001600160401b038111156134da57600080fd5b6134e68582860161346d565b90969095509350505050565b602080825282518282018190526000919060409081850190868401855b828110156135455761353584835180516001600160a01b03168252602090810151910152565b928401929085019060010161350f565b5091979650505050505050565b634e487b7160e01b600052604160045260246000fd5b60405160a081016001600160401b038111828210171561358a5761358a613552565b60405290565b604051606081016001600160401b038111828210171561358a5761358a613552565b604051601f8201601f191681016001600160401b03811182821017156135da576135da613552565b604052919050565b60006001600160401b038211156135fb576135fb613552565b5060051b60200190565b6001600160a01b038116811461361a57600080fd5b50565b600082601f83011261362e57600080fd5b8135602061364361363e836135e2565b6135b2565b8083825260208201915060208460051b87010193508684111561366557600080fd5b602086015b848110156121d857803561367d81613605565b835291830191830161366a565b60006020828403121561369c57600080fd5b81356001600160401b038111156136b257600080fd5b611b8d8482850161361d565b80516001600160a01b031682526020810151602083015260408101516040830152606081015160608301526080810151608083015260a081015160a083015260c081015160c083015260e081015160e0830152610100808201518184015250610120808201518184015250610140808201518184015250610160808201516137498285018215159052565b505061018081810151908301526101a080820151908301526101c0808201516001600160a01b0316908301526101e080820151908301526102008082015190830152610220808201519083015261024080820151908301526102608082015190830152610280808201511515908301526102a0908101511515910152565b6020808252825182820181905260009190848201906040850190845b8181101561380a576137f68385516136be565b928401926102c092909201916001016137e3565b50909695505050505050565b803561382181613605565b919050565b60006020828403121561383857600080fd5b813561318781613605565b602080825282516001600160a01b03168282015282810151604080840191909152808401516060808501528051608085018190526000939291830191849160a08701905b808410156138c1576138ad82865180516001600160a01b03168252602090810151910152565b938501936001939093019290820190613887565b50979650505050505050565b6102c0810161044482846136be565b6000806000606084860312156138f157600080fd5b83356138fc81613605565b9250602084013561390c81613605565b9150604084013561391c81613605565b809150509250925092565b6000806040838503121561393a57600080fd5b823561394581613605565b9150602083013561395581613605565b809150509250929050565b600060208083018184528085518083526040925060408601915060408160051b8701018488016000805b84811015613a2d57898403603f19018652825180516001600160a01b03908116865289820151168986015287810151888601526060908101516080918601829052805191860182905289019060a086019084905b80821015613a1857613a0483855180516001600160a01b03168252602090810151910152565b928b0192918a0191600191909101906139de565b5050968901969450509187019160010161398a565b50919998505050505050505050565b600080600060408486031215613a5157600080fd5b83356001600160401b03811115613a6757600080fd5b613a738682870161346d565b909450925050602084013561391c81613605565b80516001600160a01b031682526020808201519083015260408082015190830152606080820151908301526080808201519083015260a090810151910152565b6020808252825182820181905260009190848201906040850190845b8181101561380a57613af6838551613a87565b9284019260c09290920191600101613ae3565b81516001600160a01b031681526020808301519082015260408101610444565b60006001600160401b03821115613b4257613b42613552565b50601f01601f191660200190565b60008060408385031215613b6357600080fd5b8235613b6e81613605565b91506020838101356001600160401b0380821115613b8b57600080fd5b9085019060a08288031215613b9f57600080fd5b613ba7613568565b823582811115613bb657600080fd5b83019150601f82018813613bc957600080fd5b8135613bd761363e82613b29565b8181528986838601011115613beb57600080fd5b818685018783013760008683830101528083525050613c0b848401613816565b84820152613c1b60408401613816565b60408201526060830135606082015260808301356080820152809450505050509250929050565b60005b83811015613c5d578181015183820152602001613c45565b50506000910152565b60008151808452613c7e816020860160208601613c42565b601f01601f19169290920160200192915050565b60008151808452602080850194506020840160005b83811015613cce57613cba8783516136be565b6102c0969096019590820190600101613ca7565b509495945050505050565b60006101e08251818552613cef82860182613c66565b9150506020830151613d0c60208601826001600160a01b03169052565b506040830151613d2760408601826001600160a01b03169052565b50606083015160608501526080830151608085015260a083015184820360a0860152613d538282613c66565b91505060c083015184820360c0860152613d6d8282613c66565b91505060e083015184820360e0860152613d878282613c66565b91505061010080840151613da5828701826001600160a01b03169052565b5050610120838101519085015261014080840151908501526101608084015190850152610180808401516001600160a01b0316908501526101a0808401511515908501526101c08084015185830382870152613e018382613c92565b9695505050505050565b6020815260006131876020830184613cd9565b60c081016104448284613a87565b6020808252825182820181905260009190848201906040850190845b8181101561380a5783516001600160a01b031683529284019291840191600101613e48565b600080600060608486031215613e8257600080fd5b8335613e8d81613605565b925060208401356001600160401b03811115613ea857600080fd5b613eb48682870161361d565b925050604084013561391c81613605565b602080825282518282018190526000919060409081850190868401855b8281101561354557815180516001600160a01b031685528681015115158786015285015115158585015260609093019290850190600101613ee2565b600060208083016020845280855180835260408601915060408160051b87010192506020870160005b82811015613f7557603f19888603018452613f63858351613cd9565b94509285019290850190600101613f47565b5092979650505050505050565b634e487b7160e01b600052603260045260246000fd5b60006020808385031215613fab57600080fd5b82516001600160401b03811115613fc157600080fd5b8301601f81018513613fd257600080fd5b8051613fe061363e826135e2565b81815260059190911b82018301908381019087831115613fff57600080fd5b928401925b8284101561402657835161401781613605565b82529284019290840190614004565b979650505050505050565b60006020828403121561404357600080fd5b815161318781613605565b60006020828403121561406057600080fd5b5051919050565b634e487b7160e01b600052601160045260246000fd5b808202811582820484141761044457610444614067565b6000826140b157634e487b7160e01b600052601260045260246000fd5b500490565b8082018082111561044457610444614067565b8051801515811461382157600080fd5b6000806000606084860312156140ee57600080fd5b6140f7846140c9565b925060208401519150604084015190509250925092565b60006020828403121561412057600080fd5b815160ff8116811461318757600080fd5b60006020828403121561414357600080fd5b613187826140c9565b6000602080838503121561415f57600080fd5b82516001600160401b0381111561417557600080fd5b8301601f8101851361418657600080fd5b805161419461363e826135e2565b81815260059190911b820183019083810190878311156141b357600080fd5b928401925b828410156140265783516141cb81613605565b825292840192908401906141b8565b600082601f8301126141eb57600080fd5b81516141f961363e82613b29565b81815284602083860101111561420e57600080fd5b611b8d826020830160208701613c42565b60006020828403121561423157600080fd5b81516001600160401b038082111561424857600080fd5b908301906060828603121561425c57600080fd5b614264613590565b82518281111561427357600080fd5b61427f878286016141da565b82525060208301518281111561429457600080fd5b6142a0878286016141da565b6020830152506040830151828111156142b857600080fd5b6142c4878286016141da565b60408301525095945050505050565b600060a082840312156142e557600080fd5b6142ed613568565b905081516001600160401b0381111561430557600080fd5b614311848285016141da565b825250602082015161432281613605565b6020820152604082015161433581613605565b80604083015250606082015160608201526080820151608082015292915050565b60006020828403121561436857600080fd5b81516001600160401b0381111561437e57600080fd5b611b8d848285016142d3565b6000602080838503121561439d57600080fd5b82516001600160401b038111156143b357600080fd5b8301601f810185136143c457600080fd5b80516143d261363e826135e2565b81815260059190911b820183019083810190878311156143f157600080fd5b928401925b8284101561402657835161440981613605565b825292840192908401906143f6565b6000602080838503121561442b57600080fd5b82516001600160401b038082111561444257600080fd5b818501915085601f83011261445657600080fd5b815161446461363e826135e2565b81815260059190911b8301840190848101908883111561448357600080fd5b8585015b838110156144bb5780518581111561449f5760008081fd5b6144ad8b89838a01016142d3565b845250918601918601614487565b5098975050505050505050565b634e487b7160e01b600052602160045260246000fd5b6001600160a01b0383168152604081016009831061450c57634e487b7160e01b600052602160045260246000fd5b8260208301529392505050565b600060ff821660ff810361452f5761452f614067565b60010192915050565b80516001600160e01b038116811461382157600080fd5b60008060006060848603121561456457600080fd5b6140f784614538565b805163ffffffff8116811461382157600080fd5b60008060006060848603121561459657600080fd5b61459f84614538565b92506145ad6020850161456d565b91506145bb6040850161456d565b90509250925092565b6000602082840312156145d657600080fd5b61318782614538565b8181038181111561044457610444614067565b6020815260006131876020830184613c6656fea2646970667358221220e61005757d8e238ed335801966fbabcdd38a6aad1e822b630146d8f96d65d41164736f6c63430008190033", + "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106101165760003560e01c80637f1a7828116100a2578063c7ad089511610071578063c7ad0895146102e1578063d77ebf9614610318578063e1d146fb14610338578063f1ef0eb014610340578063f246e34f1461036057600080fd5b80637f1a7828146102645780639d823c0214610284578063a035b0ae14610297578063b3124239146102c157600080fd5b806355dd9515116100e957806355dd9515146101a45780636857249c146101cf5780637a27db57146102045780637c51b642146102245780637c84e3b31461024457600080fd5b80631f884fdf1461011b57806325777efe146101445780633e3e399c14610164578063421368b014610184575b600080fd5b61012e6101293660046134b1565b610380565b60405161013b91906134f2565b60405180910390f35b61015761015236600461368a565b61044a565b60405161013b91906137c7565b610177610172366004613826565b6104ff565b60405161013b9190613843565b610197610192366004613826565b610871565b60405161013b91906138cd565b6101b76101b23660046138dc565b61114a565b6040516001600160a01b03909116815260200161013b565b6101f67f000000000000000000000000000000000000000000000000000000000000000081565b60405190815260200161013b565b610217610212366004613927565b6111ca565b60405161013b9190613960565b610237610232366004613a3c565b6114e1565b60405161013b9190613ac7565b610257610252366004613826565b61159a565b60405161013b9190613b09565b610277610272366004613b50565b611704565b60405161013b9190613e0b565b610277610292366004613927565b611b0e565b6102aa6102a5366004613927565b611b95565b60408051921515835290151560208301520161013b565b6102d46102cf366004613927565b611c76565b60405161013b9190613e1e565b6103087f000000000000000000000000000000000000000000000000000000000000000081565b604051901515815260200161013b565b61032b610326366004613927565b611f5d565b60405161013b9190613e2c565b6101f6611fd0565b61035361034e366004613e6d565b612003565b60405161013b9190613ec5565b61037361036e366004613826565b6121e3565b60405161013b9190613f1e565b6060816000816001600160401b0381111561039d5761039d613552565b6040519080825280602002602001820160405280156103e257816020015b60408051808201909152600080825260208201528152602001906001900390816103bb5790505b50905060005b8281101561043f5761041a86868381811061040557610405613f82565b90506020020160208101906102529190613826565b82828151811061042c5761042c613f82565b60209081029190910101526001016103e8565b509150505b92915050565b80516060906000816001600160401b0381111561046957610469613552565b6040519080825280602002602001820160405280156104a257816020015b61048f6132d3565b8152602001906001900390816104875790505b50905060005b828110156104f7576104d28582815181106104c5576104c5613f82565b6020026020010151610871565b8282815181106104e4576104e4613f82565b60209081029190910101526001016104a8565b509392505050565b604080516060808201835260008083526020830152918101919091526000808390506000816001600160a01b031663b0772d0b6040518163ffffffff1660e01b8152600401600060405180830381865afa158015610561573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526105899190810190613f98565b90506000826001600160a01b0316637dc0d1d06040518163ffffffff1660e01b8152600401602060405180830381865afa1580156105cb573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105ef9190614031565b9050600082516001600160401b0381111561060c5761060c613552565b60405190808252806020026020018201604052801561065157816020015b604080518082019091526000808252602082015281526020019060019003908161062a5790505b509050610681604051806060016040528060006001600160a01b0316815260200160008152602001606081525090565b6001600160a01b03881681526040810182905260005b845181101561085d5760408051808201909152600080825260208201528582815181106106c6576106c6613f82565b602002602001015181600001906001600160a01b031690816001600160a01b031681525050670de0b6b3a7640000856001600160a01b031663fc57d4df88858151811061071557610715613f82565b60200260200101516040518263ffffffff1660e01b815260040161074891906001600160a01b0391909116815260200190565b602060405180830381865afa158015610765573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610789919061404e565b87848151811061079b5761079b613f82565b60200260200101516001600160a01b031663bbcac5576040518163ffffffff1660e01b8152600401602060405180830381865afa1580156107e0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610804919061404e565b61080e919061407d565b6108189190614094565b6020820152604083015180518291908490811061083757610837613f82565b602002602001018190525080602001518861085291906140b6565b975050600101610697565b506020810195909552509295945050505050565b6108796132d3565b60008290506000836001600160a01b0316635fe3b5676040518163ffffffff1660e01b8152600401602060405180830381865afa1580156108be573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108e29190614031565b604051638e8f294b60e01b81526001600160a01b0384811660048301529192508291829160009182918291851690638e8f294b90602401606060405180830381865afa158015610936573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061095a91906140d9565b9250925092506000896001600160a01b0316636f307dc36040518163ffffffff1660e01b8152600401602060405180830381865afa1580156109a0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109c49190614031565b9050604051806102c00160405280896001600160a01b031681526020018b6001600160a01b031663182df0f56040518163ffffffff1660e01b8152600401602060405180830381865afa158015610a1f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a43919061404e565b815260200160008c6001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610a88573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610aac919061404e565b11610ab8576000610b1a565b8b6001600160a01b031663ae9d70b06040518163ffffffff1660e01b8152600401602060405180830381865afa158015610af6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b1a919061404e565b81526020018b6001600160a01b031663f8f9da286040518163ffffffff1660e01b8152600401602060405180830381865afa158015610b5d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b81919061404e565b81526020018b6001600160a01b031663173b99046040518163ffffffff1660e01b8152600401602060405180830381865afa158015610bc4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610be8919061404e565b81526040516302c3bcbb60e01b81526001600160a01b038b811660048301526020909201918916906302c3bcbb90602401602060405180830381865afa158015610c36573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c5a919061404e565b815260405163252c221960e11b81526001600160a01b038b81166004830152602090920191891690634a58443290602401602060405180830381865afa158015610ca8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ccc919061404e565b81526020018b6001600160a01b03166347bd37186040518163ffffffff1660e01b8152600401602060405180830381865afa158015610d0f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d33919061404e565b81526020018b6001600160a01b0316638f840ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610d76573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d9a919061404e565b81526020018b6001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610ddd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e01919061404e565b81526020018b6001600160a01b0316633b1d21a26040518163ffffffff1660e01b8152600401602060405180830381865afa158015610e44573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e68919061404e565b81526020018515158152602001848152602001838152602001826001600160a01b031681526020018b6001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015610ece573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ef2919061410e565b60ff168152602001826001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015610f38573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f5c919061410e565b60ff168152602001610f6e898b61230c565b8152604051637db94b9560e01b81526001600160a01b038b81166004830152602090920191881690637db94b9590602401602060405180830381865afa158015610fbc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fe0919061404e565b815260405163c5c0542560e01b81526001600160a01b038b8116600483015260209092019188169063c5c0542590602401602060405180830381865afa15801561102e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611052919061404e565b815260405163f45fe69f60e01b81526001600160a01b038b8116600483015260209092019188169063f45fe69f90602401602060405180830381865afa1580156110a0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110c49190614131565b1515815260405163460d60c560e11b81526001600160a01b038b81166004830152602090920191881690638c1ac18a90602401602060405180830381865afa158015611114573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111389190614131565b151590529a9950505050505050505050565b60405163266e0a7f60e01b81526001600160a01b0383811660048301528281166024830152600091859182169063266e0a7f90604401602060405180830381865afa15801561119d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111c19190614031565b95945050505050565b60606000826001600160a01b031663b0772d0b6040518163ffffffff1660e01b8152600401600060405180830381865afa15801561120c573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526112349190810190613f98565b90506000836001600160a01b03166361252fd16040518163ffffffff1660e01b8152600401600060405180830381865afa158015611276573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261129e919081019061414c565b9050600081516001600160401b038111156112bb576112bb613552565b60405190808252806020026020018201604052801561130c57816020015b60408051608081018252600080825260208083018290529282015260608082015282526000199092019101816112d95790505b50905060005b82518110156114d757604080516080810182526000808252602082018190529181019190915260608082015283828151811061135057611350613f82565b60209081029190910101516001600160a01b03168152835184908390811061137a5761137a613f82565b60200260200101516001600160a01b031663f7c618c16040518163ffffffff1660e01b8152600401602060405180830381865afa1580156113bf573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113e39190614031565b6001600160a01b03166020820152835184908390811061140557611405613f82565b6020908102919091010151604051631627ee8960e01b81526001600160a01b038a8116600483015290911690631627ee8990602401602060405180830381865afa158015611457573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061147b919061404e565b8160400181815250506114a8888686858151811061149b5761149b613f82565b60200260200101516123cf565b8160600181905250808383815181106114c3576114c3613f82565b602090810291909101015250600101611312565b5095945050505050565b6060826000816001600160401b038111156114fe576114fe613552565b60405190808252806020026020018201604052801561153757816020015b611524613392565b81526020019060019003908161151c5790505b50905060005b828110156114d75761157587878381811061155a5761155a613f82565b905060200201602081019061156f9190613826565b86611c76565b82828151811061158757611587613f82565b602090810291909101015260010161153d565b60408051808201909152600080825260208201526000826001600160a01b0316635fe3b5676040518163ffffffff1660e01b8152600401602060405180830381865afa1580156115ee573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116129190614031565b90506000816001600160a01b0316637dc0d1d06040518163ffffffff1660e01b8152600401602060405180830381865afa158015611654573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116789190614031565b6040805180820182526001600160a01b03808816808352925163fc57d4df60e01b815260048101939093529293509160208301919084169063fc57d4df90602401602060405180830381865afa1580156116d6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116fa919061404e565b9052949350505050565b61170c6133d1565b6000826040015190506000611780826001600160a01b031663b0772d0b6040518163ffffffff1660e01b8152600401600060405180830381865afa158015611758573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526101529190810190613f98565b6040516328ebbe8b60e21b81526001600160a01b03848116600483015291925060009187169063a3aefa2c90602401600060405180830381865afa1580156117cc573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526117f4919081019061421f565b905082806118006133d1565b875181526020808901516001600160a01b03908116828401528781166040808501919091526060808c0151908501526080808c015190850152865160a08501528683015160c08501528681015160e085015280516307dc0d1d60e41b8152905191861692637dc0d1d0926004808401938290030181865afa158015611889573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118ad9190614031565b8161010001906001600160a01b031690816001600160a01b031681525050826001600160a01b031663e87554466040518163ffffffff1660e01b8152600401602060405180830381865afa158015611909573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061192d919061404e565b81610120018181525050826001600160a01b0316634ada90af6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611975573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611999919061404e565b81610140018181525050826001600160a01b031663db5c65de6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156119e1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a05919061404e565b81610160018181525050816001600160a01b031663d7c46d2d6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611a4d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a719190614031565b8161018001906001600160a01b031690816001600160a01b031681525050816001600160a01b031663f582cffd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611acd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611af19190614131565b15156101a08201526101c081019490945250919695505050505050565b611b166133d1565b604051637aee632d60e01b81526001600160a01b0383811660048301528491611b8d91839190821690637aee632d90602401600060405180830381865afa158015611b65573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526102729190810190614356565b949350505050565b6000806000849050806001600160a01b031663f582cffd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611bdb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611bff9190614131565b604051639dcd31fd60e01b81526001600160a01b038681166004830152831690639dcd31fd90602401602060405180830381865afa158015611c45573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c699190614131565b92509250505b9250929050565b611c7e613392565b6040516370a0823160e01b81526001600160a01b038381166004830152600091908516906370a0823190602401602060405180830381865afa158015611cc8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611cec919061404e565b6040516305eff7ef60e21b81526001600160a01b0385811660048301529192506000918616906317bfdfbc906024016020604051808303816000875af1158015611d3a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d5e919061404e565b604051633af9e66960e01b81526001600160a01b038681166004830152919250600091871690633af9e669906024016020604051808303816000875af1158015611dac573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611dd0919061404e565b90506000806000886001600160a01b0316636f307dc36040518163ffffffff1660e01b8152600401602060405180830381865afa158015611e15573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e399190614031565b6040516370a0823160e01b81526001600160a01b038a81166004830152919250908216906370a0823190602401602060405180830381865afa158015611e83573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ea7919061404e565b604051636eb1769f60e11b81526001600160a01b038a811660048301528b811660248301529194509082169063dd62ed3e90604401602060405180830381865afa158015611ef9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f1d919061404e565b6040805160c0810182526001600160a01b038c168152602081019890985287019590955250506060840191909152608083015260a0820152905092915050565b604051631e6db74760e31b81526001600160a01b038281166004830152606091849182169063f36dba3890602401600060405180830381865afa158015611fa8573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611b8d919081019061438a565b6000611ffe7f000000000000000000000000000000000000000000000000000000000000000063ffffffff16565b905090565b815160609084906000816001600160401b0381111561202457612024613552565b60405190808252806020026020018201604052801561206f57816020015b60408051606081018252600080825260208083018290529282015282526000199092019101816120425790505b50905060005b828110156121d857600087828151811061209157612091613f82565b602002602001015190506040518060600160405280826001600160a01b03168152602001866001600160a01b031663f45fe69f846040518263ffffffff1660e01b81526004016120f091906001600160a01b0391909116815260200190565b602060405180830381865afa15801561210d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121319190614131565b151581526040516327cccc8b60e21b81526001600160a01b0384811660048301528a81166024830152602090920191881690639f33322c90604401602060405180830381865afa158015612189573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121ad9190614131565b15158152508383815181106121c4576121c4613f82565b602090810291909101015250600101612075565b509695505050505050565b60606000826001600160a01b031663d88ff1f46040518163ffffffff1660e01b8152600401600060405180830381865afa158015612225573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261224d9190810190614418565b80519091506000816001600160401b0381111561226c5761226c613552565b6040519080825280602002602001820160405280156122a557816020015b6122926133d1565b81526020019060019003908161228a5790505b50905060005b828110156122fb576122d6868583815181106122c9576122c9613f82565b6020026020010151611704565b8282815181106122e8576122e8613f82565b60209081029190910101526001016122ab565b50949350505050565b4390565b4290565b60008060005b600860ff8216116104f7576000856001600160a01b031663e85a2960868460ff166008811115612344576123446144c8565b6040518363ffffffff1660e01b81526004016123619291906144de565b602060405180830381865afa15801561237e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123a29190614131565b6123ad5760006123b0565b60015b60ff9081169083161b9290921791506123c881614519565b9050612312565b6060600083516001600160401b038111156123ec576123ec613552565b60405190808252806020026020018201604052801561243157816020015b604080518082019091526000808252602082015281526020019060019003908161240a5790505b50905060005b84518110156122fb5761246d604051806060016040528060006001600160e01b0316815260200160008152602001600081525090565b61249a604051806060016040528060006001600160e01b0316815260200160008152602001600081525090565b7f00000000000000000000000000000000000000000000000000000000000000001561261d57856001600160a01b0316638f693ec78885815181106124e1576124e1613f82565b60200260200101516040518263ffffffff1660e01b815260040161251491906001600160a01b0391909116815260200190565b606060405180830381865afa158015612531573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612555919061454f565b604085015260208401526001600160e01b0316825286516001600160a01b0387169063818149459089908690811061258f5761258f613f82565b60200260200101516040518263ffffffff1660e01b81526004016125c291906001600160a01b0391909116815260200190565b606060405180830381865afa1580156125df573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612603919061454f565b604084015260208301526001600160e01b03168152612788565b856001600160a01b0316632c427b5788858151811061263e5761263e613f82565b60200260200101516040518263ffffffff1660e01b815260040161267191906001600160a01b0391909116815260200190565b606060405180830381865afa15801561268e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126b29190614581565b63ffffffff90811660408601521660208401526001600160e01b0316825286516001600160a01b038716906392a18235908990869081106126f5576126f5613f82565b60200260200101516040518263ffffffff1660e01b815260040161272891906001600160a01b0391909116815260200190565b606060405180830381865afa158015612745573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127699190614581565b63ffffffff90811660408501521660208301526001600160e01b031681525b600060405180602001604052808986815181106127a7576127a7613f82565b60200260200101516001600160a01b031663aa5af0fd6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156127ec573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612810919061404e565b815250905061283a88858151811061282a5761282a613f82565b602002602001015188858461292f565b61285e88858151811061284f5761284f613f82565b60200260200101518884612b30565b600061288689868151811061287557612875613f82565b6020026020010151898c8786612d27565b905060006128af8a878151811061289f5761289f613f82565b60200260200101518a8d87612f57565b60408051808201909152600080825260208201529091508a87815181106128d8576128d8613f82565b60209081029190910101516001600160a01b031681526128f882846140b6565b60208201528751819089908990811061291357612913613f82565b6020026020010181905250505050505050806001019050612437565b604051637c05a7c560e01b81526001600160a01b03858116600483015260009190851690637c05a7c590602401602060405180830381865afa158015612979573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061299d919061404e565b905060006129a9611fd0565b9050600084604001511180156129c25750836040015181115b156129ce575060408301515b60006129de82866020015161317b565b90506000811180156129f05750600083115b15612b19576000612a62886001600160a01b03166347bd37186040518163ffffffff1660e01b8152600401602060405180830381865afa158015612a38573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a5c919061404e565b8661318e565b90506000612a7083866131ac565b90506000808311612a905760405180602001604052806000815250612a9a565b612a9a82846131b8565b90506000612ac360405180602001604052808b600001516001600160e01b0316815250836131fd565b9050612afe8160000151604051806040016040528060138152602001726e657720696e646578206f766572666c6f777360681b815250613229565b6001600160e01b031689525050505060208501829052612b27565b8015612b2757602085018290525b50505050505050565b604051631d31307360e21b81526001600160a01b038481166004830152600091908416906374c4c1cc90602401602060405180830381865afa158015612b7a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b9e919061404e565b90506000612baa611fd0565b905060008360400151118015612bc35750826040015181115b15612bcf575060408201515b6000612bdf82856020015161317b565b9050600081118015612bf15750600083115b15612d11576000866001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612c36573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c5a919061404e565b90506000612c6883866131ac565b90506000808311612c885760405180602001604052806000815250612c92565b612c9282846131b8565b90506000612cbb60405180602001604052808a600001516001600160e01b0316815250836131fd565b9050612cf68160000151604051806040016040528060138152602001726e657720696e646578206f766572666c6f777360681b815250613229565b6001600160e01b031688525050505060208401829052612d1f565b8015612d1f57602084018290525b505050505050565b604080516020808201835284516001600160e01b031682528251908101928390526336fe846560e11b9092526001600160a01b0387811660248401528581166044840152600092839181908916636dfd08ca60648301602060405180830381865afa158015612d9a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612dbe919061404e565b90528051909150158015612e405750866001600160a01b031663160c3a036040518163ffffffff1660e01b8152600401602060405180830381865afa158015612e0b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e2f91906145c4565b6001600160e01b0316826000015110155b15612eb357866001600160a01b031663160c3a036040518163ffffffff1660e01b8152600401602060405180830381865afa158015612e83573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ea791906145c4565b6001600160e01b031681525b6000612ebf8383613265565b6040516395dd919360e01b81526001600160a01b038981166004830152919250600091612f3a91908c16906395dd919390602401602060405180830381865afa158015612f10573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f34919061404e565b8761318e565b90506000612f488284613291565b9b9a5050505050505050505050565b604080516020808201835283516001600160e01b0316825282519081019283905263552c097160e01b9092526001600160a01b038681166024840152848116604484015260009283918190881663552c097160648301602060405180830381865afa158015612fca573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612fee919061404e565b905280519091501580156130705750856001600160a01b031663160c3a036040518163ffffffff1660e01b8152600401602060405180830381865afa15801561303b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061305f91906145c4565b6001600160e01b0316826000015110155b156130e357856001600160a01b031663160c3a036040518163ffffffff1660e01b8152600401602060405180830381865afa1580156130b3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130d791906145c4565b6001600160e01b031681525b60006130ef8383613265565b6040516370a0823160e01b81526001600160a01b0388811660048301529192506000918a16906370a0823190602401602060405180830381865afa15801561313b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061315f919061404e565b9050600061316d8284613291565b9a9950505050505050505050565b600061318782846145df565b9392505050565b60006131876131a584670de0b6b3a76400006131ac565b83516132bb565b6000613187828461407d565b60408051602081019091526000815260405180602001604052806131f46131ee866ec097ce7bc90715b34b9f10000000006131ac565b856132bb565b90529392505050565b60408051602081019091526000815260405180602001604052806131f4856000015185600001516132c7565b6000816001600160e01b0384111561325d5760405162461bcd60e51b815260040161325491906145f2565b60405180910390fd5b509192915050565b60408051602081019091526000815260405180602001604052806131f48560000151856000015161317b565b60006ec097ce7bc90715b34b9f10000000006132b18484600001516131ac565b6131879190614094565b60006131878284614094565b600061318782846140b6565b604051806102c0016040528060006001600160a01b0316815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600015158152602001600081526020016000815260200160006001600160a01b0316815260200160008152602001600081526020016000815260200160008152602001600081526020016000151581526020016000151581525090565b6040518060c0016040528060006001600160a01b0316815260200160008152602001600081526020016000815260200160008152602001600081525090565b604051806101e001604052806060815260200160006001600160a01b0316815260200160006001600160a01b03168152602001600081526020016000815260200160608152602001606081526020016060815260200160006001600160a01b0316815260200160008152602001600081526020016000815260200160006001600160a01b03168152602001600015158152602001606081525090565b60008083601f84011261347f57600080fd5b5081356001600160401b0381111561349657600080fd5b6020830191508360208260051b8501011115611c6f57600080fd5b600080602083850312156134c457600080fd5b82356001600160401b038111156134da57600080fd5b6134e68582860161346d565b90969095509350505050565b602080825282518282018190526000919060409081850190868401855b828110156135455761353584835180516001600160a01b03168252602090810151910152565b928401929085019060010161350f565b5091979650505050505050565b634e487b7160e01b600052604160045260246000fd5b60405160a081016001600160401b038111828210171561358a5761358a613552565b60405290565b604051606081016001600160401b038111828210171561358a5761358a613552565b604051601f8201601f191681016001600160401b03811182821017156135da576135da613552565b604052919050565b60006001600160401b038211156135fb576135fb613552565b5060051b60200190565b6001600160a01b038116811461361a57600080fd5b50565b600082601f83011261362e57600080fd5b8135602061364361363e836135e2565b6135b2565b8083825260208201915060208460051b87010193508684111561366557600080fd5b602086015b848110156121d857803561367d81613605565b835291830191830161366a565b60006020828403121561369c57600080fd5b81356001600160401b038111156136b257600080fd5b611b8d8482850161361d565b80516001600160a01b031682526020810151602083015260408101516040830152606081015160608301526080810151608083015260a081015160a083015260c081015160c083015260e081015160e0830152610100808201518184015250610120808201518184015250610140808201518184015250610160808201516137498285018215159052565b505061018081810151908301526101a080820151908301526101c0808201516001600160a01b0316908301526101e080820151908301526102008082015190830152610220808201519083015261024080820151908301526102608082015190830152610280808201511515908301526102a0908101511515910152565b6020808252825182820181905260009190848201906040850190845b8181101561380a576137f68385516136be565b928401926102c092909201916001016137e3565b50909695505050505050565b803561382181613605565b919050565b60006020828403121561383857600080fd5b813561318781613605565b602080825282516001600160a01b03168282015282810151604080840191909152808401516060808501528051608085018190526000939291830191849160a08701905b808410156138c1576138ad82865180516001600160a01b03168252602090810151910152565b938501936001939093019290820190613887565b50979650505050505050565b6102c0810161044482846136be565b6000806000606084860312156138f157600080fd5b83356138fc81613605565b9250602084013561390c81613605565b9150604084013561391c81613605565b809150509250925092565b6000806040838503121561393a57600080fd5b823561394581613605565b9150602083013561395581613605565b809150509250929050565b600060208083018184528085518083526040925060408601915060408160051b8701018488016000805b84811015613a2d57898403603f19018652825180516001600160a01b03908116865289820151168986015287810151888601526060908101516080918601829052805191860182905289019060a086019084905b80821015613a1857613a0483855180516001600160a01b03168252602090810151910152565b928b0192918a0191600191909101906139de565b5050968901969450509187019160010161398a565b50919998505050505050505050565b600080600060408486031215613a5157600080fd5b83356001600160401b03811115613a6757600080fd5b613a738682870161346d565b909450925050602084013561391c81613605565b80516001600160a01b031682526020808201519083015260408082015190830152606080820151908301526080808201519083015260a090810151910152565b6020808252825182820181905260009190848201906040850190845b8181101561380a57613af6838551613a87565b9284019260c09290920191600101613ae3565b81516001600160a01b031681526020808301519082015260408101610444565b60006001600160401b03821115613b4257613b42613552565b50601f01601f191660200190565b60008060408385031215613b6357600080fd5b8235613b6e81613605565b91506020838101356001600160401b0380821115613b8b57600080fd5b9085019060a08288031215613b9f57600080fd5b613ba7613568565b823582811115613bb657600080fd5b83019150601f82018813613bc957600080fd5b8135613bd761363e82613b29565b8181528986838601011115613beb57600080fd5b818685018783013760008683830101528083525050613c0b848401613816565b84820152613c1b60408401613816565b60408201526060830135606082015260808301356080820152809450505050509250929050565b60005b83811015613c5d578181015183820152602001613c45565b50506000910152565b60008151808452613c7e816020860160208601613c42565b601f01601f19169290920160200192915050565b60008151808452602080850194506020840160005b83811015613cce57613cba8783516136be565b6102c0969096019590820190600101613ca7565b509495945050505050565b60006101e08251818552613cef82860182613c66565b9150506020830151613d0c60208601826001600160a01b03169052565b506040830151613d2760408601826001600160a01b03169052565b50606083015160608501526080830151608085015260a083015184820360a0860152613d538282613c66565b91505060c083015184820360c0860152613d6d8282613c66565b91505060e083015184820360e0860152613d878282613c66565b91505061010080840151613da5828701826001600160a01b03169052565b5050610120838101519085015261014080840151908501526101608084015190850152610180808401516001600160a01b0316908501526101a0808401511515908501526101c08084015185830382870152613e018382613c92565b9695505050505050565b6020815260006131876020830184613cd9565b60c081016104448284613a87565b6020808252825182820181905260009190848201906040850190845b8181101561380a5783516001600160a01b031683529284019291840191600101613e48565b600080600060608486031215613e8257600080fd5b8335613e8d81613605565b925060208401356001600160401b03811115613ea857600080fd5b613eb48682870161361d565b925050604084013561391c81613605565b602080825282518282018190526000919060409081850190868401855b8281101561354557815180516001600160a01b031685528681015115158786015285015115158585015260609093019290850190600101613ee2565b600060208083016020845280855180835260408601915060408160051b87010192506020870160005b82811015613f7557603f19888603018452613f63858351613cd9565b94509285019290850190600101613f47565b5092979650505050505050565b634e487b7160e01b600052603260045260246000fd5b60006020808385031215613fab57600080fd5b82516001600160401b03811115613fc157600080fd5b8301601f81018513613fd257600080fd5b8051613fe061363e826135e2565b81815260059190911b82018301908381019087831115613fff57600080fd5b928401925b8284101561402657835161401781613605565b82529284019290840190614004565b979650505050505050565b60006020828403121561404357600080fd5b815161318781613605565b60006020828403121561406057600080fd5b5051919050565b634e487b7160e01b600052601160045260246000fd5b808202811582820484141761044457610444614067565b6000826140b157634e487b7160e01b600052601260045260246000fd5b500490565b8082018082111561044457610444614067565b8051801515811461382157600080fd5b6000806000606084860312156140ee57600080fd5b6140f7846140c9565b925060208401519150604084015190509250925092565b60006020828403121561412057600080fd5b815160ff8116811461318757600080fd5b60006020828403121561414357600080fd5b613187826140c9565b6000602080838503121561415f57600080fd5b82516001600160401b0381111561417557600080fd5b8301601f8101851361418657600080fd5b805161419461363e826135e2565b81815260059190911b820183019083810190878311156141b357600080fd5b928401925b828410156140265783516141cb81613605565b825292840192908401906141b8565b600082601f8301126141eb57600080fd5b81516141f961363e82613b29565b81815284602083860101111561420e57600080fd5b611b8d826020830160208701613c42565b60006020828403121561423157600080fd5b81516001600160401b038082111561424857600080fd5b908301906060828603121561425c57600080fd5b614264613590565b82518281111561427357600080fd5b61427f878286016141da565b82525060208301518281111561429457600080fd5b6142a0878286016141da565b6020830152506040830151828111156142b857600080fd5b6142c4878286016141da565b60408301525095945050505050565b600060a082840312156142e557600080fd5b6142ed613568565b905081516001600160401b0381111561430557600080fd5b614311848285016141da565b825250602082015161432281613605565b6020820152604082015161433581613605565b80604083015250606082015160608201526080820151608082015292915050565b60006020828403121561436857600080fd5b81516001600160401b0381111561437e57600080fd5b611b8d848285016142d3565b6000602080838503121561439d57600080fd5b82516001600160401b038111156143b357600080fd5b8301601f810185136143c457600080fd5b80516143d261363e826135e2565b81815260059190911b820183019083810190878311156143f157600080fd5b928401925b8284101561402657835161440981613605565b825292840192908401906143f6565b6000602080838503121561442b57600080fd5b82516001600160401b038082111561444257600080fd5b818501915085601f83011261445657600080fd5b815161446461363e826135e2565b81815260059190911b8301840190848101908883111561448357600080fd5b8585015b838110156144bb5780518581111561449f5760008081fd5b6144ad8b89838a01016142d3565b845250918601918601614487565b5098975050505050505050565b634e487b7160e01b600052602160045260246000fd5b6001600160a01b0383168152604081016009831061450c57634e487b7160e01b600052602160045260246000fd5b8260208301529392505050565b600060ff821660ff810361452f5761452f614067565b60010192915050565b80516001600160e01b038116811461382157600080fd5b60008060006060848603121561456457600080fd5b6140f784614538565b805163ffffffff8116811461382157600080fd5b60008060006060848603121561459657600080fd5b61459f84614538565b92506145ad6020850161456d565b91506145bb6040850161456d565b90509250925092565b6000602082840312156145d657600080fd5b61318782614538565b8181038181111561044457610444614067565b6020815260006131876020830184613c6656fea2646970667358221220e61005757d8e238ed335801966fbabcdd38a6aad1e822b630146d8f96d65d41164736f6c63430008190033", + "devdoc": { + "author": "Venus", + "details": "Spoke pools expose additional state that is not included in the shared PoolLens data, including the deviation-bounded oracle, the supply and liquidation allowlists, liquidation thresholds and liquidation incentives. Those reads are named `spoke*` or `getSpokePool*` and return spoke-shaped structs. The rest of the surface mirrors `PoolLens` under the same names and struct shapes, so that reading a spoke pool takes one address rather than two. Those reads go through getters both kinds of pool share. Access-controlled call permissions are deliberately not reported: `AccessControlManager` derives the role from its own caller, so asked from a lens it answers `false` for an account that can in fact call. This contract holds no state and is versioned by redeployment.", + "kind": "dev", + "methods": { + "constructor": { + "custom:oz-upgrades-unsafe-allow": "constructor", + "params": { + "blocksPerYear_": "The number of blocks per year", + "timeBased_": "A boolean indicating whether the contract is based on time or block" + } + }, + "getAllSpokePools(address)": { + "details": "Not intended to be called in a transaction due to its gas cost. The registry must contain only spoke pools.", + "params": { + "poolRegistryAddress": "The registry to enumerate" + }, + "returns": { + "_0": "The data of every pool in the registry" + } + }, + "getBlockNumberOrTimestamp()": { + "details": "Function to simply retrieve block number or block timestamp", + "returns": { + "_0": "Current block number or block timestamp" + } + }, + "getPendingRewards(address,address)": { + "params": { + "account": "The user account.", + "comptrollerAddress": "address" + }, + "returns": { + "_0": "Pending rewards array" + } + }, + "getPoolBadDebt(address)": { + "params": { + "comptrollerAddress": "Address of the comptroller" + }, + "returns": { + "_0": "badDebtSummary A struct with comptroller address, total bad debut denominated in usd, and a break down of bad debt by market" + } + }, + "getPoolsSupportedByAsset(address,address)": { + "params": { + "asset": "The underlying asset of vToken", + "poolRegistryAddress": "The address of the PoolRegistry contract" + }, + "returns": { + "_0": "A list of Comptroller contracts" + } + }, + "getSpokePoolByComptroller(address,address)": { + "params": { + "comptroller": "The pool's comptroller", + "poolRegistryAddress": "The registry containing the pool" + }, + "returns": { + "_0": "The pool's data" + } + }, + "getSpokePoolData(address,(string,address,address,uint256,uint256))": { + "params": { + "poolRegistryAddress": "The registry containing the pool", + "venusPool": "The pool's registry entry" + }, + "returns": { + "_0": "The pool's data, including its markets" + } + }, + "getVTokenForAsset(address,address,address)": { + "params": { + "asset": "The underlyingAsset of VToken", + "comptroller": "The pool comptroller", + "poolRegistryAddress": "The address of the PoolRegistry contract" + }, + "returns": { + "_0": "Address of the vToken" + } + }, + "spokeLiquidationPermission(address,address)": { + "params": { + "comptroller": "The spoke pool's comptroller", + "liquidator": "The account to test" + }, + "returns": { + "accountAllowlisted": "Whether the account is allowlisted", + "allowlistEnabled": "Whether liquidation is restricted to allowlisted accounts" + } + }, + "spokeSupplyPermissions(address,address[],address)": { + "details": "The account checked is the one receiving the minted vTokens, which can differ from the payer when using `mintBehalf`.", + "params": { + "account": "The account to test", + "comptroller": "The spoke pool's comptroller", + "vTokens": "The markets to test" + }, + "returns": { + "_0": "One entry per market, in the order given" + } + }, + "spokeVTokenMetadata(address)": { + "params": { + "vToken": "The market to read" + }, + "returns": { + "_0": "The market's metadata" + } + }, + "spokeVTokenMetadataAll(address[])": { + "params": { + "vTokens": "The markets to read" + }, + "returns": { + "_0": "One entry per market, in the order given" + } + }, + "vTokenBalances(address,address)": { + "params": { + "account": "The user Account", + "vToken": "vToken address" + }, + "returns": { + "_0": "A struct containing the balances data" + } + }, + "vTokenBalancesAll(address[],address)": { + "params": { + "account": "The user Account", + "vTokens": "The list of vToken addresses" + }, + "returns": { + "_0": "A list of structs containing balances data" + } + }, + "vTokenUnderlyingPrice(address)": { + "params": { + "vToken": "vToken address" + }, + "returns": { + "_0": "The price data for each asset" + } + }, + "vTokenUnderlyingPriceAll(address[])": { + "params": { + "vTokens": "The list of vToken addresses" + }, + "returns": { + "_0": "An array containing the price data for each asset" + } + } + }, + "title": "SpokePoolLens", + "version": 1 + }, + "userdoc": { + "errors": { + "InvalidBlocksPerYear()": [ + { + "notice": "Thrown when blocks per year is invalid" + } + ], + "InvalidTimeBasedConfiguration()": [ + { + "notice": "Thrown when time based but blocks per year is provided" + } + ] + }, + "kind": "user", + "methods": { + "blocksOrSecondsPerYear()": { + "notice": "Stores blocksPerYear if isTimeBased is true else secondsPerYear is stored" + }, + "getAllSpokePools(address)": { + "notice": "Queries every pool and market in a spoke pool registry" + }, + "getPendingRewards(address,address)": { + "notice": "Returns the pending rewards for a user for a given pool." + }, + "getPoolBadDebt(address)": { + "notice": "Returns a summary of a pool's bad debt broken down by market" + }, + "getPoolsSupportedByAsset(address,address)": { + "notice": "Returns all pools that support the specified underlying asset" + }, + "getSpokePoolByComptroller(address,address)": { + "notice": "Queries a spoke pool by its comptroller address" + }, + "getSpokePoolData(address,(string,address,address,uint256,uint256))": { + "notice": "Queries a spoke pool from its registry entry" + }, + "getVTokenForAsset(address,address,address)": { + "notice": "Returns vToken holding the specified underlying asset in the specified pool" + }, + "isTimeBased()": { + "notice": "Acknowledges if a contract is time based or not" + }, + "spokeLiquidationPermission(address,address)": { + "notice": "Returns whether an account may seize collateral in a spoke pool" + }, + "spokeSupplyPermissions(address,address[],address)": { + "notice": "Returns whether an account may supply to each specified market" + }, + "spokeVTokenMetadata(address)": { + "notice": "Queries the metadata of one market of a spoke pool" + }, + "spokeVTokenMetadataAll(address[])": { + "notice": "Queries the metadata of every given market of a spoke pool" + }, + "vTokenBalances(address,address)": { + "notice": "Queries the user's supply/borrow balances in the specified vToken" + }, + "vTokenBalancesAll(address[],address)": { + "notice": "Queries the user's supply/borrow balances in vTokens" + }, + "vTokenUnderlyingPrice(address)": { + "notice": "Returns the price data for the underlying asset of the specified vToken" + }, + "vTokenUnderlyingPriceAll(address[])": { + "notice": "Returns the price data for the underlying assets of the specified vTokens" + } + }, + "notice": "Reads pool and market state specific to a spoke pool", + "version": 1 + }, + "storageLayout": { + "storage": [ + { + "astId": 2222, + "contract": "contracts/Lens/SpokePoolLens.sol:SpokePoolLens", + "label": "__gap", + "offset": 0, + "slot": "0", + "type": "t_array(t_uint256)48_storage" + } + ], + "types": { + "t_array(t_uint256)48_storage": { + "base": "t_uint256", + "encoding": "inplace", + "label": "uint256[48]", + "numberOfBytes": "1536" + }, + "t_uint256": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + } + } + } +} diff --git a/deployments/bsctestnet/SpokePoolRegistry.json b/deployments/bsctestnet/SpokePoolRegistry.json new file mode 100644 index 000000000..83939dd96 --- /dev/null +++ b/deployments/bsctestnet/SpokePoolRegistry.json @@ -0,0 +1,940 @@ +{ + "address": "0xeAA45288d804971e5a76f33559e629F5b2b1Cb8B", + "abi": [ + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "previousAdmin", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "newAdmin", + "type": "address" + } + ], + "name": "AdminChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "beacon", + "type": "address" + } + ], + "name": "BeaconUpgraded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "implementation", + "type": "address" + } + ], + "name": "Upgraded", + "type": "event" + }, + { + "stateMutability": "payable", + "type": "fallback" + }, + { + "inputs": [], + "name": "admin", + "outputs": [ + { + "internalType": "address", + "name": "admin_", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "implementation", + "outputs": [ + { + "internalType": "address", + "name": "implementation_", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newImplementation", + "type": "address" + } + ], + "name": "upgradeTo", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newImplementation", + "type": "address" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "upgradeToAndCall", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "stateMutability": "payable", + "type": "receive" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "address", + "name": "calledContract", + "type": "address" + }, + { + "internalType": "string", + "name": "methodSignature", + "type": "string" + } + ], + "name": "Unauthorized", + "type": "error" + }, + { + "inputs": [], + "name": "ZeroAddressNotAllowed", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint8", + "name": "version", + "type": "uint8" + } + ], + "name": "Initialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "comptroller", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "vTokenAddress", + "type": "address" + } + ], + "name": "MarketAdded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "oldAccessControlManager", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "newAccessControlManager", + "type": "address" + } + ], + "name": "NewAccessControlManager", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferStarted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "comptroller", + "type": "address" + }, + { + "components": [ + { + "internalType": "string", + "name": "category", + "type": "string" + }, + { + "internalType": "string", + "name": "logoURL", + "type": "string" + }, + { + "internalType": "string", + "name": "description", + "type": "string" + } + ], + "indexed": false, + "internalType": "struct PoolRegistryInterface.VenusPoolMetaData", + "name": "oldMetadata", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "string", + "name": "category", + "type": "string" + }, + { + "internalType": "string", + "name": "logoURL", + "type": "string" + }, + { + "internalType": "string", + "name": "description", + "type": "string" + } + ], + "indexed": false, + "internalType": "struct PoolRegistryInterface.VenusPoolMetaData", + "name": "newMetadata", + "type": "tuple" + } + ], + "name": "PoolMetadataUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "comptroller", + "type": "address" + }, + { + "indexed": false, + "internalType": "string", + "name": "oldName", + "type": "string" + }, + { + "indexed": false, + "internalType": "string", + "name": "newName", + "type": "string" + } + ], + "name": "PoolNameSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "comptroller", + "type": "address" + }, + { + "components": [ + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "internalType": "address", + "name": "creator", + "type": "address" + }, + { + "internalType": "address", + "name": "comptroller", + "type": "address" + }, + { + "internalType": "uint256", + "name": "blockPosted", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "timestampPosted", + "type": "uint256" + } + ], + "indexed": false, + "internalType": "struct PoolRegistryInterface.VenusPool", + "name": "pool", + "type": "tuple" + } + ], + "name": "PoolRegistered", + "type": "event" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "accessControlManager", + "outputs": [ + { + "internalType": "contract IAccessControlManagerV8", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "contract VToken", + "name": "vToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "collateralFactor", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "liquidationThreshold", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "initialSupply", + "type": "uint256" + }, + { + "internalType": "address", + "name": "vTokenReceiver", + "type": "address" + }, + { + "internalType": "uint256", + "name": "supplyCap", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "borrowCap", + "type": "uint256" + } + ], + "internalType": "struct PoolRegistry.AddMarketInput", + "name": "input", + "type": "tuple" + } + ], + "name": "addMarket", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "internalType": "contract Comptroller", + "name": "comptroller", + "type": "address" + }, + { + "internalType": "uint256", + "name": "closeFactor", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "liquidationIncentive", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minLiquidatableCollateral", + "type": "uint256" + } + ], + "name": "addPool", + "outputs": [ + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "getAllPools", + "outputs": [ + { + "components": [ + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "internalType": "address", + "name": "creator", + "type": "address" + }, + { + "internalType": "address", + "name": "comptroller", + "type": "address" + }, + { + "internalType": "uint256", + "name": "blockPosted", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "timestampPosted", + "type": "uint256" + } + ], + "internalType": "struct PoolRegistryInterface.VenusPool[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "comptroller", + "type": "address" + } + ], + "name": "getPoolByComptroller", + "outputs": [ + { + "components": [ + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "internalType": "address", + "name": "creator", + "type": "address" + }, + { + "internalType": "address", + "name": "comptroller", + "type": "address" + }, + { + "internalType": "uint256", + "name": "blockPosted", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "timestampPosted", + "type": "uint256" + } + ], + "internalType": "struct PoolRegistryInterface.VenusPool", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "asset", + "type": "address" + } + ], + "name": "getPoolsSupportedByAsset", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "comptroller", + "type": "address" + }, + { + "internalType": "address", + "name": "asset", + "type": "address" + } + ], + "name": "getVTokenForAsset", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "comptroller", + "type": "address" + } + ], + "name": "getVenusPoolMetadata", + "outputs": [ + { + "components": [ + { + "internalType": "string", + "name": "category", + "type": "string" + }, + { + "internalType": "string", + "name": "logoURL", + "type": "string" + }, + { + "internalType": "string", + "name": "description", + "type": "string" + } + ], + "internalType": "struct PoolRegistryInterface.VenusPoolMetaData", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "accessControlManager_", + "type": "address" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "metadata", + "outputs": [ + { + "internalType": "string", + "name": "category", + "type": "string" + }, + { + "internalType": "string", + "name": "logoURL", + "type": "string" + }, + { + "internalType": "string", + "name": "description", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pendingOwner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "accessControlManager_", + "type": "address" + } + ], + "name": "setAccessControlManager", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "comptroller", + "type": "address" + }, + { + "internalType": "string", + "name": "name", + "type": "string" + } + ], + "name": "setPoolName", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "comptroller", + "type": "address" + }, + { + "components": [ + { + "internalType": "string", + "name": "category", + "type": "string" + }, + { + "internalType": "string", + "name": "logoURL", + "type": "string" + }, + { + "internalType": "string", + "name": "description", + "type": "string" + } + ], + "internalType": "struct PoolRegistryInterface.VenusPoolMetaData", + "name": "metadata_", + "type": "tuple" + } + ], + "name": "updatePoolMetadata", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_logic", + "type": "address" + }, + { + "internalType": "address", + "name": "admin_", + "type": "address" + }, + { + "internalType": "bytes", + "name": "_data", + "type": "bytes" + } + ], + "stateMutability": "payable", + "type": "constructor" + } + ], + "transactionHash": "0x091063dfd1125f31dd14d7c31f494d352edd25c3f48f01869463f2ffe0624d9c", + "receipt": { + "to": null, + "from": "0x33C6476F88eeA28D7E7900F759B4597704Ef95B7", + "contractAddress": "0xeAA45288d804971e5a76f33559e629F5b2b1Cb8B", + "transactionIndex": 4, + "gasUsed": "605368", + "logsBloom": "0x00000000000000000000000000000000400000000000000000800000000000000004000000040000000000000000000000000000000000000000000000008000000000000000000000000000000002000001000002000000000000000000000000000000020000000000000000000800000000800000000000000000000000400000000000000000000000400000000010000000000080008000000000800000000000000040000000000000000400000000000000800000000000000000000000000020200000000000000000040000000000000400000000000000000020000000000000000000000000000000000000000800000200000000000000000000", + "blockHash": "0x1327761aa714fe841718f40431971a18c5f2a29187e3311b3ac4dca1092e7c61", + "transactionHash": "0x091063dfd1125f31dd14d7c31f494d352edd25c3f48f01869463f2ffe0624d9c", + "logs": [ + { + "transactionIndex": 4, + "blockNumber": 129844919, + "transactionHash": "0x091063dfd1125f31dd14d7c31f494d352edd25c3f48f01869463f2ffe0624d9c", + "address": "0xeAA45288d804971e5a76f33559e629F5b2b1Cb8B", + "topics": [ + "0xbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b", + "0x000000000000000000000000266e65b6d81802f2bafa0c07288a62891172dad8" + ], + "data": "0x", + "logIndex": 53, + "blockHash": "0x1327761aa714fe841718f40431971a18c5f2a29187e3311b3ac4dca1092e7c61" + }, + { + "transactionIndex": 4, + "blockNumber": 129844919, + "transactionHash": "0x091063dfd1125f31dd14d7c31f494d352edd25c3f48f01869463f2ffe0624d9c", + "address": "0xeAA45288d804971e5a76f33559e629F5b2b1Cb8B", + "topics": [ + "0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x00000000000000000000000033c6476f88eea28d7e7900f759b4597704ef95b7" + ], + "data": "0x", + "logIndex": 54, + "blockHash": "0x1327761aa714fe841718f40431971a18c5f2a29187e3311b3ac4dca1092e7c61" + }, + { + "transactionIndex": 4, + "blockNumber": 129844919, + "transactionHash": "0x091063dfd1125f31dd14d7c31f494d352edd25c3f48f01869463f2ffe0624d9c", + "address": "0xeAA45288d804971e5a76f33559e629F5b2b1Cb8B", + "topics": ["0x66fd58e82f7b31a2a5c30e0888f3093efe4e111b00cd2b0c31fe014601293aa0"], + "data": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000045f8a08f534f34a97187626e05d4b6648eeaa9aa", + "logIndex": 55, + "blockHash": "0x1327761aa714fe841718f40431971a18c5f2a29187e3311b3ac4dca1092e7c61" + }, + { + "transactionIndex": 4, + "blockNumber": 129844919, + "transactionHash": "0x091063dfd1125f31dd14d7c31f494d352edd25c3f48f01869463f2ffe0624d9c", + "address": "0xeAA45288d804971e5a76f33559e629F5b2b1Cb8B", + "topics": ["0x7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498"], + "data": "0x0000000000000000000000000000000000000000000000000000000000000001", + "logIndex": 56, + "blockHash": "0x1327761aa714fe841718f40431971a18c5f2a29187e3311b3ac4dca1092e7c61" + }, + { + "transactionIndex": 4, + "blockNumber": 129844919, + "transactionHash": "0x091063dfd1125f31dd14d7c31f494d352edd25c3f48f01869463f2ffe0624d9c", + "address": "0xeAA45288d804971e5a76f33559e629F5b2b1Cb8B", + "topics": ["0x7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f"], + "data": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000007877ffd62649b6a1557b55d4c20fcbab17344c91", + "logIndex": 57, + "blockHash": "0x1327761aa714fe841718f40431971a18c5f2a29187e3311b3ac4dca1092e7c61" + } + ], + "blockNumber": 129844919, + "cumulativeGasUsed": "6713259", + "status": 1, + "byzantium": true + }, + "args": [ + "0x266e65b6D81802F2BAFa0c07288A62891172DAD8", + "0x7877ffd62649b6a1557b55d4c20fcbab17344c91", + "0xc4d66de800000000000000000000000045f8a08f534f34a97187626e05d4b6648eeaa9aa" + ], + "numDeployments": 1, + "solcInputHash": "02cfe3974aa10474c22406a3f2870e89", + "metadata": "{\"compiler\":{\"version\":\"0.8.25+commit.b61c2a91\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_logic\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"admin_\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"_data\",\"type\":\"bytes\"}],\"stateMutability\":\"payable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"previousAdmin\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"newAdmin\",\"type\":\"address\"}],\"name\":\"AdminChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"beacon\",\"type\":\"address\"}],\"name\":\"BeaconUpgraded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"implementation\",\"type\":\"address\"}],\"name\":\"Upgraded\",\"type\":\"event\"},{\"stateMutability\":\"payable\",\"type\":\"fallback\"},{\"inputs\":[],\"name\":\"admin\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"admin_\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"implementation\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"implementation_\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newImplementation\",\"type\":\"address\"}],\"name\":\"upgradeTo\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newImplementation\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"upgradeToAndCall\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"stateMutability\":\"payable\",\"type\":\"receive\"}],\"devdoc\":{\"details\":\"This contract implements a proxy that is upgradeable by an admin. To avoid https://medium.com/nomic-labs-blog/malicious-backdoors-in-ethereum-proxies-62629adf3357[proxy selector clashing], which can potentially be used in an attack, this contract uses the https://blog.openzeppelin.com/the-transparent-proxy-pattern/[transparent proxy pattern]. This pattern implies two things that go hand in hand: 1. If any account other than the admin calls the proxy, the call will be forwarded to the implementation, even if that call matches one of the admin functions exposed by the proxy itself. 2. If the admin calls the proxy, it can access the admin functions, but its calls will never be forwarded to the implementation. If the admin tries to call a function on the implementation it will fail with an error that says \\\"admin cannot fallback to proxy target\\\". These properties mean that the admin account can only be used for admin actions like upgrading the proxy or changing the admin, so it's best if it's a dedicated account that is not used for anything else. This will avoid headaches due to sudden errors when trying to call a function from the proxy implementation. Our recommendation is for the dedicated account to be an instance of the {ProxyAdmin} contract. If set up this way, you should think of the `ProxyAdmin` instance as the real administrative interface of your proxy.\",\"events\":{\"AdminChanged(address,address)\":{\"details\":\"Emitted when the admin account has changed.\"},\"BeaconUpgraded(address)\":{\"details\":\"Emitted when the beacon is upgraded.\"},\"Upgraded(address)\":{\"details\":\"Emitted when the implementation is upgraded.\"}},\"kind\":\"dev\",\"methods\":{\"admin()\":{\"details\":\"Returns the current admin. NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyAdmin}. TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call. `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103`\"},\"constructor\":{\"details\":\"Initializes an upgradeable proxy managed by `_admin`, backed by the implementation at `_logic`, and optionally initialized with `_data` as explained in {ERC1967Proxy-constructor}.\"},\"implementation()\":{\"details\":\"Returns the current implementation. NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyImplementation}. TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call. `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc`\"},\"upgradeTo(address)\":{\"details\":\"Upgrade the implementation of the proxy. NOTE: Only the admin can call this function. See {ProxyAdmin-upgrade}.\"},\"upgradeToAndCall(address,bytes)\":{\"details\":\"Upgrade the implementation of the proxy, and then call a function from the new implementation as specified by `data`, which should be an encoded function call. This is useful to initialize new storage variables in the proxied contract. NOTE: Only the admin can call this function. See {ProxyAdmin-upgradeAndCall}.\"}},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"hardhat-deploy/solc_0.8/proxy/OptimizedTransparentUpgradeableProxy.sol\":\"OptimizedTransparentUpgradeableProxy\"},\"evmVersion\":\"paris\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\",\"useLiteralContent\":true},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[]},\"sources\":{\"hardhat-deploy/solc_0.8/openzeppelin/interfaces/draft-IERC1822.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts (last updated v4.5.0-rc.0) (interfaces/draft-IERC1822.sol)\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev ERC1822: Universal Upgradeable Proxy Standard (UUPS) documents a method for upgradeability through a simplified\\n * proxy whose upgrades are fully controlled by the current implementation.\\n */\\ninterface IERC1822Proxiable {\\n /**\\n * @dev Returns the storage slot that the proxiable contract assumes is being used to store the implementation\\n * address.\\n *\\n * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks\\n * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this\\n * function revert if invoked through a proxy.\\n */\\n function proxiableUUID() external view returns (bytes32);\\n}\\n\",\"keccak256\":\"0x93b4e21c931252739a1ec13ea31d3d35a5c068be3163ccab83e4d70c40355f03\",\"license\":\"MIT\"},\"hardhat-deploy/solc_0.8/openzeppelin/proxy/ERC1967/ERC1967Proxy.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts v4.4.1 (proxy/ERC1967/ERC1967Proxy.sol)\\n\\npragma solidity ^0.8.0;\\n\\nimport \\\"../Proxy.sol\\\";\\nimport \\\"./ERC1967Upgrade.sol\\\";\\n\\n/**\\n * @dev This contract implements an upgradeable proxy. It is upgradeable because calls are delegated to an\\n * implementation address that can be changed. This address is stored in storage in the location specified by\\n * https://eips.ethereum.org/EIPS/eip-1967[EIP1967], so that it doesn't conflict with the storage layout of the\\n * implementation behind the proxy.\\n */\\ncontract ERC1967Proxy is Proxy, ERC1967Upgrade {\\n /**\\n * @dev Initializes the upgradeable proxy with an initial implementation specified by `_logic`.\\n *\\n * If `_data` is nonempty, it's used as data in a delegate call to `_logic`. This will typically be an encoded\\n * function call, and allows initializating the storage of the proxy like a Solidity constructor.\\n */\\n constructor(address _logic, bytes memory _data) payable {\\n assert(_IMPLEMENTATION_SLOT == bytes32(uint256(keccak256(\\\"eip1967.proxy.implementation\\\")) - 1));\\n _upgradeToAndCall(_logic, _data, false);\\n }\\n\\n /**\\n * @dev Returns the current implementation address.\\n */\\n function _implementation() internal view virtual override returns (address impl) {\\n return ERC1967Upgrade._getImplementation();\\n }\\n}\\n\",\"keccak256\":\"0x6309f9f39dc6f4f45a24f296543867aa358e32946cd6b2874627a996d606b3a0\",\"license\":\"MIT\"},\"hardhat-deploy/solc_0.8/openzeppelin/proxy/ERC1967/ERC1967Upgrade.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts (last updated v4.5.0-rc.0) (proxy/ERC1967/ERC1967Upgrade.sol)\\n\\npragma solidity ^0.8.2;\\n\\nimport \\\"../beacon/IBeacon.sol\\\";\\nimport \\\"../../interfaces/draft-IERC1822.sol\\\";\\nimport \\\"../../utils/Address.sol\\\";\\nimport \\\"../../utils/StorageSlot.sol\\\";\\n\\n/**\\n * @dev This abstract contract provides getters and event emitting update functions for\\n * https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots.\\n *\\n * _Available since v4.1._\\n *\\n * @custom:oz-upgrades-unsafe-allow delegatecall\\n */\\nabstract contract ERC1967Upgrade {\\n // This is the keccak-256 hash of \\\"eip1967.proxy.rollback\\\" subtracted by 1\\n bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143;\\n\\n /**\\n * @dev Storage slot with the address of the current implementation.\\n * This is the keccak-256 hash of \\\"eip1967.proxy.implementation\\\" subtracted by 1, and is\\n * validated in the constructor.\\n */\\n bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;\\n\\n /**\\n * @dev Emitted when the implementation is upgraded.\\n */\\n event Upgraded(address indexed implementation);\\n\\n /**\\n * @dev Returns the current implementation address.\\n */\\n function _getImplementation() internal view returns (address) {\\n return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;\\n }\\n\\n /**\\n * @dev Stores a new address in the EIP1967 implementation slot.\\n */\\n function _setImplementation(address newImplementation) private {\\n require(Address.isContract(newImplementation), \\\"ERC1967: new implementation is not a contract\\\");\\n StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;\\n }\\n\\n /**\\n * @dev Perform implementation upgrade\\n *\\n * Emits an {Upgraded} event.\\n */\\n function _upgradeTo(address newImplementation) internal {\\n _setImplementation(newImplementation);\\n emit Upgraded(newImplementation);\\n }\\n\\n /**\\n * @dev Perform implementation upgrade with additional setup call.\\n *\\n * Emits an {Upgraded} event.\\n */\\n function _upgradeToAndCall(\\n address newImplementation,\\n bytes memory data,\\n bool forceCall\\n ) internal {\\n _upgradeTo(newImplementation);\\n if (data.length > 0 || forceCall) {\\n Address.functionDelegateCall(newImplementation, data);\\n }\\n }\\n\\n /**\\n * @dev Perform implementation upgrade with security checks for UUPS proxies, and additional setup call.\\n *\\n * Emits an {Upgraded} event.\\n */\\n function _upgradeToAndCallUUPS(\\n address newImplementation,\\n bytes memory data,\\n bool forceCall\\n ) internal {\\n // Upgrades from old implementations will perform a rollback test. This test requires the new\\n // implementation to upgrade back to the old, non-ERC1822 compliant, implementation. Removing\\n // this special case will break upgrade paths from old UUPS implementation to new ones.\\n if (StorageSlot.getBooleanSlot(_ROLLBACK_SLOT).value) {\\n _setImplementation(newImplementation);\\n } else {\\n try IERC1822Proxiable(newImplementation).proxiableUUID() returns (bytes32 slot) {\\n require(slot == _IMPLEMENTATION_SLOT, \\\"ERC1967Upgrade: unsupported proxiableUUID\\\");\\n } catch {\\n revert(\\\"ERC1967Upgrade: new implementation is not UUPS\\\");\\n }\\n _upgradeToAndCall(newImplementation, data, forceCall);\\n }\\n }\\n\\n /**\\n * @dev Storage slot with the admin of the contract.\\n * This is the keccak-256 hash of \\\"eip1967.proxy.admin\\\" subtracted by 1, and is\\n * validated in the constructor.\\n */\\n bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;\\n\\n /**\\n * @dev Emitted when the admin account has changed.\\n */\\n event AdminChanged(address previousAdmin, address newAdmin);\\n\\n /**\\n * @dev Returns the current admin.\\n */\\n function _getAdmin() internal view virtual returns (address) {\\n return StorageSlot.getAddressSlot(_ADMIN_SLOT).value;\\n }\\n\\n /**\\n * @dev Stores a new address in the EIP1967 admin slot.\\n */\\n function _setAdmin(address newAdmin) private {\\n require(newAdmin != address(0), \\\"ERC1967: new admin is the zero address\\\");\\n StorageSlot.getAddressSlot(_ADMIN_SLOT).value = newAdmin;\\n }\\n\\n /**\\n * @dev Changes the admin of the proxy.\\n *\\n * Emits an {AdminChanged} event.\\n */\\n function _changeAdmin(address newAdmin) internal {\\n emit AdminChanged(_getAdmin(), newAdmin);\\n _setAdmin(newAdmin);\\n }\\n\\n /**\\n * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.\\n * This is bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)) and is validated in the constructor.\\n */\\n bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;\\n\\n /**\\n * @dev Emitted when the beacon is upgraded.\\n */\\n event BeaconUpgraded(address indexed beacon);\\n\\n /**\\n * @dev Returns the current beacon.\\n */\\n function _getBeacon() internal view returns (address) {\\n return StorageSlot.getAddressSlot(_BEACON_SLOT).value;\\n }\\n\\n /**\\n * @dev Stores a new beacon in the EIP1967 beacon slot.\\n */\\n function _setBeacon(address newBeacon) private {\\n require(Address.isContract(newBeacon), \\\"ERC1967: new beacon is not a contract\\\");\\n require(Address.isContract(IBeacon(newBeacon).implementation()), \\\"ERC1967: beacon implementation is not a contract\\\");\\n StorageSlot.getAddressSlot(_BEACON_SLOT).value = newBeacon;\\n }\\n\\n /**\\n * @dev Perform beacon upgrade with additional setup call. Note: This upgrades the address of the beacon, it does\\n * not upgrade the implementation contained in the beacon (see {UpgradeableBeacon-_setImplementation} for that).\\n *\\n * Emits a {BeaconUpgraded} event.\\n */\\n function _upgradeBeaconToAndCall(\\n address newBeacon,\\n bytes memory data,\\n bool forceCall\\n ) internal {\\n _setBeacon(newBeacon);\\n emit BeaconUpgraded(newBeacon);\\n if (data.length > 0 || forceCall) {\\n Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data);\\n }\\n }\\n}\\n\",\"keccak256\":\"0x17668652127feebed0ce8d9431ef95ccc8c4292f03e3b8cf06c6ca16af396633\",\"license\":\"MIT\"},\"hardhat-deploy/solc_0.8/openzeppelin/proxy/Proxy.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts (last updated v4.5.0-rc.0) (proxy/Proxy.sol)\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM\\n * instruction `delegatecall`. We refer to the second contract as the _implementation_ behind the proxy, and it has to\\n * be specified by overriding the virtual {_implementation} function.\\n *\\n * Additionally, delegation to the implementation can be triggered manually through the {_fallback} function, or to a\\n * different contract through the {_delegate} function.\\n *\\n * The success and return data of the delegated call will be returned back to the caller of the proxy.\\n */\\nabstract contract Proxy {\\n /**\\n * @dev Delegates the current call to `implementation`.\\n *\\n * This function does not return to its internal call site, it will return directly to the external caller.\\n */\\n function _delegate(address implementation) internal virtual {\\n assembly {\\n // Copy msg.data. We take full control of memory in this inline assembly\\n // block because it will not return to Solidity code. We overwrite the\\n // Solidity scratch pad at memory position 0.\\n calldatacopy(0, 0, calldatasize())\\n\\n // Call the implementation.\\n // out and outsize are 0 because we don't know the size yet.\\n let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)\\n\\n // Copy the returned data.\\n returndatacopy(0, 0, returndatasize())\\n\\n switch result\\n // delegatecall returns 0 on error.\\n case 0 {\\n revert(0, returndatasize())\\n }\\n default {\\n return(0, returndatasize())\\n }\\n }\\n }\\n\\n /**\\n * @dev This is a virtual function that should be overriden so it returns the address to which the fallback function\\n * and {_fallback} should delegate.\\n */\\n function _implementation() internal view virtual returns (address);\\n\\n /**\\n * @dev Delegates the current call to the address returned by `_implementation()`.\\n *\\n * This function does not return to its internall call site, it will return directly to the external caller.\\n */\\n function _fallback() internal virtual {\\n _beforeFallback();\\n _delegate(_implementation());\\n }\\n\\n /**\\n * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other\\n * function in the contract matches the call data.\\n */\\n fallback() external payable virtual {\\n _fallback();\\n }\\n\\n /**\\n * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if call data\\n * is empty.\\n */\\n receive() external payable virtual {\\n _fallback();\\n }\\n\\n /**\\n * @dev Hook that is called before falling back to the implementation. Can happen as part of a manual `_fallback`\\n * call, or as part of the Solidity `fallback` or `receive` functions.\\n *\\n * If overriden should call `super._beforeFallback()`.\\n */\\n function _beforeFallback() internal virtual {}\\n}\\n\",\"keccak256\":\"0xd5d1fd16e9faff7fcb3a52e02a8d49156f42a38a03f07b5f1810c21c2149a8ab\",\"license\":\"MIT\"},\"hardhat-deploy/solc_0.8/openzeppelin/proxy/beacon/IBeacon.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts v4.4.1 (proxy/beacon/IBeacon.sol)\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev This is the interface that {BeaconProxy} expects of its beacon.\\n */\\ninterface IBeacon {\\n /**\\n * @dev Must return an address that can be used as a delegate call target.\\n *\\n * {BeaconProxy} will check that this address is a contract.\\n */\\n function implementation() external view returns (address);\\n}\\n\",\"keccak256\":\"0xd50a3421ac379ccb1be435fa646d66a65c986b4924f0849839f08692f39dde61\",\"license\":\"MIT\"},\"hardhat-deploy/solc_0.8/openzeppelin/utils/Address.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts (last updated v4.5.0-rc.0) (utils/Address.sol)\\n\\npragma solidity ^0.8.1;\\n\\n/**\\n * @dev Collection of functions related to the address type\\n */\\nlibrary Address {\\n /**\\n * @dev Returns true if `account` is a contract.\\n *\\n * [IMPORTANT]\\n * ====\\n * It is unsafe to assume that an address for which this function returns\\n * false is an externally-owned account (EOA) and not a contract.\\n *\\n * Among others, `isContract` will return false for the following\\n * types of addresses:\\n *\\n * - an externally-owned account\\n * - a contract in construction\\n * - an address where a contract will be created\\n * - an address where a contract lived, but was destroyed\\n * ====\\n *\\n * [IMPORTANT]\\n * ====\\n * You shouldn't rely on `isContract` to protect against flash loan attacks!\\n *\\n * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets\\n * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract\\n * constructor.\\n * ====\\n */\\n function isContract(address account) internal view returns (bool) {\\n // This method relies on extcodesize/address.code.length, which returns 0\\n // for contracts in construction, since the code is only stored at the end\\n // of the constructor execution.\\n\\n return account.code.length > 0;\\n }\\n\\n /**\\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\\n * `recipient`, forwarding all available gas and reverting on errors.\\n *\\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\\n * imposed by `transfer`, making them unable to receive funds via\\n * `transfer`. {sendValue} removes this limitation.\\n *\\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\\n *\\n * IMPORTANT: because control is transferred to `recipient`, care must be\\n * taken to not create reentrancy vulnerabilities. Consider using\\n * {ReentrancyGuard} or the\\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\\n */\\n function sendValue(address payable recipient, uint256 amount) internal {\\n require(address(this).balance >= amount, \\\"Address: insufficient balance\\\");\\n\\n (bool success, ) = recipient.call{value: amount}(\\\"\\\");\\n require(success, \\\"Address: unable to send value, recipient may have reverted\\\");\\n }\\n\\n /**\\n * @dev Performs a Solidity function call using a low level `call`. A\\n * plain `call` is an unsafe replacement for a function call: use this\\n * function instead.\\n *\\n * If `target` reverts with a revert reason, it is bubbled up by this\\n * function (like regular Solidity function calls).\\n *\\n * Returns the raw returned data. To convert to the expected return value,\\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\\n *\\n * Requirements:\\n *\\n * - `target` must be a contract.\\n * - calling `target` with `data` must not revert.\\n *\\n * _Available since v3.1._\\n */\\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\\n return functionCall(target, data, \\\"Address: low-level call failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\\n * `errorMessage` as a fallback revert reason when `target` reverts.\\n *\\n * _Available since v3.1._\\n */\\n function functionCall(\\n address target,\\n bytes memory data,\\n string memory errorMessage\\n ) internal returns (bytes memory) {\\n return functionCallWithValue(target, data, 0, errorMessage);\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\\n * but also transferring `value` wei to `target`.\\n *\\n * Requirements:\\n *\\n * - the calling contract must have an ETH balance of at least `value`.\\n * - the called Solidity function must be `payable`.\\n *\\n * _Available since v3.1._\\n */\\n function functionCallWithValue(\\n address target,\\n bytes memory data,\\n uint256 value\\n ) internal returns (bytes memory) {\\n return functionCallWithValue(target, data, value, \\\"Address: low-level call with value failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\\n * with `errorMessage` as a fallback revert reason when `target` reverts.\\n *\\n * _Available since v3.1._\\n */\\n function functionCallWithValue(\\n address target,\\n bytes memory data,\\n uint256 value,\\n string memory errorMessage\\n ) internal returns (bytes memory) {\\n require(address(this).balance >= value, \\\"Address: insufficient balance for call\\\");\\n require(isContract(target), \\\"Address: call to non-contract\\\");\\n\\n (bool success, bytes memory returndata) = target.call{value: value}(data);\\n return verifyCallResult(success, returndata, errorMessage);\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\\n * but performing a static call.\\n *\\n * _Available since v3.3._\\n */\\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\\n return functionStaticCall(target, data, \\\"Address: low-level static call failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\\n * but performing a static call.\\n *\\n * _Available since v3.3._\\n */\\n function functionStaticCall(\\n address target,\\n bytes memory data,\\n string memory errorMessage\\n ) internal view returns (bytes memory) {\\n require(isContract(target), \\\"Address: static call to non-contract\\\");\\n\\n (bool success, bytes memory returndata) = target.staticcall(data);\\n return verifyCallResult(success, returndata, errorMessage);\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\\n * but performing a delegate call.\\n *\\n * _Available since v3.4._\\n */\\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\\n return functionDelegateCall(target, data, \\\"Address: low-level delegate call failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\\n * but performing a delegate call.\\n *\\n * _Available since v3.4._\\n */\\n function functionDelegateCall(\\n address target,\\n bytes memory data,\\n string memory errorMessage\\n ) internal returns (bytes memory) {\\n require(isContract(target), \\\"Address: delegate call to non-contract\\\");\\n\\n (bool success, bytes memory returndata) = target.delegatecall(data);\\n return verifyCallResult(success, returndata, errorMessage);\\n }\\n\\n /**\\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\\n * revert reason using the provided one.\\n *\\n * _Available since v4.3._\\n */\\n function verifyCallResult(\\n bool success,\\n bytes memory returndata,\\n string memory errorMessage\\n ) internal pure returns (bytes memory) {\\n if (success) {\\n return returndata;\\n } else {\\n // Look for revert reason and bubble it up if present\\n if (returndata.length > 0) {\\n // The easiest way to bubble the revert reason is using memory via assembly\\n\\n assembly {\\n let returndata_size := mload(returndata)\\n revert(add(32, returndata), returndata_size)\\n }\\n } else {\\n revert(errorMessage);\\n }\\n }\\n }\\n}\\n\",\"keccak256\":\"0x3777e696b62134e6177440dbe6e6601c0c156a443f57167194b67e75527439de\",\"license\":\"MIT\"},\"hardhat-deploy/solc_0.8/openzeppelin/utils/StorageSlot.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts v4.4.1 (utils/StorageSlot.sol)\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev Library for reading and writing primitive types to specific storage slots.\\n *\\n * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.\\n * This library helps with reading and writing to such slots without the need for inline assembly.\\n *\\n * The functions in this library return Slot structs that contain a `value` member that can be used to read or write.\\n *\\n * Example usage to set ERC1967 implementation slot:\\n * ```\\n * contract ERC1967 {\\n * bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;\\n *\\n * function _getImplementation() internal view returns (address) {\\n * return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;\\n * }\\n *\\n * function _setImplementation(address newImplementation) internal {\\n * require(Address.isContract(newImplementation), \\\"ERC1967: new implementation is not a contract\\\");\\n * StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;\\n * }\\n * }\\n * ```\\n *\\n * _Available since v4.1 for `address`, `bool`, `bytes32`, and `uint256`._\\n */\\nlibrary StorageSlot {\\n struct AddressSlot {\\n address value;\\n }\\n\\n struct BooleanSlot {\\n bool value;\\n }\\n\\n struct Bytes32Slot {\\n bytes32 value;\\n }\\n\\n struct Uint256Slot {\\n uint256 value;\\n }\\n\\n /**\\n * @dev Returns an `AddressSlot` with member `value` located at `slot`.\\n */\\n function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {\\n assembly {\\n r.slot := slot\\n }\\n }\\n\\n /**\\n * @dev Returns an `BooleanSlot` with member `value` located at `slot`.\\n */\\n function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {\\n assembly {\\n r.slot := slot\\n }\\n }\\n\\n /**\\n * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.\\n */\\n function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {\\n assembly {\\n r.slot := slot\\n }\\n }\\n\\n /**\\n * @dev Returns an `Uint256Slot` with member `value` located at `slot`.\\n */\\n function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {\\n assembly {\\n r.slot := slot\\n }\\n }\\n}\\n\",\"keccak256\":\"0xfe1b7a9aa2a530a9e705b220e26cd584e2fbdc9602a3a1066032b12816b46aca\",\"license\":\"MIT\"},\"hardhat-deploy/solc_0.8/proxy/OptimizedTransparentUpgradeableProxy.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts v4.4.1 (proxy/transparent/TransparentUpgradeableProxy.sol)\\n\\npragma solidity ^0.8.0;\\n\\nimport \\\"../openzeppelin/proxy/ERC1967/ERC1967Proxy.sol\\\";\\n\\n/**\\n * @dev This contract implements a proxy that is upgradeable by an admin.\\n *\\n * To avoid https://medium.com/nomic-labs-blog/malicious-backdoors-in-ethereum-proxies-62629adf3357[proxy selector\\n * clashing], which can potentially be used in an attack, this contract uses the\\n * https://blog.openzeppelin.com/the-transparent-proxy-pattern/[transparent proxy pattern]. This pattern implies two\\n * things that go hand in hand:\\n *\\n * 1. If any account other than the admin calls the proxy, the call will be forwarded to the implementation, even if\\n * that call matches one of the admin functions exposed by the proxy itself.\\n * 2. If the admin calls the proxy, it can access the admin functions, but its calls will never be forwarded to the\\n * implementation. If the admin tries to call a function on the implementation it will fail with an error that says\\n * \\\"admin cannot fallback to proxy target\\\".\\n *\\n * These properties mean that the admin account can only be used for admin actions like upgrading the proxy or changing\\n * the admin, so it's best if it's a dedicated account that is not used for anything else. This will avoid headaches due\\n * to sudden errors when trying to call a function from the proxy implementation.\\n *\\n * Our recommendation is for the dedicated account to be an instance of the {ProxyAdmin} contract. If set up this way,\\n * you should think of the `ProxyAdmin` instance as the real administrative interface of your proxy.\\n */\\ncontract OptimizedTransparentUpgradeableProxy is ERC1967Proxy {\\n address internal immutable _ADMIN;\\n\\n /**\\n * @dev Initializes an upgradeable proxy managed by `_admin`, backed by the implementation at `_logic`, and\\n * optionally initialized with `_data` as explained in {ERC1967Proxy-constructor}.\\n */\\n constructor(\\n address _logic,\\n address admin_,\\n bytes memory _data\\n ) payable ERC1967Proxy(_logic, _data) {\\n assert(_ADMIN_SLOT == bytes32(uint256(keccak256(\\\"eip1967.proxy.admin\\\")) - 1));\\n _ADMIN = admin_;\\n\\n // still store it to work with EIP-1967\\n bytes32 slot = _ADMIN_SLOT;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n sstore(slot, admin_)\\n }\\n emit AdminChanged(address(0), admin_);\\n }\\n\\n /**\\n * @dev Modifier used internally that will delegate the call to the implementation unless the sender is the admin.\\n */\\n modifier ifAdmin() {\\n if (msg.sender == _getAdmin()) {\\n _;\\n } else {\\n _fallback();\\n }\\n }\\n\\n /**\\n * @dev Returns the current admin.\\n *\\n * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyAdmin}.\\n *\\n * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the\\n * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.\\n * `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103`\\n */\\n function admin() external ifAdmin returns (address admin_) {\\n admin_ = _getAdmin();\\n }\\n\\n /**\\n * @dev Returns the current implementation.\\n *\\n * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyImplementation}.\\n *\\n * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the\\n * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.\\n * `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc`\\n */\\n function implementation() external ifAdmin returns (address implementation_) {\\n implementation_ = _implementation();\\n }\\n\\n /**\\n * @dev Upgrade the implementation of the proxy.\\n *\\n * NOTE: Only the admin can call this function. See {ProxyAdmin-upgrade}.\\n */\\n function upgradeTo(address newImplementation) external ifAdmin {\\n _upgradeToAndCall(newImplementation, bytes(\\\"\\\"), false);\\n }\\n\\n /**\\n * @dev Upgrade the implementation of the proxy, and then call a function from the new implementation as specified\\n * by `data`, which should be an encoded function call. This is useful to initialize new storage variables in the\\n * proxied contract.\\n *\\n * NOTE: Only the admin can call this function. See {ProxyAdmin-upgradeAndCall}.\\n */\\n function upgradeToAndCall(address newImplementation, bytes calldata data) external payable ifAdmin {\\n _upgradeToAndCall(newImplementation, data, true);\\n }\\n\\n /**\\n * @dev Returns the current admin.\\n */\\n function _admin() internal view virtual returns (address) {\\n return _getAdmin();\\n }\\n\\n /**\\n * @dev Makes sure the admin cannot access the fallback function. See {Proxy-_beforeFallback}.\\n */\\n function _beforeFallback() internal virtual override {\\n require(msg.sender != _getAdmin(), \\\"TransparentUpgradeableProxy: admin cannot fallback to proxy target\\\");\\n super._beforeFallback();\\n }\\n\\n function _getAdmin() internal view virtual override returns (address) {\\n return _ADMIN;\\n }\\n}\\n\",\"keccak256\":\"0xa30117644e27fa5b49e162aae2f62b36c1aca02f801b8c594d46e2024963a534\",\"license\":\"MIT\"}},\"version\":1}", + "bytecode": "0x60a0604052604051610d1a380380610d1a833981016040819052610022916103d2565b828161004f60017f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbd6104a2565b600080516020610cd38339815191521461006b5761006b6104c3565b61007782826000610128565b506100a5905060017fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61046104a2565b600080516020610cb3833981519152146100c1576100c16104c3565b6001600160a01b0382166080819052600080516020610cb38339815191528381556040805160008152602081019390935290917f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f910160405180910390a150505050610528565b61013183610154565b60008251118061013e5750805b1561014f5761014d8383610194565b505b505050565b61015d816101c2565b6040516001600160a01b038216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b60606101b98383604051806060016040528060278152602001610cf360279139610263565b90505b92915050565b6001600160a01b0381163b6102345760405162461bcd60e51b815260206004820152602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201526c1bdd08184818dbdb9d1c9858dd609a1b60648201526084015b60405180910390fd5b600080516020610cd383398151915280546001600160a01b0319166001600160a01b0392909216919091179055565b60606001600160a01b0384163b6102cb5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f6044820152651b9d1c9858dd60d21b606482015260840161022b565b600080856001600160a01b0316856040516102e691906104d9565b600060405180830381855af49150503d8060008114610321576040519150601f19603f3d011682016040523d82523d6000602084013e610326565b606091505b509092509050610337828286610343565b925050505b9392505050565b6060831561035257508161033c565b8251156103625782518084602001fd5b8160405162461bcd60e51b815260040161022b91906104f5565b80516001600160a01b038116811461039357600080fd5b919050565b634e487b7160e01b600052604160045260246000fd5b60005b838110156103c95781810151838201526020016103b1565b50506000910152565b6000806000606084860312156103e757600080fd5b6103f08461037c565b92506103fe6020850161037c565b60408501519092506001600160401b038082111561041b57600080fd5b818601915086601f83011261042f57600080fd5b81518181111561044157610441610398565b604051601f8201601f19908116603f0116810190838211818310171561046957610469610398565b8160405282815289602084870101111561048257600080fd5b6104938360208301602088016103ae565b80955050505050509250925092565b818103818111156101bc57634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052600160045260246000fd5b600082516104eb8184602087016103ae565b9190910192915050565b60208152600082518060208401526105148160408501602087016103ae565b601f01601f19169190910160400192915050565b60805161074e6105656000396000818160ef01528181610145015281816101c701528181610211015281816102420152610266015261074e6000f3fe6080604052600436106100435760003560e01c80633659cfe61461005a5780634f1ef2861461007a5780635c60da1b1461008d578063f851a440146100be57610052565b36610052576100506100d3565b005b6100506100d3565b34801561006657600080fd5b506100506100753660046105e0565b6100ed565b6100506100883660046105fb565b610143565b34801561009957600080fd5b506100a26101c3565b6040516001600160a01b03909116815260200160405180910390f35b3480156100ca57600080fd5b506100a261020d565b6100db610264565b6100eb6100e6610312565b610345565b565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316330361013b5761013881604051806020016040528060008152506000610369565b50565b6101386100d3565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031633036101bb576101b68383838080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525060019250610369915050565b505050565b6101b66100d3565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03163303610202576101fd610312565b905090565b61020a6100d3565b90565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316330361020257507f000000000000000000000000000000000000000000000000000000000000000090565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031633036100eb5760405162461bcd60e51b815260206004820152604260248201527f5472616e73706172656e745570677261646561626c6550726f78793a2061646d60448201527f696e2063616e6e6f742066616c6c6261636b20746f2070726f78792074617267606482015261195d60f21b608482015260a4015b60405180910390fd5b60006101fd7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc546001600160a01b031690565b3660008037600080366000845af43d6000803e808015610364573d6000f35b3d6000fd5b61037283610394565b60008251118061037f5750805b156101b65761038e83836103d4565b50505050565b61039d81610400565b6040516001600160a01b038216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b60606103f983836040518060600160405280602781526020016106f2602791396104ae565b9392505050565b6001600160a01b0381163b61046d5760405162461bcd60e51b815260206004820152602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201526c1bdd08184818dbdb9d1c9858dd609a1b6064820152608401610309565b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc80546001600160a01b0319166001600160a01b0392909216919091179055565b60606001600160a01b0384163b6105165760405162461bcd60e51b815260206004820152602660248201527f416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f6044820152651b9d1c9858dd60d21b6064820152608401610309565b600080856001600160a01b03168560405161053191906106a2565b600060405180830381855af49150503d806000811461056c576040519150601f19603f3d011682016040523d82523d6000602084013e610571565b606091505b509150915061058182828661058b565b9695505050505050565b6060831561059a5750816103f9565b8251156105aa5782518084602001fd5b8160405162461bcd60e51b815260040161030991906106be565b80356001600160a01b03811681146105db57600080fd5b919050565b6000602082840312156105f257600080fd5b6103f9826105c4565b60008060006040848603121561061057600080fd5b610619846105c4565b9250602084013567ffffffffffffffff8082111561063657600080fd5b818601915086601f83011261064a57600080fd5b81358181111561065957600080fd5b87602082850101111561066b57600080fd5b6020830194508093505050509250925092565b60005b83811015610699578181015183820152602001610681565b50506000910152565b600082516106b481846020870161067e565b9190910192915050565b60208152600082518060208401526106dd81604085016020870161067e565b601f01601f1916919091016040019291505056fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a26469706673582212207502388c3ffe7f7efd00a218c70c5d02b728d9a5b26b11a37a02a8761f7f520764736f6c63430008190033b53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564", + "deployedBytecode": "0x6080604052600436106100435760003560e01c80633659cfe61461005a5780634f1ef2861461007a5780635c60da1b1461008d578063f851a440146100be57610052565b36610052576100506100d3565b005b6100506100d3565b34801561006657600080fd5b506100506100753660046105e0565b6100ed565b6100506100883660046105fb565b610143565b34801561009957600080fd5b506100a26101c3565b6040516001600160a01b03909116815260200160405180910390f35b3480156100ca57600080fd5b506100a261020d565b6100db610264565b6100eb6100e6610312565b610345565b565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316330361013b5761013881604051806020016040528060008152506000610369565b50565b6101386100d3565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031633036101bb576101b68383838080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525060019250610369915050565b505050565b6101b66100d3565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03163303610202576101fd610312565b905090565b61020a6100d3565b90565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316330361020257507f000000000000000000000000000000000000000000000000000000000000000090565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031633036100eb5760405162461bcd60e51b815260206004820152604260248201527f5472616e73706172656e745570677261646561626c6550726f78793a2061646d60448201527f696e2063616e6e6f742066616c6c6261636b20746f2070726f78792074617267606482015261195d60f21b608482015260a4015b60405180910390fd5b60006101fd7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc546001600160a01b031690565b3660008037600080366000845af43d6000803e808015610364573d6000f35b3d6000fd5b61037283610394565b60008251118061037f5750805b156101b65761038e83836103d4565b50505050565b61039d81610400565b6040516001600160a01b038216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b60606103f983836040518060600160405280602781526020016106f2602791396104ae565b9392505050565b6001600160a01b0381163b61046d5760405162461bcd60e51b815260206004820152602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201526c1bdd08184818dbdb9d1c9858dd609a1b6064820152608401610309565b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc80546001600160a01b0319166001600160a01b0392909216919091179055565b60606001600160a01b0384163b6105165760405162461bcd60e51b815260206004820152602660248201527f416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f6044820152651b9d1c9858dd60d21b6064820152608401610309565b600080856001600160a01b03168560405161053191906106a2565b600060405180830381855af49150503d806000811461056c576040519150601f19603f3d011682016040523d82523d6000602084013e610571565b606091505b509150915061058182828661058b565b9695505050505050565b6060831561059a5750816103f9565b8251156105aa5782518084602001fd5b8160405162461bcd60e51b815260040161030991906106be565b80356001600160a01b03811681146105db57600080fd5b919050565b6000602082840312156105f257600080fd5b6103f9826105c4565b60008060006040848603121561061057600080fd5b610619846105c4565b9250602084013567ffffffffffffffff8082111561063657600080fd5b818601915086601f83011261064a57600080fd5b81358181111561065957600080fd5b87602082850101111561066b57600080fd5b6020830194508093505050509250925092565b60005b83811015610699578181015183820152602001610681565b50506000910152565b600082516106b481846020870161067e565b9190910192915050565b60208152600082518060208401526106dd81604085016020870161067e565b601f01601f1916919091016040019291505056fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a26469706673582212207502388c3ffe7f7efd00a218c70c5d02b728d9a5b26b11a37a02a8761f7f520764736f6c63430008190033", + "execute": { + "methodName": "initialize", + "args": ["0x45f8a08F534f34A97187626E05d4b6648Eeaa9AA"] + }, + "implementation": "0x266e65b6D81802F2BAFa0c07288A62891172DAD8", + "devdoc": { + "details": "This contract implements a proxy that is upgradeable by an admin. To avoid https://medium.com/nomic-labs-blog/malicious-backdoors-in-ethereum-proxies-62629adf3357[proxy selector clashing], which can potentially be used in an attack, this contract uses the https://blog.openzeppelin.com/the-transparent-proxy-pattern/[transparent proxy pattern]. This pattern implies two things that go hand in hand: 1. If any account other than the admin calls the proxy, the call will be forwarded to the implementation, even if that call matches one of the admin functions exposed by the proxy itself. 2. If the admin calls the proxy, it can access the admin functions, but its calls will never be forwarded to the implementation. If the admin tries to call a function on the implementation it will fail with an error that says \"admin cannot fallback to proxy target\". These properties mean that the admin account can only be used for admin actions like upgrading the proxy or changing the admin, so it's best if it's a dedicated account that is not used for anything else. This will avoid headaches due to sudden errors when trying to call a function from the proxy implementation. Our recommendation is for the dedicated account to be an instance of the {ProxyAdmin} contract. If set up this way, you should think of the `ProxyAdmin` instance as the real administrative interface of your proxy.", + "events": { + "AdminChanged(address,address)": { + "details": "Emitted when the admin account has changed." + }, + "BeaconUpgraded(address)": { + "details": "Emitted when the beacon is upgraded." + }, + "Upgraded(address)": { + "details": "Emitted when the implementation is upgraded." + } + }, + "kind": "dev", + "methods": { + "admin()": { + "details": "Returns the current admin. NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyAdmin}. TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call. `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103`" + }, + "constructor": { + "details": "Initializes an upgradeable proxy managed by `_admin`, backed by the implementation at `_logic`, and optionally initialized with `_data` as explained in {ERC1967Proxy-constructor}." + }, + "implementation()": { + "details": "Returns the current implementation. NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyImplementation}. TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call. `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc`" + }, + "upgradeTo(address)": { + "details": "Upgrade the implementation of the proxy. NOTE: Only the admin can call this function. See {ProxyAdmin-upgrade}." + }, + "upgradeToAndCall(address,bytes)": { + "details": "Upgrade the implementation of the proxy, and then call a function from the new implementation as specified by `data`, which should be an encoded function call. This is useful to initialize new storage variables in the proxied contract. NOTE: Only the admin can call this function. See {ProxyAdmin-upgradeAndCall}." + } + }, + "version": 1 + }, + "userdoc": { + "kind": "user", + "methods": {}, + "version": 1 + }, + "storageLayout": { + "storage": [], + "types": null + } +} diff --git a/deployments/bsctestnet/SpokePoolRegistry_Implementation.json b/deployments/bsctestnet/SpokePoolRegistry_Implementation.json new file mode 100644 index 000000000..33cb09195 --- /dev/null +++ b/deployments/bsctestnet/SpokePoolRegistry_Implementation.json @@ -0,0 +1,1194 @@ +{ + "address": "0x266e65b6D81802F2BAFa0c07288A62891172DAD8", + "abi": [ + { + "inputs": [], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "address", + "name": "calledContract", + "type": "address" + }, + { + "internalType": "string", + "name": "methodSignature", + "type": "string" + } + ], + "name": "Unauthorized", + "type": "error" + }, + { + "inputs": [], + "name": "ZeroAddressNotAllowed", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint8", + "name": "version", + "type": "uint8" + } + ], + "name": "Initialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "comptroller", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "vTokenAddress", + "type": "address" + } + ], + "name": "MarketAdded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "oldAccessControlManager", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "newAccessControlManager", + "type": "address" + } + ], + "name": "NewAccessControlManager", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferStarted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "comptroller", + "type": "address" + }, + { + "components": [ + { + "internalType": "string", + "name": "category", + "type": "string" + }, + { + "internalType": "string", + "name": "logoURL", + "type": "string" + }, + { + "internalType": "string", + "name": "description", + "type": "string" + } + ], + "indexed": false, + "internalType": "struct PoolRegistryInterface.VenusPoolMetaData", + "name": "oldMetadata", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "string", + "name": "category", + "type": "string" + }, + { + "internalType": "string", + "name": "logoURL", + "type": "string" + }, + { + "internalType": "string", + "name": "description", + "type": "string" + } + ], + "indexed": false, + "internalType": "struct PoolRegistryInterface.VenusPoolMetaData", + "name": "newMetadata", + "type": "tuple" + } + ], + "name": "PoolMetadataUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "comptroller", + "type": "address" + }, + { + "indexed": false, + "internalType": "string", + "name": "oldName", + "type": "string" + }, + { + "indexed": false, + "internalType": "string", + "name": "newName", + "type": "string" + } + ], + "name": "PoolNameSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "comptroller", + "type": "address" + }, + { + "components": [ + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "internalType": "address", + "name": "creator", + "type": "address" + }, + { + "internalType": "address", + "name": "comptroller", + "type": "address" + }, + { + "internalType": "uint256", + "name": "blockPosted", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "timestampPosted", + "type": "uint256" + } + ], + "indexed": false, + "internalType": "struct PoolRegistryInterface.VenusPool", + "name": "pool", + "type": "tuple" + } + ], + "name": "PoolRegistered", + "type": "event" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "accessControlManager", + "outputs": [ + { + "internalType": "contract IAccessControlManagerV8", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "contract VToken", + "name": "vToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "collateralFactor", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "liquidationThreshold", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "initialSupply", + "type": "uint256" + }, + { + "internalType": "address", + "name": "vTokenReceiver", + "type": "address" + }, + { + "internalType": "uint256", + "name": "supplyCap", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "borrowCap", + "type": "uint256" + } + ], + "internalType": "struct PoolRegistry.AddMarketInput", + "name": "input", + "type": "tuple" + } + ], + "name": "addMarket", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "internalType": "contract Comptroller", + "name": "comptroller", + "type": "address" + }, + { + "internalType": "uint256", + "name": "closeFactor", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "liquidationIncentive", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minLiquidatableCollateral", + "type": "uint256" + } + ], + "name": "addPool", + "outputs": [ + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "getAllPools", + "outputs": [ + { + "components": [ + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "internalType": "address", + "name": "creator", + "type": "address" + }, + { + "internalType": "address", + "name": "comptroller", + "type": "address" + }, + { + "internalType": "uint256", + "name": "blockPosted", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "timestampPosted", + "type": "uint256" + } + ], + "internalType": "struct PoolRegistryInterface.VenusPool[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "comptroller", + "type": "address" + } + ], + "name": "getPoolByComptroller", + "outputs": [ + { + "components": [ + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "internalType": "address", + "name": "creator", + "type": "address" + }, + { + "internalType": "address", + "name": "comptroller", + "type": "address" + }, + { + "internalType": "uint256", + "name": "blockPosted", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "timestampPosted", + "type": "uint256" + } + ], + "internalType": "struct PoolRegistryInterface.VenusPool", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "asset", + "type": "address" + } + ], + "name": "getPoolsSupportedByAsset", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "comptroller", + "type": "address" + }, + { + "internalType": "address", + "name": "asset", + "type": "address" + } + ], + "name": "getVTokenForAsset", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "comptroller", + "type": "address" + } + ], + "name": "getVenusPoolMetadata", + "outputs": [ + { + "components": [ + { + "internalType": "string", + "name": "category", + "type": "string" + }, + { + "internalType": "string", + "name": "logoURL", + "type": "string" + }, + { + "internalType": "string", + "name": "description", + "type": "string" + } + ], + "internalType": "struct PoolRegistryInterface.VenusPoolMetaData", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "accessControlManager_", + "type": "address" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "metadata", + "outputs": [ + { + "internalType": "string", + "name": "category", + "type": "string" + }, + { + "internalType": "string", + "name": "logoURL", + "type": "string" + }, + { + "internalType": "string", + "name": "description", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pendingOwner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "accessControlManager_", + "type": "address" + } + ], + "name": "setAccessControlManager", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "comptroller", + "type": "address" + }, + { + "internalType": "string", + "name": "name", + "type": "string" + } + ], + "name": "setPoolName", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "comptroller", + "type": "address" + }, + { + "components": [ + { + "internalType": "string", + "name": "category", + "type": "string" + }, + { + "internalType": "string", + "name": "logoURL", + "type": "string" + }, + { + "internalType": "string", + "name": "description", + "type": "string" + } + ], + "internalType": "struct PoolRegistryInterface.VenusPoolMetaData", + "name": "metadata_", + "type": "tuple" + } + ], + "name": "updatePoolMetadata", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } + ], + "transactionHash": "0x49a565708a2562fd7cd6390b5491f79aa701ef7b6d834fd995c1365a01bbaffe", + "receipt": { + "to": null, + "from": "0x33C6476F88eeA28D7E7900F759B4597704Ef95B7", + "contractAddress": "0x266e65b6D81802F2BAFa0c07288A62891172DAD8", + "transactionIndex": 0, + "gasUsed": "2612517", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000080000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0x27181e48f28bbc161fc441b6bb2ab0d1496b25c904cc6c13936c1a641899de9e", + "transactionHash": "0x49a565708a2562fd7cd6390b5491f79aa701ef7b6d834fd995c1365a01bbaffe", + "logs": [ + { + "transactionIndex": 0, + "blockNumber": 129844739, + "transactionHash": "0x49a565708a2562fd7cd6390b5491f79aa701ef7b6d834fd995c1365a01bbaffe", + "address": "0x266e65b6D81802F2BAFa0c07288A62891172DAD8", + "topics": ["0x7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498"], + "data": "0x00000000000000000000000000000000000000000000000000000000000000ff", + "logIndex": 0, + "blockHash": "0x27181e48f28bbc161fc441b6bb2ab0d1496b25c904cc6c13936c1a641899de9e" + } + ], + "blockNumber": 129844739, + "cumulativeGasUsed": "2612517", + "status": 1, + "byzantium": true + }, + "args": [], + "numDeployments": 1, + "solcInputHash": "02cfe3974aa10474c22406a3f2870e89", + "metadata": "{\"compiler\":{\"version\":\"0.8.25+commit.b61c2a91\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"calledContract\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"methodSignature\",\"type\":\"string\"}],\"name\":\"Unauthorized\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ZeroAddressNotAllowed\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"version\",\"type\":\"uint8\"}],\"name\":\"Initialized\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"comptroller\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"vTokenAddress\",\"type\":\"address\"}],\"name\":\"MarketAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"oldAccessControlManager\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"newAccessControlManager\",\"type\":\"address\"}],\"name\":\"NewAccessControlManager\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousOwner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"OwnershipTransferStarted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousOwner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"comptroller\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"string\",\"name\":\"category\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"logoURL\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"description\",\"type\":\"string\"}],\"indexed\":false,\"internalType\":\"struct PoolRegistryInterface.VenusPoolMetaData\",\"name\":\"oldMetadata\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"string\",\"name\":\"category\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"logoURL\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"description\",\"type\":\"string\"}],\"indexed\":false,\"internalType\":\"struct PoolRegistryInterface.VenusPoolMetaData\",\"name\":\"newMetadata\",\"type\":\"tuple\"}],\"name\":\"PoolMetadataUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"comptroller\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"oldName\",\"type\":\"string\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"newName\",\"type\":\"string\"}],\"name\":\"PoolNameSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"comptroller\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"},{\"internalType\":\"address\",\"name\":\"creator\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"comptroller\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"blockPosted\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"timestampPosted\",\"type\":\"uint256\"}],\"indexed\":false,\"internalType\":\"struct PoolRegistryInterface.VenusPool\",\"name\":\"pool\",\"type\":\"tuple\"}],\"name\":\"PoolRegistered\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"accessControlManager\",\"outputs\":[{\"internalType\":\"contract IAccessControlManagerV8\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"contract VToken\",\"name\":\"vToken\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"collateralFactor\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"liquidationThreshold\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"initialSupply\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"vTokenReceiver\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"supplyCap\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"borrowCap\",\"type\":\"uint256\"}],\"internalType\":\"struct PoolRegistry.AddMarketInput\",\"name\":\"input\",\"type\":\"tuple\"}],\"name\":\"addMarket\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"},{\"internalType\":\"contract Comptroller\",\"name\":\"comptroller\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"closeFactor\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"liquidationIncentive\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"minLiquidatableCollateral\",\"type\":\"uint256\"}],\"name\":\"addPool\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"index\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getAllPools\",\"outputs\":[{\"components\":[{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"},{\"internalType\":\"address\",\"name\":\"creator\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"comptroller\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"blockPosted\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"timestampPosted\",\"type\":\"uint256\"}],\"internalType\":\"struct PoolRegistryInterface.VenusPool[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"comptroller\",\"type\":\"address\"}],\"name\":\"getPoolByComptroller\",\"outputs\":[{\"components\":[{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"},{\"internalType\":\"address\",\"name\":\"creator\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"comptroller\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"blockPosted\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"timestampPosted\",\"type\":\"uint256\"}],\"internalType\":\"struct PoolRegistryInterface.VenusPool\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"asset\",\"type\":\"address\"}],\"name\":\"getPoolsSupportedByAsset\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"comptroller\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"asset\",\"type\":\"address\"}],\"name\":\"getVTokenForAsset\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"comptroller\",\"type\":\"address\"}],\"name\":\"getVenusPoolMetadata\",\"outputs\":[{\"components\":[{\"internalType\":\"string\",\"name\":\"category\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"logoURL\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"description\",\"type\":\"string\"}],\"internalType\":\"struct PoolRegistryInterface.VenusPoolMetaData\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"accessControlManager_\",\"type\":\"address\"}],\"name\":\"initialize\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"metadata\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"category\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"logoURL\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"description\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"pendingOwner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"renounceOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"accessControlManager_\",\"type\":\"address\"}],\"name\":\"setAccessControlManager\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"comptroller\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"}],\"name\":\"setPoolName\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"comptroller\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"string\",\"name\":\"category\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"logoURL\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"description\",\"type\":\"string\"}],\"internalType\":\"struct PoolRegistryInterface.VenusPoolMetaData\",\"name\":\"metadata_\",\"type\":\"tuple\"}],\"name\":\"updatePoolMetadata\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}],\"devdoc\":{\"author\":\"Venus\",\"events\":{\"Initialized(uint8)\":{\"details\":\"Triggered when the contract has been initialized or reinitialized.\"}},\"kind\":\"dev\",\"methods\":{\"acceptOwnership()\":{\"details\":\"The new owner accepts the ownership transfer.\"},\"addMarket((address,uint256,uint256,uint256,address,uint256,uint256))\":{\"custom:error\":\"ZeroAddressNotAllowed is thrown when vToken address is zeroZeroAddressNotAllowed is thrown when vTokenReceiver address is zero\",\"params\":{\"input\":\"The structure describing the parameters for adding a market to a pool\"}},\"addPool(string,address,uint256,uint256,uint256)\":{\"custom:error\":\"ZeroAddressNotAllowed is thrown when Comptroller address is zeroZeroAddressNotAllowed is thrown when price oracle address is zero\",\"details\":\"Price oracle must be configured before adding a pool\",\"params\":{\"closeFactor\":\"The pool's close factor (scaled by 1e18)\",\"comptroller\":\"Pool's Comptroller contract\",\"liquidationIncentive\":\"The pool's liquidation incentive (scaled by 1e18)\",\"minLiquidatableCollateral\":\"Minimal collateral for regular (non-batch) liquidations flow\",\"name\":\"The name of the pool\"},\"returns\":{\"index\":\"The index of the registered Venus pool\"}},\"constructor\":{\"custom:oz-upgrades-unsafe-allow\":\"constructor\"},\"getAllPools()\":{\"details\":\"This function is not designed to be called in a transaction: it is too gas-intensive\",\"returns\":{\"_0\":\"A list of all pools within PoolRegistry, with details for each pool\"}},\"getPoolByComptroller(address)\":{\"params\":{\"comptroller\":\"The comptroller proxy address associated to the pool\"},\"returns\":{\"_0\":\"Returns Venus pool\"}},\"getVenusPoolMetadata(address)\":{\"params\":{\"comptroller\":\"comptroller of Venus pool\"},\"returns\":{\"_0\":\"Returns Metadata of Venus pool\"}},\"initialize(address)\":{\"params\":{\"accessControlManager_\":\"AccessControlManager contract address\"}},\"owner()\":{\"details\":\"Returns the address of the current owner.\"},\"pendingOwner()\":{\"details\":\"Returns the address of the pending owner.\"},\"renounceOwnership()\":{\"details\":\"Leaves the contract without owner. It will not be possible to call `onlyOwner` functions. Can only be called by the current owner. NOTE: Renouncing ownership will leave the contract without an owner, thereby disabling any functionality that is only available to the owner.\"},\"setAccessControlManager(address)\":{\"custom:access\":\"Only Governance\",\"custom:event\":\"Emits NewAccessControlManager event\",\"details\":\"Admin function to set address of AccessControlManager\",\"params\":{\"accessControlManager_\":\"The new address of the AccessControlManager\"}},\"setPoolName(address,string)\":{\"params\":{\"comptroller\":\"Pool's Comptroller\",\"name\":\"New pool name\"}},\"transferOwnership(address)\":{\"details\":\"Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one. Can only be called by the current owner.\"},\"updatePoolMetadata(address,(string,string,string))\":{\"params\":{\"comptroller\":\"Pool's Comptroller\",\"metadata_\":\"New pool metadata\"}}},\"stateVariables\":{\"_numberOfPools\":{\"details\":\"Total number of pools created.\"},\"_poolByComptroller\":{\"details\":\"Maps comptroller address to Venus pool Index.\"},\"_poolsByID\":{\"details\":\"Maps pool ID to pool's comptroller address\"},\"_supportedPools\":{\"details\":\"Maps asset to list of supported pools.\"},\"_vTokens\":{\"details\":\"Maps pool's comptroller address to asset to vToken.\"}},\"title\":\"PoolRegistry\",\"version\":1},\"userdoc\":{\"errors\":{\"Unauthorized(address,address,string)\":[{\"notice\":\"Thrown when the action is prohibited by AccessControlManager\"}],\"ZeroAddressNotAllowed()\":[{\"notice\":\"Thrown if the supplied address is a zero address where it is not allowed\"}]},\"events\":{\"MarketAdded(address,address)\":{\"notice\":\"Emitted when a Market is added to the pool.\"},\"NewAccessControlManager(address,address)\":{\"notice\":\"Emitted when access control manager contract address is changed\"},\"PoolMetadataUpdated(address,(string,string,string),(string,string,string))\":{\"notice\":\"Emitted when a pool metadata is updated.\"},\"PoolNameSet(address,string,string)\":{\"notice\":\"Emitted when a pool name is set.\"},\"PoolRegistered(address,(string,address,address,uint256,uint256))\":{\"notice\":\"Emitted when a new Venus pool is added to the directory.\"}},\"kind\":\"user\",\"methods\":{\"accessControlManager()\":{\"notice\":\"Returns the address of the access control manager contract\"},\"addMarket((address,uint256,uint256,uint256,address,uint256,uint256))\":{\"notice\":\"Add a market to an existing pool and then mint to provide initial supply\"},\"addPool(string,address,uint256,uint256,uint256)\":{\"notice\":\"Adds a new Venus pool to the directory\"},\"getAllPools()\":{\"notice\":\"Returns arrays of all Venus pools' data\"},\"getPoolsSupportedByAsset(address)\":{\"notice\":\"Get the addresss of the Pools supported that include a market for the provided asset\"},\"getVTokenForAsset(address,address)\":{\"notice\":\"Get the address of the VToken contract in the Pool where the underlying token is the provided asset\"},\"initialize(address)\":{\"notice\":\"Initializes the deployer to owner\"},\"metadata(address)\":{\"notice\":\"Maps pool's comptroller address to metadata.\"},\"setAccessControlManager(address)\":{\"notice\":\"Sets the address of AccessControlManager\"},\"setPoolName(address,string)\":{\"notice\":\"Modify existing Venus pool name\"},\"updatePoolMetadata(address,(string,string,string))\":{\"notice\":\"Update metadata of an existing pool\"}},\"notice\":\"The Isolated Pools architecture centers around the `PoolRegistry` contract. The `PoolRegistry` maintains a directory of isolated lending pools and can perform actions like creating and registering new pools, adding new markets to existing pools, setting and updating the pool's required metadata, and providing the getter methods to get information on the pools. Isolated lending has three main components: PoolRegistry, pools, and markets. The PoolRegistry is responsible for managing pools. It can create new pools, update pool metadata and manage markets within pools. PoolRegistry contains getter methods to get the details of any existing pool like `getVTokenForAsset` and `getPoolsSupportedByAsset`. It also contains methods for updating pool metadata (`updatePoolMetadata`) and setting pool name (`setPoolName`). The directory of pools is managed through two mappings: `_poolByComptroller` which is a hashmap with the comptroller address as the key and `VenusPool` as the value and `_poolsByID` which is an array of comptroller addresses. Individual pools can be accessed by calling `getPoolByComptroller` with the pool's comptroller address. `_poolsByID` is used to iterate through all of the pools. PoolRegistry also contains a map of asset addresses called `_supportedPools` that maps to an array of assets suppored by each pool. This array of pools by asset is retrieved by calling `getPoolsSupportedByAsset`. PoolRegistry registers new isolated pools in the directory with the `createRegistryPool` method. Isolated pools are composed of independent markets with specific assets and custom risk management configurations according to their markets.\",\"version\":1}},\"settings\":{\"compilationTarget\":{\"contracts/Pool/PoolRegistry.sol\":\"PoolRegistry\"},\"evmVersion\":\"paris\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\",\"useLiteralContent\":true},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[]},\"sources\":{\"@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable2Step.sol)\\n\\npragma solidity ^0.8.0;\\n\\nimport \\\"./OwnableUpgradeable.sol\\\";\\nimport {Initializable} from \\\"../proxy/utils/Initializable.sol\\\";\\n\\n/**\\n * @dev Contract module which provides access control mechanism, where\\n * there is an account (an owner) that can be granted exclusive access to\\n * specific functions.\\n *\\n * By default, the owner account will be the one that deploys the contract. This\\n * can later be changed with {transferOwnership} and {acceptOwnership}.\\n *\\n * This module is used through inheritance. It will make available all functions\\n * from parent (Ownable).\\n */\\nabstract contract Ownable2StepUpgradeable is Initializable, OwnableUpgradeable {\\n address private _pendingOwner;\\n\\n event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner);\\n\\n function __Ownable2Step_init() internal onlyInitializing {\\n __Ownable_init_unchained();\\n }\\n\\n function __Ownable2Step_init_unchained() internal onlyInitializing {\\n }\\n /**\\n * @dev Returns the address of the pending owner.\\n */\\n function pendingOwner() public view virtual returns (address) {\\n return _pendingOwner;\\n }\\n\\n /**\\n * @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one.\\n * Can only be called by the current owner.\\n */\\n function transferOwnership(address newOwner) public virtual override onlyOwner {\\n _pendingOwner = newOwner;\\n emit OwnershipTransferStarted(owner(), newOwner);\\n }\\n\\n /**\\n * @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner.\\n * Internal function without access restriction.\\n */\\n function _transferOwnership(address newOwner) internal virtual override {\\n delete _pendingOwner;\\n super._transferOwnership(newOwner);\\n }\\n\\n /**\\n * @dev The new owner accepts the ownership transfer.\\n */\\n function acceptOwnership() public virtual {\\n address sender = _msgSender();\\n require(pendingOwner() == sender, \\\"Ownable2Step: caller is not the new owner\\\");\\n _transferOwnership(sender);\\n }\\n\\n /**\\n * @dev This empty reserved space is put in place to allow future versions to add new\\n * variables without shifting down storage in the inheritance chain.\\n * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\\n */\\n uint256[49] private __gap;\\n}\\n\",\"keccak256\":\"0x9140dabc466abab21b48b72dbda26736b1183a310d0e677d3719d201df026510\",\"license\":\"MIT\"},\"@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)\\n\\npragma solidity ^0.8.0;\\n\\nimport \\\"../utils/ContextUpgradeable.sol\\\";\\nimport {Initializable} from \\\"../proxy/utils/Initializable.sol\\\";\\n\\n/**\\n * @dev Contract module which provides a basic access control mechanism, where\\n * there is an account (an owner) that can be granted exclusive access to\\n * specific functions.\\n *\\n * By default, the owner account will be the one that deploys the contract. This\\n * can later be changed with {transferOwnership}.\\n *\\n * This module is used through inheritance. It will make available the modifier\\n * `onlyOwner`, which can be applied to your functions to restrict their use to\\n * the owner.\\n */\\nabstract contract OwnableUpgradeable is Initializable, ContextUpgradeable {\\n address private _owner;\\n\\n event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\\n\\n /**\\n * @dev Initializes the contract setting the deployer as the initial owner.\\n */\\n function __Ownable_init() internal onlyInitializing {\\n __Ownable_init_unchained();\\n }\\n\\n function __Ownable_init_unchained() internal onlyInitializing {\\n _transferOwnership(_msgSender());\\n }\\n\\n /**\\n * @dev Throws if called by any account other than the owner.\\n */\\n modifier onlyOwner() {\\n _checkOwner();\\n _;\\n }\\n\\n /**\\n * @dev Returns the address of the current owner.\\n */\\n function owner() public view virtual returns (address) {\\n return _owner;\\n }\\n\\n /**\\n * @dev Throws if the sender is not the owner.\\n */\\n function _checkOwner() internal view virtual {\\n require(owner() == _msgSender(), \\\"Ownable: caller is not the owner\\\");\\n }\\n\\n /**\\n * @dev Leaves the contract without owner. It will not be possible to call\\n * `onlyOwner` functions. Can only be called by the current owner.\\n *\\n * NOTE: Renouncing ownership will leave the contract without an owner,\\n * thereby disabling any functionality that is only available to the owner.\\n */\\n function renounceOwnership() public virtual onlyOwner {\\n _transferOwnership(address(0));\\n }\\n\\n /**\\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\\n * Can only be called by the current owner.\\n */\\n function transferOwnership(address newOwner) public virtual onlyOwner {\\n require(newOwner != address(0), \\\"Ownable: new owner is the zero address\\\");\\n _transferOwnership(newOwner);\\n }\\n\\n /**\\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\\n * Internal function without access restriction.\\n */\\n function _transferOwnership(address newOwner) internal virtual {\\n address oldOwner = _owner;\\n _owner = newOwner;\\n emit OwnershipTransferred(oldOwner, newOwner);\\n }\\n\\n /**\\n * @dev This empty reserved space is put in place to allow future versions to add new\\n * variables without shifting down storage in the inheritance chain.\\n * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\\n */\\n uint256[49] private __gap;\\n}\\n\",\"keccak256\":\"0x359a1ab89b46b9aba7bcad3fb651924baf4893d15153049b9976b0fc9be1358e\",\"license\":\"MIT\"},\"@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts (last updated v4.9.0) (proxy/utils/Initializable.sol)\\n\\npragma solidity ^0.8.2;\\n\\nimport \\\"../../utils/AddressUpgradeable.sol\\\";\\n\\n/**\\n * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed\\n * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an\\n * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer\\n * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.\\n *\\n * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be\\n * reused. This mechanism prevents re-execution of each \\\"step\\\" but allows the creation of new initialization steps in\\n * case an upgrade adds a module that needs to be initialized.\\n *\\n * For example:\\n *\\n * [.hljs-theme-light.nopadding]\\n * ```solidity\\n * contract MyToken is ERC20Upgradeable {\\n * function initialize() initializer public {\\n * __ERC20_init(\\\"MyToken\\\", \\\"MTK\\\");\\n * }\\n * }\\n *\\n * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {\\n * function initializeV2() reinitializer(2) public {\\n * __ERC20Permit_init(\\\"MyToken\\\");\\n * }\\n * }\\n * ```\\n *\\n * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as\\n * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.\\n *\\n * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure\\n * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.\\n *\\n * [CAUTION]\\n * ====\\n * Avoid leaving a contract uninitialized.\\n *\\n * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation\\n * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke\\n * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:\\n *\\n * [.hljs-theme-light.nopadding]\\n * ```\\n * /// @custom:oz-upgrades-unsafe-allow constructor\\n * constructor() {\\n * _disableInitializers();\\n * }\\n * ```\\n * ====\\n */\\nabstract contract Initializable {\\n /**\\n * @dev Indicates that the contract has been initialized.\\n * @custom:oz-retyped-from bool\\n */\\n uint8 private _initialized;\\n\\n /**\\n * @dev Indicates that the contract is in the process of being initialized.\\n */\\n bool private _initializing;\\n\\n /**\\n * @dev Triggered when the contract has been initialized or reinitialized.\\n */\\n event Initialized(uint8 version);\\n\\n /**\\n * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,\\n * `onlyInitializing` functions can be used to initialize parent contracts.\\n *\\n * Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a\\n * constructor.\\n *\\n * Emits an {Initialized} event.\\n */\\n modifier initializer() {\\n bool isTopLevelCall = !_initializing;\\n require(\\n (isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1),\\n \\\"Initializable: contract is already initialized\\\"\\n );\\n _initialized = 1;\\n if (isTopLevelCall) {\\n _initializing = true;\\n }\\n _;\\n if (isTopLevelCall) {\\n _initializing = false;\\n emit Initialized(1);\\n }\\n }\\n\\n /**\\n * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the\\n * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be\\n * used to initialize parent contracts.\\n *\\n * A reinitializer may be used after the original initialization step. This is essential to configure modules that\\n * are added through upgrades and that require initialization.\\n *\\n * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`\\n * cannot be nested. If one is invoked in the context of another, execution will revert.\\n *\\n * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in\\n * a contract, executing them in the right order is up to the developer or operator.\\n *\\n * WARNING: setting the version to 255 will prevent any future reinitialization.\\n *\\n * Emits an {Initialized} event.\\n */\\n modifier reinitializer(uint8 version) {\\n require(!_initializing && _initialized < version, \\\"Initializable: contract is already initialized\\\");\\n _initialized = version;\\n _initializing = true;\\n _;\\n _initializing = false;\\n emit Initialized(version);\\n }\\n\\n /**\\n * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the\\n * {initializer} and {reinitializer} modifiers, directly or indirectly.\\n */\\n modifier onlyInitializing() {\\n require(_initializing, \\\"Initializable: contract is not initializing\\\");\\n _;\\n }\\n\\n /**\\n * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.\\n * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized\\n * to any version. It is recommended to use this to lock implementation contracts that are designed to be called\\n * through proxies.\\n *\\n * Emits an {Initialized} event the first time it is successfully executed.\\n */\\n function _disableInitializers() internal virtual {\\n require(!_initializing, \\\"Initializable: contract is initializing\\\");\\n if (_initialized != type(uint8).max) {\\n _initialized = type(uint8).max;\\n emit Initialized(type(uint8).max);\\n }\\n }\\n\\n /**\\n * @dev Returns the highest version that has been initialized. See {reinitializer}.\\n */\\n function _getInitializedVersion() internal view returns (uint8) {\\n return _initialized;\\n }\\n\\n /**\\n * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.\\n */\\n function _isInitializing() internal view returns (bool) {\\n return _initializing;\\n }\\n}\\n\",\"keccak256\":\"0x89be10e757d242e9b18d5a32c9fbe2019f6d63052bbe46397a430a1d60d7f794\",\"license\":\"MIT\"},\"@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev Interface of the ERC20 standard as defined in the EIP.\\n */\\ninterface IERC20Upgradeable {\\n /**\\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\\n * another (`to`).\\n *\\n * Note that `value` may be zero.\\n */\\n event Transfer(address indexed from, address indexed to, uint256 value);\\n\\n /**\\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\\n * a call to {approve}. `value` is the new allowance.\\n */\\n event Approval(address indexed owner, address indexed spender, uint256 value);\\n\\n /**\\n * @dev Returns the amount of tokens in existence.\\n */\\n function totalSupply() external view returns (uint256);\\n\\n /**\\n * @dev Returns the amount of tokens owned by `account`.\\n */\\n function balanceOf(address account) external view returns (uint256);\\n\\n /**\\n * @dev Moves `amount` tokens from the caller's account to `to`.\\n *\\n * Returns a boolean value indicating whether the operation succeeded.\\n *\\n * Emits a {Transfer} event.\\n */\\n function transfer(address to, uint256 amount) external returns (bool);\\n\\n /**\\n * @dev Returns the remaining number of tokens that `spender` will be\\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\\n * zero by default.\\n *\\n * This value changes when {approve} or {transferFrom} are called.\\n */\\n function allowance(address owner, address spender) external view returns (uint256);\\n\\n /**\\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\\n *\\n * Returns a boolean value indicating whether the operation succeeded.\\n *\\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\\n * that someone may use both the old and the new allowance by unfortunate\\n * transaction ordering. One possible solution to mitigate this race\\n * condition is to first reduce the spender's allowance to 0 and set the\\n * desired value afterwards:\\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\\n *\\n * Emits an {Approval} event.\\n */\\n function approve(address spender, uint256 amount) external returns (bool);\\n\\n /**\\n * @dev Moves `amount` tokens from `from` to `to` using the\\n * allowance mechanism. `amount` is then deducted from the caller's\\n * allowance.\\n *\\n * Returns a boolean value indicating whether the operation succeeded.\\n *\\n * Emits a {Transfer} event.\\n */\\n function transferFrom(address from, address to, uint256 amount) external returns (bool);\\n}\\n\",\"keccak256\":\"0x0e1f0f5f62f67a881cd1a9597acbc0a5e4071f3c2c10449a183b922ae7272e3f\",\"license\":\"MIT\"},\"@openzeppelin/contracts-upgradeable/token/ERC20/extensions/IERC20PermitUpgradeable.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts (last updated v4.9.4) (token/ERC20/extensions/IERC20Permit.sol)\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in\\n * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].\\n *\\n * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by\\n * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't\\n * need to send a transaction, and thus is not required to hold Ether at all.\\n *\\n * ==== Security Considerations\\n *\\n * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature\\n * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be\\n * considered as an intention to spend the allowance in any specific way. The second is that because permits have\\n * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should\\n * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be\\n * generally recommended is:\\n *\\n * ```solidity\\n * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {\\n * try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}\\n * doThing(..., value);\\n * }\\n *\\n * function doThing(..., uint256 value) public {\\n * token.safeTransferFrom(msg.sender, address(this), value);\\n * ...\\n * }\\n * ```\\n *\\n * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of\\n * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also\\n * {SafeERC20-safeTransferFrom}).\\n *\\n * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so\\n * contracts should have entry points that don't rely on permit.\\n */\\ninterface IERC20PermitUpgradeable {\\n /**\\n * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,\\n * given ``owner``'s signed approval.\\n *\\n * IMPORTANT: The same issues {IERC20-approve} has related to transaction\\n * ordering also apply here.\\n *\\n * Emits an {Approval} event.\\n *\\n * Requirements:\\n *\\n * - `spender` cannot be the zero address.\\n * - `deadline` must be a timestamp in the future.\\n * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`\\n * over the EIP712-formatted function arguments.\\n * - the signature must use ``owner``'s current nonce (see {nonces}).\\n *\\n * For more information on the signature format, see the\\n * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP\\n * section].\\n *\\n * CAUTION: See Security Considerations above.\\n */\\n function permit(\\n address owner,\\n address spender,\\n uint256 value,\\n uint256 deadline,\\n uint8 v,\\n bytes32 r,\\n bytes32 s\\n ) external;\\n\\n /**\\n * @dev Returns the current nonce for `owner`. This value must be\\n * included whenever a signature is generated for {permit}.\\n *\\n * Every successful call to {permit} increases ``owner``'s nonce by one. This\\n * prevents a signature from being used multiple times.\\n */\\n function nonces(address owner) external view returns (uint256);\\n\\n /**\\n * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.\\n */\\n // solhint-disable-next-line func-name-mixedcase\\n function DOMAIN_SEPARATOR() external view returns (bytes32);\\n}\\n\",\"keccak256\":\"0x07e881de3b9f6d2c07909f193f24b96c7fe4ea60013260f3f25aecd8bab3c2f8\",\"license\":\"MIT\"},\"@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts (last updated v4.9.3) (token/ERC20/utils/SafeERC20.sol)\\n\\npragma solidity ^0.8.0;\\n\\nimport \\\"../IERC20Upgradeable.sol\\\";\\nimport \\\"../extensions/IERC20PermitUpgradeable.sol\\\";\\nimport \\\"../../../utils/AddressUpgradeable.sol\\\";\\n\\n/**\\n * @title SafeERC20\\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\\n * contract returns false). Tokens that return no value (and instead revert or\\n * throw on failure) are also supported, non-reverting calls are assumed to be\\n * successful.\\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\\n */\\nlibrary SafeERC20Upgradeable {\\n using AddressUpgradeable for address;\\n\\n /**\\n * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,\\n * non-reverting calls are assumed to be successful.\\n */\\n function safeTransfer(IERC20Upgradeable token, address to, uint256 value) internal {\\n _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\\n }\\n\\n /**\\n * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the\\n * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.\\n */\\n function safeTransferFrom(IERC20Upgradeable token, address from, address to, uint256 value) internal {\\n _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\\n }\\n\\n /**\\n * @dev Deprecated. This function has issues similar to the ones found in\\n * {IERC20-approve}, and its usage is discouraged.\\n *\\n * Whenever possible, use {safeIncreaseAllowance} and\\n * {safeDecreaseAllowance} instead.\\n */\\n function safeApprove(IERC20Upgradeable token, address spender, uint256 value) internal {\\n // safeApprove should only be called when setting an initial allowance,\\n // or when resetting it to zero. To increase and decrease it, use\\n // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\\n require(\\n (value == 0) || (token.allowance(address(this), spender) == 0),\\n \\\"SafeERC20: approve from non-zero to non-zero allowance\\\"\\n );\\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\\n }\\n\\n /**\\n * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,\\n * non-reverting calls are assumed to be successful.\\n */\\n function safeIncreaseAllowance(IERC20Upgradeable token, address spender, uint256 value) internal {\\n uint256 oldAllowance = token.allowance(address(this), spender);\\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));\\n }\\n\\n /**\\n * @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,\\n * non-reverting calls are assumed to be successful.\\n */\\n function safeDecreaseAllowance(IERC20Upgradeable token, address spender, uint256 value) internal {\\n unchecked {\\n uint256 oldAllowance = token.allowance(address(this), spender);\\n require(oldAllowance >= value, \\\"SafeERC20: decreased allowance below zero\\\");\\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));\\n }\\n }\\n\\n /**\\n * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,\\n * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval\\n * to be set to zero before setting it to a non-zero value, such as USDT.\\n */\\n function forceApprove(IERC20Upgradeable token, address spender, uint256 value) internal {\\n bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);\\n\\n if (!_callOptionalReturnBool(token, approvalCall)) {\\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));\\n _callOptionalReturn(token, approvalCall);\\n }\\n }\\n\\n /**\\n * @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.\\n * Revert on invalid signature.\\n */\\n function safePermit(\\n IERC20PermitUpgradeable token,\\n address owner,\\n address spender,\\n uint256 value,\\n uint256 deadline,\\n uint8 v,\\n bytes32 r,\\n bytes32 s\\n ) internal {\\n uint256 nonceBefore = token.nonces(owner);\\n token.permit(owner, spender, value, deadline, v, r, s);\\n uint256 nonceAfter = token.nonces(owner);\\n require(nonceAfter == nonceBefore + 1, \\\"SafeERC20: permit did not succeed\\\");\\n }\\n\\n /**\\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\\n * on the return value: the return value is optional (but if data is returned, it must not be false).\\n * @param token The token targeted by the call.\\n * @param data The call data (encoded using abi.encode or one of its variants).\\n */\\n function _callOptionalReturn(IERC20Upgradeable token, bytes memory data) private {\\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\\n // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that\\n // the target address contains contract code and also asserts for success in the low-level call.\\n\\n bytes memory returndata = address(token).functionCall(data, \\\"SafeERC20: low-level call failed\\\");\\n require(returndata.length == 0 || abi.decode(returndata, (bool)), \\\"SafeERC20: ERC20 operation did not succeed\\\");\\n }\\n\\n /**\\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\\n * on the return value: the return value is optional (but if data is returned, it must not be false).\\n * @param token The token targeted by the call.\\n * @param data The call data (encoded using abi.encode or one of its variants).\\n *\\n * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.\\n */\\n function _callOptionalReturnBool(IERC20Upgradeable token, bytes memory data) private returns (bool) {\\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\\n // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false\\n // and not revert is the subcall reverts.\\n\\n (bool success, bytes memory returndata) = address(token).call(data);\\n return\\n success && (returndata.length == 0 || abi.decode(returndata, (bool))) && AddressUpgradeable.isContract(address(token));\\n }\\n}\\n\",\"keccak256\":\"0x23b997be73d3dd46885262704f0f8cfc7273fdadfe303d37969a9561373972b5\",\"license\":\"MIT\"},\"@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)\\n\\npragma solidity ^0.8.1;\\n\\n/**\\n * @dev Collection of functions related to the address type\\n */\\nlibrary AddressUpgradeable {\\n /**\\n * @dev Returns true if `account` is a contract.\\n *\\n * [IMPORTANT]\\n * ====\\n * It is unsafe to assume that an address for which this function returns\\n * false is an externally-owned account (EOA) and not a contract.\\n *\\n * Among others, `isContract` will return false for the following\\n * types of addresses:\\n *\\n * - an externally-owned account\\n * - a contract in construction\\n * - an address where a contract will be created\\n * - an address where a contract lived, but was destroyed\\n *\\n * Furthermore, `isContract` will also return true if the target contract within\\n * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,\\n * which only has an effect at the end of a transaction.\\n * ====\\n *\\n * [IMPORTANT]\\n * ====\\n * You shouldn't rely on `isContract` to protect against flash loan attacks!\\n *\\n * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets\\n * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract\\n * constructor.\\n * ====\\n */\\n function isContract(address account) internal view returns (bool) {\\n // This method relies on extcodesize/address.code.length, which returns 0\\n // for contracts in construction, since the code is only stored at the end\\n // of the constructor execution.\\n\\n return account.code.length > 0;\\n }\\n\\n /**\\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\\n * `recipient`, forwarding all available gas and reverting on errors.\\n *\\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\\n * imposed by `transfer`, making them unable to receive funds via\\n * `transfer`. {sendValue} removes this limitation.\\n *\\n * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].\\n *\\n * IMPORTANT: because control is transferred to `recipient`, care must be\\n * taken to not create reentrancy vulnerabilities. Consider using\\n * {ReentrancyGuard} or the\\n * https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\\n */\\n function sendValue(address payable recipient, uint256 amount) internal {\\n require(address(this).balance >= amount, \\\"Address: insufficient balance\\\");\\n\\n (bool success, ) = recipient.call{value: amount}(\\\"\\\");\\n require(success, \\\"Address: unable to send value, recipient may have reverted\\\");\\n }\\n\\n /**\\n * @dev Performs a Solidity function call using a low level `call`. A\\n * plain `call` is an unsafe replacement for a function call: use this\\n * function instead.\\n *\\n * If `target` reverts with a revert reason, it is bubbled up by this\\n * function (like regular Solidity function calls).\\n *\\n * Returns the raw returned data. To convert to the expected return value,\\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\\n *\\n * Requirements:\\n *\\n * - `target` must be a contract.\\n * - calling `target` with `data` must not revert.\\n *\\n * _Available since v3.1._\\n */\\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\\n return functionCallWithValue(target, data, 0, \\\"Address: low-level call failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\\n * `errorMessage` as a fallback revert reason when `target` reverts.\\n *\\n * _Available since v3.1._\\n */\\n function functionCall(\\n address target,\\n bytes memory data,\\n string memory errorMessage\\n ) internal returns (bytes memory) {\\n return functionCallWithValue(target, data, 0, errorMessage);\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\\n * but also transferring `value` wei to `target`.\\n *\\n * Requirements:\\n *\\n * - the calling contract must have an ETH balance of at least `value`.\\n * - the called Solidity function must be `payable`.\\n *\\n * _Available since v3.1._\\n */\\n function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {\\n return functionCallWithValue(target, data, value, \\\"Address: low-level call with value failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\\n * with `errorMessage` as a fallback revert reason when `target` reverts.\\n *\\n * _Available since v3.1._\\n */\\n function functionCallWithValue(\\n address target,\\n bytes memory data,\\n uint256 value,\\n string memory errorMessage\\n ) internal returns (bytes memory) {\\n require(address(this).balance >= value, \\\"Address: insufficient balance for call\\\");\\n (bool success, bytes memory returndata) = target.call{value: value}(data);\\n return verifyCallResultFromTarget(target, success, returndata, errorMessage);\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\\n * but performing a static call.\\n *\\n * _Available since v3.3._\\n */\\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\\n return functionStaticCall(target, data, \\\"Address: low-level static call failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\\n * but performing a static call.\\n *\\n * _Available since v3.3._\\n */\\n function functionStaticCall(\\n address target,\\n bytes memory data,\\n string memory errorMessage\\n ) internal view returns (bytes memory) {\\n (bool success, bytes memory returndata) = target.staticcall(data);\\n return verifyCallResultFromTarget(target, success, returndata, errorMessage);\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\\n * but performing a delegate call.\\n *\\n * _Available since v3.4._\\n */\\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\\n return functionDelegateCall(target, data, \\\"Address: low-level delegate call failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\\n * but performing a delegate call.\\n *\\n * _Available since v3.4._\\n */\\n function functionDelegateCall(\\n address target,\\n bytes memory data,\\n string memory errorMessage\\n ) internal returns (bytes memory) {\\n (bool success, bytes memory returndata) = target.delegatecall(data);\\n return verifyCallResultFromTarget(target, success, returndata, errorMessage);\\n }\\n\\n /**\\n * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling\\n * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.\\n *\\n * _Available since v4.8._\\n */\\n function verifyCallResultFromTarget(\\n address target,\\n bool success,\\n bytes memory returndata,\\n string memory errorMessage\\n ) internal view returns (bytes memory) {\\n if (success) {\\n if (returndata.length == 0) {\\n // only check isContract if the call was successful and the return data is empty\\n // otherwise we already know that it was a contract\\n require(isContract(target), \\\"Address: call to non-contract\\\");\\n }\\n return returndata;\\n } else {\\n _revert(returndata, errorMessage);\\n }\\n }\\n\\n /**\\n * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the\\n * revert reason or using the provided one.\\n *\\n * _Available since v4.3._\\n */\\n function verifyCallResult(\\n bool success,\\n bytes memory returndata,\\n string memory errorMessage\\n ) internal pure returns (bytes memory) {\\n if (success) {\\n return returndata;\\n } else {\\n _revert(returndata, errorMessage);\\n }\\n }\\n\\n function _revert(bytes memory returndata, string memory errorMessage) private pure {\\n // Look for revert reason and bubble it up if present\\n if (returndata.length > 0) {\\n // The easiest way to bubble the revert reason is using memory via assembly\\n /// @solidity memory-safe-assembly\\n assembly {\\n let returndata_size := mload(returndata)\\n revert(add(32, returndata), returndata_size)\\n }\\n } else {\\n revert(errorMessage);\\n }\\n }\\n}\\n\",\"keccak256\":\"0x9c80f545915582e63fe206c6ce27cbe85a86fc10b9cd2a0e8c9488fb7c2ee422\",\"license\":\"MIT\"},\"@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts (last updated v4.9.4) (utils/Context.sol)\\n\\npragma solidity ^0.8.0;\\nimport {Initializable} from \\\"../proxy/utils/Initializable.sol\\\";\\n\\n/**\\n * @dev Provides information about the current execution context, including the\\n * sender of the transaction and its data. While these are generally available\\n * via msg.sender and msg.data, they should not be accessed in such a direct\\n * manner, since when dealing with meta-transactions the account sending and\\n * paying for execution may not be the actual sender (as far as an application\\n * is concerned).\\n *\\n * This contract is only required for intermediate, library-like contracts.\\n */\\nabstract contract ContextUpgradeable is Initializable {\\n function __Context_init() internal onlyInitializing {\\n }\\n\\n function __Context_init_unchained() internal onlyInitializing {\\n }\\n function _msgSender() internal view virtual returns (address) {\\n return msg.sender;\\n }\\n\\n function _msgData() internal view virtual returns (bytes calldata) {\\n return msg.data;\\n }\\n\\n function _contextSuffixLength() internal view virtual returns (uint256) {\\n return 0;\\n }\\n\\n /**\\n * @dev This empty reserved space is put in place to allow future versions to add new\\n * variables without shifting down storage in the inheritance chain.\\n * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\\n */\\n uint256[50] private __gap;\\n}\\n\",\"keccak256\":\"0x75097e35253e7fb282ee4d7f27a80eaacfa759923185bf17302a89cbc059c5ef\",\"license\":\"MIT\"},\"@openzeppelin/contracts/access/IAccessControl.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev External interface of AccessControl declared to support ERC165 detection.\\n */\\ninterface IAccessControl {\\n /**\\n * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`\\n *\\n * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite\\n * {RoleAdminChanged} not being emitted signaling this.\\n *\\n * _Available since v3.1._\\n */\\n event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);\\n\\n /**\\n * @dev Emitted when `account` is granted `role`.\\n *\\n * `sender` is the account that originated the contract call, an admin role\\n * bearer except when using {AccessControl-_setupRole}.\\n */\\n event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);\\n\\n /**\\n * @dev Emitted when `account` is revoked `role`.\\n *\\n * `sender` is the account that originated the contract call:\\n * - if using `revokeRole`, it is the admin role bearer\\n * - if using `renounceRole`, it is the role bearer (i.e. `account`)\\n */\\n event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);\\n\\n /**\\n * @dev Returns `true` if `account` has been granted `role`.\\n */\\n function hasRole(bytes32 role, address account) external view returns (bool);\\n\\n /**\\n * @dev Returns the admin role that controls `role`. See {grantRole} and\\n * {revokeRole}.\\n *\\n * To change a role's admin, use {AccessControl-_setRoleAdmin}.\\n */\\n function getRoleAdmin(bytes32 role) external view returns (bytes32);\\n\\n /**\\n * @dev Grants `role` to `account`.\\n *\\n * If `account` had not been already granted `role`, emits a {RoleGranted}\\n * event.\\n *\\n * Requirements:\\n *\\n * - the caller must have ``role``'s admin role.\\n */\\n function grantRole(bytes32 role, address account) external;\\n\\n /**\\n * @dev Revokes `role` from `account`.\\n *\\n * If `account` had been granted `role`, emits a {RoleRevoked} event.\\n *\\n * Requirements:\\n *\\n * - the caller must have ``role``'s admin role.\\n */\\n function revokeRole(bytes32 role, address account) external;\\n\\n /**\\n * @dev Revokes `role` from the calling account.\\n *\\n * Roles are often managed via {grantRole} and {revokeRole}: this function's\\n * purpose is to provide a mechanism for accounts to lose their privileges\\n * if they are compromised (such as when a trusted device is misplaced).\\n *\\n * If the calling account had been granted `role`, emits a {RoleRevoked}\\n * event.\\n *\\n * Requirements:\\n *\\n * - the caller must be `account`.\\n */\\n function renounceRole(bytes32 role, address account) external;\\n}\\n\",\"keccak256\":\"0x59ce320a585d7e1f163cd70390a0ef2ff9cec832e2aa544293a00692465a7a57\",\"license\":\"MIT\"},\"@venusprotocol/governance-contracts/contracts/Governance/AccessControlledV8.sol\":{\"content\":\"// SPDX-License-Identifier: BSD-3-Clause\\npragma solidity 0.8.25;\\n\\nimport \\\"@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol\\\";\\nimport \\\"@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol\\\";\\n\\nimport \\\"./IAccessControlManagerV8.sol\\\";\\n\\n/**\\n * @title AccessControlledV8\\n * @author Venus\\n * @notice This contract is helper between access control manager and actual contract. This contract further inherited by other contract (using solidity 0.8.13)\\n * to integrate access controlled mechanism. It provides initialise methods and verifying access methods.\\n */\\nabstract contract AccessControlledV8 is Initializable, Ownable2StepUpgradeable {\\n /// @notice Access control manager contract\\n IAccessControlManagerV8 internal _accessControlManager;\\n\\n /**\\n * @dev This empty reserved space is put in place to allow future versions to add new\\n * variables without shifting down storage in the inheritance chain.\\n * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\\n */\\n uint256[49] private __gap;\\n\\n /// @notice Emitted when access control manager contract address is changed\\n event NewAccessControlManager(address oldAccessControlManager, address newAccessControlManager);\\n\\n /// @notice Thrown when the action is prohibited by AccessControlManager\\n error Unauthorized(address sender, address calledContract, string methodSignature);\\n\\n function __AccessControlled_init(address accessControlManager_) internal onlyInitializing {\\n __Ownable2Step_init();\\n __AccessControlled_init_unchained(accessControlManager_);\\n }\\n\\n function __AccessControlled_init_unchained(address accessControlManager_) internal onlyInitializing {\\n _setAccessControlManager(accessControlManager_);\\n }\\n\\n /**\\n * @notice Sets the address of AccessControlManager\\n * @dev Admin function to set address of AccessControlManager\\n * @param accessControlManager_ The new address of the AccessControlManager\\n * @custom:event Emits NewAccessControlManager event\\n * @custom:access Only Governance\\n */\\n function setAccessControlManager(address accessControlManager_) external onlyOwner {\\n _setAccessControlManager(accessControlManager_);\\n }\\n\\n /**\\n * @notice Returns the address of the access control manager contract\\n */\\n function accessControlManager() external view returns (IAccessControlManagerV8) {\\n return _accessControlManager;\\n }\\n\\n /**\\n * @dev Internal function to set address of AccessControlManager\\n * @param accessControlManager_ The new address of the AccessControlManager\\n */\\n function _setAccessControlManager(address accessControlManager_) internal {\\n require(address(accessControlManager_) != address(0), \\\"invalid acess control manager address\\\");\\n address oldAccessControlManager = address(_accessControlManager);\\n _accessControlManager = IAccessControlManagerV8(accessControlManager_);\\n emit NewAccessControlManager(oldAccessControlManager, accessControlManager_);\\n }\\n\\n /**\\n * @notice Reverts if the call is not allowed by AccessControlManager\\n * @param signature Method signature\\n */\\n function _checkAccessAllowed(string memory signature) internal view {\\n bool isAllowedToCall = _accessControlManager.isAllowedToCall(msg.sender, signature);\\n\\n if (!isAllowedToCall) {\\n revert Unauthorized(msg.sender, address(this), signature);\\n }\\n }\\n}\\n\",\"keccak256\":\"0xfe0fd441c84ac907cabc88db69ef04f6d7532d770c7e6a1dfe6e7d6305debb49\",\"license\":\"BSD-3-Clause\"},\"@venusprotocol/governance-contracts/contracts/Governance/IAccessControlManagerV8.sol\":{\"content\":\"// SPDX-License-Identifier: BSD-3-Clause\\npragma solidity ^0.8.25;\\n\\nimport \\\"@openzeppelin/contracts/access/IAccessControl.sol\\\";\\n\\n/**\\n * @title IAccessControlManagerV8\\n * @author Venus\\n * @notice Interface implemented by the `AccessControlManagerV8` contract.\\n */\\ninterface IAccessControlManagerV8 is IAccessControl {\\n function giveCallPermission(address contractAddress, string calldata functionSig, address accountToPermit) external;\\n\\n function revokeCallPermission(\\n address contractAddress,\\n string calldata functionSig,\\n address accountToRevoke\\n ) external;\\n\\n function isAllowedToCall(address account, string calldata functionSig) external view returns (bool);\\n\\n function hasPermission(\\n address account,\\n address contractAddress,\\n string calldata functionSig\\n ) external view returns (bool);\\n}\\n\",\"keccak256\":\"0xaa29b098440d0b3a131c5ecdf25ce548790c1b5ac7bf9b5c0264b6af6f7a1e0b\",\"license\":\"BSD-3-Clause\"},\"@venusprotocol/oracle/contracts/interfaces/OracleInterface.sol\":{\"content\":\"// SPDX-License-Identifier: BSD-3-Clause\\npragma solidity ^0.8.25;\\n\\ninterface OracleInterface {\\n function getPrice(address asset) external view returns (uint256);\\n}\\n\\ninterface ResilientOracleInterface is OracleInterface {\\n function updatePrice(address vToken) external;\\n\\n function updateAssetPrice(address asset) external;\\n\\n function getUnderlyingPrice(address vToken) external view returns (uint256);\\n}\\n\\ninterface BoundValidatorInterface {\\n function validatePriceWithAnchorPrice(\\n address asset,\\n uint256 reporterPrice,\\n uint256 anchorPrice\\n ) external view returns (bool);\\n}\\n\",\"keccak256\":\"0xd3bbb7c9eef19e8f467342df6034ef95399a00964646fb8c82b438968ae3a8c0\",\"license\":\"BSD-3-Clause\"},\"@venusprotocol/protocol-reserve/contracts/Interfaces/IProtocolShareReserve.sol\":{\"content\":\"// SPDX-License-Identifier: BSD-3-Clause\\npragma solidity ^0.8.25;\\n\\ninterface IProtocolShareReserve {\\n /// @notice it represents the type of vToken income\\n enum IncomeType {\\n SPREAD,\\n LIQUIDATION,\\n ERC4626_WRAPPER_REWARDS,\\n FLASHLOAN\\n }\\n\\n function updateAssetsState(\\n address comptroller,\\n address asset,\\n IncomeType incomeType\\n ) external;\\n}\\n\",\"keccak256\":\"0x0f341bbef8f12885d79b7acaad7aaf11b84809e8f6b059ef4efc011c8f6c39a5\",\"license\":\"BSD-3-Clause\"},\"@venusprotocol/solidity-utilities/contracts/TimeManagerV8.sol\":{\"content\":\"// SPDX-License-Identifier: BSD-3-Clause\\npragma solidity 0.8.25;\\n\\nimport { SECONDS_PER_YEAR } from \\\"./constants.sol\\\";\\n\\nabstract contract TimeManagerV8 {\\n /// @notice Stores blocksPerYear if isTimeBased is true else secondsPerYear is stored\\n /// @custom:oz-upgrades-unsafe-allow state-variable-immutable\\n uint256 public immutable blocksOrSecondsPerYear;\\n\\n /// @notice Acknowledges if a contract is time based or not\\n /// @custom:oz-upgrades-unsafe-allow state-variable-immutable\\n bool public immutable isTimeBased;\\n\\n /// @notice Stores the current block timestamp or block number depending on isTimeBased\\n /// @custom:oz-upgrades-unsafe-allow state-variable-immutable\\n function() view returns (uint256) private immutable _getCurrentSlot;\\n\\n /**\\n * @dev This empty reserved space is put in place to allow future versions to add new\\n * variables without shifting down storage in the inheritance chain\\n * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\\n */\\n uint256[48] private __gap;\\n\\n /// @notice Thrown when blocks per year is invalid\\n error InvalidBlocksPerYear();\\n\\n /// @notice Thrown when time based but blocks per year is provided\\n error InvalidTimeBasedConfiguration();\\n\\n /**\\n * @param timeBased_ A boolean indicating whether the contract is based on time or block\\n * If timeBased is true than blocksPerYear_ param is ignored as blocksOrSecondsPerYear is set to SECONDS_PER_YEAR\\n * @param blocksPerYear_ The number of blocks per year\\n * @custom:error InvalidBlocksPerYear is thrown if blocksPerYear entered is zero and timeBased is false\\n * @custom:error InvalidTimeBasedConfiguration is thrown if blocksPerYear entered is non zero and timeBased is true\\n * @custom:oz-upgrades-unsafe-allow constructor\\n */\\n constructor(bool timeBased_, uint256 blocksPerYear_) {\\n if (!timeBased_ && blocksPerYear_ == 0) {\\n revert InvalidBlocksPerYear();\\n }\\n\\n if (timeBased_ && blocksPerYear_ != 0) {\\n revert InvalidTimeBasedConfiguration();\\n }\\n\\n isTimeBased = timeBased_;\\n blocksOrSecondsPerYear = timeBased_ ? SECONDS_PER_YEAR : blocksPerYear_;\\n _getCurrentSlot = timeBased_ ? _getBlockTimestamp : _getBlockNumber;\\n }\\n\\n /**\\n * @dev Function to simply retrieve block number or block timestamp\\n * @return Current block number or block timestamp\\n */\\n function getBlockNumberOrTimestamp() public view virtual returns (uint256) {\\n return _getCurrentSlot();\\n }\\n\\n /**\\n * @dev Returns the current timestamp in seconds\\n * @return The current timestamp\\n */\\n function _getBlockTimestamp() private view returns (uint256) {\\n return block.timestamp;\\n }\\n\\n /**\\n * @dev Returns the current block number\\n * @return The current block number\\n */\\n function _getBlockNumber() private view returns (uint256) {\\n return block.number;\\n }\\n}\\n\",\"keccak256\":\"0x57a2bbb9b8e02b1c0a5c0e305fef1328a22db56c3d4b148c362010a6e767243c\",\"license\":\"BSD-3-Clause\"},\"@venusprotocol/solidity-utilities/contracts/constants.sol\":{\"content\":\"// SPDX-License-Identifier: BSD-3-Clause\\npragma solidity ^0.8.25;\\n\\n/// @dev Base unit for computations, usually used in scaling (multiplications, divisions)\\nuint256 constant EXP_SCALE = 1e18;\\n\\n/// @dev A unit (literal one) in EXP_SCALE, usually used in additions/subtractions\\nuint256 constant MANTISSA_ONE = EXP_SCALE;\\n\\n/// @dev The approximate number of seconds per year\\nuint256 constant SECONDS_PER_YEAR = 31_536_000;\\n\",\"keccak256\":\"0x14de93ead464da249af31bea0e3bcfb62ec693bea3475fb4d90f055ac81dc5eb\",\"license\":\"BSD-3-Clause\"},\"@venusprotocol/venus-protocol/contracts/Tokens/Prime/Interfaces/IPrime.sol\":{\"content\":\"// SPDX-License-Identifier: BSD-3-Clause\\npragma solidity ^0.8.25;\\n\\nimport { PrimeStorageV1 } from \\\"../PrimeStorage.sol\\\";\\n\\n/**\\n * @title IPrime\\n * @author Venus\\n * @notice Interface for Prime Token\\n */\\ninterface IPrime {\\n struct APRInfo {\\n // supply APR of the user in BPS\\n uint256 supplyAPR;\\n // borrow APR of the user in BPS\\n uint256 borrowAPR;\\n // total score of the market\\n uint256 totalScore;\\n // score of the user\\n uint256 userScore;\\n // capped XVS balance of the user\\n uint256 xvsBalanceForScore;\\n // capital of the user\\n uint256 capital;\\n // capped supply of the user\\n uint256 cappedSupply;\\n // capped borrow of the user\\n uint256 cappedBorrow;\\n // capped supply of user in USD\\n uint256 supplyCapUSD;\\n // capped borrow of user in USD\\n uint256 borrowCapUSD;\\n }\\n\\n struct Capital {\\n // capital of the user\\n uint256 capital;\\n // capped supply of the user\\n uint256 cappedSupply;\\n // capped borrow of the user\\n uint256 cappedBorrow;\\n // capped supply of user in USD\\n uint256 supplyCapUSD;\\n // capped borrow of user in USD\\n uint256 borrowCapUSD;\\n }\\n\\n /**\\n * @notice Returns boosted pending interest accrued for a user for all markets\\n * @param user the account for which to get the accrued interests\\n * @return pendingRewards the number of underlying tokens accrued by the user for all markets\\n */\\n function getPendingRewards(address user) external returns (PrimeStorageV1.PendingReward[] memory pendingRewards);\\n\\n /**\\n * @notice Update total score of multiple users and market\\n * @param users accounts for which we need to update score\\n */\\n function updateScores(address[] memory users) external;\\n\\n /**\\n * @notice Update value of alpha\\n * @param _alphaNumerator numerator of alpha. If alpha is 0.5 then numerator is 1\\n * @param _alphaDenominator denominator of alpha. If alpha is 0.5 then denominator is 2\\n */\\n function updateAlpha(uint128 _alphaNumerator, uint128 _alphaDenominator) external;\\n\\n /**\\n * @notice Update multipliers for a market\\n * @param market address of the market vToken\\n * @param supplyMultiplier new supply multiplier for the market, scaled by 1e18\\n * @param borrowMultiplier new borrow multiplier for the market, scaled by 1e18\\n */\\n function updateMultipliers(address market, uint256 supplyMultiplier, uint256 borrowMultiplier) external;\\n\\n /**\\n * @notice Add a market to prime program\\n * @param comptroller address of the comptroller\\n * @param market address of the market vToken\\n * @param supplyMultiplier the multiplier for supply cap. It should be converted to 1e18\\n * @param borrowMultiplier the multiplier for borrow cap. It should be converted to 1e18\\n */\\n function addMarket(\\n address comptroller,\\n address market,\\n uint256 supplyMultiplier,\\n uint256 borrowMultiplier\\n ) external;\\n\\n /**\\n * @notice Set limits for total tokens that can be minted\\n * @param _irrevocableLimit total number of irrevocable tokens that can be minted\\n * @param _revocableLimit total number of revocable tokens that can be minted\\n */\\n function setLimit(uint256 _irrevocableLimit, uint256 _revocableLimit) external;\\n\\n /**\\n * @notice Directly issue prime tokens to users\\n * @param isIrrevocable are the tokens being issued\\n * @param users list of address to issue tokens to\\n */\\n function issue(bool isIrrevocable, address[] calldata users) external;\\n\\n /**\\n * @notice Executed by XVSVault whenever user's XVSVault balance changes\\n * @param user the account address whose balance was updated\\n */\\n function xvsUpdated(address user) external;\\n\\n /**\\n * @notice accrues interest and updates score for an user for a specific market\\n * @param user the account address for which to accrue interest and update score\\n * @param market the market for which to accrue interest and update score\\n */\\n function accrueInterestAndUpdateScore(address user, address market) external;\\n\\n /**\\n * @notice For claiming prime token when staking period is completed\\n */\\n function claim() external;\\n\\n /**\\n * @notice For burning any prime token\\n * @param user the account address for which the prime token will be burned\\n */\\n function burn(address user) external;\\n\\n /**\\n * @notice To pause or unpause claiming of interest\\n */\\n function togglePause() external;\\n\\n /**\\n * @notice For user to claim boosted yield\\n * @param vToken the market for which claim the accrued interest\\n * @return amount the amount of tokens transferred to the user\\n */\\n function claimInterest(address vToken) external returns (uint256);\\n\\n /**\\n * @notice For user to claim boosted yield\\n * @param vToken the market for which claim the accrued interest\\n * @param user the user for which to claim the accrued interest\\n * @return amount the amount of tokens transferred to the user\\n */\\n function claimInterest(address vToken, address user) external returns (uint256);\\n\\n /**\\n * @notice Distributes income from market since last distribution\\n * @param vToken the market for which to distribute the income\\n */\\n function accrueInterest(address vToken) external;\\n\\n /**\\n * @notice Returns boosted interest accrued for a user\\n * @param vToken the market for which to fetch the accrued interest\\n * @param user the account for which to get the accrued interest\\n * @return interestAccrued the number of underlying tokens accrued by the user since the last accrual\\n */\\n function getInterestAccrued(address vToken, address user) external returns (uint256);\\n\\n /**\\n * @notice Retrieves an array of all available markets\\n * @return an array of addresses representing all available markets\\n */\\n function getAllMarkets() external view returns (address[] memory);\\n\\n /**\\n * @notice fetch the numbers of seconds remaining for staking period to complete\\n * @param user the account address for which we are checking the remaining time\\n * @return timeRemaining the number of seconds the user needs to wait to claim prime token\\n */\\n function claimTimeRemaining(address user) external view returns (uint256);\\n\\n /**\\n * @notice Returns supply and borrow APR for user for a given market\\n * @param market the market for which to fetch the APR\\n * @param user the account for which to get the APR\\n * @return aprInfo APR information for the user for the given market\\n */\\n function calculateAPR(address market, address user) external view returns (APRInfo memory aprInfo);\\n\\n /**\\n * @notice Returns supply and borrow APR for estimated supply, borrow and XVS staked\\n * @param market the market for which to fetch the APR\\n * @param user the account for which to get the APR\\n * @param borrow hypothetical borrow amount\\n * @param supply hypothetical supply amount\\n * @param xvsStaked hypothetical staked XVS amount\\n * @return aprInfo APR information for the user for the given market\\n */\\n function estimateAPR(\\n address market,\\n address user,\\n uint256 borrow,\\n uint256 supply,\\n uint256 xvsStaked\\n ) external view returns (APRInfo memory aprInfo);\\n\\n /**\\n * @notice the total income that's going to be distributed in a year to prime token holders\\n * @param vToken the market for which to fetch the total income that's going to distributed in a year\\n * @return amount the total income\\n */\\n function incomeDistributionYearly(address vToken) external view returns (uint256 amount);\\n\\n /**\\n * @notice Returns if user is a prime holder\\n * @return isPrimeHolder true if user is a prime holder\\n */\\n function isUserPrimeHolder(address user) external view returns (bool);\\n\\n /**\\n * @notice Set the limit for the loops can iterate to avoid the DOS\\n * @param loopsLimit Number of loops limit\\n */\\n function setMaxLoopsLimit(uint256 loopsLimit) external;\\n\\n /**\\n * @notice Update staked at timestamp for multiple users\\n * @param users accounts for which we need to update staked at timestamp\\n * @param timestamps new staked at timestamp for the users\\n */\\n function setStakedAt(address[] calldata users, uint256[] calldata timestamps) external;\\n}\\n\",\"keccak256\":\"0xd112f60183416ad796093b4a83a80ddc1b8b655965f02ae55ca82e2a0c68f97d\",\"license\":\"BSD-3-Clause\"},\"@venusprotocol/venus-protocol/contracts/Tokens/Prime/PrimeStorage.sol\":{\"content\":\"// SPDX-License-Identifier: BSD-3-Clause\\npragma solidity 0.8.25;\\n\\nimport { ResilientOracleInterface } from \\\"@venusprotocol/oracle/contracts/interfaces/OracleInterface.sol\\\";\\n\\n/**\\n * @title PrimeStorageV1\\n * @author Venus\\n * @notice Storage for Prime Token\\n */\\ncontract PrimeStorageV1 {\\n struct Token {\\n bool exists;\\n bool isIrrevocable;\\n }\\n\\n struct Market {\\n uint256 supplyMultiplier;\\n uint256 borrowMultiplier;\\n uint256 rewardIndex;\\n uint256 sumOfMembersScore;\\n bool exists;\\n }\\n\\n struct Interest {\\n uint256 accrued;\\n uint256 score;\\n uint256 rewardIndex;\\n }\\n\\n struct PendingReward {\\n address vToken;\\n address rewardToken;\\n uint256 amount;\\n }\\n\\n /// @notice Base unit for computations, usually used in scaling (multiplications, divisions)\\n uint256 internal constant EXP_SCALE = 1e18;\\n\\n /// @notice maximum BPS = 100%\\n uint256 internal constant MAXIMUM_BPS = 1e4;\\n\\n /// @notice Mapping to get prime token's metadata\\n mapping(address => Token) public tokens;\\n\\n /// @notice Tracks total irrevocable tokens minted\\n uint256 public totalIrrevocable;\\n\\n /// @notice Tracks total revocable tokens minted\\n uint256 public totalRevocable;\\n\\n /// @notice Indicates maximum revocable tokens that can be minted\\n uint256 public revocableLimit;\\n\\n /// @notice Indicates maximum irrevocable tokens that can be minted\\n uint256 public irrevocableLimit;\\n\\n /// @notice Tracks when prime token eligible users started staking for claiming prime token\\n mapping(address => uint256) public stakedAt;\\n\\n /// @notice vToken to market configuration\\n mapping(address => Market) public markets;\\n\\n /// @notice vToken to user to user index\\n mapping(address => mapping(address => Interest)) public interests;\\n\\n /// @notice A list of boosted markets\\n address[] internal _allMarkets;\\n\\n /// @notice numerator of alpha. Ex: if alpha is 0.5 then this will be 1\\n uint128 public alphaNumerator;\\n\\n /// @notice denominator of alpha. Ex: if alpha is 0.5 then this will be 2\\n uint128 public alphaDenominator;\\n\\n /// @notice address of XVS vault\\n address public xvsVault;\\n\\n /// @notice address of XVS vault reward token\\n address public xvsVaultRewardToken;\\n\\n /// @notice address of XVS vault pool id\\n uint256 public xvsVaultPoolId;\\n\\n /// @notice mapping to check if a account's score was updated in the round\\n mapping(uint256 => mapping(address => bool)) public isScoreUpdated;\\n\\n /// @notice unique id for next round\\n uint256 public nextScoreUpdateRoundId;\\n\\n /// @notice total number of accounts whose score needs to be updated\\n uint256 public totalScoreUpdatesRequired;\\n\\n /// @notice total number of accounts whose score is yet to be updated\\n uint256 public pendingScoreUpdates;\\n\\n /// @notice mapping used to find if an asset is part of prime markets\\n mapping(address => address) public vTokenForAsset;\\n\\n /// @notice Address of core pool comptroller contract\\n address internal corePoolComptroller;\\n\\n /// @notice unreleased income from PLP that's already distributed to prime holders\\n /// @dev mapping of asset address => amount\\n mapping(address => uint256) public unreleasedPLPIncome;\\n\\n /// @notice The address of PLP contract\\n address public primeLiquidityProvider;\\n\\n /// @notice The address of ResilientOracle contract\\n ResilientOracleInterface public oracle;\\n\\n /// @notice The address of PoolRegistry contract\\n address public poolRegistry;\\n\\n /// @dev This empty reserved space is put in place to allow future versions to add new\\n /// variables without shifting down storage in the inheritance chain.\\n uint256[26] private __gap;\\n}\\n\",\"keccak256\":\"0x5285c875114db2ea2be0b81b65722d5761806217022db733323c4e03365a95e7\",\"license\":\"BSD-3-Clause\"},\"contracts/Comptroller.sol\":{\"content\":\"// SPDX-License-Identifier: BSD-3-Clause\\npragma solidity 0.8.25;\\n\\nimport { Ownable2StepUpgradeable } from \\\"@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol\\\";\\nimport { ResilientOracleInterface } from \\\"@venusprotocol/oracle/contracts/interfaces/OracleInterface.sol\\\";\\nimport { AccessControlledV8 } from \\\"@venusprotocol/governance-contracts/contracts/Governance/AccessControlledV8.sol\\\";\\nimport { IPrime } from \\\"@venusprotocol/venus-protocol/contracts/Tokens/Prime/Interfaces/IPrime.sol\\\";\\n\\nimport { ComptrollerInterface, Action } from \\\"./ComptrollerInterface.sol\\\";\\nimport { ComptrollerStorage } from \\\"./ComptrollerStorage.sol\\\";\\nimport { ExponentialNoError } from \\\"./ExponentialNoError.sol\\\";\\nimport { VToken } from \\\"./VToken.sol\\\";\\nimport { RewardsDistributor } from \\\"./Rewards/RewardsDistributor.sol\\\";\\nimport { MaxLoopsLimitHelper } from \\\"./MaxLoopsLimitHelper.sol\\\";\\nimport { ensureNonzeroAddress } from \\\"./lib/validators.sol\\\";\\n\\n/**\\n * @title Comptroller\\n * @author Venus\\n * @notice The Comptroller is designed to provide checks for all minting, redeeming, transferring, borrowing, lending, repaying, liquidating,\\n * and seizing done by the `vToken` contract. Each pool has one `Comptroller` checking these interactions across markets. When a user interacts\\n * with a given market by one of these main actions, a call is made to a corresponding hook in the associated `Comptroller`, which either allows\\n * or reverts the transaction. These hooks also update supply and borrow rewards as they are called. The comptroller holds the logic for assessing\\n * liquidity snapshots of an account via the collateral factor and liquidation threshold. This check determines the collateral needed for a borrow,\\n * as well as how much of a borrow may be liquidated. A user may borrow a portion of their collateral with the maximum amount determined by the\\n * markets collateral factor. However, if their borrowed amount exceeds an amount calculated using the market\\u2019s corresponding liquidation threshold,\\n * the borrow is eligible for liquidation.\\n *\\n * The `Comptroller` also includes two functions `liquidateAccount()` and `healAccount()`, which are meant to handle accounts that do not exceed\\n * the `minLiquidatableCollateral` for the `Comptroller`:\\n *\\n * - `healAccount()`: This function is called to seize all of a given user\\u2019s collateral, requiring the `msg.sender` repay a certain percentage\\n * of the debt calculated by `collateral/(borrows*liquidationIncentive)`. The function can only be called if the calculated percentage does not exceed\\n * 100%, because otherwise no `badDebt` would be created and `liquidateAccount()` should be used instead. The difference in the actual amount of debt\\n * and debt paid off is recorded as `badDebt` for each market, which can then be auctioned off for the risk reserves of the associated pool.\\n * - `liquidateAccount()`: This function can only be called if the collateral seized will cover all borrows of an account, as well as the liquidation\\n * incentive. Otherwise, the pool will incur bad debt, in which case the function `healAccount()` should be used instead. This function skips the logic\\n * verifying that the repay amount does not exceed the close factor.\\n */\\ncontract Comptroller is\\n Ownable2StepUpgradeable,\\n AccessControlledV8,\\n ComptrollerStorage,\\n ComptrollerInterface,\\n ExponentialNoError,\\n MaxLoopsLimitHelper\\n{\\n // PoolRegistry, immutable to save on gas\\n /// @custom:oz-upgrades-unsafe-allow state-variable-immutable\\n address public immutable poolRegistry;\\n\\n /// @notice Emitted when an account enters a market\\n event MarketEntered(VToken indexed vToken, address indexed account);\\n\\n /// @notice Emitted when an account exits a market\\n event MarketExited(VToken indexed vToken, address indexed account);\\n\\n /// @notice Emitted when close factor is changed by admin\\n event NewCloseFactor(uint256 oldCloseFactorMantissa, uint256 newCloseFactorMantissa);\\n\\n /// @notice Emitted when a collateral factor is changed by admin\\n event NewCollateralFactor(VToken vToken, uint256 oldCollateralFactorMantissa, uint256 newCollateralFactorMantissa);\\n\\n /// @notice Emitted when liquidation threshold is changed by admin\\n event NewLiquidationThreshold(\\n VToken vToken,\\n uint256 oldLiquidationThresholdMantissa,\\n uint256 newLiquidationThresholdMantissa\\n );\\n\\n /// @notice Emitted when liquidation incentive is changed by admin\\n event NewLiquidationIncentive(uint256 oldLiquidationIncentiveMantissa, uint256 newLiquidationIncentiveMantissa);\\n\\n /// @notice Emitted when price oracle is changed\\n event NewPriceOracle(ResilientOracleInterface oldPriceOracle, ResilientOracleInterface newPriceOracle);\\n\\n /// @notice Emitted when an action is paused on a market\\n event ActionPausedMarket(VToken vToken, Action action, bool pauseState);\\n\\n /// @notice Emitted when borrow cap for a vToken is changed\\n event NewBorrowCap(VToken indexed vToken, uint256 newBorrowCap);\\n\\n /// @notice Emitted when the collateral threshold (in USD) for non-batch liquidations is changed\\n event NewMinLiquidatableCollateral(uint256 oldMinLiquidatableCollateral, uint256 newMinLiquidatableCollateral);\\n\\n /// @notice Emitted when supply cap for a vToken is changed\\n event NewSupplyCap(VToken indexed vToken, uint256 newSupplyCap);\\n\\n /// @notice Emitted when a rewards distributor is added\\n event NewRewardsDistributor(address indexed rewardsDistributor, address indexed rewardToken);\\n\\n /// @notice Emitted when a market is supported\\n event MarketSupported(VToken vToken);\\n\\n /// @notice Emitted when prime token contract address is changed\\n event NewPrimeToken(IPrime oldPrimeToken, IPrime newPrimeToken);\\n\\n /// @notice Emitted when forced liquidation is enabled or disabled for a market\\n event IsForcedLiquidationEnabledUpdated(address indexed vToken, bool enable);\\n\\n /// @notice Emitted when a market is unlisted\\n event MarketUnlisted(address indexed vToken);\\n /// @notice Emitted when the borrowing or redeeming delegate rights are updated for an account\\n event DelegateUpdated(address indexed approver, address indexed delegate, bool approved);\\n\\n /// @notice Thrown when collateral factor exceeds the upper bound\\n error InvalidCollateralFactor();\\n\\n /// @notice Thrown when liquidation threshold exceeds the collateral factor\\n error InvalidLiquidationThreshold();\\n\\n /// @notice Thrown when the action is only available to specific sender, but the real sender was different\\n error UnexpectedSender(address expectedSender, address actualSender);\\n\\n /// @notice Thrown when the oracle returns an invalid price for some asset\\n error PriceError(address vToken);\\n\\n /// @notice Thrown if VToken unexpectedly returned a nonzero error code while trying to get account snapshot\\n error SnapshotError(address vToken, address user);\\n\\n /// @notice Thrown when the market is not listed\\n error MarketNotListed(address market);\\n\\n /// @notice Thrown when a market has an unexpected comptroller\\n error ComptrollerMismatch();\\n\\n /// @notice Thrown when user is not member of market\\n error MarketNotCollateral(address vToken, address user);\\n\\n /// @notice Thrown when borrow action is not paused\\n error BorrowActionNotPaused();\\n\\n /// @notice Thrown when mint action is not paused\\n error MintActionNotPaused();\\n\\n /// @notice Thrown when redeem action is not paused\\n error RedeemActionNotPaused();\\n\\n /// @notice Thrown when repay action is not paused\\n error RepayActionNotPaused();\\n\\n /// @notice Thrown when seize action is not paused\\n error SeizeActionNotPaused();\\n\\n /// @notice Thrown when exit market action is not paused\\n error ExitMarketActionNotPaused();\\n\\n /// @notice Thrown when transfer action is not paused\\n error TransferActionNotPaused();\\n\\n /// @notice Thrown when enter market action is not paused\\n error EnterMarketActionNotPaused();\\n\\n /// @notice Thrown when liquidate action is not paused\\n error LiquidateActionNotPaused();\\n\\n /// @notice Thrown when borrow cap is not zero\\n error BorrowCapIsNotZero();\\n\\n /// @notice Thrown when supply cap is not zero\\n error SupplyCapIsNotZero();\\n\\n /// @notice Thrown when collateral factor is not zero\\n error CollateralFactorIsNotZero();\\n\\n /**\\n * @notice Thrown during the liquidation if user's total collateral amount is lower than\\n * a predefined threshold. In this case only batch liquidations (either liquidateAccount\\n * or healAccount) are available.\\n */\\n error MinimalCollateralViolated(uint256 expectedGreaterThan, uint256 actual);\\n error CollateralExceedsThreshold(uint256 expectedLessThanOrEqualTo, uint256 actual);\\n error InsufficientCollateral(uint256 collateralToSeize, uint256 availableCollateral);\\n\\n /// @notice Thrown when the account doesn't have enough liquidity to redeem or borrow\\n error InsufficientLiquidity();\\n\\n /// @notice Thrown when trying to liquidate a healthy account\\n error InsufficientShortfall();\\n\\n /// @notice Thrown when trying to repay more than allowed by close factor\\n error TooMuchRepay();\\n\\n /// @notice Thrown if the user is trying to exit a market in which they have an outstanding debt\\n error NonzeroBorrowBalance();\\n\\n /// @notice Thrown when trying to perform an action that is paused\\n error ActionPaused(address market, Action action);\\n\\n /// @notice Thrown when trying to add a market that is already listed\\n error MarketAlreadyListed(address market);\\n\\n /// @notice Thrown if the supply cap is exceeded\\n error SupplyCapExceeded(address market, uint256 cap);\\n\\n /// @notice Thrown if the borrow cap is exceeded\\n error BorrowCapExceeded(address market, uint256 cap);\\n\\n /// @notice Thrown if delegate approval status is already set to the requested value\\n error DelegationStatusUnchanged();\\n\\n /// @param poolRegistry_ Pool registry address\\n /// @custom:oz-upgrades-unsafe-allow constructor\\n /// @custom:error ZeroAddressNotAllowed is thrown when pool registry address is zero\\n constructor(address poolRegistry_) {\\n ensureNonzeroAddress(poolRegistry_);\\n\\n poolRegistry = poolRegistry_;\\n _disableInitializers();\\n }\\n\\n /**\\n * @param loopLimit Limit for the loops can iterate to avoid the DOS\\n * @param accessControlManager Access control manager contract address\\n */\\n function initialize(uint256 loopLimit, address accessControlManager) external initializer {\\n __Ownable2Step_init();\\n __AccessControlled_init_unchained(accessControlManager);\\n\\n _setMaxLoopsLimit(loopLimit);\\n }\\n\\n /**\\n * @notice Add assets to be included in account liquidity calculation; enabling them to be used as collateral\\n * @param vTokens The list of addresses of the vToken markets to be enabled\\n * @return errors An array of NO_ERROR for compatibility with Venus core tooling\\n * @custom:event MarketEntered is emitted for each market on success\\n * @custom:error ActionPaused error is thrown if entering any of the markets is paused\\n * @custom:error MarketNotListed error is thrown if any of the markets is not listed\\n * @custom:access Not restricted\\n */\\n function enterMarkets(address[] memory vTokens) external override returns (uint256[] memory) {\\n uint256 len = vTokens.length;\\n\\n uint256[] memory results = new uint256[](len);\\n for (uint256 i; i < len; ++i) {\\n VToken vToken = VToken(vTokens[i]);\\n\\n _addToMarket(vToken, msg.sender);\\n results[i] = NO_ERROR;\\n }\\n\\n return results;\\n }\\n\\n /**\\n * @notice Unlist a market by setting isListed to false\\n * @dev Checks if all actions are paused, borrow/supply caps is set to 0 and collateral factor is to 0.\\n * @param market The address of the market (token) to unlist\\n * @return uint256 Always NO_ERROR for compatibility with Venus core tooling\\n * @custom:event MarketUnlisted is emitted on success\\n * @custom:error MarketNotListed error is thrown when the market is not listed\\n * @custom:error BorrowActionNotPaused error is thrown if borrow action is not paused\\n * @custom:error MintActionNotPaused error is thrown if mint action is not paused\\n * @custom:error RedeemActionNotPaused error is thrown if redeem action is not paused\\n * @custom:error RepayActionNotPaused error is thrown if repay action is not paused\\n * @custom:error EnterMarketActionNotPaused error is thrown if enter market action is not paused\\n * @custom:error LiquidateActionNotPaused error is thrown if liquidate action is not paused\\n * @custom:error BorrowCapIsNotZero error is thrown if borrow cap is not zero\\n * @custom:error SupplyCapIsNotZero error is thrown if supply cap is not zero\\n * @custom:error CollateralFactorIsNotZero error is thrown if collateral factor is not zero\\n */\\n function unlistMarket(address market) external returns (uint256) {\\n _checkAccessAllowed(\\\"unlistMarket(address)\\\");\\n\\n Market storage _market = markets[market];\\n\\n if (!_market.isListed) {\\n revert MarketNotListed(market);\\n }\\n\\n if (!actionPaused(market, Action.BORROW)) {\\n revert BorrowActionNotPaused();\\n }\\n\\n if (!actionPaused(market, Action.MINT)) {\\n revert MintActionNotPaused();\\n }\\n\\n if (!actionPaused(market, Action.REDEEM)) {\\n revert RedeemActionNotPaused();\\n }\\n\\n if (!actionPaused(market, Action.REPAY)) {\\n revert RepayActionNotPaused();\\n }\\n\\n if (!actionPaused(market, Action.SEIZE)) {\\n revert SeizeActionNotPaused();\\n }\\n\\n if (!actionPaused(market, Action.ENTER_MARKET)) {\\n revert EnterMarketActionNotPaused();\\n }\\n\\n if (!actionPaused(market, Action.LIQUIDATE)) {\\n revert LiquidateActionNotPaused();\\n }\\n\\n if (!actionPaused(market, Action.TRANSFER)) {\\n revert TransferActionNotPaused();\\n }\\n\\n if (!actionPaused(market, Action.EXIT_MARKET)) {\\n revert ExitMarketActionNotPaused();\\n }\\n\\n if (borrowCaps[market] != 0) {\\n revert BorrowCapIsNotZero();\\n }\\n\\n if (supplyCaps[market] != 0) {\\n revert SupplyCapIsNotZero();\\n }\\n\\n if (_market.collateralFactorMantissa != 0) {\\n revert CollateralFactorIsNotZero();\\n }\\n\\n _market.isListed = false;\\n emit MarketUnlisted(market);\\n\\n return NO_ERROR;\\n }\\n\\n /**\\n * @notice Grants or revokes the borrowing or redeeming delegate rights to / from an account\\n * If allowed, the delegate will be able to borrow funds on behalf of the sender\\n * Upon a delegated borrow, the delegate will receive the funds, and the borrower\\n * will see the debt on their account\\n * Upon a delegated redeem, the delegate will receive the redeemed amount and the approver\\n * will see a deduction in his vToken balance\\n * @param delegate The address to update the rights for\\n * @param approved Whether to grant (true) or revoke (false) the borrowing or redeeming rights\\n * @custom:event DelegateUpdated emits on success\\n * @custom:error ZeroAddressNotAllowed is thrown when delegate address is zero\\n * @custom:error DelegationStatusUnchanged is thrown if approval status is already set to the requested value\\n * @custom:access Not restricted\\n */\\n function updateDelegate(address delegate, bool approved) external {\\n ensureNonzeroAddress(delegate);\\n if (approvedDelegates[msg.sender][delegate] == approved) {\\n revert DelegationStatusUnchanged();\\n }\\n\\n approvedDelegates[msg.sender][delegate] = approved;\\n emit DelegateUpdated(msg.sender, delegate, approved);\\n }\\n\\n /**\\n * @notice Removes asset from sender's account liquidity calculation; disabling them as collateral\\n * @dev Sender must not have an outstanding borrow balance in the asset,\\n * or be providing necessary collateral for an outstanding borrow.\\n * @param vTokenAddress The address of the asset to be removed\\n * @return error Always NO_ERROR for compatibility with Venus core tooling\\n * @custom:event MarketExited is emitted on success\\n * @custom:error ActionPaused error is thrown if exiting the market is paused\\n * @custom:error NonzeroBorrowBalance error is thrown if the user has an outstanding borrow in this market\\n * @custom:error MarketNotListed error is thrown when the market is not listed\\n * @custom:error InsufficientLiquidity error is thrown if exiting the market would lead to user's insolvency\\n * @custom:error SnapshotError is thrown if some vToken fails to return the account's supply and borrows\\n * @custom:error PriceError is thrown if the oracle returns an incorrect price for some asset\\n * @custom:access Not restricted\\n */\\n function exitMarket(address vTokenAddress) external override returns (uint256) {\\n _checkActionPauseState(vTokenAddress, Action.EXIT_MARKET);\\n VToken vToken = VToken(vTokenAddress);\\n /* Get sender tokensHeld and amountOwed underlying from the vToken */\\n (uint256 tokensHeld, uint256 amountOwed, ) = _safeGetAccountSnapshot(vToken, msg.sender);\\n\\n /* Fail if the sender has a borrow balance */\\n if (amountOwed != 0) {\\n revert NonzeroBorrowBalance();\\n }\\n\\n /* Fail if the sender is not permitted to redeem all of their tokens */\\n _checkRedeemAllowed(vTokenAddress, msg.sender, tokensHeld);\\n\\n Market storage marketToExit = markets[address(vToken)];\\n\\n /* Return true if the sender is not already \\u2018in\\u2019 the market */\\n if (!marketToExit.accountMembership[msg.sender]) {\\n return NO_ERROR;\\n }\\n\\n /* Set vToken account membership to false */\\n delete marketToExit.accountMembership[msg.sender];\\n\\n /* Delete vToken from the account\\u2019s list of assets */\\n // load into memory for faster iteration\\n VToken[] memory userAssetList = accountAssets[msg.sender];\\n uint256 len = userAssetList.length;\\n\\n uint256 assetIndex = len;\\n for (uint256 i; i < len; ++i) {\\n if (userAssetList[i] == vToken) {\\n assetIndex = i;\\n break;\\n }\\n }\\n\\n // We *must* have found the asset in the list or our redundant data structure is broken\\n assert(assetIndex < len);\\n\\n // copy last item in list to location of item to be removed, reduce length by 1\\n VToken[] storage storedList = accountAssets[msg.sender];\\n storedList[assetIndex] = storedList[storedList.length - 1];\\n storedList.pop();\\n\\n emit MarketExited(vToken, msg.sender);\\n\\n return NO_ERROR;\\n }\\n\\n /*** Policy Hooks ***/\\n\\n /**\\n * @notice Checks if the account should be allowed to mint tokens in the given market\\n * @param vToken The market to verify the mint against\\n * @param minter The account which would get the minted tokens\\n * @param mintAmount The amount of underlying being supplied to the market in exchange for tokens\\n * @custom:error ActionPaused error is thrown if supplying to this market is paused\\n * @custom:error MarketNotListed error is thrown when the market is not listed\\n * @custom:error SupplyCapExceeded error is thrown if the total supply exceeds the cap after minting\\n * @custom:access Not restricted\\n */\\n function preMintHook(address vToken, address minter, uint256 mintAmount) external override {\\n _checkActionPauseState(vToken, Action.MINT);\\n\\n if (!markets[vToken].isListed) {\\n revert MarketNotListed(address(vToken));\\n }\\n\\n uint256 supplyCap = supplyCaps[vToken];\\n // Skipping the cap check for uncapped coins to save some gas\\n if (supplyCap != type(uint256).max) {\\n uint256 vTokenSupply = VToken(vToken).totalSupply();\\n Exp memory exchangeRate = Exp({ mantissa: VToken(vToken).exchangeRateStored() });\\n uint256 nextTotalSupply = mul_ScalarTruncateAddUInt(exchangeRate, vTokenSupply, mintAmount);\\n if (nextTotalSupply > supplyCap) {\\n revert SupplyCapExceeded(vToken, supplyCap);\\n }\\n }\\n\\n // Keep the flywheel moving\\n uint256 rewardDistributorsCount = rewardsDistributors.length;\\n\\n for (uint256 i; i < rewardDistributorsCount; ++i) {\\n RewardsDistributor rewardsDistributor = rewardsDistributors[i];\\n rewardsDistributor.updateRewardTokenSupplyIndex(vToken);\\n rewardsDistributor.distributeSupplierRewardToken(vToken, minter);\\n }\\n }\\n\\n /**\\n * @notice Validates mint, accrues interest and updates score in prime. Reverts on rejection. May emit logs.\\n * @param vToken Asset being minted\\n * @param minter The address minting the tokens\\n * @param actualMintAmount The amount of the underlying asset being minted\\n * @param mintTokens The number of tokens being minted\\n */\\n // solhint-disable-next-line no-unused-vars\\n function mintVerify(address vToken, address minter, uint256 actualMintAmount, uint256 mintTokens) external {\\n if (address(prime) != address(0)) {\\n prime.accrueInterestAndUpdateScore(minter, vToken);\\n }\\n }\\n\\n /**\\n * @notice Checks if the account should be allowed to redeem tokens in the given market\\n * @param vToken The market to verify the redeem against\\n * @param redeemer The account which would redeem the tokens\\n * @param redeemTokens The number of vTokens to exchange for the underlying asset in the market\\n * @custom:error ActionPaused error is thrown if withdrawals are paused in this market\\n * @custom:error MarketNotListed error is thrown when the market is not listed\\n * @custom:error InsufficientLiquidity error is thrown if the withdrawal would lead to user's insolvency\\n * @custom:error SnapshotError is thrown if some vToken fails to return the account's supply and borrows\\n * @custom:error PriceError is thrown if the oracle returns an incorrect price for some asset\\n * @custom:access Not restricted\\n */\\n function preRedeemHook(address vToken, address redeemer, uint256 redeemTokens) external override {\\n _checkActionPauseState(vToken, Action.REDEEM);\\n\\n _checkRedeemAllowed(vToken, redeemer, redeemTokens);\\n\\n // Keep the flywheel moving\\n uint256 rewardDistributorsCount = rewardsDistributors.length;\\n\\n for (uint256 i; i < rewardDistributorsCount; ++i) {\\n RewardsDistributor rewardsDistributor = rewardsDistributors[i];\\n rewardsDistributor.updateRewardTokenSupplyIndex(vToken);\\n rewardsDistributor.distributeSupplierRewardToken(vToken, redeemer);\\n }\\n }\\n\\n /**\\n * @notice Validates redeem, accrues interest and updates score in prime. Reverts on rejection. May emit logs.\\n * @param vToken Asset being redeemed\\n * @param redeemer The address redeeming the tokens\\n * @param redeemAmount The amount of the underlying asset being redeemed\\n * @param redeemTokens The number of tokens being redeemed\\n */\\n function redeemVerify(address vToken, address redeemer, uint256 redeemAmount, uint256 redeemTokens) external {\\n if (address(prime) != address(0)) {\\n prime.accrueInterestAndUpdateScore(redeemer, vToken);\\n }\\n }\\n\\n /**\\n * @notice Validates repayBorrow, accrues interest and updates score in prime. Reverts on rejection. May emit logs.\\n * @param vToken Asset being repaid\\n * @param payer The address repaying the borrow\\n * @param borrower The address of the borrower\\n * @param actualRepayAmount The amount of underlying being repaid\\n */\\n function repayBorrowVerify(\\n address vToken,\\n address payer, // solhint-disable-line no-unused-vars\\n address borrower,\\n uint256 actualRepayAmount, // solhint-disable-line no-unused-vars\\n uint256 borrowerIndex // solhint-disable-line no-unused-vars\\n ) external {\\n if (address(prime) != address(0)) {\\n prime.accrueInterestAndUpdateScore(borrower, vToken);\\n }\\n }\\n\\n /**\\n * @notice Validates liquidateBorrow, accrues interest and updates score in prime. Reverts on rejection. May emit logs.\\n * @param vTokenBorrowed Asset which was borrowed by the borrower\\n * @param vTokenCollateral Asset which was used as collateral and will be seized\\n * @param liquidator The address repaying the borrow and seizing the collateral\\n * @param borrower The address of the borrower\\n * @param actualRepayAmount The amount of underlying being repaid\\n * @param seizeTokens The amount of collateral token that will be seized\\n */\\n function liquidateBorrowVerify(\\n address vTokenBorrowed,\\n address vTokenCollateral, // solhint-disable-line no-unused-vars\\n address liquidator,\\n address borrower,\\n uint256 actualRepayAmount, // solhint-disable-line no-unused-vars\\n uint256 seizeTokens // solhint-disable-line no-unused-vars\\n ) external {\\n if (address(prime) != address(0)) {\\n prime.accrueInterestAndUpdateScore(borrower, vTokenBorrowed);\\n prime.accrueInterestAndUpdateScore(liquidator, vTokenBorrowed);\\n }\\n }\\n\\n /**\\n * @notice Validates seize, accrues interest and updates score in prime. Reverts on rejection. May emit logs.\\n * @param vTokenCollateral Asset which was used as collateral and will be seized\\n * @param vTokenBorrowed Asset which was borrowed by the borrower\\n * @param liquidator The address repaying the borrow and seizing the collateral\\n * @param borrower The address of the borrower\\n * @param seizeTokens The number of collateral tokens to seize\\n */\\n function seizeVerify(\\n address vTokenCollateral,\\n address vTokenBorrowed, // solhint-disable-line no-unused-vars\\n address liquidator,\\n address borrower,\\n uint256 seizeTokens // solhint-disable-line no-unused-vars\\n ) external {\\n if (address(prime) != address(0)) {\\n prime.accrueInterestAndUpdateScore(borrower, vTokenCollateral);\\n prime.accrueInterestAndUpdateScore(liquidator, vTokenCollateral);\\n }\\n }\\n\\n /**\\n * @notice Validates transfer, accrues interest and updates score in prime. Reverts on rejection. May emit logs.\\n * @param vToken Asset being transferred\\n * @param src The account which sources the tokens\\n * @param dst The account which receives the tokens\\n * @param transferTokens The number of vTokens to transfer\\n */\\n // solhint-disable-next-line no-unused-vars\\n function transferVerify(address vToken, address src, address dst, uint256 transferTokens) external {\\n if (address(prime) != address(0)) {\\n prime.accrueInterestAndUpdateScore(src, vToken);\\n prime.accrueInterestAndUpdateScore(dst, vToken);\\n }\\n }\\n\\n /**\\n * @notice Checks if the account should be allowed to borrow the underlying asset of the given market\\n * @param vToken The market to verify the borrow against\\n * @param borrower The account which would borrow the asset\\n * @param borrowAmount The amount of underlying the account would borrow\\n * @custom:error ActionPaused error is thrown if borrowing is paused in this market\\n * @custom:error MarketNotListed error is thrown when the market is not listed\\n * @custom:error InsufficientLiquidity error is thrown if there is not enough collateral to borrow\\n * @custom:error BorrowCapExceeded is thrown if the borrow cap will be exceeded should this borrow succeed\\n * @custom:error SnapshotError is thrown if some vToken fails to return the account's supply and borrows\\n * @custom:error PriceError is thrown if the oracle returns an incorrect price for some asset\\n * @custom:access Not restricted if vToken is enabled as collateral, otherwise only vToken\\n */\\n /// disable-eslint\\n function preBorrowHook(address vToken, address borrower, uint256 borrowAmount) external override {\\n _checkActionPauseState(vToken, Action.BORROW);\\n\\n if (!markets[vToken].isListed) {\\n revert MarketNotListed(address(vToken));\\n }\\n\\n if (!markets[vToken].accountMembership[borrower]) {\\n // only vTokens may call borrowAllowed if borrower not in market\\n _checkSenderIs(vToken);\\n\\n // attempt to add borrower to the market or revert\\n _addToMarket(VToken(msg.sender), borrower);\\n }\\n\\n // Update the prices of tokens\\n updatePrices(borrower);\\n\\n if (oracle.getUnderlyingPrice(vToken) == 0) {\\n revert PriceError(address(vToken));\\n }\\n\\n uint256 borrowCap = borrowCaps[vToken];\\n // Skipping the cap check for uncapped coins to save some gas\\n if (borrowCap != type(uint256).max) {\\n uint256 totalBorrows = VToken(vToken).totalBorrows();\\n uint256 badDebt = VToken(vToken).badDebt();\\n uint256 nextTotalBorrows = totalBorrows + borrowAmount + badDebt;\\n if (nextTotalBorrows > borrowCap) {\\n revert BorrowCapExceeded(vToken, borrowCap);\\n }\\n }\\n\\n AccountLiquiditySnapshot memory snapshot = _getHypotheticalLiquiditySnapshot(\\n borrower,\\n VToken(vToken),\\n 0,\\n borrowAmount,\\n _getCollateralFactor\\n );\\n\\n if (snapshot.shortfall > 0) {\\n revert InsufficientLiquidity();\\n }\\n\\n Exp memory borrowIndex = Exp({ mantissa: VToken(vToken).borrowIndex() });\\n\\n // Keep the flywheel moving\\n uint256 rewardDistributorsCount = rewardsDistributors.length;\\n\\n for (uint256 i; i < rewardDistributorsCount; ++i) {\\n RewardsDistributor rewardsDistributor = rewardsDistributors[i];\\n rewardsDistributor.updateRewardTokenBorrowIndex(vToken, borrowIndex);\\n rewardsDistributor.distributeBorrowerRewardToken(vToken, borrower, borrowIndex);\\n }\\n }\\n\\n /**\\n * @notice Validates borrow, accrues interest and updates score in prime. Reverts on rejection. May emit logs.\\n * @param vToken Asset whose underlying is being borrowed\\n * @param borrower The address borrowing the underlying\\n * @param borrowAmount The amount of the underlying asset requested to borrow\\n */\\n // solhint-disable-next-line no-unused-vars\\n function borrowVerify(address vToken, address borrower, uint256 borrowAmount) external {\\n if (address(prime) != address(0)) {\\n prime.accrueInterestAndUpdateScore(borrower, vToken);\\n }\\n }\\n\\n /**\\n * @notice Checks if the account should be allowed to repay a borrow in the given market\\n * @param vToken The market to verify the repay against\\n * @param borrower The account which would borrowed the asset\\n * @custom:error ActionPaused error is thrown if repayments are paused in this market\\n * @custom:error MarketNotListed error is thrown when the market is not listed\\n * @custom:access Not restricted\\n */\\n function preRepayHook(address vToken, address borrower) external override {\\n _checkActionPauseState(vToken, Action.REPAY);\\n\\n oracle.updatePrice(vToken);\\n\\n if (!markets[vToken].isListed) {\\n revert MarketNotListed(address(vToken));\\n }\\n\\n // Keep the flywheel moving\\n uint256 rewardDistributorsCount = rewardsDistributors.length;\\n\\n for (uint256 i; i < rewardDistributorsCount; ++i) {\\n Exp memory borrowIndex = Exp({ mantissa: VToken(vToken).borrowIndex() });\\n RewardsDistributor rewardsDistributor = rewardsDistributors[i];\\n rewardsDistributor.updateRewardTokenBorrowIndex(vToken, borrowIndex);\\n rewardsDistributor.distributeBorrowerRewardToken(vToken, borrower, borrowIndex);\\n }\\n }\\n\\n /**\\n * @notice Checks if the liquidation should be allowed to occur\\n * @param vTokenBorrowed Asset which was borrowed by the borrower\\n * @param vTokenCollateral Asset which was used as collateral and will be seized\\n * @param borrower The address of the borrower\\n * @param repayAmount The amount of underlying being repaid\\n * @param skipLiquidityCheck Allows the borrow to be liquidated regardless of the account liquidity\\n * @custom:error ActionPaused error is thrown if liquidations are paused in this market\\n * @custom:error MarketNotListed error is thrown if either collateral or borrowed token is not listed\\n * @custom:error TooMuchRepay error is thrown if the liquidator is trying to repay more than allowed by close factor\\n * @custom:error MinimalCollateralViolated is thrown if the users' total collateral is lower than the threshold for non-batch liquidations\\n * @custom:error InsufficientShortfall is thrown when trying to liquidate a healthy account\\n * @custom:error SnapshotError is thrown if some vToken fails to return the account's supply and borrows\\n * @custom:error PriceError is thrown if the oracle returns an incorrect price for some asset\\n */\\n function preLiquidateHook(\\n address vTokenBorrowed,\\n address vTokenCollateral,\\n address borrower,\\n uint256 repayAmount,\\n bool skipLiquidityCheck\\n ) external override {\\n // Pause Action.LIQUIDATE on BORROWED TOKEN to prevent liquidating it.\\n // If we want to pause liquidating to vTokenCollateral, we should pause\\n // Action.SEIZE on it\\n _checkActionPauseState(vTokenBorrowed, Action.LIQUIDATE);\\n\\n // Update the prices of tokens\\n updatePrices(borrower);\\n\\n if (!markets[vTokenBorrowed].isListed) {\\n revert MarketNotListed(address(vTokenBorrowed));\\n }\\n if (!markets[vTokenCollateral].isListed) {\\n revert MarketNotListed(address(vTokenCollateral));\\n }\\n\\n uint256 borrowBalance = VToken(vTokenBorrowed).borrowBalanceStored(borrower);\\n\\n /* Allow accounts to be liquidated if it is a forced liquidation */\\n if (skipLiquidityCheck || isForcedLiquidationEnabled[vTokenBorrowed]) {\\n if (repayAmount > borrowBalance) {\\n revert TooMuchRepay();\\n }\\n return;\\n }\\n\\n /* The borrower must have shortfall and collateral > threshold in order to be liquidatable */\\n AccountLiquiditySnapshot memory snapshot = _getCurrentLiquiditySnapshot(borrower, _getLiquidationThreshold);\\n\\n if (snapshot.totalCollateral <= minLiquidatableCollateral) {\\n /* The liquidator should use either liquidateAccount or healAccount */\\n revert MinimalCollateralViolated(minLiquidatableCollateral, snapshot.totalCollateral);\\n }\\n\\n if (snapshot.shortfall == 0) {\\n revert InsufficientShortfall();\\n }\\n\\n /* The liquidator may not repay more than what is allowed by the closeFactor */\\n uint256 maxClose = mul_ScalarTruncate(Exp({ mantissa: closeFactorMantissa }), borrowBalance);\\n if (repayAmount > maxClose) {\\n revert TooMuchRepay();\\n }\\n }\\n\\n /**\\n * @notice Checks if the seizing of assets should be allowed to occur\\n * @param vTokenCollateral Asset which was used as collateral and will be seized\\n * @param seizerContract Contract that tries to seize the asset (either borrowed vToken or Comptroller)\\n * @param liquidator The address repaying the borrow and seizing the collateral\\n * @param borrower The address of the borrower\\n * @custom:error ActionPaused error is thrown if seizing this type of collateral is paused\\n * @custom:error MarketNotListed error is thrown if either collateral or borrowed token is not listed\\n * @custom:error ComptrollerMismatch error is when seizer contract or seized asset belong to different pools\\n * @custom:access Not restricted\\n */\\n function preSeizeHook(\\n address vTokenCollateral,\\n address seizerContract,\\n address liquidator,\\n address borrower\\n ) external override {\\n // Pause Action.SEIZE on COLLATERAL to prevent seizing it.\\n // If we want to pause liquidating vTokenBorrowed, we should pause\\n // Action.LIQUIDATE on it\\n _checkActionPauseState(vTokenCollateral, Action.SEIZE);\\n\\n Market storage market = markets[vTokenCollateral];\\n\\n if (!market.isListed) {\\n revert MarketNotListed(vTokenCollateral);\\n }\\n\\n if (seizerContract == address(this)) {\\n // If Comptroller is the seizer, just check if collateral's comptroller\\n // is equal to the current address\\n if (address(VToken(vTokenCollateral).comptroller()) != address(this)) {\\n revert ComptrollerMismatch();\\n }\\n } else {\\n // If the seizer is not the Comptroller, check that the seizer is a\\n // listed market, and that the markets' comptrollers match\\n if (!markets[seizerContract].isListed) {\\n revert MarketNotListed(seizerContract);\\n }\\n if (VToken(vTokenCollateral).comptroller() != VToken(seizerContract).comptroller()) {\\n revert ComptrollerMismatch();\\n }\\n }\\n\\n if (!market.accountMembership[borrower]) {\\n revert MarketNotCollateral(vTokenCollateral, borrower);\\n }\\n\\n // Keep the flywheel moving\\n uint256 rewardDistributorsCount = rewardsDistributors.length;\\n\\n for (uint256 i; i < rewardDistributorsCount; ++i) {\\n RewardsDistributor rewardsDistributor = rewardsDistributors[i];\\n rewardsDistributor.updateRewardTokenSupplyIndex(vTokenCollateral);\\n rewardsDistributor.distributeSupplierRewardToken(vTokenCollateral, borrower);\\n rewardsDistributor.distributeSupplierRewardToken(vTokenCollateral, liquidator);\\n }\\n }\\n\\n /**\\n * @notice Checks if the account should be allowed to transfer tokens in the given market\\n * @param vToken The market to verify the transfer against\\n * @param src The account which sources the tokens\\n * @param dst The account which receives the tokens\\n * @param transferTokens The number of vTokens to transfer\\n * @custom:error ActionPaused error is thrown if withdrawals are paused in this market\\n * @custom:error MarketNotListed error is thrown when the market is not listed\\n * @custom:error InsufficientLiquidity error is thrown if the withdrawal would lead to user's insolvency\\n * @custom:error SnapshotError is thrown if some vToken fails to return the account's supply and borrows\\n * @custom:error PriceError is thrown if the oracle returns an incorrect price for some asset\\n * @custom:access Not restricted\\n */\\n function preTransferHook(address vToken, address src, address dst, uint256 transferTokens) external override {\\n _checkActionPauseState(vToken, Action.TRANSFER);\\n\\n // Currently the only consideration is whether or not\\n // the src is allowed to redeem this many tokens\\n _checkRedeemAllowed(vToken, src, transferTokens);\\n\\n // Keep the flywheel moving\\n uint256 rewardDistributorsCount = rewardsDistributors.length;\\n\\n for (uint256 i; i < rewardDistributorsCount; ++i) {\\n RewardsDistributor rewardsDistributor = rewardsDistributors[i];\\n rewardsDistributor.updateRewardTokenSupplyIndex(vToken);\\n rewardsDistributor.distributeSupplierRewardToken(vToken, src);\\n rewardsDistributor.distributeSupplierRewardToken(vToken, dst);\\n }\\n }\\n\\n /*** Pool-level operations ***/\\n\\n /**\\n * @notice Seizes all the remaining collateral, makes msg.sender repay the existing\\n * borrows, and treats the rest of the debt as bad debt (for each market).\\n * The sender has to repay a certain percentage of the debt, computed as\\n * collateral / (borrows * liquidationIncentive).\\n * @param user account to heal\\n * @custom:error CollateralExceedsThreshold error is thrown when the collateral is too big for healing\\n * @custom:error SnapshotError is thrown if some vToken fails to return the account's supply and borrows\\n * @custom:error PriceError is thrown if the oracle returns an incorrect price for some asset\\n * @custom:access Not restricted\\n */\\n function healAccount(address user) external {\\n VToken[] memory userAssets = getAssetsIn(user);\\n uint256 userAssetsCount = userAssets.length;\\n\\n address liquidator = msg.sender;\\n {\\n ResilientOracleInterface oracle_ = oracle;\\n // We need all user's markets to be fresh for the computations to be correct\\n for (uint256 i; i < userAssetsCount; ++i) {\\n userAssets[i].accrueInterest();\\n oracle_.updatePrice(address(userAssets[i]));\\n }\\n }\\n\\n AccountLiquiditySnapshot memory snapshot = _getCurrentLiquiditySnapshot(user, _getLiquidationThreshold);\\n\\n if (snapshot.totalCollateral > minLiquidatableCollateral) {\\n revert CollateralExceedsThreshold(minLiquidatableCollateral, snapshot.totalCollateral);\\n }\\n\\n if (snapshot.shortfall == 0) {\\n revert InsufficientShortfall();\\n }\\n\\n // percentage = collateral / (borrows * liquidation incentive)\\n Exp memory collateral = Exp({ mantissa: snapshot.totalCollateral });\\n Exp memory scaledBorrows = mul_(\\n Exp({ mantissa: snapshot.borrows }),\\n Exp({ mantissa: liquidationIncentiveMantissa })\\n );\\n\\n Exp memory percentage = div_(collateral, scaledBorrows);\\n if (lessThanExp(Exp({ mantissa: MANTISSA_ONE }), percentage)) {\\n revert CollateralExceedsThreshold(scaledBorrows.mantissa, collateral.mantissa);\\n }\\n\\n for (uint256 i; i < userAssetsCount; ++i) {\\n VToken market = userAssets[i];\\n\\n (uint256 tokens, uint256 borrowBalance, ) = _safeGetAccountSnapshot(market, user);\\n uint256 repaymentAmount = mul_ScalarTruncate(percentage, borrowBalance);\\n\\n // Seize the entire collateral\\n if (tokens != 0) {\\n market.seize(liquidator, user, tokens);\\n }\\n // Repay a certain percentage of the borrow, forgive the rest\\n if (borrowBalance != 0) {\\n market.healBorrow(liquidator, user, repaymentAmount);\\n }\\n }\\n }\\n\\n /**\\n * @notice Liquidates all borrows of the borrower. Callable only if the collateral is less than\\n * a predefined threshold, and the account collateral can be seized to cover all borrows. If\\n * the collateral is higher than the threshold, use regular liquidations. If the collateral is\\n * below the threshold, and the account is insolvent, use healAccount.\\n * @param borrower the borrower address\\n * @param orders an array of liquidation orders\\n * @custom:error CollateralExceedsThreshold error is thrown when the collateral is too big for a batch liquidation\\n * @custom:error InsufficientCollateral error is thrown when there is not enough collateral to cover the debt\\n * @custom:error SnapshotError is thrown if some vToken fails to return the account's supply and borrows\\n * @custom:error PriceError is thrown if the oracle returns an incorrect price for some asset\\n * @custom:access Not restricted\\n */\\n function liquidateAccount(address borrower, LiquidationOrder[] calldata orders) external {\\n // We will accrue interest and update the oracle prices later during the liquidation\\n\\n AccountLiquiditySnapshot memory snapshot = _getCurrentLiquiditySnapshot(borrower, _getLiquidationThreshold);\\n\\n if (snapshot.totalCollateral > minLiquidatableCollateral) {\\n // You should use the regular vToken.liquidateBorrow(...) call\\n revert CollateralExceedsThreshold(minLiquidatableCollateral, snapshot.totalCollateral);\\n }\\n\\n uint256 collateralToSeize = mul_ScalarTruncate(\\n Exp({ mantissa: liquidationIncentiveMantissa }),\\n snapshot.borrows\\n );\\n if (collateralToSeize >= snapshot.totalCollateral) {\\n // There is not enough collateral to seize. Use healBorrow to repay some part of the borrow\\n // and record bad debt.\\n revert InsufficientCollateral(collateralToSeize, snapshot.totalCollateral);\\n }\\n\\n if (snapshot.shortfall == 0) {\\n revert InsufficientShortfall();\\n }\\n\\n uint256 ordersCount = orders.length;\\n\\n _ensureMaxLoops(ordersCount / 2);\\n\\n for (uint256 i; i < ordersCount; ++i) {\\n if (!markets[address(orders[i].vTokenBorrowed)].isListed) {\\n revert MarketNotListed(address(orders[i].vTokenBorrowed));\\n }\\n if (!markets[address(orders[i].vTokenCollateral)].isListed) {\\n revert MarketNotListed(address(orders[i].vTokenCollateral));\\n }\\n\\n LiquidationOrder calldata order = orders[i];\\n order.vTokenBorrowed.forceLiquidateBorrow(\\n msg.sender,\\n borrower,\\n order.repayAmount,\\n order.vTokenCollateral,\\n true\\n );\\n }\\n\\n VToken[] memory borrowMarkets = getAssetsIn(borrower);\\n uint256 marketsCount = borrowMarkets.length;\\n\\n for (uint256 i; i < marketsCount; ++i) {\\n (, uint256 borrowBalance, ) = _safeGetAccountSnapshot(borrowMarkets[i], borrower);\\n require(borrowBalance == 0, \\\"Nonzero borrow balance after liquidation\\\");\\n }\\n }\\n\\n /**\\n * @notice Sets the closeFactor to use when liquidating borrows\\n * @param newCloseFactorMantissa New close factor, scaled by 1e18\\n * @custom:event Emits NewCloseFactor on success\\n * @custom:access Controlled by AccessControlManager\\n */\\n function setCloseFactor(uint256 newCloseFactorMantissa) external {\\n _checkAccessAllowed(\\\"setCloseFactor(uint256)\\\");\\n require(MAX_CLOSE_FACTOR_MANTISSA >= newCloseFactorMantissa, \\\"Close factor greater than maximum close factor\\\");\\n require(MIN_CLOSE_FACTOR_MANTISSA <= newCloseFactorMantissa, \\\"Close factor smaller than minimum close factor\\\");\\n\\n uint256 oldCloseFactorMantissa = closeFactorMantissa;\\n closeFactorMantissa = newCloseFactorMantissa;\\n emit NewCloseFactor(oldCloseFactorMantissa, newCloseFactorMantissa);\\n }\\n\\n /**\\n * @notice Sets the collateralFactor for a market\\n * @dev This function is restricted by the AccessControlManager\\n * @param vToken The market to set the factor on\\n * @param newCollateralFactorMantissa The new collateral factor, scaled by 1e18\\n * @param newLiquidationThresholdMantissa The new liquidation threshold, scaled by 1e18\\n * @custom:event Emits NewCollateralFactor when collateral factor is updated\\n * and NewLiquidationThreshold when liquidation threshold is updated\\n * @custom:error MarketNotListed error is thrown when the market is not listed\\n * @custom:error InvalidCollateralFactor error is thrown when collateral factor is too high\\n * @custom:error InvalidLiquidationThreshold error is thrown when liquidation threshold is lower than collateral factor\\n * @custom:error PriceError is thrown when the oracle returns an invalid price for the asset\\n * @custom:access Controlled by AccessControlManager\\n */\\n function setCollateralFactor(\\n VToken vToken,\\n uint256 newCollateralFactorMantissa,\\n uint256 newLiquidationThresholdMantissa\\n ) external {\\n _checkAccessAllowed(\\\"setCollateralFactor(address,uint256,uint256)\\\");\\n\\n // Verify market is listed\\n Market storage market = markets[address(vToken)];\\n if (!market.isListed) {\\n revert MarketNotListed(address(vToken));\\n }\\n\\n // Check collateral factor <= 0.9\\n if (newCollateralFactorMantissa > MAX_COLLATERAL_FACTOR_MANTISSA) {\\n revert InvalidCollateralFactor();\\n }\\n\\n // Ensure that liquidation threshold <= 1\\n if (newLiquidationThresholdMantissa > MANTISSA_ONE) {\\n revert InvalidLiquidationThreshold();\\n }\\n\\n // Ensure that liquidation threshold >= CF\\n if (newLiquidationThresholdMantissa < newCollateralFactorMantissa) {\\n revert InvalidLiquidationThreshold();\\n }\\n\\n // If collateral factor != 0, fail if price == 0\\n if (newCollateralFactorMantissa != 0 && oracle.getUnderlyingPrice(address(vToken)) == 0) {\\n revert PriceError(address(vToken));\\n }\\n\\n uint256 oldCollateralFactorMantissa = market.collateralFactorMantissa;\\n if (newCollateralFactorMantissa != oldCollateralFactorMantissa) {\\n market.collateralFactorMantissa = newCollateralFactorMantissa;\\n emit NewCollateralFactor(vToken, oldCollateralFactorMantissa, newCollateralFactorMantissa);\\n }\\n\\n uint256 oldLiquidationThresholdMantissa = market.liquidationThresholdMantissa;\\n if (newLiquidationThresholdMantissa != oldLiquidationThresholdMantissa) {\\n market.liquidationThresholdMantissa = newLiquidationThresholdMantissa;\\n emit NewLiquidationThreshold(vToken, oldLiquidationThresholdMantissa, newLiquidationThresholdMantissa);\\n }\\n }\\n\\n /**\\n * @notice Sets liquidationIncentive\\n * @dev This function is restricted by the AccessControlManager\\n * @param newLiquidationIncentiveMantissa New liquidationIncentive scaled by 1e18\\n * @custom:event Emits NewLiquidationIncentive on success\\n * @custom:access Controlled by AccessControlManager\\n */\\n function setLiquidationIncentive(uint256 newLiquidationIncentiveMantissa) external {\\n require(newLiquidationIncentiveMantissa >= MANTISSA_ONE, \\\"liquidation incentive should be greater than 1e18\\\");\\n\\n _checkAccessAllowed(\\\"setLiquidationIncentive(uint256)\\\");\\n\\n // Save current value for use in log\\n uint256 oldLiquidationIncentiveMantissa = liquidationIncentiveMantissa;\\n\\n // Set liquidation incentive to new incentive\\n liquidationIncentiveMantissa = newLiquidationIncentiveMantissa;\\n\\n // Emit event with old incentive, new incentive\\n emit NewLiquidationIncentive(oldLiquidationIncentiveMantissa, newLiquidationIncentiveMantissa);\\n }\\n\\n /**\\n * @notice Add the market to the markets mapping and set it as listed\\n * @dev Only callable by the PoolRegistry\\n * @param vToken The address of the market (token) to list\\n * @custom:error MarketAlreadyListed is thrown if the market is already listed in this pool\\n * @custom:access Only PoolRegistry\\n */\\n function supportMarket(VToken vToken) external {\\n _checkSenderIs(poolRegistry);\\n\\n if (markets[address(vToken)].isListed) {\\n revert MarketAlreadyListed(address(vToken));\\n }\\n\\n require(vToken.isVToken(), \\\"Comptroller: Invalid vToken\\\"); // Sanity check to make sure its really a VToken\\n\\n Market storage newMarket = markets[address(vToken)];\\n newMarket.isListed = true;\\n newMarket.collateralFactorMantissa = 0;\\n newMarket.liquidationThresholdMantissa = 0;\\n\\n _addMarket(address(vToken));\\n\\n uint256 rewardDistributorsCount = rewardsDistributors.length;\\n\\n for (uint256 i; i < rewardDistributorsCount; ++i) {\\n rewardsDistributors[i].initializeMarket(address(vToken));\\n }\\n\\n emit MarketSupported(vToken);\\n }\\n\\n /**\\n * @notice Set the given borrow caps for the given vToken markets. Borrowing that brings total borrows to or above borrow cap will revert.\\n * @dev This function is restricted by the AccessControlManager\\n * @dev A borrow cap of type(uint256).max corresponds to unlimited borrowing.\\n * @dev Borrow caps smaller than the current total borrows are accepted. This way, new borrows will not be allowed\\n until the total borrows amount goes below the new borrow cap\\n * @param vTokens The addresses of the markets (tokens) to change the borrow caps for\\n * @param newBorrowCaps The new borrow cap values in underlying to be set. A value of type(uint256).max corresponds to unlimited borrowing.\\n * @custom:access Controlled by AccessControlManager\\n */\\n function setMarketBorrowCaps(VToken[] calldata vTokens, uint256[] calldata newBorrowCaps) external {\\n _checkAccessAllowed(\\\"setMarketBorrowCaps(address[],uint256[])\\\");\\n\\n uint256 numMarkets = vTokens.length;\\n uint256 numBorrowCaps = newBorrowCaps.length;\\n\\n require(numMarkets != 0 && numMarkets == numBorrowCaps, \\\"invalid input\\\");\\n\\n _ensureMaxLoops(numMarkets);\\n\\n for (uint256 i; i < numMarkets; ++i) {\\n borrowCaps[address(vTokens[i])] = newBorrowCaps[i];\\n emit NewBorrowCap(vTokens[i], newBorrowCaps[i]);\\n }\\n }\\n\\n /**\\n * @notice Set the given supply caps for the given vToken markets. Supply that brings total Supply to or above supply cap will revert.\\n * @dev This function is restricted by the AccessControlManager\\n * @dev A supply cap of type(uint256).max corresponds to unlimited supply.\\n * @dev Supply caps smaller than the current total supplies are accepted. This way, new supplies will not be allowed\\n until the total supplies amount goes below the new supply cap\\n * @param vTokens The addresses of the markets (tokens) to change the supply caps for\\n * @param newSupplyCaps The new supply cap values in underlying to be set. A value of type(uint256).max corresponds to unlimited supply.\\n * @custom:access Controlled by AccessControlManager\\n */\\n function setMarketSupplyCaps(VToken[] calldata vTokens, uint256[] calldata newSupplyCaps) external {\\n _checkAccessAllowed(\\\"setMarketSupplyCaps(address[],uint256[])\\\");\\n uint256 vTokensCount = vTokens.length;\\n\\n require(vTokensCount != 0, \\\"invalid number of markets\\\");\\n require(vTokensCount == newSupplyCaps.length, \\\"invalid number of markets\\\");\\n\\n _ensureMaxLoops(vTokensCount);\\n\\n for (uint256 i; i < vTokensCount; ++i) {\\n supplyCaps[address(vTokens[i])] = newSupplyCaps[i];\\n emit NewSupplyCap(vTokens[i], newSupplyCaps[i]);\\n }\\n }\\n\\n /**\\n * @notice Pause/unpause specified actions\\n * @dev This function is restricted by the AccessControlManager\\n * @param marketsList Markets to pause/unpause the actions on\\n * @param actionsList List of action ids to pause/unpause\\n * @param paused The new paused state (true=paused, false=unpaused)\\n * @custom:access Controlled by AccessControlManager\\n */\\n function setActionsPaused(VToken[] calldata marketsList, Action[] calldata actionsList, bool paused) external {\\n _checkAccessAllowed(\\\"setActionsPaused(address[],uint256[],bool)\\\");\\n\\n uint256 marketsCount = marketsList.length;\\n uint256 actionsCount = actionsList.length;\\n\\n _ensureMaxLoops(marketsCount * actionsCount);\\n\\n for (uint256 marketIdx; marketIdx < marketsCount; ++marketIdx) {\\n for (uint256 actionIdx; actionIdx < actionsCount; ++actionIdx) {\\n _setActionPaused(address(marketsList[marketIdx]), actionsList[actionIdx], paused);\\n }\\n }\\n }\\n\\n /**\\n * @notice Set the given collateral threshold for non-batch liquidations. Regular liquidations\\n * will fail if the collateral amount is less than this threshold. Liquidators should use batch\\n * operations like liquidateAccount or healAccount.\\n * @dev This function is restricted by the AccessControlManager\\n * @param newMinLiquidatableCollateral The new min liquidatable collateral (in USD).\\n * @custom:access Controlled by AccessControlManager\\n */\\n function setMinLiquidatableCollateral(uint256 newMinLiquidatableCollateral) external {\\n _checkAccessAllowed(\\\"setMinLiquidatableCollateral(uint256)\\\");\\n\\n uint256 oldMinLiquidatableCollateral = minLiquidatableCollateral;\\n minLiquidatableCollateral = newMinLiquidatableCollateral;\\n emit NewMinLiquidatableCollateral(oldMinLiquidatableCollateral, newMinLiquidatableCollateral);\\n }\\n\\n /**\\n * @notice Add a new RewardsDistributor and initialize it with all markets. We can add several RewardsDistributor\\n * contracts with the same rewardToken, and there could be overlaping among them considering the last reward slot (block or second)\\n * @dev Only callable by the admin\\n * @param _rewardsDistributor Address of the RewardDistributor contract to add\\n * @custom:access Only Governance\\n * @custom:event Emits NewRewardsDistributor with distributor address\\n */\\n function addRewardsDistributor(RewardsDistributor _rewardsDistributor) external onlyOwner {\\n require(!rewardsDistributorExists[address(_rewardsDistributor)], \\\"already exists\\\");\\n\\n uint256 rewardsDistributorsLen = rewardsDistributors.length;\\n _ensureMaxLoops(rewardsDistributorsLen + 1);\\n\\n rewardsDistributors.push(_rewardsDistributor);\\n rewardsDistributorExists[address(_rewardsDistributor)] = true;\\n\\n uint256 marketsCount = allMarkets.length;\\n\\n for (uint256 i; i < marketsCount; ++i) {\\n _rewardsDistributor.initializeMarket(address(allMarkets[i]));\\n }\\n\\n emit NewRewardsDistributor(address(_rewardsDistributor), address(_rewardsDistributor.rewardToken()));\\n }\\n\\n /**\\n * @notice Sets a new price oracle for the Comptroller\\n * @dev Only callable by the admin\\n * @param newOracle Address of the new price oracle to set\\n * @custom:event Emits NewPriceOracle on success\\n * @custom:error ZeroAddressNotAllowed is thrown when the new oracle address is zero\\n */\\n function setPriceOracle(ResilientOracleInterface newOracle) external onlyOwner {\\n ensureNonzeroAddress(address(newOracle));\\n\\n ResilientOracleInterface oldOracle = oracle;\\n oracle = newOracle;\\n emit NewPriceOracle(oldOracle, newOracle);\\n }\\n\\n /**\\n * @notice Set the for loop iteration limit to avoid DOS\\n * @param limit Limit for the max loops can execute at a time\\n */\\n function setMaxLoopsLimit(uint256 limit) external onlyOwner {\\n _setMaxLoopsLimit(limit);\\n }\\n\\n /**\\n * @notice Sets the prime token contract for the comptroller\\n * @param _prime Address of the Prime contract\\n */\\n function setPrimeToken(IPrime _prime) external onlyOwner {\\n ensureNonzeroAddress(address(_prime));\\n\\n emit NewPrimeToken(prime, _prime);\\n prime = _prime;\\n }\\n\\n /**\\n * @notice Enables forced liquidations for a market. If forced liquidation is enabled,\\n * borrows in the market may be liquidated regardless of the account liquidity\\n * @param vTokenBorrowed Borrowed vToken\\n * @param enable Whether to enable forced liquidations\\n */\\n function setForcedLiquidation(address vTokenBorrowed, bool enable) external {\\n _checkAccessAllowed(\\\"setForcedLiquidation(address,bool)\\\");\\n ensureNonzeroAddress(vTokenBorrowed);\\n\\n if (!markets[vTokenBorrowed].isListed) {\\n revert MarketNotListed(vTokenBorrowed);\\n }\\n\\n isForcedLiquidationEnabled[vTokenBorrowed] = enable;\\n emit IsForcedLiquidationEnabledUpdated(vTokenBorrowed, enable);\\n }\\n\\n /**\\n * @notice Determine the current account liquidity with respect to liquidation threshold requirements\\n * @dev The interface of this function is intentionally kept compatible with Compound and Venus Core\\n * @param account The account get liquidity for\\n * @return error Always NO_ERROR for compatibility with Venus core tooling\\n * @return liquidity Account liquidity in excess of liquidation threshold requirements,\\n * @return shortfall Account shortfall below liquidation threshold requirements\\n */\\n function getAccountLiquidity(\\n address account\\n ) external view returns (uint256 error, uint256 liquidity, uint256 shortfall) {\\n AccountLiquiditySnapshot memory snapshot = _getCurrentLiquiditySnapshot(account, _getLiquidationThreshold);\\n return (NO_ERROR, snapshot.liquidity, snapshot.shortfall);\\n }\\n\\n /**\\n * @notice Determine the current account liquidity with respect to collateral requirements\\n * @dev The interface of this function is intentionally kept compatible with Compound and Venus Core\\n * @param account The account get liquidity for\\n * @return error Always NO_ERROR for compatibility with Venus core tooling\\n * @return liquidity Account liquidity in excess of collateral requirements,\\n * @return shortfall Account shortfall below collateral requirements\\n */\\n function getBorrowingPower(\\n address account\\n ) external view returns (uint256 error, uint256 liquidity, uint256 shortfall) {\\n AccountLiquiditySnapshot memory snapshot = _getCurrentLiquiditySnapshot(account, _getCollateralFactor);\\n return (NO_ERROR, snapshot.liquidity, snapshot.shortfall);\\n }\\n\\n /**\\n * @notice Determine what the account liquidity would be if the given amounts were redeemed/borrowed\\n * @dev The interface of this function is intentionally kept compatible with Compound and Venus Core\\n * @param vTokenModify The market to hypothetically redeem/borrow in\\n * @param account The account to determine liquidity for\\n * @param redeemTokens The number of tokens to hypothetically redeem\\n * @param borrowAmount The amount of underlying to hypothetically borrow\\n * @return error Always NO_ERROR for compatibility with Venus core tooling\\n * @return liquidity Hypothetical account liquidity in excess of collateral requirements,\\n * @return shortfall Hypothetical account shortfall below collateral requirements\\n */\\n function getHypotheticalAccountLiquidity(\\n address account,\\n address vTokenModify,\\n uint256 redeemTokens,\\n uint256 borrowAmount\\n ) external view returns (uint256 error, uint256 liquidity, uint256 shortfall) {\\n AccountLiquiditySnapshot memory snapshot = _getHypotheticalLiquiditySnapshot(\\n account,\\n VToken(vTokenModify),\\n redeemTokens,\\n borrowAmount,\\n _getCollateralFactor\\n );\\n return (NO_ERROR, snapshot.liquidity, snapshot.shortfall);\\n }\\n\\n /**\\n * @notice Return all of the markets\\n * @dev The automatic getter may be used to access an individual market.\\n * @return markets The list of market addresses\\n */\\n function getAllMarkets() external view override returns (VToken[] memory) {\\n return allMarkets;\\n }\\n\\n /**\\n * @notice Check if a market is marked as listed (active)\\n * @param vToken vToken Address for the market to check\\n * @return listed True if listed otherwise false\\n */\\n function isMarketListed(VToken vToken) external view returns (bool) {\\n return markets[address(vToken)].isListed;\\n }\\n\\n /*** Assets You Are In ***/\\n\\n /**\\n * @notice Returns whether the given account is entered in a given market\\n * @param account The address of the account to check\\n * @param vToken The vToken to check\\n * @return True if the account is in the market specified, otherwise false.\\n */\\n function checkMembership(address account, VToken vToken) external view returns (bool) {\\n return markets[address(vToken)].accountMembership[account];\\n }\\n\\n /**\\n * @notice Calculate number of tokens of collateral asset to seize given an underlying amount\\n * @dev Used in liquidation (called in vToken.liquidateBorrowFresh)\\n * @param vTokenBorrowed The address of the borrowed vToken\\n * @param vTokenCollateral The address of the collateral vToken\\n * @param actualRepayAmount The amount of vTokenBorrowed underlying to convert into vTokenCollateral tokens\\n * @return error Always NO_ERROR for compatibility with Venus core tooling\\n * @return tokensToSeize Number of vTokenCollateral tokens to be seized in a liquidation\\n * @custom:error PriceError if the oracle returns an invalid price\\n */\\n function liquidateCalculateSeizeTokens(\\n address vTokenBorrowed,\\n address vTokenCollateral,\\n uint256 actualRepayAmount\\n ) external view override returns (uint256 error, uint256 tokensToSeize) {\\n /* Read oracle prices for borrowed and collateral markets */\\n uint256 priceBorrowedMantissa = _safeGetUnderlyingPrice(VToken(vTokenBorrowed));\\n uint256 priceCollateralMantissa = _safeGetUnderlyingPrice(VToken(vTokenCollateral));\\n\\n /*\\n * Get the exchange rate and calculate the number of collateral tokens to seize:\\n * seizeAmount = actualRepayAmount * liquidationIncentive * priceBorrowed / priceCollateral\\n * seizeTokens = seizeAmount / exchangeRate\\n * = actualRepayAmount * (liquidationIncentive * priceBorrowed) / (priceCollateral * exchangeRate)\\n */\\n uint256 exchangeRateMantissa = VToken(vTokenCollateral).exchangeRateStored(); // Note: reverts on error\\n uint256 seizeTokens;\\n Exp memory numerator;\\n Exp memory denominator;\\n Exp memory ratio;\\n\\n numerator = mul_(Exp({ mantissa: liquidationIncentiveMantissa }), Exp({ mantissa: priceBorrowedMantissa }));\\n denominator = mul_(Exp({ mantissa: priceCollateralMantissa }), Exp({ mantissa: exchangeRateMantissa }));\\n ratio = div_(numerator, denominator);\\n\\n seizeTokens = mul_ScalarTruncate(ratio, actualRepayAmount);\\n\\n return (NO_ERROR, seizeTokens);\\n }\\n\\n /**\\n * @notice Returns reward speed given a vToken\\n * @param vToken The vToken to get the reward speeds for\\n * @return rewardSpeeds Array of total supply and borrow speeds and reward token for all reward distributors\\n */\\n function getRewardsByMarket(address vToken) external view returns (RewardSpeeds[] memory rewardSpeeds) {\\n uint256 rewardsDistributorsLength = rewardsDistributors.length;\\n rewardSpeeds = new RewardSpeeds[](rewardsDistributorsLength);\\n for (uint256 i; i < rewardsDistributorsLength; ++i) {\\n RewardsDistributor rewardsDistributor = rewardsDistributors[i];\\n address rewardToken = address(rewardsDistributor.rewardToken());\\n rewardSpeeds[i] = RewardSpeeds({\\n rewardToken: rewardToken,\\n supplySpeed: rewardsDistributor.rewardTokenSupplySpeeds(vToken),\\n borrowSpeed: rewardsDistributor.rewardTokenBorrowSpeeds(vToken)\\n });\\n }\\n return rewardSpeeds;\\n }\\n\\n /**\\n * @notice Return all reward distributors for this pool\\n * @return Array of RewardDistributor addresses\\n */\\n function getRewardDistributors() external view returns (RewardsDistributor[] memory) {\\n return rewardsDistributors;\\n }\\n\\n /**\\n * @notice A marker method that returns true for a valid Comptroller contract\\n * @return Always true\\n */\\n function isComptroller() external pure override returns (bool) {\\n return true;\\n }\\n\\n /**\\n * @notice Update the prices of all the tokens associated with the provided account\\n * @param account Address of the account to get associated tokens with\\n */\\n function updatePrices(address account) public {\\n VToken[] memory vTokens = getAssetsIn(account);\\n uint256 vTokensCount = vTokens.length;\\n\\n ResilientOracleInterface oracle_ = oracle;\\n\\n for (uint256 i; i < vTokensCount; ++i) {\\n oracle_.updatePrice(address(vTokens[i]));\\n }\\n }\\n\\n /**\\n * @notice Checks if a certain action is paused on a market\\n * @param market vToken address\\n * @param action Action to check\\n * @return paused True if the action is paused otherwise false\\n */\\n function actionPaused(address market, Action action) public view returns (bool) {\\n return _actionPaused[market][action];\\n }\\n\\n /**\\n * @notice Returns the assets an account has entered\\n * @param account The address of the account to pull assets for\\n * @return A list with the assets the account has entered\\n */\\n function getAssetsIn(address account) public view returns (VToken[] memory) {\\n uint256 len;\\n VToken[] memory _accountAssets = accountAssets[account];\\n uint256 _accountAssetsLength = _accountAssets.length;\\n\\n VToken[] memory assetsIn = new VToken[](_accountAssetsLength);\\n\\n for (uint256 i; i < _accountAssetsLength; ++i) {\\n Market storage market = markets[address(_accountAssets[i])];\\n if (market.isListed) {\\n assetsIn[len] = _accountAssets[i];\\n ++len;\\n }\\n }\\n\\n assembly {\\n mstore(assetsIn, len)\\n }\\n\\n return assetsIn;\\n }\\n\\n /**\\n * @notice Add the market to the borrower's \\\"assets in\\\" for liquidity calculations\\n * @param vToken The market to enter\\n * @param borrower The address of the account to modify\\n */\\n function _addToMarket(VToken vToken, address borrower) internal {\\n _checkActionPauseState(address(vToken), Action.ENTER_MARKET);\\n Market storage marketToJoin = markets[address(vToken)];\\n\\n if (!marketToJoin.isListed) {\\n revert MarketNotListed(address(vToken));\\n }\\n\\n if (marketToJoin.accountMembership[borrower]) {\\n // already joined\\n return;\\n }\\n\\n // survived the gauntlet, add to list\\n // NOTE: we store these somewhat redundantly as a significant optimization\\n // this avoids having to iterate through the list for the most common use cases\\n // that is, only when we need to perform liquidity checks\\n // and not whenever we want to check if an account is in a particular market\\n marketToJoin.accountMembership[borrower] = true;\\n accountAssets[borrower].push(vToken);\\n\\n emit MarketEntered(vToken, borrower);\\n }\\n\\n /**\\n * @notice Internal function to validate that a market hasn't already been added\\n * and if it hasn't adds it\\n * @param vToken The market to support\\n */\\n function _addMarket(address vToken) internal {\\n uint256 marketsCount = allMarkets.length;\\n\\n for (uint256 i; i < marketsCount; ++i) {\\n if (allMarkets[i] == VToken(vToken)) {\\n revert MarketAlreadyListed(vToken);\\n }\\n }\\n allMarkets.push(VToken(vToken));\\n marketsCount = allMarkets.length;\\n _ensureMaxLoops(marketsCount);\\n }\\n\\n /**\\n * @dev Pause/unpause an action on a market\\n * @param market Market to pause/unpause the action on\\n * @param action Action id to pause/unpause\\n * @param paused The new paused state (true=paused, false=unpaused)\\n */\\n function _setActionPaused(address market, Action action, bool paused) internal {\\n require(markets[market].isListed, \\\"cannot pause a market that is not listed\\\");\\n _actionPaused[market][action] = paused;\\n emit ActionPausedMarket(VToken(market), action, paused);\\n }\\n\\n /**\\n * @dev Internal function to check that vTokens can be safely redeemed for the underlying asset.\\n * @param vToken Address of the vTokens to redeem\\n * @param redeemer Account redeeming the tokens\\n * @param redeemTokens The number of tokens to redeem\\n */\\n function _checkRedeemAllowed(address vToken, address redeemer, uint256 redeemTokens) internal {\\n Market storage market = markets[vToken];\\n\\n if (!market.isListed) {\\n revert MarketNotListed(address(vToken));\\n }\\n\\n /* If the redeemer is not 'in' the market, then we can bypass the liquidity check */\\n if (!market.accountMembership[redeemer]) {\\n return;\\n }\\n\\n // Update the prices of tokens\\n updatePrices(redeemer);\\n\\n /* Otherwise, perform a hypothetical liquidity check to guard against shortfall */\\n AccountLiquiditySnapshot memory snapshot = _getHypotheticalLiquiditySnapshot(\\n redeemer,\\n VToken(vToken),\\n redeemTokens,\\n 0,\\n _getCollateralFactor\\n );\\n if (snapshot.shortfall > 0) {\\n revert InsufficientLiquidity();\\n }\\n }\\n\\n /**\\n * @notice Get the total collateral, weighted collateral, borrow balance, liquidity, shortfall\\n * @param account The account to get the snapshot for\\n * @param weight The function to compute the weight of the collateral \\u2013\\u00a0either collateral factor or\\n * liquidation threshold. Accepts the address of the vToken and returns the weight as Exp.\\n * @dev Note that we calculate the exchangeRateStored for each collateral vToken using stored data,\\n * without calculating accumulated interest.\\n * @return snapshot Account liquidity snapshot\\n */\\n function _getCurrentLiquiditySnapshot(\\n address account,\\n function(VToken) internal view returns (Exp memory) weight\\n ) internal view returns (AccountLiquiditySnapshot memory snapshot) {\\n return _getHypotheticalLiquiditySnapshot(account, VToken(address(0)), 0, 0, weight);\\n }\\n\\n /**\\n * @notice Determine what the supply/borrow balances would be if the given amounts were redeemed/borrowed\\n * @param vTokenModify The market to hypothetically redeem/borrow in\\n * @param account The account to determine liquidity for\\n * @param redeemTokens The number of tokens to hypothetically redeem\\n * @param borrowAmount The amount of underlying to hypothetically borrow\\n * @param weight The function to compute the weight of the collateral \\u2013\\u00a0either collateral factor or\\n liquidation threshold. Accepts the address of the VToken and returns the weight\\n * @dev Note that we calculate the exchangeRateStored for each collateral vToken using stored data,\\n * without calculating accumulated interest.\\n * @return snapshot Account liquidity snapshot\\n */\\n function _getHypotheticalLiquiditySnapshot(\\n address account,\\n VToken vTokenModify,\\n uint256 redeemTokens,\\n uint256 borrowAmount,\\n function(VToken) internal view returns (Exp memory) weight\\n ) internal view returns (AccountLiquiditySnapshot memory snapshot) {\\n // For each asset the account is in\\n VToken[] memory assets = getAssetsIn(account);\\n uint256 assetsCount = assets.length;\\n\\n for (uint256 i; i < assetsCount; ++i) {\\n VToken asset = assets[i];\\n\\n // Read the balances and exchange rate from the vToken\\n (uint256 vTokenBalance, uint256 borrowBalance, uint256 exchangeRateMantissa) = _safeGetAccountSnapshot(\\n asset,\\n account\\n );\\n\\n // Get the normalized price of the asset\\n Exp memory oraclePrice = Exp({ mantissa: _safeGetUnderlyingPrice(asset) });\\n\\n // Pre-compute conversion factors from vTokens -> usd\\n Exp memory vTokenPrice = mul_(Exp({ mantissa: exchangeRateMantissa }), oraclePrice);\\n Exp memory weightedVTokenPrice = mul_(weight(asset), vTokenPrice);\\n\\n // weightedCollateral += weightedVTokenPrice * vTokenBalance\\n snapshot.weightedCollateral = mul_ScalarTruncateAddUInt(\\n weightedVTokenPrice,\\n vTokenBalance,\\n snapshot.weightedCollateral\\n );\\n\\n // totalCollateral += vTokenPrice * vTokenBalance\\n snapshot.totalCollateral = mul_ScalarTruncateAddUInt(vTokenPrice, vTokenBalance, snapshot.totalCollateral);\\n\\n // borrows += oraclePrice * borrowBalance\\n snapshot.borrows = mul_ScalarTruncateAddUInt(oraclePrice, borrowBalance, snapshot.borrows);\\n\\n // Calculate effects of interacting with vTokenModify\\n if (asset == vTokenModify) {\\n // redeem effect\\n // effects += tokensToDenom * redeemTokens\\n snapshot.effects = mul_ScalarTruncateAddUInt(weightedVTokenPrice, redeemTokens, snapshot.effects);\\n\\n // borrow effect\\n // effects += oraclePrice * borrowAmount\\n snapshot.effects = mul_ScalarTruncateAddUInt(oraclePrice, borrowAmount, snapshot.effects);\\n }\\n }\\n\\n uint256 borrowPlusEffects = snapshot.borrows + snapshot.effects;\\n // These are safe, as the underflow condition is checked first\\n unchecked {\\n if (snapshot.weightedCollateral > borrowPlusEffects) {\\n snapshot.liquidity = snapshot.weightedCollateral - borrowPlusEffects;\\n snapshot.shortfall = 0;\\n } else {\\n snapshot.liquidity = 0;\\n snapshot.shortfall = borrowPlusEffects - snapshot.weightedCollateral;\\n }\\n }\\n\\n return snapshot;\\n }\\n\\n /**\\n * @dev Retrieves price from oracle for an asset and checks it is nonzero\\n * @param asset Address for asset to query price\\n * @return Underlying price\\n */\\n function _safeGetUnderlyingPrice(VToken asset) internal view returns (uint256) {\\n uint256 oraclePriceMantissa = oracle.getUnderlyingPrice(address(asset));\\n if (oraclePriceMantissa == 0) {\\n revert PriceError(address(asset));\\n }\\n return oraclePriceMantissa;\\n }\\n\\n /**\\n * @dev Return collateral factor for a market\\n * @param asset Address for asset\\n * @return Collateral factor as exponential\\n */\\n function _getCollateralFactor(VToken asset) internal view returns (Exp memory) {\\n return Exp({ mantissa: markets[address(asset)].collateralFactorMantissa });\\n }\\n\\n /**\\n * @dev Retrieves liquidation threshold for a market as an exponential\\n * @param asset Address for asset to liquidation threshold\\n * @return Liquidation threshold as exponential\\n */\\n function _getLiquidationThreshold(VToken asset) internal view returns (Exp memory) {\\n return Exp({ mantissa: markets[address(asset)].liquidationThresholdMantissa });\\n }\\n\\n /**\\n * @dev Returns supply and borrow balances of user in vToken, reverts on failure\\n * @param vToken Market to query\\n * @param user Account address\\n * @return vTokenBalance Balance of vTokens, the same as vToken.balanceOf(user)\\n * @return borrowBalance Borrowed amount, including the interest\\n * @return exchangeRateMantissa Stored exchange rate\\n */\\n function _safeGetAccountSnapshot(\\n VToken vToken,\\n address user\\n ) internal view returns (uint256 vTokenBalance, uint256 borrowBalance, uint256 exchangeRateMantissa) {\\n uint256 err;\\n (err, vTokenBalance, borrowBalance, exchangeRateMantissa) = vToken.getAccountSnapshot(user);\\n if (err != 0) {\\n revert SnapshotError(address(vToken), user);\\n }\\n return (vTokenBalance, borrowBalance, exchangeRateMantissa);\\n }\\n\\n /// @notice Reverts if the call is not from expectedSender\\n /// @param expectedSender Expected transaction sender\\n function _checkSenderIs(address expectedSender) internal view {\\n if (msg.sender != expectedSender) {\\n revert UnexpectedSender(expectedSender, msg.sender);\\n }\\n }\\n\\n /// @notice Reverts if a certain action is paused on a market\\n /// @param market Market to check\\n /// @param action Action to check\\n function _checkActionPauseState(address market, Action action) private view {\\n if (actionPaused(market, action)) {\\n revert ActionPaused(market, action);\\n }\\n }\\n}\\n\",\"keccak256\":\"0x7c6e1c6264e4681f82a9ac1bcd9155197a930033291ee5561ad97a56006f5e9c\",\"license\":\"BSD-3-Clause\"},\"contracts/ComptrollerInterface.sol\":{\"content\":\"// SPDX-License-Identifier: BSD-3-Clause\\npragma solidity ^0.8.25;\\n\\nimport { ResilientOracleInterface } from \\\"@venusprotocol/oracle/contracts/interfaces/OracleInterface.sol\\\";\\n\\nimport { VToken } from \\\"./VToken.sol\\\";\\nimport { RewardsDistributor } from \\\"./Rewards/RewardsDistributor.sol\\\";\\n\\nenum Action {\\n MINT,\\n REDEEM,\\n BORROW,\\n REPAY,\\n SEIZE,\\n LIQUIDATE,\\n TRANSFER,\\n ENTER_MARKET,\\n EXIT_MARKET\\n}\\n\\n/**\\n * @title ComptrollerInterface\\n * @author Venus\\n * @notice Interface implemented by the `Comptroller` contract.\\n */\\ninterface ComptrollerInterface {\\n /*** Assets You Are In ***/\\n\\n function enterMarkets(address[] calldata vTokens) external returns (uint256[] memory);\\n\\n function exitMarket(address vToken) external returns (uint256);\\n\\n /*** Policy Hooks ***/\\n\\n function preMintHook(address vToken, address minter, uint256 mintAmount) external;\\n\\n function preRedeemHook(address vToken, address redeemer, uint256 redeemTokens) external;\\n\\n function preBorrowHook(address vToken, address borrower, uint256 borrowAmount) external;\\n\\n function preRepayHook(address vToken, address borrower) external;\\n\\n function preLiquidateHook(\\n address vTokenBorrowed,\\n address vTokenCollateral,\\n address borrower,\\n uint256 repayAmount,\\n bool skipLiquidityCheck\\n ) external;\\n\\n function preSeizeHook(\\n address vTokenCollateral,\\n address vTokenBorrowed,\\n address liquidator,\\n address borrower\\n ) external;\\n\\n function borrowVerify(address vToken, address borrower, uint borrowAmount) external;\\n\\n function mintVerify(address vToken, address minter, uint mintAmount, uint mintTokens) external;\\n\\n function redeemVerify(address vToken, address redeemer, uint redeemAmount, uint redeemTokens) external;\\n\\n function repayBorrowVerify(\\n address vToken,\\n address payer,\\n address borrower,\\n uint repayAmount,\\n uint borrowerIndex\\n ) external;\\n\\n function liquidateBorrowVerify(\\n address vTokenBorrowed,\\n address vTokenCollateral,\\n address liquidator,\\n address borrower,\\n uint repayAmount,\\n uint seizeTokens\\n ) external;\\n\\n function seizeVerify(\\n address vTokenCollateral,\\n address vTokenBorrowed,\\n address liquidator,\\n address borrower,\\n uint seizeTokens\\n ) external;\\n\\n function transferVerify(address vToken, address src, address dst, uint transferTokens) external;\\n\\n function preTransferHook(address vToken, address src, address dst, uint256 transferTokens) external;\\n\\n function isComptroller() external view returns (bool);\\n\\n /*** Liquidity/Liquidation Calculations ***/\\n\\n function liquidateCalculateSeizeTokens(\\n address vTokenBorrowed,\\n address vTokenCollateral,\\n uint256 repayAmount\\n ) external view returns (uint256, uint256);\\n\\n function getAllMarkets() external view returns (VToken[] memory);\\n\\n function actionPaused(address market, Action action) external view returns (bool);\\n}\\n\\n/**\\n * @title ComptrollerViewInterface\\n * @author Venus\\n * @notice Interface implemented by the `Comptroller` contract, including only some util view functions.\\n */\\ninterface ComptrollerViewInterface {\\n function markets(address) external view returns (bool, uint256);\\n\\n function oracle() external view returns (ResilientOracleInterface);\\n\\n function getAssetsIn(address) external view returns (VToken[] memory);\\n\\n function closeFactorMantissa() external view returns (uint256);\\n\\n function liquidationIncentiveMantissa() external view returns (uint256);\\n\\n function minLiquidatableCollateral() external view returns (uint256);\\n\\n function getRewardDistributors() external view returns (RewardsDistributor[] memory);\\n\\n function getAllMarkets() external view returns (VToken[] memory);\\n\\n function borrowCaps(address) external view returns (uint256);\\n\\n function supplyCaps(address) external view returns (uint256);\\n\\n function approvedDelegates(address user, address delegate) external view returns (bool);\\n}\\n\",\"keccak256\":\"0xcbf7f9977472650c61345b7cbde23e81f626e1f8863bab4c6ce3dacfc7604919\",\"license\":\"BSD-3-Clause\"},\"contracts/ComptrollerStorage.sol\":{\"content\":\"// SPDX-License-Identifier: BSD-3-Clause\\npragma solidity 0.8.25;\\n\\nimport { ResilientOracleInterface } from \\\"@venusprotocol/oracle/contracts/interfaces/OracleInterface.sol\\\";\\n\\nimport { VToken } from \\\"./VToken.sol\\\";\\nimport { RewardsDistributor } from \\\"./Rewards/RewardsDistributor.sol\\\";\\nimport { IPrime } from \\\"@venusprotocol/venus-protocol/contracts/Tokens/Prime/Interfaces/IPrime.sol\\\";\\nimport { Action } from \\\"./ComptrollerInterface.sol\\\";\\n\\n/**\\n * @title ComptrollerStorage\\n * @author Venus\\n * @notice Storage layout for the `Comptroller` contract.\\n */\\ncontract ComptrollerStorage {\\n struct LiquidationOrder {\\n VToken vTokenCollateral;\\n VToken vTokenBorrowed;\\n uint256 repayAmount;\\n }\\n\\n struct AccountLiquiditySnapshot {\\n uint256 totalCollateral;\\n uint256 weightedCollateral;\\n uint256 borrows;\\n uint256 effects;\\n uint256 liquidity;\\n uint256 shortfall;\\n }\\n\\n struct RewardSpeeds {\\n address rewardToken;\\n uint256 supplySpeed;\\n uint256 borrowSpeed;\\n }\\n\\n struct Market {\\n // Whether or not this market is listed\\n bool isListed;\\n // Multiplier representing the most one can borrow against their collateral in this market.\\n // For instance, 0.9 to allow borrowing 90% of collateral value.\\n // Must be between 0 and 1, and stored as a mantissa.\\n uint256 collateralFactorMantissa;\\n // Multiplier representing the collateralization after which the borrow is eligible\\n // for liquidation. For instance, 0.8 liquidate when the borrow is 80% of collateral\\n // value. Must be between 0 and collateral factor, stored as a mantissa.\\n uint256 liquidationThresholdMantissa;\\n // Per-market mapping of \\\"accounts in this asset\\\"\\n mapping(address => bool) accountMembership;\\n }\\n\\n /**\\n * @notice Oracle which gives the price of any given asset\\n */\\n ResilientOracleInterface public oracle;\\n\\n /**\\n * @notice Multiplier used to calculate the maximum repayAmount when liquidating a borrow\\n */\\n uint256 public closeFactorMantissa;\\n\\n /**\\n * @notice Multiplier representing the discount on collateral that a liquidator receives\\n */\\n uint256 public liquidationIncentiveMantissa;\\n\\n /**\\n * @notice Per-account mapping of \\\"assets you are in\\\"\\n */\\n mapping(address => VToken[]) public accountAssets;\\n\\n /**\\n * @notice Official mapping of vTokens -> Market metadata\\n * @dev Used e.g. to determine if a market is supported\\n */\\n mapping(address => Market) public markets;\\n\\n /// @notice A list of all markets\\n VToken[] public allMarkets;\\n\\n /// @notice Borrow caps enforced by borrowAllowed for each vToken address. Defaults to zero which restricts borrowing.\\n mapping(address => uint256) public borrowCaps;\\n\\n /// @notice Minimal collateral required for regular (non-batch) liquidations\\n uint256 public minLiquidatableCollateral;\\n\\n /// @notice Supply caps enforced by mintAllowed for each vToken address. Defaults to zero which corresponds to minting not allowed\\n mapping(address => uint256) public supplyCaps;\\n\\n /// @notice True if a certain action is paused on a certain market\\n mapping(address => mapping(Action => bool)) internal _actionPaused;\\n\\n // List of Reward Distributors added\\n RewardsDistributor[] internal rewardsDistributors;\\n\\n // Used to check if rewards distributor is added\\n mapping(address => bool) internal rewardsDistributorExists;\\n\\n /// @notice Flag indicating whether forced liquidation enabled for a market\\n mapping(address => bool) public isForcedLiquidationEnabled;\\n\\n uint256 internal constant NO_ERROR = 0;\\n\\n // closeFactorMantissa must be strictly greater than this value\\n uint256 internal constant MIN_CLOSE_FACTOR_MANTISSA = 0.05e18; // 0.05\\n\\n // closeFactorMantissa must not exceed this value\\n uint256 internal constant MAX_CLOSE_FACTOR_MANTISSA = 0.9e18; // 0.9\\n\\n // No collateralFactorMantissa may exceed this value\\n uint256 internal constant MAX_COLLATERAL_FACTOR_MANTISSA = 0.95e18; // 0.95\\n\\n /// Prime token address\\n IPrime public prime;\\n\\n /// @notice Whether the delegate is allowed to borrow or redeem on behalf of the user\\n //mapping(address user => mapping (address delegate => bool approved)) public approvedDelegates;\\n mapping(address => mapping(address => bool)) public approvedDelegates;\\n\\n /**\\n * @dev This empty reserved space is put in place to allow future versions to add new\\n * variables without shifting down storage in the inheritance chain.\\n * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\\n */\\n uint256[47] private __gap;\\n}\\n\",\"keccak256\":\"0x4df44cb65ac152bac2be4b4545430e703005a74a55b72e144100b16421bd8bfd\",\"license\":\"BSD-3-Clause\"},\"contracts/ErrorReporter.sol\":{\"content\":\"// SPDX-License-Identifier: BSD-3-Clause\\npragma solidity 0.8.25;\\n\\n/**\\n * @title TokenErrorReporter\\n * @author Venus\\n * @notice Errors that can be thrown by the `VToken` contract.\\n */\\ncontract TokenErrorReporter {\\n uint256 public constant NO_ERROR = 0; // support legacy return codes\\n\\n error TransferNotAllowed();\\n\\n error MintFreshnessCheck();\\n\\n error RedeemFreshnessCheck();\\n error RedeemTransferOutNotPossible();\\n\\n error BorrowFreshnessCheck();\\n error BorrowCashNotAvailable();\\n error DelegateNotApproved();\\n\\n error RepayBorrowFreshnessCheck();\\n\\n error HealBorrowUnauthorized();\\n error ForceLiquidateBorrowUnauthorized();\\n\\n error LiquidateFreshnessCheck();\\n error LiquidateCollateralFreshnessCheck();\\n error LiquidateAccrueCollateralInterestFailed(uint256 errorCode);\\n error LiquidateLiquidatorIsBorrower();\\n error LiquidateCloseAmountIsZero();\\n error LiquidateCloseAmountIsUintMax();\\n\\n error LiquidateSeizeLiquidatorIsBorrower();\\n\\n error ProtocolSeizeShareTooBig();\\n\\n error SetReserveFactorFreshCheck();\\n error SetReserveFactorBoundsCheck();\\n\\n error AddReservesFactorFreshCheck(uint256 actualAddAmount);\\n\\n error ReduceReservesFreshCheck();\\n error ReduceReservesCashNotAvailable();\\n error ReduceReservesCashValidation();\\n\\n error SetInterestRateModelFreshCheck();\\n}\\n\",\"keccak256\":\"0x7a7ced1caaac2d9242659ebd50b99f308c725ce958ac9e11f7ada404b1d97a7b\",\"license\":\"BSD-3-Clause\"},\"contracts/ExponentialNoError.sol\":{\"content\":\"// SPDX-License-Identifier: BSD-3-Clause\\npragma solidity 0.8.25;\\n\\nimport { EXP_SCALE as EXP_SCALE_, MANTISSA_ONE as MANTISSA_ONE_ } from \\\"./lib/constants.sol\\\";\\n\\n/**\\n * @title Exponential module for storing fixed-precision decimals\\n * @author Compound\\n * @notice Exp is a struct which stores decimals with a fixed precision of 18 decimal places.\\n * Thus, if we wanted to store the 5.1, mantissa would store 5.1e18. That is:\\n * `Exp({mantissa: 5100000000000000000})`.\\n */\\ncontract ExponentialNoError {\\n struct Exp {\\n uint256 mantissa;\\n }\\n\\n struct Double {\\n uint256 mantissa;\\n }\\n\\n uint256 internal constant EXP_SCALE = EXP_SCALE_;\\n uint256 internal constant DOUBLE_SCALE = 1e36;\\n uint256 internal constant HALF_EXP_SCALE = EXP_SCALE / 2;\\n uint256 internal constant MANTISSA_ONE = MANTISSA_ONE_;\\n\\n /**\\n * @dev Truncates the given exp to a whole number value.\\n * For example, truncate(Exp{mantissa: 15 * EXP_SCALE}) = 15\\n */\\n function truncate(Exp memory exp) internal pure returns (uint256) {\\n // Note: We are not using careful math here as we're performing a division that cannot fail\\n return exp.mantissa / EXP_SCALE;\\n }\\n\\n /**\\n * @dev Multiply an Exp by a scalar, then truncate to return an unsigned integer.\\n */\\n // solhint-disable-next-line func-name-mixedcase\\n function mul_ScalarTruncate(Exp memory a, uint256 scalar) internal pure returns (uint256) {\\n Exp memory product = mul_(a, scalar);\\n return truncate(product);\\n }\\n\\n /**\\n * @dev Multiply an Exp by a scalar, truncate, then add an to an unsigned integer, returning an unsigned integer.\\n */\\n // solhint-disable-next-line func-name-mixedcase\\n function mul_ScalarTruncateAddUInt(Exp memory a, uint256 scalar, uint256 addend) internal pure returns (uint256) {\\n Exp memory product = mul_(a, scalar);\\n return add_(truncate(product), addend);\\n }\\n\\n /**\\n * @dev Checks if first Exp is less than second Exp.\\n */\\n function lessThanExp(Exp memory left, Exp memory right) internal pure returns (bool) {\\n return left.mantissa < right.mantissa;\\n }\\n\\n function safe224(uint256 n, string memory errorMessage) internal pure returns (uint224) {\\n require(n <= type(uint224).max, errorMessage);\\n return uint224(n);\\n }\\n\\n function safe32(uint256 n, string memory errorMessage) internal pure returns (uint32) {\\n require(n <= type(uint32).max, errorMessage);\\n return uint32(n);\\n }\\n\\n function add_(Exp memory a, Exp memory b) internal pure returns (Exp memory) {\\n return Exp({ mantissa: add_(a.mantissa, b.mantissa) });\\n }\\n\\n function add_(Double memory a, Double memory b) internal pure returns (Double memory) {\\n return Double({ mantissa: add_(a.mantissa, b.mantissa) });\\n }\\n\\n function add_(uint256 a, uint256 b) internal pure returns (uint256) {\\n return a + b;\\n }\\n\\n function sub_(Exp memory a, Exp memory b) internal pure returns (Exp memory) {\\n return Exp({ mantissa: sub_(a.mantissa, b.mantissa) });\\n }\\n\\n function sub_(Double memory a, Double memory b) internal pure returns (Double memory) {\\n return Double({ mantissa: sub_(a.mantissa, b.mantissa) });\\n }\\n\\n function sub_(uint256 a, uint256 b) internal pure returns (uint256) {\\n return a - b;\\n }\\n\\n function mul_(Exp memory a, Exp memory b) internal pure returns (Exp memory) {\\n return Exp({ mantissa: mul_(a.mantissa, b.mantissa) / EXP_SCALE });\\n }\\n\\n function mul_(Exp memory a, uint256 b) internal pure returns (Exp memory) {\\n return Exp({ mantissa: mul_(a.mantissa, b) });\\n }\\n\\n function mul_(uint256 a, Exp memory b) internal pure returns (uint256) {\\n return mul_(a, b.mantissa) / EXP_SCALE;\\n }\\n\\n function mul_(Double memory a, Double memory b) internal pure returns (Double memory) {\\n return Double({ mantissa: mul_(a.mantissa, b.mantissa) / DOUBLE_SCALE });\\n }\\n\\n function mul_(Double memory a, uint256 b) internal pure returns (Double memory) {\\n return Double({ mantissa: mul_(a.mantissa, b) });\\n }\\n\\n function mul_(uint256 a, Double memory b) internal pure returns (uint256) {\\n return mul_(a, b.mantissa) / DOUBLE_SCALE;\\n }\\n\\n function mul_(uint256 a, uint256 b) internal pure returns (uint256) {\\n return a * b;\\n }\\n\\n function div_(Exp memory a, Exp memory b) internal pure returns (Exp memory) {\\n return Exp({ mantissa: div_(mul_(a.mantissa, EXP_SCALE), b.mantissa) });\\n }\\n\\n function div_(Exp memory a, uint256 b) internal pure returns (Exp memory) {\\n return Exp({ mantissa: div_(a.mantissa, b) });\\n }\\n\\n function div_(uint256 a, Exp memory b) internal pure returns (uint256) {\\n return div_(mul_(a, EXP_SCALE), b.mantissa);\\n }\\n\\n function div_(Double memory a, Double memory b) internal pure returns (Double memory) {\\n return Double({ mantissa: div_(mul_(a.mantissa, DOUBLE_SCALE), b.mantissa) });\\n }\\n\\n function div_(Double memory a, uint256 b) internal pure returns (Double memory) {\\n return Double({ mantissa: div_(a.mantissa, b) });\\n }\\n\\n function div_(uint256 a, Double memory b) internal pure returns (uint256) {\\n return div_(mul_(a, DOUBLE_SCALE), b.mantissa);\\n }\\n\\n function div_(uint256 a, uint256 b) internal pure returns (uint256) {\\n return a / b;\\n }\\n\\n function fraction(uint256 a, uint256 b) internal pure returns (Double memory) {\\n return Double({ mantissa: div_(mul_(a, DOUBLE_SCALE), b) });\\n }\\n}\\n\",\"keccak256\":\"0x8afbe8a24fe3539c124e718681ed3dcebd24c40dd53095786c0155998213b9b0\",\"license\":\"BSD-3-Clause\"},\"contracts/InterestRateModel.sol\":{\"content\":\"// SPDX-License-Identifier: BSD-3-Clause\\npragma solidity 0.8.25;\\n\\n/**\\n * @title Compound's InterestRateModel Interface\\n * @author Compound\\n */\\nabstract contract InterestRateModel {\\n /**\\n * @notice Calculates the current borrow interest rate per slot (block or second)\\n * @param cash The total amount of cash the market has\\n * @param borrows The total amount of borrows the market has outstanding\\n * @param reserves The total amount of reserves the market has\\n * @param badDebt The amount of badDebt in the market\\n * @return The borrow rate percentage per slot (block or second) as a mantissa (scaled by EXP_SCALE)\\n */\\n function getBorrowRate(\\n uint256 cash,\\n uint256 borrows,\\n uint256 reserves,\\n uint256 badDebt\\n ) external view virtual returns (uint256);\\n\\n /**\\n * @notice Calculates the current supply interest rate per slot (block or second)\\n * @param cash The total amount of cash the market has\\n * @param borrows The total amount of borrows the market has outstanding\\n * @param reserves The total amount of reserves the market has\\n * @param reserveFactorMantissa The current reserve factor the market has\\n * @param badDebt The amount of badDebt in the market\\n * @return The supply rate percentage per slot (block or second) as a mantissa (scaled by EXP_SCALE)\\n */\\n function getSupplyRate(\\n uint256 cash,\\n uint256 borrows,\\n uint256 reserves,\\n uint256 reserveFactorMantissa,\\n uint256 badDebt\\n ) external view virtual returns (uint256);\\n\\n /**\\n * @notice Indicator that this is an InterestRateModel contract (for inspection)\\n * @return Always true\\n */\\n function isInterestRateModel() external pure virtual returns (bool) {\\n return true;\\n }\\n}\\n\",\"keccak256\":\"0xc4fda1ab75ebe4b187b707c4f10c58780f343cf343c537f641dc75d3cd28ab51\",\"license\":\"BSD-3-Clause\"},\"contracts/MaxLoopsLimitHelper.sol\":{\"content\":\"// SPDX-License-Identifier: BSD-3-Clause\\npragma solidity 0.8.25;\\n\\n/**\\n * @title MaxLoopsLimitHelper\\n * @author Venus\\n * @notice Abstract contract used to avoid collection with too many items that would generate gas errors and DoS.\\n */\\nabstract contract MaxLoopsLimitHelper {\\n // Limit for the loops to avoid the DOS\\n uint256 public maxLoopsLimit;\\n\\n /**\\n * @dev This empty reserved space is put in place to allow future versions to add new\\n * variables without shifting down storage in the inheritance chain.\\n * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\\n */\\n uint256[49] private __gap;\\n\\n /// @notice Emitted when max loops limit is set\\n event MaxLoopsLimitUpdated(uint256 oldMaxLoopsLimit, uint256 newmaxLoopsLimit);\\n\\n /// @notice Thrown an error on maxLoopsLimit exceeds for any loop\\n error MaxLoopsLimitExceeded(uint256 loopsLimit, uint256 requiredLoops);\\n\\n /**\\n * @notice Set the limit for the loops can iterate to avoid the DOS\\n * @param limit Limit for the max loops can execute at a time\\n */\\n function _setMaxLoopsLimit(uint256 limit) internal {\\n require(limit > maxLoopsLimit, \\\"Comptroller: Invalid maxLoopsLimit\\\");\\n\\n uint256 oldMaxLoopsLimit = maxLoopsLimit;\\n maxLoopsLimit = limit;\\n\\n emit MaxLoopsLimitUpdated(oldMaxLoopsLimit, limit);\\n }\\n\\n /**\\n * @notice Compare the maxLoopsLimit with number of the times loop iterate\\n * @param len Length of the loops iterate\\n * @custom:error MaxLoopsLimitExceeded error is thrown when loops length exceeds maxLoopsLimit\\n */\\n function _ensureMaxLoops(uint256 len) internal view {\\n if (len > maxLoopsLimit) {\\n revert MaxLoopsLimitExceeded(maxLoopsLimit, len);\\n }\\n }\\n}\\n\",\"keccak256\":\"0x4c25e30635485d162177effa384eee51768b0141a567a0da16ff6ad673274166\",\"license\":\"BSD-3-Clause\"},\"contracts/Pool/PoolRegistry.sol\":{\"content\":\"// SPDX-License-Identifier: BSD-3-Clause\\npragma solidity 0.8.25;\\n\\nimport { Ownable2StepUpgradeable } from \\\"@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol\\\";\\nimport { SafeERC20Upgradeable } from \\\"@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol\\\";\\nimport { IERC20Upgradeable } from \\\"@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol\\\";\\nimport { AccessControlledV8 } from \\\"@venusprotocol/governance-contracts/contracts/Governance/AccessControlledV8.sol\\\";\\n\\nimport { PoolRegistryInterface } from \\\"./PoolRegistryInterface.sol\\\";\\nimport { Comptroller } from \\\"../Comptroller.sol\\\";\\nimport { VToken } from \\\"../VToken.sol\\\";\\nimport { ensureNonzeroAddress } from \\\"../lib/validators.sol\\\";\\n\\n/**\\n * @title PoolRegistry\\n * @author Venus\\n * @notice The Isolated Pools architecture centers around the `PoolRegistry` contract. The `PoolRegistry` maintains a directory of isolated lending\\n * pools and can perform actions like creating and registering new pools, adding new markets to existing pools, setting and updating the pool's required\\n * metadata, and providing the getter methods to get information on the pools.\\n *\\n * Isolated lending has three main components: PoolRegistry, pools, and markets. The PoolRegistry is responsible for managing pools.\\n * It can create new pools, update pool metadata and manage markets within pools. PoolRegistry contains getter methods to get the details of\\n * any existing pool like `getVTokenForAsset` and `getPoolsSupportedByAsset`. It also contains methods for updating pool metadata (`updatePoolMetadata`)\\n * and setting pool name (`setPoolName`).\\n *\\n * The directory of pools is managed through two mappings: `_poolByComptroller` which is a hashmap with the comptroller address as the key and `VenusPool` as\\n * the value and `_poolsByID` which is an array of comptroller addresses. Individual pools can be accessed by calling `getPoolByComptroller` with the pool's\\n * comptroller address. `_poolsByID` is used to iterate through all of the pools.\\n *\\n * PoolRegistry also contains a map of asset addresses called `_supportedPools` that maps to an array of assets suppored by each pool. This array of pools by\\n * asset is retrieved by calling `getPoolsSupportedByAsset`.\\n *\\n * PoolRegistry registers new isolated pools in the directory with the `createRegistryPool` method. Isolated pools are composed of independent markets with\\n * specific assets and custom risk management configurations according to their markets.\\n */\\ncontract PoolRegistry is Ownable2StepUpgradeable, AccessControlledV8, PoolRegistryInterface {\\n using SafeERC20Upgradeable for IERC20Upgradeable;\\n\\n struct AddMarketInput {\\n VToken vToken;\\n uint256 collateralFactor;\\n uint256 liquidationThreshold;\\n uint256 initialSupply;\\n address vTokenReceiver;\\n uint256 supplyCap;\\n uint256 borrowCap;\\n }\\n\\n uint256 internal constant MAX_POOL_NAME_LENGTH = 100;\\n\\n /**\\n * @notice Maps pool's comptroller address to metadata.\\n */\\n mapping(address => VenusPoolMetaData) public metadata;\\n\\n /**\\n * @dev Maps pool ID to pool's comptroller address\\n */\\n mapping(uint256 => address) private _poolsByID;\\n\\n /**\\n * @dev Total number of pools created.\\n */\\n uint256 private _numberOfPools;\\n\\n /**\\n * @dev Maps comptroller address to Venus pool Index.\\n */\\n mapping(address => VenusPool) private _poolByComptroller;\\n\\n /**\\n * @dev Maps pool's comptroller address to asset to vToken.\\n */\\n mapping(address => mapping(address => address)) private _vTokens;\\n\\n /**\\n * @dev Maps asset to list of supported pools.\\n */\\n mapping(address => address[]) private _supportedPools;\\n\\n /**\\n * @notice Emitted when a new Venus pool is added to the directory.\\n */\\n event PoolRegistered(address indexed comptroller, VenusPool pool);\\n\\n /**\\n * @notice Emitted when a pool name is set.\\n */\\n event PoolNameSet(address indexed comptroller, string oldName, string newName);\\n\\n /**\\n * @notice Emitted when a pool metadata is updated.\\n */\\n event PoolMetadataUpdated(\\n address indexed comptroller,\\n VenusPoolMetaData oldMetadata,\\n VenusPoolMetaData newMetadata\\n );\\n\\n /**\\n * @notice Emitted when a Market is added to the pool.\\n */\\n event MarketAdded(address indexed comptroller, address indexed vTokenAddress);\\n\\n /// @custom:oz-upgrades-unsafe-allow constructor\\n constructor() {\\n // Note that the contract is upgradeable. Use initialize() or reinitializers\\n // to set the state variables.\\n _disableInitializers();\\n }\\n\\n /**\\n * @notice Initializes the deployer to owner\\n * @param accessControlManager_ AccessControlManager contract address\\n */\\n function initialize(address accessControlManager_) external initializer {\\n __Ownable2Step_init();\\n __AccessControlled_init_unchained(accessControlManager_);\\n }\\n\\n /**\\n * @notice Adds a new Venus pool to the directory\\n * @dev Price oracle must be configured before adding a pool\\n * @param name The name of the pool\\n * @param comptroller Pool's Comptroller contract\\n * @param closeFactor The pool's close factor (scaled by 1e18)\\n * @param liquidationIncentive The pool's liquidation incentive (scaled by 1e18)\\n * @param minLiquidatableCollateral Minimal collateral for regular (non-batch) liquidations flow\\n * @return index The index of the registered Venus pool\\n * @custom:error ZeroAddressNotAllowed is thrown when Comptroller address is zero\\n * @custom:error ZeroAddressNotAllowed is thrown when price oracle address is zero\\n */\\n function addPool(\\n string calldata name,\\n Comptroller comptroller,\\n uint256 closeFactor,\\n uint256 liquidationIncentive,\\n uint256 minLiquidatableCollateral\\n ) external virtual returns (uint256 index) {\\n _checkAccessAllowed(\\\"addPool(string,address,uint256,uint256,uint256)\\\");\\n // Input validation\\n ensureNonzeroAddress(address(comptroller));\\n ensureNonzeroAddress(address(comptroller.oracle()));\\n\\n uint256 poolId = _registerPool(name, address(comptroller));\\n\\n // Set Venus pool parameters\\n comptroller.setCloseFactor(closeFactor);\\n comptroller.setLiquidationIncentive(liquidationIncentive);\\n comptroller.setMinLiquidatableCollateral(minLiquidatableCollateral);\\n\\n return poolId;\\n }\\n\\n /**\\n * @notice Add a market to an existing pool and then mint to provide initial supply\\n * @param input The structure describing the parameters for adding a market to a pool\\n * @custom:error ZeroAddressNotAllowed is thrown when vToken address is zero\\n * @custom:error ZeroAddressNotAllowed is thrown when vTokenReceiver address is zero\\n */\\n function addMarket(AddMarketInput memory input) external {\\n _checkAccessAllowed(\\\"addMarket(AddMarketInput)\\\");\\n ensureNonzeroAddress(address(input.vToken));\\n ensureNonzeroAddress(input.vTokenReceiver);\\n require(input.initialSupply > 0, \\\"PoolRegistry: initialSupply is zero\\\");\\n\\n VToken vToken = input.vToken;\\n address vTokenAddress = address(vToken);\\n address comptrollerAddress = address(vToken.comptroller());\\n Comptroller comptroller = Comptroller(comptrollerAddress);\\n address underlyingAddress = vToken.underlying();\\n IERC20Upgradeable underlying = IERC20Upgradeable(underlyingAddress);\\n\\n require(_poolByComptroller[comptrollerAddress].creator != address(0), \\\"PoolRegistry: Pool not registered\\\");\\n // solhint-disable-next-line reason-string\\n require(\\n _vTokens[comptrollerAddress][underlyingAddress] == address(0),\\n \\\"PoolRegistry: Market already added for asset comptroller combination\\\"\\n );\\n\\n comptroller.supportMarket(vToken);\\n comptroller.setCollateralFactor(vToken, input.collateralFactor, input.liquidationThreshold);\\n\\n uint256[] memory newSupplyCaps = new uint256[](1);\\n uint256[] memory newBorrowCaps = new uint256[](1);\\n VToken[] memory vTokens = new VToken[](1);\\n\\n newSupplyCaps[0] = input.supplyCap;\\n newBorrowCaps[0] = input.borrowCap;\\n vTokens[0] = vToken;\\n\\n comptroller.setMarketSupplyCaps(vTokens, newSupplyCaps);\\n comptroller.setMarketBorrowCaps(vTokens, newBorrowCaps);\\n\\n _vTokens[comptrollerAddress][underlyingAddress] = vTokenAddress;\\n _supportedPools[underlyingAddress].push(comptrollerAddress);\\n\\n uint256 amountToSupply = _transferIn(underlying, msg.sender, input.initialSupply);\\n underlying.forceApprove(vTokenAddress, 0);\\n underlying.forceApprove(vTokenAddress, amountToSupply);\\n vToken.mintBehalf(input.vTokenReceiver, amountToSupply);\\n\\n emit MarketAdded(comptrollerAddress, vTokenAddress);\\n }\\n\\n /**\\n * @notice Modify existing Venus pool name\\n * @param comptroller Pool's Comptroller\\n * @param name New pool name\\n */\\n function setPoolName(address comptroller, string calldata name) external {\\n _checkAccessAllowed(\\\"setPoolName(address,string)\\\");\\n _ensureValidName(name);\\n VenusPool storage pool = _poolByComptroller[comptroller];\\n string memory oldName = pool.name;\\n pool.name = name;\\n emit PoolNameSet(comptroller, oldName, name);\\n }\\n\\n /**\\n * @notice Update metadata of an existing pool\\n * @param comptroller Pool's Comptroller\\n * @param metadata_ New pool metadata\\n */\\n function updatePoolMetadata(address comptroller, VenusPoolMetaData calldata metadata_) external {\\n _checkAccessAllowed(\\\"updatePoolMetadata(address,VenusPoolMetaData)\\\");\\n VenusPoolMetaData memory oldMetadata = metadata[comptroller];\\n metadata[comptroller] = metadata_;\\n emit PoolMetadataUpdated(comptroller, oldMetadata, metadata_);\\n }\\n\\n /**\\n * @notice Returns arrays of all Venus pools' data\\n * @dev This function is not designed to be called in a transaction: it is too gas-intensive\\n * @return A list of all pools within PoolRegistry, with details for each pool\\n */\\n function getAllPools() external view override returns (VenusPool[] memory) {\\n uint256 numberOfPools_ = _numberOfPools; // storage load to save gas\\n VenusPool[] memory _pools = new VenusPool[](numberOfPools_);\\n for (uint256 i = 1; i <= numberOfPools_; ++i) {\\n address comptroller = _poolsByID[i];\\n _pools[i - 1] = (_poolByComptroller[comptroller]);\\n }\\n return _pools;\\n }\\n\\n /**\\n * @param comptroller The comptroller proxy address associated to the pool\\n * @return Returns Venus pool\\n */\\n function getPoolByComptroller(address comptroller) external view override returns (VenusPool memory) {\\n return _poolByComptroller[comptroller];\\n }\\n\\n /**\\n * @param comptroller comptroller of Venus pool\\n * @return Returns Metadata of Venus pool\\n */\\n function getVenusPoolMetadata(address comptroller) external view override returns (VenusPoolMetaData memory) {\\n return metadata[comptroller];\\n }\\n\\n function getVTokenForAsset(address comptroller, address asset) external view override returns (address) {\\n return _vTokens[comptroller][asset];\\n }\\n\\n function getPoolsSupportedByAsset(address asset) external view override returns (address[] memory) {\\n return _supportedPools[asset];\\n }\\n\\n /**\\n * @dev Adds a new Venus pool to the directory (without checking msg.sender).\\n * @param name The name of the pool\\n * @param comptroller The pool's Comptroller proxy contract address\\n * @return The index of the registered Venus pool\\n */\\n function _registerPool(string calldata name, address comptroller) internal returns (uint256) {\\n VenusPool storage storedPool = _poolByComptroller[comptroller];\\n\\n require(storedPool.creator == address(0), \\\"PoolRegistry: Pool already exists in the directory.\\\");\\n _ensureValidName(name);\\n\\n ++_numberOfPools;\\n uint256 numberOfPools_ = _numberOfPools; // cache on stack to save storage read gas\\n\\n VenusPool memory pool = VenusPool(name, msg.sender, comptroller, block.number, block.timestamp);\\n\\n _poolsByID[numberOfPools_] = comptroller;\\n _poolByComptroller[comptroller] = pool;\\n\\n emit PoolRegistered(comptroller, pool);\\n return numberOfPools_;\\n }\\n\\n function _transferIn(IERC20Upgradeable token, address from, uint256 amount) internal returns (uint256) {\\n uint256 balanceBefore = token.balanceOf(address(this));\\n token.safeTransferFrom(from, address(this), amount);\\n uint256 balanceAfter = token.balanceOf(address(this));\\n return balanceAfter - balanceBefore;\\n }\\n\\n function _ensureValidName(string calldata name) internal pure {\\n require(bytes(name).length <= MAX_POOL_NAME_LENGTH, \\\"Pool's name is too large\\\");\\n }\\n}\\n\",\"keccak256\":\"0xafbb871f7b4db3ef439d846baa5f18a136192f9b43ea695c81262a77b06c4b25\",\"license\":\"BSD-3-Clause\"},\"contracts/Pool/PoolRegistryInterface.sol\":{\"content\":\"// SPDX-License-Identifier: BSD-3-Clause\\npragma solidity ^0.8.25;\\n\\n/**\\n * @title PoolRegistryInterface\\n * @author Venus\\n * @notice Interface implemented by `PoolRegistry`.\\n */\\ninterface PoolRegistryInterface {\\n /**\\n * @notice Struct for a Venus interest rate pool.\\n */\\n struct VenusPool {\\n string name;\\n address creator;\\n address comptroller;\\n uint256 blockPosted;\\n uint256 timestampPosted;\\n }\\n\\n /**\\n * @notice Struct for a Venus interest rate pool metadata.\\n */\\n struct VenusPoolMetaData {\\n string category;\\n string logoURL;\\n string description;\\n }\\n\\n /// @notice Get all pools in PoolRegistry\\n function getAllPools() external view returns (VenusPool[] memory);\\n\\n /// @notice Get a pool by comptroller address\\n function getPoolByComptroller(address comptroller) external view returns (VenusPool memory);\\n\\n /// @notice Get the address of the VToken contract in the Pool where the underlying token is the provided asset\\n function getVTokenForAsset(address comptroller, address asset) external view returns (address);\\n\\n /// @notice Get the addresss of the Pools supported that include a market for the provided asset\\n function getPoolsSupportedByAsset(address asset) external view returns (address[] memory);\\n\\n /// @notice Get the metadata of a Pool by comptroller address\\n function getVenusPoolMetadata(address comptroller) external view returns (VenusPoolMetaData memory);\\n}\\n\",\"keccak256\":\"0x7b39cda3b372a686501ce3c2aad288c6af410148110318249d75d4516729c92c\",\"license\":\"BSD-3-Clause\"},\"contracts/Rewards/RewardsDistributor.sol\":{\"content\":\"// SPDX-License-Identifier: BSD-3-Clause\\npragma solidity 0.8.25;\\n\\nimport { Ownable2StepUpgradeable } from \\\"@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol\\\";\\nimport { IERC20Upgradeable } from \\\"@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol\\\";\\nimport { SafeERC20Upgradeable } from \\\"@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol\\\";\\nimport { AccessControlledV8 } from \\\"@venusprotocol/governance-contracts/contracts/Governance/AccessControlledV8.sol\\\";\\nimport { TimeManagerV8 } from \\\"@venusprotocol/solidity-utilities/contracts/TimeManagerV8.sol\\\";\\n\\nimport { ExponentialNoError } from \\\"../ExponentialNoError.sol\\\";\\nimport { VToken } from \\\"../VToken.sol\\\";\\nimport { Comptroller } from \\\"../Comptroller.sol\\\";\\nimport { MaxLoopsLimitHelper } from \\\"../MaxLoopsLimitHelper.sol\\\";\\nimport { RewardsDistributorStorage } from \\\"./RewardsDistributorStorage.sol\\\";\\n\\n/**\\n * @title `RewardsDistributor`\\n * @author Venus\\n * @notice Contract used to configure, track and distribute rewards to users based on their actions (borrows and supplies) in the protocol.\\n * Users can receive additional rewards through a `RewardsDistributor`. Each `RewardsDistributor` proxy is initialized with a specific reward\\n * token and `Comptroller`, which can then distribute the reward token to users that supply or borrow in the associated pool.\\n * Authorized users can set the reward token borrow and supply speeds for each market in the pool. This sets a fixed amount of reward\\n * token to be released each slot (block or second) for borrowers and suppliers, which is distributed based on a user\\u2019s percentage of the borrows or supplies\\n * respectively. The owner can also set up reward distributions to contributor addresses (distinct from suppliers and borrowers) by setting\\n * their contributor reward token speed, which similarly allocates a fixed amount of reward token per slot (block or second).\\n *\\n * The owner has the ability to transfer any amount of reward tokens held by the contract to any other address. Rewards are not distributed\\n * automatically and must be claimed by a user calling `claimRewardToken()`. Users should be aware that it is up to the owner and other centralized\\n * entities to ensure that the `RewardsDistributor` holds enough tokens to distribute the accumulated rewards of users and contributors.\\n */\\ncontract RewardsDistributor is\\n ExponentialNoError,\\n Ownable2StepUpgradeable,\\n AccessControlledV8,\\n MaxLoopsLimitHelper,\\n RewardsDistributorStorage,\\n TimeManagerV8\\n{\\n using SafeERC20Upgradeable for IERC20Upgradeable;\\n\\n /// @notice The initial REWARD TOKEN index for a market\\n uint224 public constant INITIAL_INDEX = 1e36;\\n\\n /// @notice Emitted when REWARD TOKEN is distributed to a supplier\\n event DistributedSupplierRewardToken(\\n VToken indexed vToken,\\n address indexed supplier,\\n uint256 rewardTokenDelta,\\n uint256 rewardTokenTotal,\\n uint256 rewardTokenSupplyIndex\\n );\\n\\n /// @notice Emitted when REWARD TOKEN is distributed to a borrower\\n event DistributedBorrowerRewardToken(\\n VToken indexed vToken,\\n address indexed borrower,\\n uint256 rewardTokenDelta,\\n uint256 rewardTokenTotal,\\n uint256 rewardTokenBorrowIndex\\n );\\n\\n /// @notice Emitted when a new supply-side REWARD TOKEN speed is calculated for a market\\n event RewardTokenSupplySpeedUpdated(VToken indexed vToken, uint256 newSpeed);\\n\\n /// @notice Emitted when a new borrow-side REWARD TOKEN speed is calculated for a market\\n event RewardTokenBorrowSpeedUpdated(VToken indexed vToken, uint256 newSpeed);\\n\\n /// @notice Emitted when REWARD TOKEN is granted by admin\\n event RewardTokenGranted(address indexed recipient, uint256 amount);\\n\\n /// @notice Emitted when a new REWARD TOKEN speed is set for a contributor\\n event ContributorRewardTokenSpeedUpdated(address indexed contributor, uint256 newSpeed);\\n\\n /// @notice Emitted when a market is initialized\\n event MarketInitialized(address indexed vToken);\\n\\n /// @notice Emitted when a reward token supply index is updated\\n event RewardTokenSupplyIndexUpdated(address indexed vToken);\\n\\n /// @notice Emitted when a reward token borrow index is updated\\n event RewardTokenBorrowIndexUpdated(address indexed vToken, Exp marketBorrowIndex);\\n\\n /// @notice Emitted when a reward for contributor is updated\\n event ContributorRewardsUpdated(address indexed contributor, uint256 rewardAccrued);\\n\\n /// @notice Emitted when a reward token last rewarding block for supply is updated\\n event SupplyLastRewardingBlockUpdated(address indexed vToken, uint32 newBlock);\\n\\n /// @notice Emitted when a reward token last rewarding block for borrow is updated\\n event BorrowLastRewardingBlockUpdated(address indexed vToken, uint32 newBlock);\\n\\n /// @notice Emitted when a reward token last rewarding timestamp for supply is updated\\n event SupplyLastRewardingBlockTimestampUpdated(address indexed vToken, uint256 newTimestamp);\\n\\n /// @notice Emitted when a reward token last rewarding timestamp for borrow is updated\\n event BorrowLastRewardingBlockTimestampUpdated(address indexed vToken, uint256 newTimestamp);\\n\\n modifier onlyComptroller() {\\n require(address(comptroller) == msg.sender, \\\"Only comptroller can call this function\\\");\\n _;\\n }\\n\\n /**\\n * @param timeBased_ A boolean indicating whether the contract is based on time or block.\\n * @param blocksPerYear_ The number of blocks per year\\n * @custom:oz-upgrades-unsafe-allow constructor\\n */\\n constructor(bool timeBased_, uint256 blocksPerYear_) TimeManagerV8(timeBased_, blocksPerYear_) {\\n // Note that the contract is upgradeable. Use initialize() or reinitializers\\n // to set the state variables.\\n _disableInitializers();\\n }\\n\\n /**\\n * @notice RewardsDistributor initializer\\n * @dev Initializes the deployer to owner\\n * @param comptroller_ Comptroller to attach the reward distributor to\\n * @param rewardToken_ Reward token to distribute\\n * @param loopsLimit_ Maximum number of iterations for the loops in this contract\\n * @param accessControlManager_ AccessControlManager contract address\\n */\\n function initialize(\\n Comptroller comptroller_,\\n IERC20Upgradeable rewardToken_,\\n uint256 loopsLimit_,\\n address accessControlManager_\\n ) external initializer {\\n comptroller = comptroller_;\\n rewardToken = rewardToken_;\\n __Ownable2Step_init();\\n __AccessControlled_init_unchained(accessControlManager_);\\n\\n _setMaxLoopsLimit(loopsLimit_);\\n }\\n\\n /**\\n * @notice Initializes the market state for a specific vToken\\n * @param vToken The address of the vToken to be initialized\\n * @custom:event MarketInitialized emits on success\\n * @custom:access Only Comptroller\\n */\\n function initializeMarket(address vToken) external onlyComptroller {\\n uint256 blockNumberOrTimestamp = getBlockNumberOrTimestamp();\\n\\n isTimeBased\\n ? _initializeMarketTimestampBased(vToken, blockNumberOrTimestamp)\\n : _initializeMarketBlockBased(vToken, safe32(blockNumberOrTimestamp, \\\"block number exceeds 32 bits\\\"));\\n\\n emit MarketInitialized(vToken);\\n }\\n\\n /*** Reward Token Distribution ***/\\n\\n /**\\n * @notice Calculate reward token accrued by a borrower and possibly transfer it to them\\n * Borrowers will begin to accrue after the first interaction with the protocol.\\n * @dev This function should only be called when the user has a borrow position in the market\\n * (e.g. Comptroller.preBorrowHook, and Comptroller.preRepayHook)\\n * We avoid an external call to check if they are in the market to save gas because this function is called in many places\\n * @param vToken The market in which the borrower is interacting\\n * @param borrower The address of the borrower to distribute REWARD TOKEN to\\n * @param marketBorrowIndex The current global borrow index of vToken\\n */\\n function distributeBorrowerRewardToken(\\n address vToken,\\n address borrower,\\n Exp memory marketBorrowIndex\\n ) external onlyComptroller {\\n _distributeBorrowerRewardToken(vToken, borrower, marketBorrowIndex);\\n }\\n\\n function updateRewardTokenSupplyIndex(address vToken) external onlyComptroller {\\n _updateRewardTokenSupplyIndex(vToken);\\n }\\n\\n /**\\n * @notice Transfer REWARD TOKEN to the recipient\\n * @dev Note: If there is not enough REWARD TOKEN, we do not perform the transfer all\\n * @param recipient The address of the recipient to transfer REWARD TOKEN to\\n * @param amount The amount of REWARD TOKEN to (possibly) transfer\\n */\\n function grantRewardToken(address recipient, uint256 amount) external onlyOwner {\\n uint256 amountLeft = _grantRewardToken(recipient, amount);\\n require(amountLeft == 0, \\\"insufficient rewardToken for grant\\\");\\n emit RewardTokenGranted(recipient, amount);\\n }\\n\\n function updateRewardTokenBorrowIndex(address vToken, Exp memory marketBorrowIndex) external onlyComptroller {\\n _updateRewardTokenBorrowIndex(vToken, marketBorrowIndex);\\n }\\n\\n /**\\n * @notice Set REWARD TOKEN borrow and supply speeds for the specified markets\\n * @param vTokens The markets whose REWARD TOKEN speed to update\\n * @param supplySpeeds New supply-side REWARD TOKEN speed for the corresponding market\\n * @param borrowSpeeds New borrow-side REWARD TOKEN speed for the corresponding market\\n */\\n function setRewardTokenSpeeds(\\n VToken[] memory vTokens,\\n uint256[] memory supplySpeeds,\\n uint256[] memory borrowSpeeds\\n ) external {\\n _checkAccessAllowed(\\\"setRewardTokenSpeeds(address[],uint256[],uint256[])\\\");\\n uint256 numTokens = vTokens.length;\\n require(numTokens == supplySpeeds.length && numTokens == borrowSpeeds.length, \\\"invalid setRewardTokenSpeeds\\\");\\n\\n for (uint256 i; i < numTokens; ++i) {\\n _setRewardTokenSpeed(vTokens[i], supplySpeeds[i], borrowSpeeds[i]);\\n }\\n }\\n\\n /**\\n * @notice Set REWARD TOKEN last rewarding block for the specified markets, used when contract is block based\\n * @param vTokens The markets whose REWARD TOKEN last rewarding block to update\\n * @param supplyLastRewardingBlocks New supply-side REWARD TOKEN last rewarding block for the corresponding market\\n * @param borrowLastRewardingBlocks New borrow-side REWARD TOKEN last rewarding block for the corresponding market\\n */\\n function setLastRewardingBlocks(\\n VToken[] calldata vTokens,\\n uint32[] calldata supplyLastRewardingBlocks,\\n uint32[] calldata borrowLastRewardingBlocks\\n ) external {\\n _checkAccessAllowed(\\\"setLastRewardingBlocks(address[],uint32[],uint32[])\\\");\\n require(!isTimeBased, \\\"Block-based operation only\\\");\\n\\n uint256 numTokens = vTokens.length;\\n require(\\n numTokens == supplyLastRewardingBlocks.length && numTokens == borrowLastRewardingBlocks.length,\\n \\\"RewardsDistributor::setLastRewardingBlocks invalid input\\\"\\n );\\n\\n for (uint256 i; i < numTokens; ) {\\n _setLastRewardingBlock(vTokens[i], supplyLastRewardingBlocks[i], borrowLastRewardingBlocks[i]);\\n unchecked {\\n ++i;\\n }\\n }\\n }\\n\\n /**\\n * @notice Set REWARD TOKEN last rewarding block timestamp for the specified markets, used when contract is time based\\n * @param vTokens The markets whose REWARD TOKEN last rewarding block to update\\n * @param supplyLastRewardingBlockTimestamps New supply-side REWARD TOKEN last rewarding block timestamp for the corresponding market\\n * @param borrowLastRewardingBlockTimestamps New borrow-side REWARD TOKEN last rewarding block timestamp for the corresponding market\\n */\\n function setLastRewardingBlockTimestamps(\\n VToken[] calldata vTokens,\\n uint256[] calldata supplyLastRewardingBlockTimestamps,\\n uint256[] calldata borrowLastRewardingBlockTimestamps\\n ) external {\\n _checkAccessAllowed(\\\"setLastRewardingBlockTimestamps(address[],uint256[],uint256[])\\\");\\n require(isTimeBased, \\\"Time-based operation only\\\");\\n\\n uint256 numTokens = vTokens.length;\\n require(\\n numTokens == supplyLastRewardingBlockTimestamps.length &&\\n numTokens == borrowLastRewardingBlockTimestamps.length,\\n \\\"RewardsDistributor::setLastRewardingBlockTimestamps invalid input\\\"\\n );\\n\\n for (uint256 i; i < numTokens; ) {\\n _setLastRewardingBlockTimestamp(\\n vTokens[i],\\n supplyLastRewardingBlockTimestamps[i],\\n borrowLastRewardingBlockTimestamps[i]\\n );\\n unchecked {\\n ++i;\\n }\\n }\\n }\\n\\n /**\\n * @notice Set REWARD TOKEN speed for a single contributor\\n * @param contributor The contributor whose REWARD TOKEN speed to update\\n * @param rewardTokenSpeed New REWARD TOKEN speed for contributor\\n */\\n function setContributorRewardTokenSpeed(address contributor, uint256 rewardTokenSpeed) external onlyOwner {\\n // note that REWARD TOKEN speed could be set to 0 to halt liquidity rewards for a contributor\\n updateContributorRewards(contributor);\\n if (rewardTokenSpeed == 0) {\\n // release storage\\n delete lastContributorBlock[contributor];\\n } else {\\n lastContributorBlock[contributor] = getBlockNumberOrTimestamp();\\n }\\n rewardTokenContributorSpeeds[contributor] = rewardTokenSpeed;\\n\\n emit ContributorRewardTokenSpeedUpdated(contributor, rewardTokenSpeed);\\n }\\n\\n function distributeSupplierRewardToken(address vToken, address supplier) external onlyComptroller {\\n _distributeSupplierRewardToken(vToken, supplier);\\n }\\n\\n /**\\n * @notice Claim all the rewardToken accrued by holder in all markets\\n * @param holder The address to claim REWARD TOKEN for\\n */\\n function claimRewardToken(address holder) external {\\n return claimRewardToken(holder, comptroller.getAllMarkets());\\n }\\n\\n /**\\n * @notice Set the limit for the loops can iterate to avoid the DOS\\n * @param limit Limit for the max loops can execute at a time\\n */\\n function setMaxLoopsLimit(uint256 limit) external onlyOwner {\\n _setMaxLoopsLimit(limit);\\n }\\n\\n /**\\n * @notice Calculate additional accrued REWARD TOKEN for a contributor since last accrual\\n * @param contributor The address to calculate contributor rewards for\\n */\\n function updateContributorRewards(address contributor) public {\\n uint256 rewardTokenSpeed = rewardTokenContributorSpeeds[contributor];\\n uint256 blockNumberOrTimestamp = getBlockNumberOrTimestamp();\\n uint256 deltaBlocksOrTimestamp = sub_(blockNumberOrTimestamp, lastContributorBlock[contributor]);\\n if (deltaBlocksOrTimestamp > 0 && rewardTokenSpeed > 0) {\\n uint256 newAccrued = mul_(deltaBlocksOrTimestamp, rewardTokenSpeed);\\n uint256 contributorAccrued = add_(rewardTokenAccrued[contributor], newAccrued);\\n\\n rewardTokenAccrued[contributor] = contributorAccrued;\\n lastContributorBlock[contributor] = blockNumberOrTimestamp;\\n\\n emit ContributorRewardsUpdated(contributor, rewardTokenAccrued[contributor]);\\n }\\n }\\n\\n /**\\n * @notice Claim all the rewardToken accrued by holder in the specified markets\\n * @param holder The address to claim REWARD TOKEN for\\n * @param vTokens The list of markets to claim REWARD TOKEN in\\n */\\n function claimRewardToken(address holder, VToken[] memory vTokens) public {\\n uint256 vTokensCount = vTokens.length;\\n\\n _ensureMaxLoops(vTokensCount);\\n\\n for (uint256 i; i < vTokensCount; ++i) {\\n VToken vToken = vTokens[i];\\n require(comptroller.isMarketListed(vToken), \\\"market must be listed\\\");\\n Exp memory borrowIndex = Exp({ mantissa: vToken.borrowIndex() });\\n _updateRewardTokenBorrowIndex(address(vToken), borrowIndex);\\n _distributeBorrowerRewardToken(address(vToken), holder, borrowIndex);\\n _updateRewardTokenSupplyIndex(address(vToken));\\n _distributeSupplierRewardToken(address(vToken), holder);\\n }\\n rewardTokenAccrued[holder] = _grantRewardToken(holder, rewardTokenAccrued[holder]);\\n }\\n\\n /**\\n * @notice Set REWARD TOKEN last rewarding block for a single market.\\n * @param vToken market's whose reward token last rewarding block to be updated\\n * @param supplyLastRewardingBlock New supply-side REWARD TOKEN last rewarding block for market\\n * @param borrowLastRewardingBlock New borrow-side REWARD TOKEN last rewarding block for market\\n */\\n function _setLastRewardingBlock(\\n VToken vToken,\\n uint32 supplyLastRewardingBlock,\\n uint32 borrowLastRewardingBlock\\n ) internal {\\n require(comptroller.isMarketListed(vToken), \\\"rewardToken market is not listed\\\");\\n\\n uint256 blockNumber = getBlockNumberOrTimestamp();\\n\\n require(supplyLastRewardingBlock > blockNumber, \\\"setting last rewarding block in the past is not allowed\\\");\\n require(borrowLastRewardingBlock > blockNumber, \\\"setting last rewarding block in the past is not allowed\\\");\\n\\n uint32 currentSupplyLastRewardingBlock = rewardTokenSupplyState[address(vToken)].lastRewardingBlock;\\n uint32 currentBorrowLastRewardingBlock = rewardTokenBorrowState[address(vToken)].lastRewardingBlock;\\n\\n require(\\n currentSupplyLastRewardingBlock == 0 || currentSupplyLastRewardingBlock > blockNumber,\\n \\\"this RewardsDistributor is already locked\\\"\\n );\\n require(\\n currentBorrowLastRewardingBlock == 0 || currentBorrowLastRewardingBlock > blockNumber,\\n \\\"this RewardsDistributor is already locked\\\"\\n );\\n\\n if (currentSupplyLastRewardingBlock != supplyLastRewardingBlock) {\\n rewardTokenSupplyState[address(vToken)].lastRewardingBlock = supplyLastRewardingBlock;\\n emit SupplyLastRewardingBlockUpdated(address(vToken), supplyLastRewardingBlock);\\n }\\n\\n if (currentBorrowLastRewardingBlock != borrowLastRewardingBlock) {\\n rewardTokenBorrowState[address(vToken)].lastRewardingBlock = borrowLastRewardingBlock;\\n emit BorrowLastRewardingBlockUpdated(address(vToken), borrowLastRewardingBlock);\\n }\\n }\\n\\n /**\\n * @notice Set REWARD TOKEN last rewarding timestamp for a single market.\\n * @param vToken market's whose reward token last rewarding timestamp to be updated\\n * @param supplyLastRewardingBlockTimestamp New supply-side REWARD TOKEN last rewarding timestamp for market\\n * @param borrowLastRewardingBlockTimestamp New borrow-side REWARD TOKEN last rewarding timestamp for market\\n */\\n function _setLastRewardingBlockTimestamp(\\n VToken vToken,\\n uint256 supplyLastRewardingBlockTimestamp,\\n uint256 borrowLastRewardingBlockTimestamp\\n ) internal {\\n require(comptroller.isMarketListed(vToken), \\\"rewardToken market is not listed\\\");\\n\\n uint256 blockTimestamp = getBlockNumberOrTimestamp();\\n\\n require(\\n supplyLastRewardingBlockTimestamp > blockTimestamp,\\n \\\"setting last rewarding timestamp in the past is not allowed\\\"\\n );\\n require(\\n borrowLastRewardingBlockTimestamp > blockTimestamp,\\n \\\"setting last rewarding timestamp in the past is not allowed\\\"\\n );\\n\\n uint256 currentSupplyLastRewardingBlockTimestamp = rewardTokenSupplyStateTimeBased[address(vToken)]\\n .lastRewardingTimestamp;\\n uint256 currentBorrowLastRewardingBlockTimestamp = rewardTokenBorrowStateTimeBased[address(vToken)]\\n .lastRewardingTimestamp;\\n\\n require(\\n currentSupplyLastRewardingBlockTimestamp == 0 || currentSupplyLastRewardingBlockTimestamp > blockTimestamp,\\n \\\"this RewardsDistributor is already locked\\\"\\n );\\n require(\\n currentBorrowLastRewardingBlockTimestamp == 0 || currentBorrowLastRewardingBlockTimestamp > blockTimestamp,\\n \\\"this RewardsDistributor is already locked\\\"\\n );\\n\\n if (currentSupplyLastRewardingBlockTimestamp != supplyLastRewardingBlockTimestamp) {\\n rewardTokenSupplyStateTimeBased[address(vToken)].lastRewardingTimestamp = supplyLastRewardingBlockTimestamp;\\n emit SupplyLastRewardingBlockTimestampUpdated(address(vToken), supplyLastRewardingBlockTimestamp);\\n }\\n\\n if (currentBorrowLastRewardingBlockTimestamp != borrowLastRewardingBlockTimestamp) {\\n rewardTokenBorrowStateTimeBased[address(vToken)].lastRewardingTimestamp = borrowLastRewardingBlockTimestamp;\\n emit BorrowLastRewardingBlockTimestampUpdated(address(vToken), borrowLastRewardingBlockTimestamp);\\n }\\n }\\n\\n /**\\n * @notice Set REWARD TOKEN speed for a single market.\\n * @param vToken market's whose reward token rate to be updated\\n * @param supplySpeed New supply-side REWARD TOKEN speed for market\\n * @param borrowSpeed New borrow-side REWARD TOKEN speed for market\\n */\\n function _setRewardTokenSpeed(VToken vToken, uint256 supplySpeed, uint256 borrowSpeed) internal {\\n require(comptroller.isMarketListed(vToken), \\\"rewardToken market is not listed\\\");\\n\\n if (rewardTokenSupplySpeeds[address(vToken)] != supplySpeed) {\\n // Supply speed updated so let's update supply state to ensure that\\n // 1. REWARD TOKEN accrued properly for the old speed, and\\n // 2. REWARD TOKEN accrued at the new speed starts after this block.\\n _updateRewardTokenSupplyIndex(address(vToken));\\n\\n // Update speed and emit event\\n rewardTokenSupplySpeeds[address(vToken)] = supplySpeed;\\n emit RewardTokenSupplySpeedUpdated(vToken, supplySpeed);\\n }\\n\\n if (rewardTokenBorrowSpeeds[address(vToken)] != borrowSpeed) {\\n // Borrow speed updated so let's update borrow state to ensure that\\n // 1. REWARD TOKEN accrued properly for the old speed, and\\n // 2. REWARD TOKEN accrued at the new speed starts after this block.\\n Exp memory borrowIndex = Exp({ mantissa: vToken.borrowIndex() });\\n _updateRewardTokenBorrowIndex(address(vToken), borrowIndex);\\n\\n // Update speed and emit event\\n rewardTokenBorrowSpeeds[address(vToken)] = borrowSpeed;\\n emit RewardTokenBorrowSpeedUpdated(vToken, borrowSpeed);\\n }\\n }\\n\\n /**\\n * @notice Calculate REWARD TOKEN accrued by a supplier and possibly transfer it to them.\\n * @param vToken The market in which the supplier is interacting\\n * @param supplier The address of the supplier to distribute REWARD TOKEN to\\n */\\n function _distributeSupplierRewardToken(address vToken, address supplier) internal {\\n RewardToken storage supplyState = rewardTokenSupplyState[vToken];\\n TimeBasedRewardToken storage supplyStateTimeBased = rewardTokenSupplyStateTimeBased[vToken];\\n\\n uint256 supplyIndex = isTimeBased ? supplyStateTimeBased.index : supplyState.index;\\n uint256 supplierIndex = rewardTokenSupplierIndex[vToken][supplier];\\n\\n // Update supplier's index to the current index since we are distributing accrued REWARD TOKEN\\n rewardTokenSupplierIndex[vToken][supplier] = supplyIndex;\\n\\n if (supplierIndex == 0 && supplyIndex >= INITIAL_INDEX) {\\n // Covers the case where users supplied tokens before the market's supply state index was set.\\n // Rewards the user with REWARD TOKEN accrued from the start of when supplier rewards were first\\n // set for the market.\\n supplierIndex = INITIAL_INDEX;\\n }\\n\\n // Calculate change in the cumulative sum of the REWARD TOKEN per vToken accrued\\n Double memory deltaIndex = Double({ mantissa: sub_(supplyIndex, supplierIndex) });\\n\\n uint256 supplierTokens = VToken(vToken).balanceOf(supplier);\\n\\n // Calculate REWARD TOKEN accrued: vTokenAmount * accruedPerVToken\\n uint256 supplierDelta = mul_(supplierTokens, deltaIndex);\\n\\n uint256 supplierAccrued = add_(rewardTokenAccrued[supplier], supplierDelta);\\n rewardTokenAccrued[supplier] = supplierAccrued;\\n\\n emit DistributedSupplierRewardToken(VToken(vToken), supplier, supplierDelta, supplierAccrued, supplyIndex);\\n }\\n\\n /**\\n * @notice Calculate reward token accrued by a borrower and possibly transfer it to them.\\n * @param vToken The market in which the borrower is interacting\\n * @param borrower The address of the borrower to distribute REWARD TOKEN to\\n * @param marketBorrowIndex The current global borrow index of vToken\\n */\\n function _distributeBorrowerRewardToken(address vToken, address borrower, Exp memory marketBorrowIndex) internal {\\n RewardToken storage borrowState = rewardTokenBorrowState[vToken];\\n TimeBasedRewardToken storage borrowStateTimeBased = rewardTokenBorrowStateTimeBased[vToken];\\n\\n uint256 borrowIndex = isTimeBased ? borrowStateTimeBased.index : borrowState.index;\\n uint256 borrowerIndex = rewardTokenBorrowerIndex[vToken][borrower];\\n\\n // Update borrowers's index to the current index since we are distributing accrued REWARD TOKEN\\n rewardTokenBorrowerIndex[vToken][borrower] = borrowIndex;\\n\\n if (borrowerIndex == 0 && borrowIndex >= INITIAL_INDEX) {\\n // Covers the case where users borrowed tokens before the market's borrow state index was set.\\n // Rewards the user with REWARD TOKEN accrued from the start of when borrower rewards were first\\n // set for the market.\\n borrowerIndex = INITIAL_INDEX;\\n }\\n\\n // Calculate change in the cumulative sum of the REWARD TOKEN per borrowed unit accrued\\n Double memory deltaIndex = Double({ mantissa: sub_(borrowIndex, borrowerIndex) });\\n\\n uint256 borrowerAmount = div_(VToken(vToken).borrowBalanceStored(borrower), marketBorrowIndex);\\n\\n // Calculate REWARD TOKEN accrued: vTokenAmount * accruedPerBorrowedUnit\\n if (borrowerAmount != 0) {\\n uint256 borrowerDelta = mul_(borrowerAmount, deltaIndex);\\n\\n uint256 borrowerAccrued = add_(rewardTokenAccrued[borrower], borrowerDelta);\\n rewardTokenAccrued[borrower] = borrowerAccrued;\\n\\n emit DistributedBorrowerRewardToken(VToken(vToken), borrower, borrowerDelta, borrowerAccrued, borrowIndex);\\n }\\n }\\n\\n /**\\n * @notice Transfer REWARD TOKEN to the user.\\n * @dev Note: If there is not enough REWARD TOKEN, we do not perform the transfer all.\\n * @param user The address of the user to transfer REWARD TOKEN to\\n * @param amount The amount of REWARD TOKEN to (possibly) transfer\\n * @return The amount of REWARD TOKEN which was NOT transferred to the user\\n */\\n function _grantRewardToken(address user, uint256 amount) internal returns (uint256) {\\n uint256 rewardTokenRemaining = rewardToken.balanceOf(address(this));\\n if (amount > 0 && amount <= rewardTokenRemaining) {\\n rewardToken.safeTransfer(user, amount);\\n return 0;\\n }\\n return amount;\\n }\\n\\n /**\\n * @notice Accrue REWARD TOKEN to the market by updating the supply index\\n * @param vToken The market whose supply index to update\\n * @dev Index is a cumulative sum of the REWARD TOKEN per vToken accrued\\n */\\n function _updateRewardTokenSupplyIndex(address vToken) internal {\\n RewardToken storage supplyState = rewardTokenSupplyState[vToken];\\n TimeBasedRewardToken storage supplyStateTimeBased = rewardTokenSupplyStateTimeBased[vToken];\\n\\n uint256 supplySpeed = rewardTokenSupplySpeeds[vToken];\\n uint256 blockNumberOrTimestamp = getBlockNumberOrTimestamp();\\n\\n if (!isTimeBased) {\\n safe32(blockNumberOrTimestamp, \\\"block number exceeds 32 bits\\\");\\n }\\n\\n uint256 lastRewardingBlockOrTimestamp = isTimeBased\\n ? supplyStateTimeBased.lastRewardingTimestamp\\n : uint256(supplyState.lastRewardingBlock);\\n\\n if (lastRewardingBlockOrTimestamp > 0 && blockNumberOrTimestamp > lastRewardingBlockOrTimestamp) {\\n blockNumberOrTimestamp = lastRewardingBlockOrTimestamp;\\n }\\n\\n uint256 deltaBlocksOrTimestamp = sub_(\\n blockNumberOrTimestamp,\\n (isTimeBased ? supplyStateTimeBased.timestamp : uint256(supplyState.block))\\n );\\n if (deltaBlocksOrTimestamp > 0 && supplySpeed > 0) {\\n uint256 supplyTokens = VToken(vToken).totalSupply();\\n uint256 accruedSinceUpdate = mul_(deltaBlocksOrTimestamp, supplySpeed);\\n Double memory ratio = supplyTokens > 0\\n ? fraction(accruedSinceUpdate, supplyTokens)\\n : Double({ mantissa: 0 });\\n uint224 supplyIndex = isTimeBased ? supplyStateTimeBased.index : supplyState.index;\\n uint224 index = safe224(\\n add_(Double({ mantissa: supplyIndex }), ratio).mantissa,\\n \\\"new index exceeds 224 bits\\\"\\n );\\n\\n if (isTimeBased) {\\n supplyStateTimeBased.index = index;\\n supplyStateTimeBased.timestamp = blockNumberOrTimestamp;\\n } else {\\n supplyState.index = index;\\n supplyState.block = uint32(blockNumberOrTimestamp);\\n }\\n } else if (deltaBlocksOrTimestamp > 0) {\\n isTimeBased ? supplyStateTimeBased.timestamp = blockNumberOrTimestamp : supplyState.block = uint32(\\n blockNumberOrTimestamp\\n );\\n }\\n\\n emit RewardTokenSupplyIndexUpdated(vToken);\\n }\\n\\n /**\\n * @notice Accrue REWARD TOKEN to the market by updating the borrow index\\n * @param vToken The market whose borrow index to update\\n * @param marketBorrowIndex The current global borrow index of vToken\\n * @dev Index is a cumulative sum of the REWARD TOKEN per vToken accrued\\n */\\n function _updateRewardTokenBorrowIndex(address vToken, Exp memory marketBorrowIndex) internal {\\n RewardToken storage borrowState = rewardTokenBorrowState[vToken];\\n TimeBasedRewardToken storage borrowStateTimeBased = rewardTokenBorrowStateTimeBased[vToken];\\n\\n uint256 borrowSpeed = rewardTokenBorrowSpeeds[vToken];\\n uint256 blockNumberOrTimestamp = getBlockNumberOrTimestamp();\\n\\n if (!isTimeBased) {\\n safe32(blockNumberOrTimestamp, \\\"block number exceeds 32 bits\\\");\\n }\\n\\n uint256 lastRewardingBlockOrTimestamp = isTimeBased\\n ? borrowStateTimeBased.lastRewardingTimestamp\\n : uint256(borrowState.lastRewardingBlock);\\n\\n if (lastRewardingBlockOrTimestamp > 0 && blockNumberOrTimestamp > lastRewardingBlockOrTimestamp) {\\n blockNumberOrTimestamp = lastRewardingBlockOrTimestamp;\\n }\\n\\n uint256 deltaBlocksOrTimestamp = sub_(\\n blockNumberOrTimestamp,\\n (isTimeBased ? borrowStateTimeBased.timestamp : uint256(borrowState.block))\\n );\\n if (deltaBlocksOrTimestamp > 0 && borrowSpeed > 0) {\\n uint256 borrowAmount = div_(VToken(vToken).totalBorrows(), marketBorrowIndex);\\n uint256 accruedSinceUpdate = mul_(deltaBlocksOrTimestamp, borrowSpeed);\\n Double memory ratio = borrowAmount > 0\\n ? fraction(accruedSinceUpdate, borrowAmount)\\n : Double({ mantissa: 0 });\\n uint224 borrowIndex = isTimeBased ? borrowStateTimeBased.index : borrowState.index;\\n uint224 index = safe224(\\n add_(Double({ mantissa: borrowIndex }), ratio).mantissa,\\n \\\"new index exceeds 224 bits\\\"\\n );\\n\\n if (isTimeBased) {\\n borrowStateTimeBased.index = index;\\n borrowStateTimeBased.timestamp = blockNumberOrTimestamp;\\n } else {\\n borrowState.index = index;\\n borrowState.block = uint32(blockNumberOrTimestamp);\\n }\\n } else if (deltaBlocksOrTimestamp > 0) {\\n if (isTimeBased) {\\n borrowStateTimeBased.timestamp = blockNumberOrTimestamp;\\n } else {\\n borrowState.block = uint32(blockNumberOrTimestamp);\\n }\\n }\\n\\n emit RewardTokenBorrowIndexUpdated(vToken, marketBorrowIndex);\\n }\\n\\n /**\\n * @notice Initializes the market state for a specific vToken called when contract is block-based\\n * @param vToken The address of the vToken to be initialized\\n * @param blockNumber current block number\\n */\\n function _initializeMarketBlockBased(address vToken, uint32 blockNumber) internal {\\n RewardToken storage supplyState = rewardTokenSupplyState[vToken];\\n RewardToken storage borrowState = rewardTokenBorrowState[vToken];\\n\\n /*\\n * Update market state indices\\n */\\n if (supplyState.index == 0) {\\n // Initialize supply state index with default value\\n supplyState.index = INITIAL_INDEX;\\n }\\n\\n if (borrowState.index == 0) {\\n // Initialize borrow state index with default value\\n borrowState.index = INITIAL_INDEX;\\n }\\n\\n /*\\n * Update market state block numbers\\n */\\n supplyState.block = borrowState.block = blockNumber;\\n }\\n\\n /**\\n * @notice Initializes the market state for a specific vToken called when contract is time-based\\n * @param vToken The address of the vToken to be initialized\\n * @param blockTimestamp current block timestamp\\n */\\n function _initializeMarketTimestampBased(address vToken, uint256 blockTimestamp) internal {\\n TimeBasedRewardToken storage supplyState = rewardTokenSupplyStateTimeBased[vToken];\\n TimeBasedRewardToken storage borrowState = rewardTokenBorrowStateTimeBased[vToken];\\n\\n /*\\n * Update market state indices\\n */\\n if (supplyState.index == 0) {\\n // Initialize supply state index with default value\\n supplyState.index = INITIAL_INDEX;\\n }\\n\\n if (borrowState.index == 0) {\\n // Initialize borrow state index with default value\\n borrowState.index = INITIAL_INDEX;\\n }\\n\\n /*\\n * Update market state block timestamp\\n */\\n supplyState.timestamp = borrowState.timestamp = blockTimestamp;\\n }\\n}\\n\",\"keccak256\":\"0x3cc824e59c923cfe25c4f1a875959b7e9410cde8e73cd405de13f301298c697f\",\"license\":\"BSD-3-Clause\"},\"contracts/Rewards/RewardsDistributorStorage.sol\":{\"content\":\"// SPDX-License-Identifier: BSD-3-Clause\\npragma solidity 0.8.25;\\n\\nimport { IERC20Upgradeable } from \\\"@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol\\\";\\n\\nimport { Comptroller } from \\\"../Comptroller.sol\\\";\\n\\n/**\\n * @title RewardsDistributorStorage\\n * @author Venus\\n * @dev Storage for RewardsDistributor\\n */\\ncontract RewardsDistributorStorage {\\n struct RewardToken {\\n // The market's last updated rewardTokenBorrowIndex or rewardTokenSupplyIndex\\n uint224 index;\\n // The block number the index was last updated at\\n uint32 block;\\n // The block number at which to stop rewards\\n uint32 lastRewardingBlock;\\n }\\n\\n struct TimeBasedRewardToken {\\n // The market's last updated rewardTokenBorrowIndex or rewardTokenSupplyIndex\\n uint224 index;\\n // The block timestamp the index was last updated at\\n uint256 timestamp;\\n // The block timestamp at which to stop rewards\\n uint256 lastRewardingTimestamp;\\n }\\n\\n /// @notice The REWARD TOKEN market supply state for each market\\n mapping(address => RewardToken) public rewardTokenSupplyState;\\n\\n /// @notice The REWARD TOKEN borrow index for each market for each supplier as of the last time they accrued REWARD TOKEN\\n mapping(address => mapping(address => uint256)) public rewardTokenSupplierIndex;\\n\\n /// @notice The REWARD TOKEN accrued but not yet transferred to each user\\n mapping(address => uint256) public rewardTokenAccrued;\\n\\n /// @notice The rate at which rewardToken is distributed to the corresponding borrow market per slot (block or second)\\n mapping(address => uint256) public rewardTokenBorrowSpeeds;\\n\\n /// @notice The rate at which rewardToken is distributed to the corresponding supply market per slot (block or second)\\n mapping(address => uint256) public rewardTokenSupplySpeeds;\\n\\n /// @notice The REWARD TOKEN market borrow state for each market\\n mapping(address => RewardToken) public rewardTokenBorrowState;\\n\\n /// @notice The portion of REWARD TOKEN that each contributor receives per slot (block or second)\\n mapping(address => uint256) public rewardTokenContributorSpeeds;\\n\\n /// @notice Last slot (block or second) at which a contributor's REWARD TOKEN rewards have been allocated\\n mapping(address => uint256) public lastContributorBlock;\\n\\n /// @notice The REWARD TOKEN borrow index for each market for each borrower as of the last time they accrued REWARD TOKEN\\n mapping(address => mapping(address => uint256)) public rewardTokenBorrowerIndex;\\n\\n Comptroller internal comptroller;\\n\\n IERC20Upgradeable public rewardToken;\\n\\n /// @notice The REWARD TOKEN market supply state for each market\\n mapping(address => TimeBasedRewardToken) public rewardTokenSupplyStateTimeBased;\\n\\n /// @notice The REWARD TOKEN market borrow state for each market\\n mapping(address => TimeBasedRewardToken) public rewardTokenBorrowStateTimeBased;\\n\\n /**\\n * @dev This empty reserved space is put in place to allow future versions to add new\\n * variables without shifting down storage in the inheritance chain\\n * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\\n */\\n uint256[37] private __gap;\\n}\\n\",\"keccak256\":\"0x70e5015a78a2acf95277949c8b4796e10b9ac194130be006d5e5217e158070c0\",\"license\":\"BSD-3-Clause\"},\"contracts/VToken.sol\":{\"content\":\"// SPDX-License-Identifier: BSD-3-Clause\\npragma solidity 0.8.25;\\n\\nimport { Ownable2StepUpgradeable } from \\\"@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol\\\";\\nimport { IERC20Upgradeable } from \\\"@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol\\\";\\nimport { SafeERC20Upgradeable } from \\\"@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol\\\";\\nimport { AccessControlledV8 } from \\\"@venusprotocol/governance-contracts/contracts/Governance/AccessControlledV8.sol\\\";\\nimport { IProtocolShareReserve } from \\\"@venusprotocol/protocol-reserve/contracts/Interfaces/IProtocolShareReserve.sol\\\";\\n\\nimport { VTokenInterface } from \\\"./VTokenInterfaces.sol\\\";\\nimport { ComptrollerInterface, ComptrollerViewInterface } from \\\"./ComptrollerInterface.sol\\\";\\nimport { TokenErrorReporter } from \\\"./ErrorReporter.sol\\\";\\nimport { InterestRateModel } from \\\"./InterestRateModel.sol\\\";\\nimport { ExponentialNoError } from \\\"./ExponentialNoError.sol\\\";\\nimport { TimeManagerV8 } from \\\"@venusprotocol/solidity-utilities/contracts/TimeManagerV8.sol\\\";\\nimport { ensureNonzeroAddress } from \\\"./lib/validators.sol\\\";\\n\\n/**\\n * @title VToken\\n * @author Venus\\n * @notice Each asset that is supported by a pool is integrated through an instance of the `VToken` contract. As outlined in the protocol overview,\\n * each isolated pool creates its own `vToken` corresponding to an asset. Within a given pool, each included `vToken` is referred to as a market of\\n * the pool. The main actions a user regularly interacts with in a market are:\\n\\n- mint/redeem of vTokens;\\n- transfer of vTokens;\\n- borrow/repay a loan on an underlying asset;\\n- liquidate a borrow or liquidate/heal an account.\\n\\n * A user supplies the underlying asset to a pool by minting `vTokens`, where the corresponding `vToken` amount is determined by the `exchangeRate`.\\n * The `exchangeRate` will change over time, dependent on a number of factors, some of which accrue interest. Additionally, once users have minted\\n * `vToken` in a pool, they can borrow any asset in the isolated pool by using their `vToken` as collateral. In order to borrow an asset or use a `vToken`\\n * as collateral, the user must be entered into each corresponding market (else, the `vToken` will not be considered collateral for a borrow). Note that\\n * a user may borrow up to a portion of their collateral determined by the market\\u2019s collateral factor. However, if their borrowed amount exceeds an amount\\n * calculated using the market\\u2019s corresponding liquidation threshold, the borrow is eligible for liquidation. When a user repays a borrow, they must also\\n * pay off interest accrued on the borrow.\\n * \\n * The Venus protocol includes unique mechanisms for healing an account and liquidating an account. These actions are performed in the `Comptroller`\\n * and consider all borrows and collateral for which a given account is entered within a market. These functions may only be called on an account with a\\n * total collateral amount that is no larger than a universal `minLiquidatableCollateral` value, which is used for all markets within a `Comptroller`.\\n * Both functions settle all of an account\\u2019s borrows, but `healAccount()` may add `badDebt` to a vToken. For more detail, see the description of\\n * `healAccount()` and `liquidateAccount()` in the `Comptroller` summary section below.\\n */\\ncontract VToken is\\n Ownable2StepUpgradeable,\\n AccessControlledV8,\\n VTokenInterface,\\n ExponentialNoError,\\n TokenErrorReporter,\\n TimeManagerV8\\n{\\n using SafeERC20Upgradeable for IERC20Upgradeable;\\n\\n uint256 internal constant DEFAULT_PROTOCOL_SEIZE_SHARE_MANTISSA = 5e16; // 5%\\n\\n // Maximum fraction of interest that can be set aside for reserves\\n uint256 internal constant MAX_RESERVE_FACTOR_MANTISSA = 1e18;\\n\\n // Maximum borrow rate that can ever be applied per slot(block or second)\\n /// @custom:oz-upgrades-unsafe-allow state-variable-immutable\\n uint256 internal immutable MAX_BORROW_RATE_MANTISSA;\\n\\n /**\\n * Reentrancy Guard **\\n */\\n\\n /**\\n * @dev Prevents a contract from calling itself, directly or indirectly.\\n */\\n modifier nonReentrant() {\\n require(_notEntered, \\\"re-entered\\\");\\n _notEntered = false;\\n _;\\n _notEntered = true; // get a gas-refund post-Istanbul\\n }\\n\\n /**\\n * @param timeBased_ A boolean indicating whether the contract is based on time or block.\\n * @param blocksPerYear_ The number of blocks per year\\n * @param maxBorrowRateMantissa_ The maximum value of borrowing rate mantissa\\n * @custom:oz-upgrades-unsafe-allow constructor\\n */\\n constructor(\\n bool timeBased_,\\n uint256 blocksPerYear_,\\n uint256 maxBorrowRateMantissa_\\n ) TimeManagerV8(timeBased_, blocksPerYear_) {\\n // Note that the contract is upgradeable. Use initialize() or reinitializers\\n // to set the state variables.\\n require(maxBorrowRateMantissa_ <= 1e18, \\\"Max borrow rate must be <= 1e18\\\");\\n\\n MAX_BORROW_RATE_MANTISSA = maxBorrowRateMantissa_;\\n _disableInitializers();\\n }\\n\\n /**\\n * @notice Construct a new money market\\n * @param underlying_ The address of the underlying asset\\n * @param comptroller_ The address of the Comptroller\\n * @param interestRateModel_ The address of the interest rate model\\n * @param initialExchangeRateMantissa_ The initial exchange rate, scaled by 1e18\\n * @param name_ ERC-20 name of this token\\n * @param symbol_ ERC-20 symbol of this token\\n * @param decimals_ ERC-20 decimal precision of this token\\n * @param admin_ Address of the administrator of this token\\n * @param accessControlManager_ AccessControlManager contract address\\n * @param riskManagement Addresses of risk & income related contracts\\n * @param reserveFactorMantissa_ Percentage of borrow interest that goes to reserves (from 0 to 1e18)\\n * @custom:error ZeroAddressNotAllowed is thrown when admin address is zero\\n * @custom:error ZeroAddressNotAllowed is thrown when shortfall contract address is zero\\n * @custom:error ZeroAddressNotAllowed is thrown when protocol share reserve address is zero\\n */\\n function initialize(\\n address underlying_,\\n ComptrollerInterface comptroller_,\\n InterestRateModel interestRateModel_,\\n uint256 initialExchangeRateMantissa_,\\n string memory name_,\\n string memory symbol_,\\n uint8 decimals_,\\n address admin_,\\n address accessControlManager_,\\n RiskManagementInit memory riskManagement,\\n uint256 reserveFactorMantissa_\\n ) external initializer {\\n ensureNonzeroAddress(admin_);\\n\\n // Initialize the market\\n _initialize(\\n underlying_,\\n comptroller_,\\n interestRateModel_,\\n initialExchangeRateMantissa_,\\n name_,\\n symbol_,\\n decimals_,\\n admin_,\\n accessControlManager_,\\n riskManagement,\\n reserveFactorMantissa_\\n );\\n }\\n\\n /**\\n * @notice Transfer `amount` tokens from `msg.sender` to `dst`\\n * @param dst The address of the destination account\\n * @param amount The number of tokens to transfer\\n * @return success True if the transfer succeeded, reverts otherwise\\n * @custom:event Emits Transfer event on success\\n * @custom:error TransferNotAllowed is thrown if trying to transfer to self\\n * @custom:access Not restricted\\n */\\n function transfer(address dst, uint256 amount) external override nonReentrant returns (bool) {\\n _transferTokens(msg.sender, msg.sender, dst, amount);\\n return true;\\n }\\n\\n /**\\n * @notice Transfer `amount` tokens from `src` to `dst`\\n * @param src The address of the source account\\n * @param dst The address of the destination account\\n * @param amount The number of tokens to transfer\\n * @return success True if the transfer succeeded, reverts otherwise\\n * @custom:event Emits Transfer event on success\\n * @custom:error TransferNotAllowed is thrown if trying to transfer to self\\n * @custom:access Not restricted\\n */\\n function transferFrom(address src, address dst, uint256 amount) external override nonReentrant returns (bool) {\\n _transferTokens(msg.sender, src, dst, amount);\\n return true;\\n }\\n\\n /**\\n * @notice Approve `spender` to transfer up to `amount` from `src`\\n * @dev This will overwrite the approval amount for `spender`\\n * and is subject to issues noted [here](https://eips.ethereum.org/EIPS/eip-20#approve)\\n * @param spender The address of the account which may transfer tokens\\n * @param amount The number of tokens that are approved (uint256.max means infinite)\\n * @return success Whether or not the approval succeeded\\n * @custom:event Emits Approval event\\n * @custom:access Not restricted\\n * @custom:error ZeroAddressNotAllowed is thrown when spender address is zero\\n */\\n function approve(address spender, uint256 amount) external override returns (bool) {\\n ensureNonzeroAddress(spender);\\n\\n address src = msg.sender;\\n transferAllowances[src][spender] = amount;\\n emit Approval(src, spender, amount);\\n return true;\\n }\\n\\n /**\\n * @notice Increase approval for `spender`\\n * @param spender The address of the account which may transfer tokens\\n * @param addedValue The number of additional tokens spender can transfer\\n * @return success Whether or not the approval succeeded\\n * @custom:event Emits Approval event\\n * @custom:access Not restricted\\n * @custom:error ZeroAddressNotAllowed is thrown when spender address is zero\\n */\\n function increaseAllowance(address spender, uint256 addedValue) external override returns (bool) {\\n ensureNonzeroAddress(spender);\\n\\n address src = msg.sender;\\n uint256 newAllowance = transferAllowances[src][spender];\\n newAllowance += addedValue;\\n transferAllowances[src][spender] = newAllowance;\\n\\n emit Approval(src, spender, newAllowance);\\n return true;\\n }\\n\\n /**\\n * @notice Decreases approval for `spender`\\n * @param spender The address of the account which may transfer tokens\\n * @param subtractedValue The number of tokens to remove from total approval\\n * @return success Whether or not the approval succeeded\\n * @custom:event Emits Approval event\\n * @custom:access Not restricted\\n * @custom:error ZeroAddressNotAllowed is thrown when spender address is zero\\n */\\n function decreaseAllowance(address spender, uint256 subtractedValue) external override returns (bool) {\\n ensureNonzeroAddress(spender);\\n\\n address src = msg.sender;\\n uint256 currentAllowance = transferAllowances[src][spender];\\n require(currentAllowance >= subtractedValue, \\\"decreased allowance below zero\\\");\\n unchecked {\\n currentAllowance -= subtractedValue;\\n }\\n\\n transferAllowances[src][spender] = currentAllowance;\\n\\n emit Approval(src, spender, currentAllowance);\\n return true;\\n }\\n\\n /**\\n * @notice Get the underlying balance of the `owner`\\n * @dev This also accrues interest in a transaction\\n * @param owner The address of the account to query\\n * @return amount The amount of underlying owned by `owner`\\n */\\n function balanceOfUnderlying(address owner) external override returns (uint256) {\\n Exp memory exchangeRate = Exp({ mantissa: exchangeRateCurrent() });\\n return mul_ScalarTruncate(exchangeRate, accountTokens[owner]);\\n }\\n\\n /**\\n * @notice Returns the current total borrows plus accrued interest\\n * @return totalBorrows The total borrows with interest\\n */\\n function totalBorrowsCurrent() external override nonReentrant returns (uint256) {\\n accrueInterest();\\n return totalBorrows;\\n }\\n\\n /**\\n * @notice Accrue interest to updated borrowIndex and then calculate account's borrow balance using the updated borrowIndex\\n * @param account The address whose balance should be calculated after updating borrowIndex\\n * @return borrowBalance The calculated balance\\n */\\n function borrowBalanceCurrent(address account) external override nonReentrant returns (uint256) {\\n accrueInterest();\\n return _borrowBalanceStored(account);\\n }\\n\\n /**\\n * @notice Sender supplies assets into the market and receives vTokens in exchange\\n * @dev Accrues interest whether or not the operation succeeds, unless reverted\\n * @param mintAmount The amount of the underlying asset to supply\\n * @return error Always NO_ERROR for compatibility with Venus core tooling\\n * @custom:event Emits Mint and Transfer events; may emit AccrueInterest\\n * @custom:access Not restricted\\n */\\n function mint(uint256 mintAmount) external override nonReentrant returns (uint256) {\\n accrueInterest();\\n\\n _mintFresh(msg.sender, msg.sender, mintAmount);\\n return NO_ERROR;\\n }\\n\\n /**\\n * @notice Sender calls on-behalf of minter. minter supplies assets into the market and receives vTokens in exchange\\n * @dev Accrues interest whether or not the operation succeeds, unless reverted\\n * @param minter User whom the supply will be attributed to\\n * @param mintAmount The amount of the underlying asset to supply\\n * @return error Always NO_ERROR for compatibility with Venus core tooling\\n * @custom:event Emits Mint and Transfer events; may emit AccrueInterest\\n * @custom:access Not restricted\\n * @custom:error ZeroAddressNotAllowed is thrown when minter address is zero\\n */\\n function mintBehalf(address minter, uint256 mintAmount) external override nonReentrant returns (uint256) {\\n ensureNonzeroAddress(minter);\\n\\n accrueInterest();\\n\\n _mintFresh(msg.sender, minter, mintAmount);\\n return NO_ERROR;\\n }\\n\\n /**\\n * @notice Sender redeems vTokens in exchange for the underlying asset\\n * @dev Accrues interest whether or not the operation succeeds, unless reverted\\n * @param redeemTokens The number of vTokens to redeem into underlying\\n * @return error Always NO_ERROR for compatibility with Venus core tooling\\n * @custom:event Emits Redeem and Transfer events; may emit AccrueInterest\\n * @custom:error RedeemTransferOutNotPossible is thrown when the protocol has insufficient cash\\n * @custom:access Not restricted\\n */\\n function redeem(uint256 redeemTokens) external override nonReentrant returns (uint256) {\\n accrueInterest();\\n\\n _redeemFresh(msg.sender, msg.sender, redeemTokens, 0);\\n return NO_ERROR;\\n }\\n\\n /**\\n * @notice Sender redeems assets on behalf of some other address. This function is only available\\n * for senders, explicitly marked as delegates of the supplier using `comptroller.updateDelegate`\\n * @dev Accrues interest whether or not the operation succeeds, unless reverted\\n * @param redeemer The user on behalf of whom to redeem\\n * @param redeemTokens The number of vTokens to redeem into underlying\\n * @return error Always NO_ERROR for compatibility with Venus core tooling\\n * @custom:error InsufficientRedeemApproval is thrown when sender is not approved by the redeemer for the given amount\\n * @custom:error RedeemTransferOutNotPossible is thrown when the protocol has insufficient cash\\n * @custom:event Emits Redeem and Transfer events; may emit AccrueInterest\\n * @custom:access Not restricted\\n */\\n function redeemBehalf(address redeemer, uint256 redeemTokens) external override nonReentrant returns (uint256) {\\n _ensureSenderIsDelegateOf(redeemer);\\n\\n accrueInterest();\\n\\n _redeemFresh(redeemer, msg.sender, redeemTokens, 0);\\n return NO_ERROR;\\n }\\n\\n /**\\n * @notice Sender redeems vTokens in exchange for a specified amount of underlying asset\\n * @dev Accrues interest whether or not the operation succeeds, unless reverted\\n * @param redeemAmount The amount of underlying to receive from redeeming vTokens\\n * @return error Always NO_ERROR for compatibility with Venus core tooling\\n */\\n function redeemUnderlying(uint256 redeemAmount) external override nonReentrant returns (uint256) {\\n accrueInterest();\\n\\n _redeemFresh(msg.sender, msg.sender, 0, redeemAmount);\\n return NO_ERROR;\\n }\\n\\n /**\\n * @notice Sender redeems underlying assets on behalf of some other address. This function is only available\\n * for senders, explicitly marked as delegates of the supplier using `comptroller.updateDelegate`\\n * @dev Accrues interest whether or not the operation succeeds, unless reverted\\n * @param redeemer, on behalf of whom to redeem\\n * @param redeemAmount The amount of underlying to receive from redeeming vTokens\\n * @return error Always NO_ERROR for compatibility with Venus core tooling\\n * @custom:error InsufficientRedeemApproval is thrown when sender is not approved by the redeemer for the given amount\\n * @custom:event Emits Redeem and Transfer events; may emit AccrueInterest\\n * @custom:access Not restricted\\n */\\n function redeemUnderlyingBehalf(\\n address redeemer,\\n uint256 redeemAmount\\n ) external override nonReentrant returns (uint256) {\\n _ensureSenderIsDelegateOf(redeemer);\\n\\n accrueInterest();\\n\\n _redeemFresh(redeemer, msg.sender, 0, redeemAmount);\\n return NO_ERROR;\\n }\\n\\n /**\\n * @notice Sender borrows assets from the protocol to their own address\\n * @param borrowAmount The amount of the underlying asset to borrow\\n * @return error Always NO_ERROR for compatibility with Venus core tooling\\n * @custom:event Emits Borrow event; may emit AccrueInterest\\n * @custom:error BorrowCashNotAvailable is thrown when the protocol has insufficient cash\\n * @custom:access Not restricted\\n */\\n function borrow(uint256 borrowAmount) external override nonReentrant returns (uint256) {\\n accrueInterest();\\n\\n _borrowFresh(msg.sender, msg.sender, borrowAmount);\\n return NO_ERROR;\\n }\\n\\n /**\\n * @notice Sender borrows assets on behalf of some other address. This function is only available\\n * for senders, explicitly marked as delegates of the borrower using `comptroller.updateDelegate`\\n * @param borrower The borrower, on behalf of whom to borrow\\n * @param borrowAmount The amount of the underlying asset to borrow\\n * @return error Always NO_ERROR for compatibility with Venus core tooling\\n * @custom:error DelegateNotApproved is thrown if caller is not approved delegate\\n * @custom:error BorrowCashNotAvailable is thrown when the protocol has insufficient cash\\n * @custom:event Emits Borrow event; may emit AccrueInterest\\n * @custom:access Not restricted\\n */\\n function borrowBehalf(address borrower, uint256 borrowAmount) external override returns (uint256) {\\n _ensureSenderIsDelegateOf(borrower);\\n accrueInterest();\\n\\n _borrowFresh(borrower, msg.sender, borrowAmount);\\n return NO_ERROR;\\n }\\n\\n /**\\n * @notice Sender repays their own borrow\\n * @param repayAmount The amount to repay, or type(uint256).max for the full outstanding amount\\n * @return error Always NO_ERROR for compatibility with Venus core tooling\\n * @custom:event Emits RepayBorrow event; may emit AccrueInterest\\n * @custom:access Not restricted\\n */\\n function repayBorrow(uint256 repayAmount) external override nonReentrant returns (uint256) {\\n accrueInterest();\\n\\n _repayBorrowFresh(msg.sender, msg.sender, repayAmount);\\n return NO_ERROR;\\n }\\n\\n /**\\n * @notice Sender repays a borrow belonging to borrower\\n * @param borrower the account with the debt being payed off\\n * @param repayAmount The amount to repay, or type(uint256).max for the full outstanding amount\\n * @return error Always NO_ERROR for compatibility with Venus core tooling\\n * @custom:event Emits RepayBorrow event; may emit AccrueInterest\\n * @custom:access Not restricted\\n */\\n function repayBorrowBehalf(address borrower, uint256 repayAmount) external override nonReentrant returns (uint256) {\\n accrueInterest();\\n\\n _repayBorrowFresh(msg.sender, borrower, repayAmount);\\n return NO_ERROR;\\n }\\n\\n /**\\n * @notice The sender liquidates the borrowers collateral.\\n * The collateral seized is transferred to the liquidator.\\n * @param borrower The borrower of this vToken to be liquidated\\n * @param repayAmount The amount of the underlying borrowed asset to repay\\n * @param vTokenCollateral The market in which to seize collateral from the borrower\\n * @return error Always NO_ERROR for compatibility with Venus core tooling\\n * @custom:event Emits LiquidateBorrow event; may emit AccrueInterest\\n * @custom:error LiquidateAccrueCollateralInterestFailed is thrown when it is not possible to accrue interest on the collateral vToken\\n * @custom:error LiquidateCollateralFreshnessCheck is thrown when interest has not been accrued on the collateral vToken\\n * @custom:error LiquidateLiquidatorIsBorrower is thrown when trying to liquidate self\\n * @custom:error LiquidateCloseAmountIsZero is thrown when repayment amount is zero\\n * @custom:error LiquidateCloseAmountIsUintMax is thrown when repayment amount is UINT_MAX\\n * @custom:access Not restricted\\n */\\n function liquidateBorrow(\\n address borrower,\\n uint256 repayAmount,\\n VTokenInterface vTokenCollateral\\n ) external override returns (uint256) {\\n _liquidateBorrow(msg.sender, borrower, repayAmount, vTokenCollateral, false);\\n return NO_ERROR;\\n }\\n\\n /**\\n * @notice sets protocol share accumulated from liquidations\\n * @dev must be equal or less than liquidation incentive - 1\\n * @param newProtocolSeizeShareMantissa_ new protocol share mantissa\\n * @custom:event Emits NewProtocolSeizeShare event on success\\n * @custom:error Unauthorized error is thrown when the call is not authorized by AccessControlManager\\n * @custom:error ProtocolSeizeShareTooBig is thrown when the new seize share is too high\\n * @custom:access Controlled by AccessControlManager\\n */\\n function setProtocolSeizeShare(uint256 newProtocolSeizeShareMantissa_) external {\\n _checkAccessAllowed(\\\"setProtocolSeizeShare(uint256)\\\");\\n uint256 liquidationIncentive = ComptrollerViewInterface(address(comptroller)).liquidationIncentiveMantissa();\\n if (newProtocolSeizeShareMantissa_ + MANTISSA_ONE > liquidationIncentive) {\\n revert ProtocolSeizeShareTooBig();\\n }\\n\\n uint256 oldProtocolSeizeShareMantissa = protocolSeizeShareMantissa;\\n protocolSeizeShareMantissa = newProtocolSeizeShareMantissa_;\\n emit NewProtocolSeizeShare(oldProtocolSeizeShareMantissa, newProtocolSeizeShareMantissa_);\\n }\\n\\n /**\\n * @notice accrues interest and sets a new reserve factor for the protocol using _setReserveFactorFresh\\n * @dev Admin function to accrue interest and set a new reserve factor\\n * @param newReserveFactorMantissa New reserve factor (from 0 to 1e18)\\n * @custom:event Emits NewReserveFactor event; may emit AccrueInterest\\n * @custom:error Unauthorized error is thrown when the call is not authorized by AccessControlManager\\n * @custom:error SetReserveFactorBoundsCheck is thrown when the new reserve factor is too high\\n * @custom:access Controlled by AccessControlManager\\n */\\n function setReserveFactor(uint256 newReserveFactorMantissa) external override nonReentrant {\\n _checkAccessAllowed(\\\"setReserveFactor(uint256)\\\");\\n\\n accrueInterest();\\n _setReserveFactorFresh(newReserveFactorMantissa);\\n }\\n\\n /**\\n * @notice Accrues interest and reduces reserves by transferring to the protocol reserve contract\\n * @dev Gracefully return if reserves already reduced in accrueInterest\\n * @param reduceAmount Amount of reduction to reserves\\n * @custom:event Emits ReservesReduced event; may emit AccrueInterest\\n * @custom:error ReduceReservesCashNotAvailable is thrown when the vToken does not have sufficient cash\\n * @custom:error ReduceReservesCashValidation is thrown when trying to withdraw more cash than the reserves have\\n * @custom:access Not restricted\\n */\\n function reduceReserves(uint256 reduceAmount) external override nonReentrant {\\n accrueInterest();\\n if (reduceReservesBlockNumber == getBlockNumberOrTimestamp()) return;\\n _reduceReservesFresh(reduceAmount);\\n }\\n\\n /**\\n * @notice The sender adds to reserves.\\n * @param addAmount The amount of underlying token to add as reserves\\n * @custom:event Emits ReservesAdded event; may emit AccrueInterest\\n * @custom:access Not restricted\\n */\\n function addReserves(uint256 addAmount) external override nonReentrant {\\n accrueInterest();\\n _addReservesFresh(addAmount);\\n }\\n\\n /**\\n * @notice accrues interest and updates the interest rate model using _setInterestRateModelFresh\\n * @dev Admin function to accrue interest and update the interest rate model\\n * @param newInterestRateModel the new interest rate model to use\\n * @custom:event Emits NewMarketInterestRateModel event; may emit AccrueInterest\\n * @custom:error Unauthorized error is thrown when the call is not authorized by AccessControlManager\\n * @custom:access Controlled by AccessControlManager\\n */\\n function setInterestRateModel(InterestRateModel newInterestRateModel) external override {\\n _checkAccessAllowed(\\\"setInterestRateModel(address)\\\");\\n\\n accrueInterest();\\n _setInterestRateModelFresh(newInterestRateModel);\\n }\\n\\n /**\\n * @notice Repays a certain amount of debt, treats the rest of the borrow as bad debt, essentially\\n * \\\"forgiving\\\" the borrower. Healing is a situation that should rarely happen. However, some pools\\n * may list risky assets or be configured improperly \\u2013 we want to still handle such cases gracefully.\\n * We assume that Comptroller does the seizing, so this function is only available to Comptroller.\\n * @dev This function does not call any Comptroller hooks (like \\\"healAllowed\\\"), because we assume\\n * the Comptroller does all the necessary checks before calling this function.\\n * @param payer account who repays the debt\\n * @param borrower account to heal\\n * @param repayAmount amount to repay\\n * @custom:event Emits RepayBorrow, BadDebtIncreased events; may emit AccrueInterest\\n * @custom:error HealBorrowUnauthorized is thrown when the request does not come from Comptroller\\n * @custom:access Only Comptroller\\n */\\n function healBorrow(address payer, address borrower, uint256 repayAmount) external override nonReentrant {\\n if (repayAmount != 0) {\\n comptroller.preRepayHook(address(this), borrower);\\n }\\n\\n if (msg.sender != address(comptroller)) {\\n revert HealBorrowUnauthorized();\\n }\\n\\n uint256 accountBorrowsPrev = _borrowBalanceStored(borrower);\\n uint256 totalBorrowsNew = totalBorrows;\\n\\n uint256 actualRepayAmount;\\n if (repayAmount != 0) {\\n // _doTransferIn reverts if anything goes wrong, since we can't be sure if side effects occurred.\\n // We violate checks-effects-interactions here to account for tokens that take transfer fees\\n actualRepayAmount = _doTransferIn(payer, repayAmount);\\n totalBorrowsNew = totalBorrowsNew - actualRepayAmount;\\n emit RepayBorrow(\\n payer,\\n borrower,\\n actualRepayAmount,\\n accountBorrowsPrev - actualRepayAmount,\\n totalBorrowsNew\\n );\\n }\\n\\n // The transaction will fail if trying to repay too much\\n uint256 badDebtDelta = accountBorrowsPrev - actualRepayAmount;\\n if (badDebtDelta != 0) {\\n uint256 badDebtOld = badDebt;\\n uint256 badDebtNew = badDebtOld + badDebtDelta;\\n totalBorrowsNew = totalBorrowsNew - badDebtDelta;\\n badDebt = badDebtNew;\\n\\n // We treat healing as \\\"repayment\\\", where vToken is the payer\\n emit RepayBorrow(address(this), borrower, badDebtDelta, 0, totalBorrowsNew);\\n emit BadDebtIncreased(borrower, badDebtDelta, badDebtOld, badDebtNew);\\n }\\n\\n accountBorrows[borrower].principal = 0;\\n accountBorrows[borrower].interestIndex = borrowIndex;\\n totalBorrows = totalBorrowsNew;\\n\\n emit HealBorrow(payer, borrower, repayAmount);\\n }\\n\\n /**\\n * @notice The extended version of liquidations, callable only by Comptroller. May skip\\n * the close factor check. The collateral seized is transferred to the liquidator.\\n * @param liquidator The address repaying the borrow and seizing collateral\\n * @param borrower The borrower of this vToken to be liquidated\\n * @param repayAmount The amount of the underlying borrowed asset to repay\\n * @param vTokenCollateral The market in which to seize collateral from the borrower\\n * @param skipLiquidityCheck If set to true, allows to liquidate up to 100% of the borrow\\n * regardless of the account liquidity\\n * @custom:event Emits LiquidateBorrow event; may emit AccrueInterest\\n * @custom:error ForceLiquidateBorrowUnauthorized is thrown when the request does not come from Comptroller\\n * @custom:error LiquidateAccrueCollateralInterestFailed is thrown when it is not possible to accrue interest on the collateral vToken\\n * @custom:error LiquidateCollateralFreshnessCheck is thrown when interest has not been accrued on the collateral vToken\\n * @custom:error LiquidateLiquidatorIsBorrower is thrown when trying to liquidate self\\n * @custom:error LiquidateCloseAmountIsZero is thrown when repayment amount is zero\\n * @custom:error LiquidateCloseAmountIsUintMax is thrown when repayment amount is UINT_MAX\\n * @custom:access Only Comptroller\\n */\\n function forceLiquidateBorrow(\\n address liquidator,\\n address borrower,\\n uint256 repayAmount,\\n VTokenInterface vTokenCollateral,\\n bool skipLiquidityCheck\\n ) external override {\\n if (msg.sender != address(comptroller)) {\\n revert ForceLiquidateBorrowUnauthorized();\\n }\\n _liquidateBorrow(liquidator, borrower, repayAmount, vTokenCollateral, skipLiquidityCheck);\\n }\\n\\n /**\\n * @notice Transfers collateral tokens (this market) to the liquidator.\\n * @dev Will fail unless called by another vToken during the process of liquidation.\\n * It's absolutely critical to use msg.sender as the borrowed vToken and not a parameter.\\n * @param liquidator The account receiving seized collateral\\n * @param borrower The account having collateral seized\\n * @param seizeTokens The number of vTokens to seize\\n * @custom:event Emits Transfer, ReservesAdded events\\n * @custom:error LiquidateSeizeLiquidatorIsBorrower is thrown when trying to liquidate self\\n * @custom:access Not restricted\\n */\\n function seize(address liquidator, address borrower, uint256 seizeTokens) external override nonReentrant {\\n _seize(msg.sender, liquidator, borrower, seizeTokens);\\n }\\n\\n /**\\n * @notice Updates bad debt\\n * @dev Called only when bad debt is recovered from auction. Updates internal cash balance.\\n * @param recoveredAmount_ The amount of bad debt recovered\\n * @custom:event Emits BadDebtRecovered event\\n * @custom:access Only Shortfall contract\\n */\\n function badDebtRecovered(uint256 recoveredAmount_) external {\\n require(msg.sender == shortfall, \\\"only shortfall contract can update bad debt\\\");\\n require(recoveredAmount_ <= badDebt, \\\"more than bad debt recovered from auction\\\");\\n\\n uint256 badDebtOld = badDebt;\\n uint256 badDebtNew = badDebtOld - recoveredAmount_;\\n badDebt = badDebtNew;\\n internalCash += recoveredAmount_;\\n\\n emit BadDebtRecovered(badDebtOld, badDebtNew);\\n }\\n\\n /**\\n * @notice Sets protocol share reserve contract address\\n * @param protocolShareReserve_ The address of the protocol share reserve contract\\n * @custom:error ZeroAddressNotAllowed is thrown when protocol share reserve address is zero\\n * @custom:access Only Governance\\n */\\n function setProtocolShareReserve(address payable protocolShareReserve_) external onlyOwner {\\n _setProtocolShareReserve(protocolShareReserve_);\\n }\\n\\n /**\\n * @notice Sets shortfall contract address\\n * @param shortfall_ The address of the shortfall contract\\n * @custom:error ZeroAddressNotAllowed is thrown when shortfall contract address is zero\\n * @custom:access Only Governance\\n */\\n function setShortfallContract(address shortfall_) external onlyOwner {\\n _setShortfallContract(shortfall_);\\n }\\n\\n /**\\n * @notice A public function to sweep accidental ERC-20 transfers to this contract. Tokens are sent to admin (timelock)\\n * @param token The address of the ERC-20 token to sweep\\n * @custom:access Only Governance\\n */\\n function sweepToken(IERC20Upgradeable token) external override {\\n require(msg.sender == owner(), \\\"VToken::sweepToken: only admin can sweep tokens\\\");\\n require(address(token) != underlying, \\\"VToken::sweepToken: can not sweep underlying token\\\");\\n uint256 balance = token.balanceOf(address(this));\\n token.safeTransfer(owner(), balance);\\n\\n emit SweepToken(address(token));\\n }\\n\\n /**\\n * @notice Sync internalCash with the actual underlying token balance\\n * @dev Used for one-time migration after upgrade to initialize internalCash\\n * @custom:event Emits CashSynced\\n * @custom:access Controlled by AccessControlManager\\n */\\n function syncCash() external override nonReentrant {\\n _checkAccessAllowed(\\\"syncCash()\\\");\\n uint256 oldInternalCash = internalCash;\\n uint256 actualCash = IERC20Upgradeable(underlying).balanceOf(address(this));\\n internalCash = actualCash;\\n emit CashSynced(oldInternalCash, actualCash);\\n }\\n\\n /**\\n * @notice A public function to set new threshold of slot(block or second) difference after which funds will be sent to the protocol share reserve\\n * @param _newReduceReservesBlockOrTimestampDelta slot(block or second) difference value\\n * @custom:access Only Governance\\n */\\n function setReduceReservesBlockDelta(uint256 _newReduceReservesBlockOrTimestampDelta) external {\\n _checkAccessAllowed(\\\"setReduceReservesBlockDelta(uint256)\\\");\\n require(_newReduceReservesBlockOrTimestampDelta > 0, \\\"Invalid Input\\\");\\n emit NewReduceReservesBlockDelta(reduceReservesBlockDelta, _newReduceReservesBlockOrTimestampDelta);\\n reduceReservesBlockDelta = _newReduceReservesBlockOrTimestampDelta;\\n }\\n\\n /**\\n * @notice Get the current allowance from `owner` for `spender`\\n * @param owner The address of the account which owns the tokens to be spent\\n * @param spender The address of the account which may transfer tokens\\n * @return amount The number of tokens allowed to be spent (type(uint256).max means infinite)\\n */\\n function allowance(address owner, address spender) external view override returns (uint256) {\\n return transferAllowances[owner][spender];\\n }\\n\\n /**\\n * @notice Get the token balance of the `owner`\\n * @param owner The address of the account to query\\n * @return amount The number of tokens owned by `owner`\\n */\\n function balanceOf(address owner) external view override returns (uint256) {\\n return accountTokens[owner];\\n }\\n\\n /**\\n * @notice Get a snapshot of the account's balances, and the cached exchange rate\\n * @dev This is used by comptroller to more efficiently perform liquidity checks.\\n * @param account Address of the account to snapshot\\n * @return error Always NO_ERROR for compatibility with Venus core tooling\\n * @return vTokenBalance User's balance of vTokens\\n * @return borrowBalance Amount owed in terms of underlying\\n * @return exchangeRate Stored exchange rate\\n */\\n function getAccountSnapshot(\\n address account\\n )\\n external\\n view\\n override\\n returns (uint256 error, uint256 vTokenBalance, uint256 borrowBalance, uint256 exchangeRate)\\n {\\n return (NO_ERROR, accountTokens[account], _borrowBalanceStored(account), _exchangeRateStored());\\n }\\n\\n /**\\n * @notice Get the internally tracked cash balance of this vToken in the underlying asset\\n * @return cash The quantity of underlying asset tracked internally by this contract\\n */\\n function getCash() external view override returns (uint256) {\\n return _getCashPrior();\\n }\\n\\n /**\\n * @notice Returns the current per slot(block or second) borrow interest rate for this vToken\\n * @return rate The borrow interest rate per slot(block or second), scaled by 1e18\\n */\\n function borrowRatePerBlock() external view override returns (uint256) {\\n return interestRateModel.getBorrowRate(_getCashPrior(), totalBorrows, totalReserves, badDebt);\\n }\\n\\n /**\\n * @notice Returns the current per-slot(block or second) supply interest rate for this v\\n * @return rate The supply interest rate per slot(block or second), scaled by 1e18\\n */\\n function supplyRatePerBlock() external view override returns (uint256) {\\n return\\n interestRateModel.getSupplyRate(\\n _getCashPrior(),\\n totalBorrows,\\n totalReserves,\\n reserveFactorMantissa,\\n badDebt\\n );\\n }\\n\\n /**\\n * @notice Return the borrow balance of account based on stored data\\n * @param account The address whose balance should be calculated\\n * @return borrowBalance The calculated balance\\n */\\n function borrowBalanceStored(address account) external view override returns (uint256) {\\n return _borrowBalanceStored(account);\\n }\\n\\n /**\\n * @notice Calculates the exchange rate from the underlying to the VToken\\n * @dev This function does not accrue interest before calculating the exchange rate\\n * @return exchangeRate Calculated exchange rate scaled by 1e18\\n */\\n function exchangeRateStored() external view override returns (uint256) {\\n return _exchangeRateStored();\\n }\\n\\n /**\\n * @notice Accrue interest then return the up-to-date exchange rate\\n * @return exchangeRate Calculated exchange rate scaled by 1e18\\n */\\n function exchangeRateCurrent() public override nonReentrant returns (uint256) {\\n accrueInterest();\\n return _exchangeRateStored();\\n }\\n\\n /**\\n * @notice Applies accrued interest to total borrows and reserves\\n * @dev This calculates interest accrued from the last checkpointed slot(block or second)\\n * up to the current slot(block or second) and writes new checkpoint to storage and\\n * reduce spread reserves to protocol share reserve\\n * if currentSlot - reduceReservesBlockNumber >= slotDelta\\n * @return Always NO_ERROR\\n * @custom:event Emits AccrueInterest event on success\\n * @custom:access Not restricted\\n */\\n function accrueInterest() public virtual override returns (uint256) {\\n /* Remember the initial block number or timestamp */\\n uint256 currentSlotNumber = getBlockNumberOrTimestamp();\\n uint256 accrualSlotNumberPrior = accrualBlockNumber;\\n\\n /* Short-circuit accumulating 0 interest */\\n if (accrualSlotNumberPrior == currentSlotNumber) {\\n return NO_ERROR;\\n }\\n\\n /* Read the previous values out of storage */\\n uint256 cashPrior = _getCashPrior();\\n uint256 borrowsPrior = totalBorrows;\\n uint256 reservesPrior = totalReserves;\\n uint256 borrowIndexPrior = borrowIndex;\\n\\n /* Calculate the current borrow interest rate */\\n uint256 borrowRateMantissa = interestRateModel.getBorrowRate(cashPrior, borrowsPrior, reservesPrior, badDebt);\\n require(borrowRateMantissa <= MAX_BORROW_RATE_MANTISSA, \\\"borrow rate is absurdly high\\\");\\n\\n /* Calculate the number of slots elapsed since the last accrual */\\n uint256 slotDelta = currentSlotNumber - accrualSlotNumberPrior;\\n\\n /*\\n * Calculate the interest accumulated into borrows and reserves and the new index:\\n * simpleInterestFactor = borrowRate * slotDelta\\n * interestAccumulated = simpleInterestFactor * totalBorrows\\n * totalBorrowsNew = interestAccumulated + totalBorrows\\n * totalReservesNew = interestAccumulated * reserveFactor + totalReserves\\n * borrowIndexNew = simpleInterestFactor * borrowIndex + borrowIndex\\n */\\n\\n Exp memory simpleInterestFactor = mul_(Exp({ mantissa: borrowRateMantissa }), slotDelta);\\n uint256 interestAccumulated = mul_ScalarTruncate(simpleInterestFactor, borrowsPrior);\\n uint256 totalBorrowsNew = interestAccumulated + borrowsPrior;\\n uint256 totalReservesNew = mul_ScalarTruncateAddUInt(\\n Exp({ mantissa: reserveFactorMantissa }),\\n interestAccumulated,\\n reservesPrior\\n );\\n uint256 borrowIndexNew = mul_ScalarTruncateAddUInt(simpleInterestFactor, borrowIndexPrior, borrowIndexPrior);\\n\\n /////////////////////////\\n // EFFECTS & INTERACTIONS\\n // (No safe failures beyond this point)\\n\\n /* We write the previously calculated values into storage */\\n accrualBlockNumber = currentSlotNumber;\\n borrowIndex = borrowIndexNew;\\n totalBorrows = totalBorrowsNew;\\n totalReserves = totalReservesNew;\\n\\n if (currentSlotNumber - reduceReservesBlockNumber >= reduceReservesBlockDelta) {\\n reduceReservesBlockNumber = currentSlotNumber;\\n if (cashPrior < totalReservesNew) {\\n _reduceReservesFresh(cashPrior);\\n } else {\\n _reduceReservesFresh(totalReservesNew);\\n }\\n }\\n\\n /* We emit an AccrueInterest event */\\n emit AccrueInterest(cashPrior, interestAccumulated, borrowIndexNew, totalBorrowsNew);\\n\\n return NO_ERROR;\\n }\\n\\n /**\\n * @notice User supplies assets into the market and receives vTokens in exchange\\n * @dev Assumes interest has already been accrued up to the current block or timestamp\\n * @param payer The address of the account which is sending the assets for supply\\n * @param minter The address of the account which is supplying the assets\\n * @param mintAmount The amount of the underlying asset to supply\\n */\\n function _mintFresh(address payer, address minter, uint256 mintAmount) internal {\\n /* Fail if mint not allowed */\\n comptroller.preMintHook(address(this), minter, mintAmount);\\n\\n /* Verify market's slot(block or second) number equals current slot(block or second) number */\\n if (accrualBlockNumber != getBlockNumberOrTimestamp()) {\\n revert MintFreshnessCheck();\\n }\\n\\n Exp memory exchangeRate = Exp({ mantissa: _exchangeRateStored() });\\n\\n /////////////////////////\\n // EFFECTS & INTERACTIONS\\n // (No safe failures beyond this point)\\n\\n /*\\n * We call `_doTransferIn` for the minter and the mintAmount.\\n * `_doTransferIn` reverts if anything goes wrong, since we can't be sure if\\n * side-effects occurred. The function returns the amount actually transferred,\\n * in case of a fee. On success, the vToken holds an additional `actualMintAmount`\\n * of cash.\\n */\\n uint256 actualMintAmount = _doTransferIn(payer, mintAmount);\\n\\n /*\\n * We get the current exchange rate and calculate the number of vTokens to be minted:\\n * mintTokens = actualMintAmount / exchangeRate\\n */\\n\\n uint256 mintTokens = div_(actualMintAmount, exchangeRate);\\n\\n /*\\n * We calculate the new total supply of vTokens and minter token balance, checking for overflow:\\n * totalSupplyNew = totalSupply + mintTokens\\n * accountTokensNew = accountTokens[minter] + mintTokens\\n * And write them into storage\\n */\\n totalSupply = totalSupply + mintTokens;\\n uint256 balanceAfter = accountTokens[minter] + mintTokens;\\n accountTokens[minter] = balanceAfter;\\n\\n /* We emit a Mint event, and a Transfer event */\\n emit Mint(minter, actualMintAmount, mintTokens, balanceAfter);\\n emit Transfer(address(0), minter, mintTokens);\\n\\n /* We call the defense and prime accrue interest hook */\\n comptroller.mintVerify(address(this), minter, actualMintAmount, mintTokens);\\n }\\n\\n /**\\n * @notice Redeemer redeems vTokens in exchange for the underlying assets, transferred to the receiver. Redeemer and receiver can be the same\\n * address, or different addresses if the receiver was previously approved by the redeemer as a valid delegate (see Comptroller.updateDelegate)\\n * @dev Assumes interest has already been accrued up to the current slot(block or second)\\n * @param redeemer The address of the account which is redeeming the tokens\\n * @param receiver The receiver of the underlying tokens\\n * @param redeemTokensIn The number of vTokens to redeem into underlying (only one of redeemTokensIn or redeemAmountIn may be non-zero)\\n * @param redeemAmountIn The number of underlying tokens to receive from redeeming vTokens (only one of redeemTokensIn or redeemAmountIn may be non-zero)\\n */\\n function _redeemFresh(address redeemer, address receiver, uint256 redeemTokensIn, uint256 redeemAmountIn) internal {\\n require(redeemTokensIn == 0 || redeemAmountIn == 0, \\\"one of redeemTokensIn or redeemAmountIn must be zero\\\");\\n\\n /* Verify market's slot(block or second) number equals current slot(block or second) number */\\n if (accrualBlockNumber != getBlockNumberOrTimestamp()) {\\n revert RedeemFreshnessCheck();\\n }\\n\\n /* exchangeRate = invoke Exchange Rate Stored() */\\n Exp memory exchangeRate = Exp({ mantissa: _exchangeRateStored() });\\n\\n uint256 redeemTokens;\\n uint256 redeemAmount;\\n\\n /* If redeemTokensIn > 0: */\\n if (redeemTokensIn > 0) {\\n /*\\n * We calculate the exchange rate and the amount of underlying to be redeemed:\\n * redeemTokens = redeemTokensIn\\n */\\n redeemTokens = redeemTokensIn;\\n } else {\\n /*\\n * We get the current exchange rate and calculate the amount to be redeemed:\\n * redeemTokens = redeemAmountIn / exchangeRate\\n */\\n redeemTokens = div_(redeemAmountIn, exchangeRate);\\n\\n uint256 _redeemAmount = mul_(redeemTokens, exchangeRate);\\n if (_redeemAmount != 0 && _redeemAmount != redeemAmountIn) redeemTokens++; // round up\\n }\\n\\n // redeemAmount = exchangeRate * redeemTokens\\n redeemAmount = mul_ScalarTruncate(exchangeRate, redeemTokens);\\n\\n // Revert if amount is zero\\n if (redeemAmount == 0) {\\n revert(\\\"redeemAmount is zero\\\");\\n }\\n\\n /* Fail if redeem not allowed */\\n comptroller.preRedeemHook(address(this), redeemer, redeemTokens);\\n\\n /* Fail gracefully if protocol has insufficient cash */\\n if (_getCashPrior() - totalReserves < redeemAmount) {\\n revert RedeemTransferOutNotPossible();\\n }\\n\\n /////////////////////////\\n // EFFECTS & INTERACTIONS\\n // (No safe failures beyond this point)\\n\\n /*\\n * We write the previously calculated values into storage.\\n * Note: Avoid token reentrancy attacks by writing reduced supply before external transfer.\\n */\\n totalSupply = totalSupply - redeemTokens;\\n uint256 balanceAfter = accountTokens[redeemer] - redeemTokens;\\n accountTokens[redeemer] = balanceAfter;\\n\\n /*\\n * We invoke _doTransferOut for the receiver and the redeemAmount.\\n * On success, the vToken has redeemAmount less of cash.\\n * _doTransferOut reverts if anything goes wrong, since we can't be sure if side effects occurred.\\n */\\n _doTransferOut(receiver, redeemAmount);\\n\\n /* We emit a Transfer event, and a Redeem event */\\n emit Transfer(redeemer, address(this), redeemTokens);\\n emit Redeem(redeemer, redeemAmount, redeemTokens, balanceAfter);\\n\\n /* We call the defense and prime accrue interest hook */\\n comptroller.redeemVerify(address(this), redeemer, redeemAmount, redeemTokens);\\n }\\n\\n /**\\n * @notice Users or their delegates borrow assets from the protocol\\n * @param borrower User who borrows the assets\\n * @param receiver The receiver of the tokens, if called by a delegate\\n * @param borrowAmount The amount of the underlying asset to borrow\\n */\\n function _borrowFresh(address borrower, address receiver, uint256 borrowAmount) internal {\\n /* Fail if borrow not allowed */\\n comptroller.preBorrowHook(address(this), borrower, borrowAmount);\\n\\n /* Verify market's slot(block or second) number equals current slot(block or second) number */\\n if (accrualBlockNumber != getBlockNumberOrTimestamp()) {\\n revert BorrowFreshnessCheck();\\n }\\n\\n /* Fail gracefully if protocol has insufficient underlying cash */\\n if (_getCashPrior() - totalReserves < borrowAmount) {\\n revert BorrowCashNotAvailable();\\n }\\n\\n /*\\n * We calculate the new borrower and total borrow balances, failing on overflow:\\n * accountBorrowNew = accountBorrow + borrowAmount\\n * totalBorrowsNew = totalBorrows + borrowAmount\\n */\\n uint256 accountBorrowsPrev = _borrowBalanceStored(borrower);\\n uint256 accountBorrowsNew = accountBorrowsPrev + borrowAmount;\\n uint256 totalBorrowsNew = totalBorrows + borrowAmount;\\n\\n /////////////////////////\\n // EFFECTS & INTERACTIONS\\n // (No safe failures beyond this point)\\n\\n /*\\n * We write the previously calculated values into storage.\\n * Note: Avoid token reentrancy attacks by writing increased borrow before external transfer.\\n `*/\\n accountBorrows[borrower].principal = accountBorrowsNew;\\n accountBorrows[borrower].interestIndex = borrowIndex;\\n totalBorrows = totalBorrowsNew;\\n\\n /*\\n * We invoke _doTransferOut for the receiver and the borrowAmount.\\n * On success, the vToken borrowAmount less of cash.\\n * _doTransferOut reverts if anything goes wrong, since we can't be sure if side effects occurred.\\n */\\n _doTransferOut(receiver, borrowAmount);\\n\\n /* We emit a Borrow event */\\n emit Borrow(borrower, borrowAmount, accountBorrowsNew, totalBorrowsNew);\\n\\n /* We call the defense and prime accrue interest hook */\\n comptroller.borrowVerify(address(this), borrower, borrowAmount);\\n }\\n\\n /**\\n * @notice Borrows are repaid by another user (possibly the borrower).\\n * @param payer the account paying off the borrow\\n * @param borrower the account with the debt being payed off\\n * @param repayAmount the amount of underlying tokens being returned, or type(uint256).max for the full outstanding amount\\n * @return (uint) the actual repayment amount.\\n */\\n function _repayBorrowFresh(address payer, address borrower, uint256 repayAmount) internal returns (uint256) {\\n /* Fail if repayBorrow not allowed */\\n comptroller.preRepayHook(address(this), borrower);\\n\\n /* Verify market's slot(block or second) number equals current slot(block or second) number */\\n if (accrualBlockNumber != getBlockNumberOrTimestamp()) {\\n revert RepayBorrowFreshnessCheck();\\n }\\n\\n /* We fetch the amount the borrower owes, with accumulated interest */\\n uint256 accountBorrowsPrev = _borrowBalanceStored(borrower);\\n\\n uint256 repayAmountFinal = repayAmount >= accountBorrowsPrev ? accountBorrowsPrev : repayAmount;\\n\\n /////////////////////////\\n // EFFECTS & INTERACTIONS\\n // (No safe failures beyond this point)\\n\\n /*\\n * We call _doTransferIn for the payer and the repayAmount\\n * On success, the vToken holds an additional repayAmount of cash.\\n * _doTransferIn reverts if anything goes wrong, since we can't be sure if side effects occurred.\\n * it returns the amount actually transferred, in case of a fee.\\n */\\n uint256 actualRepayAmount = _doTransferIn(payer, repayAmountFinal);\\n\\n /*\\n * We calculate the new borrower and total borrow balances, failing on underflow:\\n * accountBorrowsNew = accountBorrows - actualRepayAmount\\n * totalBorrowsNew = totalBorrows - actualRepayAmount\\n */\\n uint256 accountBorrowsNew = accountBorrowsPrev - actualRepayAmount;\\n uint256 totalBorrowsNew = totalBorrows - actualRepayAmount;\\n\\n /* We write the previously calculated values into storage */\\n accountBorrows[borrower].principal = accountBorrowsNew;\\n accountBorrows[borrower].interestIndex = borrowIndex;\\n totalBorrows = totalBorrowsNew;\\n\\n /* We emit a RepayBorrow event */\\n emit RepayBorrow(payer, borrower, actualRepayAmount, accountBorrowsNew, totalBorrowsNew);\\n\\n /* We call the defense and prime accrue interest hook */\\n comptroller.repayBorrowVerify(address(this), payer, borrower, actualRepayAmount, borrowIndex);\\n\\n return actualRepayAmount;\\n }\\n\\n /**\\n * @notice The sender liquidates the borrowers collateral.\\n * The collateral seized is transferred to the liquidator.\\n * @param liquidator The address repaying the borrow and seizing collateral\\n * @param borrower The borrower of this vToken to be liquidated\\n * @param repayAmount The amount of the underlying borrowed asset to repay\\n * @param vTokenCollateral The market in which to seize collateral from the borrower\\n * @param skipLiquidityCheck If set to true, allows to liquidate up to 100% of the borrow\\n * regardless of the account liquidity\\n */\\n function _liquidateBorrow(\\n address liquidator,\\n address borrower,\\n uint256 repayAmount,\\n VTokenInterface vTokenCollateral,\\n bool skipLiquidityCheck\\n ) internal nonReentrant {\\n accrueInterest();\\n\\n uint256 error = vTokenCollateral.accrueInterest();\\n if (error != NO_ERROR) {\\n // accrueInterest emits logs on errors, but we still want to log the fact that an attempted liquidation failed\\n revert LiquidateAccrueCollateralInterestFailed(error);\\n }\\n\\n _liquidateBorrowFresh(liquidator, borrower, repayAmount, vTokenCollateral, skipLiquidityCheck);\\n }\\n\\n /**\\n * @notice The liquidator liquidates the borrowers collateral.\\n * The collateral seized is transferred to the liquidator.\\n * @param liquidator The address repaying the borrow and seizing collateral\\n * @param borrower The borrower of this vToken to be liquidated\\n * @param repayAmount The amount of the underlying borrowed asset to repay\\n * @param vTokenCollateral The market in which to seize collateral from the borrower\\n * @param skipLiquidityCheck If set to true, allows to liquidate up to 100% of the borrow\\n * regardless of the account liquidity\\n */\\n function _liquidateBorrowFresh(\\n address liquidator,\\n address borrower,\\n uint256 repayAmount,\\n VTokenInterface vTokenCollateral,\\n bool skipLiquidityCheck\\n ) internal {\\n /* Fail if liquidate not allowed */\\n comptroller.preLiquidateHook(\\n address(this),\\n address(vTokenCollateral),\\n borrower,\\n repayAmount,\\n skipLiquidityCheck\\n );\\n\\n /* Verify market's slot(block or second) number equals current slot(block or second) number */\\n if (accrualBlockNumber != getBlockNumberOrTimestamp()) {\\n revert LiquidateFreshnessCheck();\\n }\\n\\n /* Verify vTokenCollateral market's slot(block or second) number equals current slot(block or second) number */\\n if (vTokenCollateral.accrualBlockNumber() != getBlockNumberOrTimestamp()) {\\n revert LiquidateCollateralFreshnessCheck();\\n }\\n\\n /* Fail if borrower = liquidator */\\n if (borrower == liquidator) {\\n revert LiquidateLiquidatorIsBorrower();\\n }\\n\\n /* Fail if repayAmount = 0 */\\n if (repayAmount == 0) {\\n revert LiquidateCloseAmountIsZero();\\n }\\n\\n /* Fail if repayAmount = type(uint256).max */\\n if (repayAmount == type(uint256).max) {\\n revert LiquidateCloseAmountIsUintMax();\\n }\\n\\n /* Fail if repayBorrow fails */\\n uint256 actualRepayAmount = _repayBorrowFresh(liquidator, borrower, repayAmount);\\n\\n /////////////////////////\\n // EFFECTS & INTERACTIONS\\n // (No safe failures beyond this point)\\n\\n /* We calculate the number of collateral tokens that will be seized */\\n (uint256 amountSeizeError, uint256 seizeTokens) = comptroller.liquidateCalculateSeizeTokens(\\n address(this),\\n address(vTokenCollateral),\\n actualRepayAmount\\n );\\n require(amountSeizeError == NO_ERROR, \\\"LIQUIDATE_COMPTROLLER_CALCULATE_AMOUNT_SEIZE_FAILED\\\");\\n\\n /* Revert if borrower collateral token balance < seizeTokens */\\n require(vTokenCollateral.balanceOf(borrower) >= seizeTokens, \\\"LIQUIDATE_SEIZE_TOO_MUCH\\\");\\n\\n // If this is also the collateral, call _seize internally to avoid re-entrancy, otherwise make an external call\\n if (address(vTokenCollateral) == address(this)) {\\n _seize(address(this), liquidator, borrower, seizeTokens);\\n } else {\\n vTokenCollateral.seize(liquidator, borrower, seizeTokens);\\n }\\n\\n /* We emit a LiquidateBorrow event */\\n emit LiquidateBorrow(liquidator, borrower, actualRepayAmount, address(vTokenCollateral), seizeTokens);\\n\\n /* We call the defense and prime accrue interest hook */\\n comptroller.liquidateBorrowVerify(\\n address(this),\\n address(vTokenCollateral),\\n liquidator,\\n borrower,\\n actualRepayAmount,\\n seizeTokens\\n );\\n }\\n\\n /**\\n * @notice Transfers collateral tokens (this market) to the liquidator.\\n * @dev Called only during an in-kind liquidation, or by liquidateBorrow during the liquidation of another VToken.\\n * It's absolutely critical to use msg.sender as the seizer vToken and not a parameter.\\n * @param seizerContract The contract seizing the collateral (either borrowed vToken or Comptroller)\\n * @param liquidator The account receiving seized collateral\\n * @param borrower The account having collateral seized\\n * @param seizeTokens The number of vTokens to seize\\n */\\n function _seize(address seizerContract, address liquidator, address borrower, uint256 seizeTokens) internal {\\n /* Fail if seize not allowed */\\n comptroller.preSeizeHook(address(this), seizerContract, liquidator, borrower);\\n\\n /* Fail if borrower = liquidator */\\n if (borrower == liquidator) {\\n revert LiquidateSeizeLiquidatorIsBorrower();\\n }\\n\\n /*\\n * We calculate the new borrower and liquidator token balances, failing on underflow/overflow:\\n * borrowerTokensNew = accountTokens[borrower] - seizeTokens\\n * liquidatorTokensNew = accountTokens[liquidator] + seizeTokens\\n */\\n uint256 liquidationIncentiveMantissa = ComptrollerViewInterface(address(comptroller))\\n .liquidationIncentiveMantissa();\\n uint256 numerator = mul_(seizeTokens, Exp({ mantissa: protocolSeizeShareMantissa }));\\n uint256 protocolSeizeTokens = div_(numerator, Exp({ mantissa: liquidationIncentiveMantissa }));\\n uint256 liquidatorSeizeTokens = seizeTokens - protocolSeizeTokens;\\n Exp memory exchangeRate = Exp({ mantissa: _exchangeRateStored() });\\n uint256 protocolSeizeAmount = mul_ScalarTruncate(exchangeRate, protocolSeizeTokens);\\n\\n /////////////////////////\\n // EFFECTS & INTERACTIONS\\n // (No safe failures beyond this point)\\n\\n /* We write the calculated values into storage */\\n totalSupply = totalSupply - protocolSeizeTokens;\\n accountTokens[borrower] = accountTokens[borrower] - seizeTokens;\\n accountTokens[liquidator] = accountTokens[liquidator] + liquidatorSeizeTokens;\\n\\n // _doTransferOut reverts if anything goes wrong, since we can't be sure if side effects occurred.\\n // Transferring an underlying asset to the protocolShareReserve contract to channel the funds for different use.\\n _doTransferOut(protocolShareReserve, protocolSeizeAmount);\\n\\n // Update the pool asset's state in the protocol share reserve for the above transfer.\\n IProtocolShareReserve(protocolShareReserve).updateAssetsState(\\n address(comptroller),\\n underlying,\\n IProtocolShareReserve.IncomeType.LIQUIDATION\\n );\\n\\n /* Emit a Transfer event */\\n emit Transfer(borrower, liquidator, liquidatorSeizeTokens);\\n emit ProtocolSeize(borrower, protocolShareReserve, protocolSeizeAmount);\\n\\n /* We call the defense and prime accrue interest hook */\\n comptroller.seizeVerify(address(this), seizerContract, liquidator, borrower, seizeTokens);\\n }\\n\\n function _setComptroller(ComptrollerInterface newComptroller) internal {\\n ComptrollerInterface oldComptroller = comptroller;\\n // Ensure invoke comptroller.isComptroller() returns true\\n require(newComptroller.isComptroller(), \\\"marker method returned false\\\");\\n\\n // Set market's comptroller to newComptroller\\n comptroller = newComptroller;\\n\\n // Emit NewComptroller(oldComptroller, newComptroller)\\n emit NewComptroller(oldComptroller, newComptroller);\\n }\\n\\n /**\\n * @notice Sets a new reserve factor for the protocol (*requires fresh interest accrual)\\n * @dev Admin function to set a new reserve factor\\n * @param newReserveFactorMantissa New reserve factor (from 0 to 1e18)\\n */\\n function _setReserveFactorFresh(uint256 newReserveFactorMantissa) internal {\\n // Verify market's slot(block or second) number equals current slot(block or second) number\\n if (accrualBlockNumber != getBlockNumberOrTimestamp()) {\\n revert SetReserveFactorFreshCheck();\\n }\\n\\n // Check newReserveFactor \\u2264 maxReserveFactor\\n if (newReserveFactorMantissa > MAX_RESERVE_FACTOR_MANTISSA) {\\n revert SetReserveFactorBoundsCheck();\\n }\\n\\n uint256 oldReserveFactorMantissa = reserveFactorMantissa;\\n reserveFactorMantissa = newReserveFactorMantissa;\\n\\n emit NewReserveFactor(oldReserveFactorMantissa, newReserveFactorMantissa);\\n }\\n\\n /**\\n * @notice Add reserves by transferring from caller\\n * @dev Requires fresh interest accrual\\n * @param addAmount Amount of addition to reserves\\n * @return actualAddAmount The actual amount added, excluding the potential token fees\\n */\\n function _addReservesFresh(uint256 addAmount) internal returns (uint256) {\\n // totalReserves + actualAddAmount\\n uint256 totalReservesNew;\\n uint256 actualAddAmount;\\n\\n // We fail gracefully unless market's slot(block or second) number equals current slot(block or second) number\\n if (accrualBlockNumber != getBlockNumberOrTimestamp()) {\\n revert AddReservesFactorFreshCheck(actualAddAmount);\\n }\\n\\n actualAddAmount = _doTransferIn(msg.sender, addAmount);\\n totalReservesNew = totalReserves + actualAddAmount;\\n totalReserves = totalReservesNew;\\n emit ReservesAdded(msg.sender, actualAddAmount, totalReservesNew);\\n\\n return actualAddAmount;\\n }\\n\\n /**\\n * @notice Reduces reserves by transferring to the protocol reserve contract\\n * @dev Requires fresh interest accrual\\n * @param reduceAmount Amount of reduction to reserves\\n */\\n function _reduceReservesFresh(uint256 reduceAmount) internal {\\n if (reduceAmount == 0) {\\n return;\\n }\\n // totalReserves - reduceAmount\\n uint256 totalReservesNew;\\n\\n // We fail gracefully unless market's slot(block or second) number equals current slot(block or second) number\\n if (accrualBlockNumber != getBlockNumberOrTimestamp()) {\\n revert ReduceReservesFreshCheck();\\n }\\n\\n // Fail gracefully if protocol has insufficient underlying cash\\n if (_getCashPrior() < reduceAmount) {\\n revert ReduceReservesCashNotAvailable();\\n }\\n\\n // Check reduceAmount \\u2264 reserves[n] (totalReserves)\\n if (reduceAmount > totalReserves) {\\n revert ReduceReservesCashValidation();\\n }\\n\\n /////////////////////////\\n // EFFECTS & INTERACTIONS\\n // (No safe failures beyond this point)\\n\\n totalReservesNew = totalReserves - reduceAmount;\\n\\n // Store reserves[n+1] = reserves[n] - reduceAmount\\n totalReserves = totalReservesNew;\\n\\n // _doTransferOut reverts if anything goes wrong, since we can't be sure if side effects occurred.\\n // Transferring an underlying asset to the protocolShareReserve contract to channel the funds for different use.\\n _doTransferOut(protocolShareReserve, reduceAmount);\\n\\n // Update the pool asset's state in the protocol share reserve for the above transfer.\\n IProtocolShareReserve(protocolShareReserve).updateAssetsState(\\n address(comptroller),\\n underlying,\\n IProtocolShareReserve.IncomeType.SPREAD\\n );\\n\\n emit SpreadReservesReduced(protocolShareReserve, reduceAmount, totalReservesNew);\\n }\\n\\n /**\\n * @notice updates the interest rate model (*requires fresh interest accrual)\\n * @dev Admin function to update the interest rate model\\n * @param newInterestRateModel the new interest rate model to use\\n */\\n function _setInterestRateModelFresh(InterestRateModel newInterestRateModel) internal {\\n // Used to store old model for use in the event that is emitted on success\\n InterestRateModel oldInterestRateModel;\\n\\n // We fail gracefully unless market's slot(block or second) number equals current slot(block or second) number\\n if (accrualBlockNumber != getBlockNumberOrTimestamp()) {\\n revert SetInterestRateModelFreshCheck();\\n }\\n\\n // Track the market's current interest rate model\\n oldInterestRateModel = interestRateModel;\\n\\n // Ensure invoke newInterestRateModel.isInterestRateModel() returns true\\n require(newInterestRateModel.isInterestRateModel(), \\\"marker method returned false\\\");\\n\\n // Set the interest rate model to newInterestRateModel\\n interestRateModel = newInterestRateModel;\\n\\n // Emit NewMarketInterestRateModel(oldInterestRateModel, newInterestRateModel)\\n emit NewMarketInterestRateModel(oldInterestRateModel, newInterestRateModel);\\n }\\n\\n /**\\n * Safe Token **\\n */\\n\\n /**\\n * @dev Similar to ERC-20 transfer, but handles tokens that have transfer fees.\\n * This function returns the actual amount received,\\n * which may be less than `amount` if there is a fee attached to the transfer.\\n * @param from Sender of the underlying tokens\\n * @param amount Amount of underlying to transfer\\n * @return Actual amount received\\n */\\n function _doTransferIn(address from, uint256 amount) internal virtual returns (uint256) {\\n IERC20Upgradeable token = IERC20Upgradeable(underlying);\\n uint256 balanceBefore = token.balanceOf(address(this));\\n token.safeTransferFrom(from, address(this), amount);\\n uint256 balanceAfter = token.balanceOf(address(this));\\n uint256 actualAmount = balanceAfter - balanceBefore;\\n internalCash += actualAmount;\\n // Return the amount that was *actually* transferred\\n return actualAmount;\\n }\\n\\n /**\\n * @dev Just a regular ERC-20 transfer, reverts on failure\\n * @param to Receiver of the underlying tokens\\n * @param amount Amount of underlying to transfer\\n */\\n function _doTransferOut(address to, uint256 amount) internal virtual {\\n IERC20Upgradeable token = IERC20Upgradeable(underlying);\\n internalCash -= amount;\\n token.safeTransfer(to, amount);\\n }\\n\\n /**\\n * @notice Transfer `tokens` tokens from `src` to `dst` by `spender`\\n * @dev Called by both `transfer` and `transferFrom` internally\\n * @param spender The address of the account performing the transfer\\n * @param src The address of the source account\\n * @param dst The address of the destination account\\n * @param tokens The number of tokens to transfer\\n */\\n function _transferTokens(address spender, address src, address dst, uint256 tokens) internal {\\n /* Fail if transfer not allowed */\\n comptroller.preTransferHook(address(this), src, dst, tokens);\\n\\n /* Do not allow self-transfers */\\n if (src == dst) {\\n revert TransferNotAllowed();\\n }\\n\\n /* Get the allowance, infinite for the account owner */\\n uint256 startingAllowance;\\n if (spender == src) {\\n startingAllowance = type(uint256).max;\\n } else {\\n startingAllowance = transferAllowances[src][spender];\\n }\\n\\n /* Do the calculations, checking for {under,over}flow */\\n uint256 allowanceNew = startingAllowance - tokens;\\n uint256 srcTokensNew = accountTokens[src] - tokens;\\n uint256 dstTokensNew = accountTokens[dst] + tokens;\\n\\n /////////////////////////\\n // EFFECTS & INTERACTIONS\\n\\n accountTokens[src] = srcTokensNew;\\n accountTokens[dst] = dstTokensNew;\\n\\n /* Eat some of the allowance (if necessary) */\\n if (startingAllowance != type(uint256).max) {\\n transferAllowances[src][spender] = allowanceNew;\\n }\\n\\n /* We emit a Transfer event */\\n emit Transfer(src, dst, tokens);\\n\\n comptroller.transferVerify(address(this), src, dst, tokens);\\n }\\n\\n /**\\n * @notice Initialize the money market\\n * @param underlying_ The address of the underlying asset\\n * @param comptroller_ The address of the Comptroller\\n * @param interestRateModel_ The address of the interest rate model\\n * @param initialExchangeRateMantissa_ The initial exchange rate, scaled by 1e18\\n * @param name_ ERC-20 name of this token\\n * @param symbol_ ERC-20 symbol of this token\\n * @param decimals_ ERC-20 decimal precision of this token\\n * @param admin_ Address of the administrator of this token\\n * @param accessControlManager_ AccessControlManager contract address\\n * @param riskManagement Addresses of risk & income related contracts\\n * @param reserveFactorMantissa_ Percentage of borrow interest that goes to reserves (from 0 to 1e18)\\n */\\n function _initialize(\\n address underlying_,\\n ComptrollerInterface comptroller_,\\n InterestRateModel interestRateModel_,\\n uint256 initialExchangeRateMantissa_,\\n string memory name_,\\n string memory symbol_,\\n uint8 decimals_,\\n address admin_,\\n address accessControlManager_,\\n RiskManagementInit memory riskManagement,\\n uint256 reserveFactorMantissa_\\n ) internal onlyInitializing {\\n __Ownable2Step_init();\\n __AccessControlled_init_unchained(accessControlManager_);\\n require(accrualBlockNumber == 0 && borrowIndex == 0, \\\"market may only be initialized once\\\");\\n\\n // Set initial exchange rate\\n initialExchangeRateMantissa = initialExchangeRateMantissa_;\\n require(initialExchangeRateMantissa > 0, \\\"initial exchange rate must be greater than zero.\\\");\\n\\n _setComptroller(comptroller_);\\n\\n // Initialize slot(block or second) number and borrow index (slot(block or second) number mocks depend on comptroller being set)\\n accrualBlockNumber = getBlockNumberOrTimestamp();\\n borrowIndex = MANTISSA_ONE;\\n\\n // Set the interest rate model (depends on slot(block or second) number / borrow index)\\n _setInterestRateModelFresh(interestRateModel_);\\n\\n _setReserveFactorFresh(reserveFactorMantissa_);\\n\\n name = name_;\\n symbol = symbol_;\\n decimals = decimals_;\\n _setShortfallContract(riskManagement.shortfall);\\n _setProtocolShareReserve(riskManagement.protocolShareReserve);\\n protocolSeizeShareMantissa = DEFAULT_PROTOCOL_SEIZE_SHARE_MANTISSA;\\n\\n // Set underlying and sanity check it\\n underlying = underlying_;\\n IERC20Upgradeable(underlying).totalSupply();\\n\\n // The counter starts true to prevent changing it from zero to non-zero (i.e. smaller cost/refund)\\n _notEntered = true;\\n _transferOwnership(admin_);\\n }\\n\\n function _setShortfallContract(address shortfall_) internal {\\n ensureNonzeroAddress(shortfall_);\\n address oldShortfall = shortfall;\\n shortfall = shortfall_;\\n emit NewShortfallContract(oldShortfall, shortfall_);\\n }\\n\\n function _setProtocolShareReserve(address payable protocolShareReserve_) internal {\\n ensureNonzeroAddress(protocolShareReserve_);\\n address oldProtocolShareReserve = address(protocolShareReserve);\\n protocolShareReserve = protocolShareReserve_;\\n emit NewProtocolShareReserve(oldProtocolShareReserve, address(protocolShareReserve_));\\n }\\n\\n function _ensureSenderIsDelegateOf(address user) internal view {\\n if (!ComptrollerViewInterface(address(comptroller)).approvedDelegates(user, msg.sender)) {\\n revert DelegateNotApproved();\\n }\\n }\\n\\n /**\\n * @notice Gets the internally tracked cash balance of this market\\n * @dev Returns the internalCash state variable, which is updated on transfers in/out\\n * @return The quantity of underlying tokens tracked internally by this contract\\n */\\n function _getCashPrior() internal view virtual returns (uint256) {\\n return internalCash;\\n }\\n\\n /**\\n * @notice Return the borrow balance of account based on stored data\\n * @param account The address whose balance should be calculated\\n * @return borrowBalance the calculated balance\\n */\\n function _borrowBalanceStored(address account) internal view returns (uint256) {\\n /* Get borrowBalance and borrowIndex */\\n BorrowSnapshot memory borrowSnapshot = accountBorrows[account];\\n\\n /* If borrowBalance = 0 then borrowIndex is likely also 0.\\n * Rather than failing the calculation with a division by 0, we immediately return 0 in this case.\\n */\\n if (borrowSnapshot.principal == 0) {\\n return 0;\\n }\\n\\n /* Calculate new borrow balance using the interest index:\\n * recentBorrowBalance = borrower.borrowBalance * market.borrowIndex / borrower.borrowIndex\\n */\\n uint256 principalTimesIndex = borrowSnapshot.principal * borrowIndex;\\n\\n return principalTimesIndex / borrowSnapshot.interestIndex;\\n }\\n\\n /**\\n * @notice Calculates the exchange rate from the underlying to the VToken\\n * @dev This function does not accrue interest before calculating the exchange rate\\n * @return exchangeRate Calculated exchange rate scaled by 1e18\\n */\\n function _exchangeRateStored() internal view virtual returns (uint256) {\\n uint256 _totalSupply = totalSupply;\\n if (_totalSupply == 0) {\\n /*\\n * If there are no tokens minted:\\n * exchangeRate = initialExchangeRate\\n */\\n return initialExchangeRateMantissa;\\n }\\n /*\\n * Otherwise:\\n * exchangeRate = (totalCash + totalBorrows + badDebt - totalReserves) / totalSupply\\n */\\n uint256 totalCash = _getCashPrior();\\n uint256 cashPlusBorrowsMinusReserves = totalCash + totalBorrows + badDebt - totalReserves;\\n uint256 exchangeRate = (cashPlusBorrowsMinusReserves * EXP_SCALE) / _totalSupply;\\n\\n return exchangeRate;\\n }\\n}\\n\",\"keccak256\":\"0x7267f5337062ad01a5011f7725a86cc2ad0351cca0aaceadc167341f168cdef2\",\"license\":\"BSD-3-Clause\"},\"contracts/VTokenInterfaces.sol\":{\"content\":\"// SPDX-License-Identifier: BSD-3-Clause\\npragma solidity 0.8.25;\\n\\nimport { IERC20Upgradeable } from \\\"@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol\\\";\\nimport { ResilientOracleInterface } from \\\"@venusprotocol/oracle/contracts/interfaces/OracleInterface.sol\\\";\\n\\nimport { ComptrollerInterface } from \\\"./ComptrollerInterface.sol\\\";\\nimport { InterestRateModel } from \\\"./InterestRateModel.sol\\\";\\n\\n/**\\n * @title VTokenStorage\\n * @author Venus\\n * @notice Storage layout used by the `VToken` contract\\n */\\n// solhint-disable-next-line max-states-count\\ncontract VTokenStorage {\\n /**\\n * @notice Container for borrow balance information\\n * @member principal Total balance (with accrued interest), after applying the most recent balance-changing action\\n * @member interestIndex Global borrowIndex as of the most recent balance-changing action\\n */\\n struct BorrowSnapshot {\\n uint256 principal;\\n uint256 interestIndex;\\n }\\n\\n /**\\n * @dev Guard variable for re-entrancy checks\\n */\\n bool internal _notEntered;\\n\\n /**\\n * @notice Underlying asset for this VToken\\n */\\n address public underlying;\\n\\n /**\\n * @notice EIP-20 token name for this token\\n */\\n string public name;\\n\\n /**\\n * @notice EIP-20 token symbol for this token\\n */\\n string public symbol;\\n\\n /**\\n * @notice EIP-20 token decimals for this token\\n */\\n uint8 public decimals;\\n\\n /**\\n * @notice Protocol share Reserve contract address\\n */\\n address payable public protocolShareReserve;\\n\\n /**\\n * @notice Contract which oversees inter-vToken operations\\n */\\n ComptrollerInterface public comptroller;\\n\\n /**\\n * @notice Model which tells what the current interest rate should be\\n */\\n InterestRateModel public interestRateModel;\\n\\n // Initial exchange rate used when minting the first VTokens (used when totalSupply = 0)\\n uint256 internal initialExchangeRateMantissa;\\n\\n /**\\n * @notice Fraction of interest currently set aside for reserves\\n */\\n uint256 public reserveFactorMantissa;\\n\\n /**\\n * @notice Slot(block or second) number that interest was last accrued at\\n */\\n uint256 public accrualBlockNumber;\\n\\n /**\\n * @notice Accumulator of the total earned interest rate since the opening of the market\\n */\\n uint256 public borrowIndex;\\n\\n /**\\n * @notice Total amount of outstanding borrows of the underlying in this market\\n */\\n uint256 public totalBorrows;\\n\\n /**\\n * @notice Total amount of reserves of the underlying held in this market\\n */\\n uint256 public totalReserves;\\n\\n /**\\n * @notice Total number of tokens in circulation\\n */\\n uint256 public totalSupply;\\n\\n /**\\n * @notice Total bad debt of the market\\n */\\n uint256 public badDebt;\\n\\n // Official record of token balances for each account\\n mapping(address => uint256) internal accountTokens;\\n\\n // Approved token transfer amounts on behalf of others\\n mapping(address => mapping(address => uint256)) internal transferAllowances;\\n\\n // Mapping of account addresses to outstanding borrow balances\\n mapping(address => BorrowSnapshot) internal accountBorrows;\\n\\n /**\\n * @notice Share of seized collateral that is added to reserves\\n */\\n uint256 public protocolSeizeShareMantissa;\\n\\n /**\\n * @notice Storage of Shortfall contract address\\n */\\n address public shortfall;\\n\\n /**\\n * @notice delta slot (block or second) after which reserves will be reduced\\n */\\n uint256 public reduceReservesBlockDelta;\\n\\n /**\\n * @notice last slot (block or second) number at which reserves were reduced\\n */\\n uint256 public reduceReservesBlockNumber;\\n\\n /**\\n * @notice Tracked internal cash balance, immune to direct token transfers (donation attacks)\\n * @dev Updated via _doTransferIn(), _doTransferOut(), syncCash(), and badDebtRecovered(). Must be initialized via syncCash() after upgrade.\\n */\\n uint256 public internalCash;\\n\\n /**\\n * @dev This empty reserved space is put in place to allow future versions to add new\\n * variables without shifting down storage in the inheritance chain.\\n * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\\n */\\n uint256[47] private __gap;\\n}\\n\\n/**\\n * @title VTokenInterface\\n * @author Venus\\n * @notice Interface implemented by the `VToken` contract\\n */\\nabstract contract VTokenInterface is VTokenStorage {\\n struct RiskManagementInit {\\n address shortfall;\\n address payable protocolShareReserve;\\n }\\n\\n /*** Market Events ***/\\n\\n /**\\n * @notice Event emitted when interest is accrued\\n */\\n event AccrueInterest(uint256 cashPrior, uint256 interestAccumulated, uint256 borrowIndex, uint256 totalBorrows);\\n\\n /**\\n * @notice Event emitted when tokens are minted\\n */\\n event Mint(address indexed minter, uint256 mintAmount, uint256 mintTokens, uint256 accountBalance);\\n\\n /**\\n * @notice Event emitted when tokens are redeemed\\n */\\n event Redeem(address indexed redeemer, uint256 redeemAmount, uint256 redeemTokens, uint256 accountBalance);\\n\\n /**\\n * @notice Event emitted when underlying is borrowed\\n */\\n event Borrow(address indexed borrower, uint256 borrowAmount, uint256 accountBorrows, uint256 totalBorrows);\\n\\n /**\\n * @notice Event emitted when a borrow is repaid\\n */\\n event RepayBorrow(\\n address indexed payer,\\n address indexed borrower,\\n uint256 repayAmount,\\n uint256 accountBorrows,\\n uint256 totalBorrows\\n );\\n\\n /**\\n * @notice Event emitted when bad debt is accumulated on a market\\n * @param borrower borrower to \\\"forgive\\\"\\n * @param badDebtDelta amount of new bad debt recorded\\n * @param badDebtOld previous bad debt value\\n * @param badDebtNew new bad debt value\\n */\\n event BadDebtIncreased(address indexed borrower, uint256 badDebtDelta, uint256 badDebtOld, uint256 badDebtNew);\\n\\n /**\\n * @notice Event emitted when bad debt is recovered via an auction\\n * @param badDebtOld previous bad debt value\\n * @param badDebtNew new bad debt value\\n */\\n event BadDebtRecovered(uint256 badDebtOld, uint256 badDebtNew);\\n\\n /**\\n * @notice Event emitted when a borrow is liquidated\\n */\\n event LiquidateBorrow(\\n address indexed liquidator,\\n address indexed borrower,\\n uint256 repayAmount,\\n address indexed vTokenCollateral,\\n uint256 seizeTokens\\n );\\n\\n /*** Admin Events ***/\\n\\n /**\\n * @notice Event emitted when comptroller is changed\\n */\\n event NewComptroller(ComptrollerInterface indexed oldComptroller, ComptrollerInterface indexed newComptroller);\\n\\n /**\\n * @notice Event emitted when shortfall contract address is changed\\n */\\n event NewShortfallContract(address indexed oldShortfall, address indexed newShortfall);\\n\\n /**\\n * @notice Event emitted when protocol share reserve contract address is changed\\n */\\n event NewProtocolShareReserve(address indexed oldProtocolShareReserve, address indexed newProtocolShareReserve);\\n\\n /**\\n * @notice Event emitted when interestRateModel is changed\\n */\\n event NewMarketInterestRateModel(\\n InterestRateModel indexed oldInterestRateModel,\\n InterestRateModel indexed newInterestRateModel\\n );\\n\\n /**\\n * @notice Event emitted when protocol seize share is changed\\n */\\n event NewProtocolSeizeShare(uint256 oldProtocolSeizeShareMantissa, uint256 newProtocolSeizeShareMantissa);\\n\\n /**\\n * @notice Event emitted when the reserve factor is changed\\n */\\n event NewReserveFactor(uint256 oldReserveFactorMantissa, uint256 newReserveFactorMantissa);\\n\\n /**\\n * @notice Event emitted when the reserves are added\\n */\\n event ReservesAdded(address indexed benefactor, uint256 addAmount, uint256 newTotalReserves);\\n\\n /**\\n * @notice Event emitted when the spread reserves are reduced\\n */\\n event SpreadReservesReduced(address indexed protocolShareReserve, uint256 reduceAmount, uint256 newTotalReserves);\\n\\n /**\\n * @notice EIP20 Transfer event\\n */\\n event Transfer(address indexed from, address indexed to, uint256 amount);\\n\\n /**\\n * @notice EIP20 Approval event\\n */\\n event Approval(address indexed owner, address indexed spender, uint256 amount);\\n\\n /**\\n * @notice Event emitted when healing the borrow\\n */\\n event HealBorrow(address indexed payer, address indexed borrower, uint256 repayAmount);\\n\\n /**\\n * @notice Event emitted when tokens are swept\\n */\\n event SweepToken(address indexed token);\\n\\n /**\\n * @notice Event emitted when reduce reserves slot (block or second) delta is changed\\n */\\n event NewReduceReservesBlockDelta(\\n uint256 oldReduceReservesBlockOrTimestampDelta,\\n uint256 newReduceReservesBlockOrTimestampDelta\\n );\\n\\n /**\\n * @notice Event emitted when liquidation reserves are reduced\\n */\\n event ProtocolSeize(address indexed from, address indexed to, uint256 amount);\\n\\n /**\\n * @notice Event emitted when internalCash is synced with actual token balance\\n */\\n event CashSynced(uint256 oldInternalCash, uint256 newInternalCash);\\n\\n /*** User Interface ***/\\n\\n function mint(uint256 mintAmount) external virtual returns (uint256);\\n\\n function mintBehalf(address minter, uint256 mintAllowed) external virtual returns (uint256);\\n\\n function redeem(uint256 redeemTokens) external virtual returns (uint256);\\n\\n function redeemBehalf(address redeemer, uint256 redeemTokens) external virtual returns (uint256);\\n\\n function redeemUnderlying(uint256 redeemAmount) external virtual returns (uint256);\\n\\n function redeemUnderlyingBehalf(address redeemer, uint256 redeemAmount) external virtual returns (uint256);\\n\\n function borrow(uint256 borrowAmount) external virtual returns (uint256);\\n\\n function borrowBehalf(address borrwwer, uint256 borrowAmount) external virtual returns (uint256);\\n\\n function repayBorrow(uint256 repayAmount) external virtual returns (uint256);\\n\\n function repayBorrowBehalf(address borrower, uint256 repayAmount) external virtual returns (uint256);\\n\\n function liquidateBorrow(\\n address borrower,\\n uint256 repayAmount,\\n VTokenInterface vTokenCollateral\\n ) external virtual returns (uint256);\\n\\n function healBorrow(address payer, address borrower, uint256 repayAmount) external virtual;\\n\\n function forceLiquidateBorrow(\\n address liquidator,\\n address borrower,\\n uint256 repayAmount,\\n VTokenInterface vTokenCollateral,\\n bool skipCloseFactorCheck\\n ) external virtual;\\n\\n function seize(address liquidator, address borrower, uint256 seizeTokens) external virtual;\\n\\n function transfer(address dst, uint256 amount) external virtual returns (bool);\\n\\n function transferFrom(address src, address dst, uint256 amount) external virtual returns (bool);\\n\\n function accrueInterest() external virtual returns (uint256);\\n\\n function sweepToken(IERC20Upgradeable token) external virtual;\\n\\n /*** Admin Functions ***/\\n\\n function setReserveFactor(uint256 newReserveFactorMantissa) external virtual;\\n\\n function reduceReserves(uint256 reduceAmount) external virtual;\\n\\n function exchangeRateCurrent() external virtual returns (uint256);\\n\\n function borrowBalanceCurrent(address account) external virtual returns (uint256);\\n\\n function setInterestRateModel(InterestRateModel newInterestRateModel) external virtual;\\n\\n function addReserves(uint256 addAmount) external virtual;\\n\\n function syncCash() external virtual;\\n\\n function totalBorrowsCurrent() external virtual returns (uint256);\\n\\n function balanceOfUnderlying(address owner) external virtual returns (uint256);\\n\\n function approve(address spender, uint256 amount) external virtual returns (bool);\\n\\n function increaseAllowance(address spender, uint256 addedValue) external virtual returns (bool);\\n\\n function decreaseAllowance(address spender, uint256 subtractedValue) external virtual returns (bool);\\n\\n function allowance(address owner, address spender) external view virtual returns (uint256);\\n\\n function balanceOf(address owner) external view virtual returns (uint256);\\n\\n function getAccountSnapshot(address account) external view virtual returns (uint256, uint256, uint256, uint256);\\n\\n function borrowRatePerBlock() external view virtual returns (uint256);\\n\\n function supplyRatePerBlock() external view virtual returns (uint256);\\n\\n function borrowBalanceStored(address account) external view virtual returns (uint256);\\n\\n function exchangeRateStored() external view virtual returns (uint256);\\n\\n function getCash() external view virtual returns (uint256);\\n\\n /**\\n * @notice Indicator that this is a VToken contract (for inspection)\\n * @return Always true\\n */\\n function isVToken() external pure virtual returns (bool) {\\n return true;\\n }\\n}\\n\",\"keccak256\":\"0x155684e9cb12cdd8be63d2314c9452b68ee44ff98a00e0d65cd1fd7d0bdd4391\",\"license\":\"BSD-3-Clause\"},\"contracts/lib/constants.sol\":{\"content\":\"// SPDX-License-Identifier: BSD-3-Clause\\npragma solidity ^0.8.25;\\n\\n/// @dev The approximate number of seconds per year\\nuint256 constant SECONDS_PER_YEAR = 31_536_000;\\n\\n/// @dev Base unit for computations, usually used in scaling (multiplications, divisions)\\nuint256 constant EXP_SCALE = 1e18;\\n\\n/// @dev A unit (literal one) in EXP_SCALE, usually used in additions/subtractions\\nuint256 constant MANTISSA_ONE = EXP_SCALE;\\n\",\"keccak256\":\"0x54ab3a6f3bc87569ed12370f3470a1ec84cea9796d4d0ccf3d07dd4280c044aa\",\"license\":\"BSD-3-Clause\"},\"contracts/lib/validators.sol\":{\"content\":\"// SPDX-License-Identifier: BSD-3-Clause\\npragma solidity 0.8.25;\\n\\n/// @notice Thrown if the supplied address is a zero address where it is not allowed\\nerror ZeroAddressNotAllowed();\\n\\n/// @notice Checks if the provided address is nonzero, reverts otherwise\\n/// @param address_ Address to check\\n/// @custom:error ZeroAddressNotAllowed is thrown if the provided address is a zero address\\nfunction ensureNonzeroAddress(address address_) pure {\\n if (address_ == address(0)) {\\n revert ZeroAddressNotAllowed();\\n }\\n}\\n\",\"keccak256\":\"0x93bdd5cfb100f0f9a1d446857e23c3633df6ab12c266333c978428edd96b1367\",\"license\":\"BSD-3-Clause\"}},\"version\":1}", + "bytecode": "0x6080604052348015600f57600080fd5b506016601a565b60d7565b600054610100900460ff161560855760405162461bcd60e51b815260206004820152602760248201527f496e697469616c697a61626c653a20636f6e747261637420697320696e697469604482015266616c697a696e6760c81b606482015260840160405180910390fd5b60005460ff9081161460d5576000805460ff191660ff9081179091556040519081527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b565b612dcd806100e66000396000f3fe608060405234801561001057600080fd5b50600436106101165760003560e01c8063a3aefa2c116100a2578063e30c397811610071578063e30c397814610242578063eed873c214610253578063f2fde38b14610274578063f36dba3814610287578063ff94d958146102a757600080fd5b8063a3aefa2c146101e9578063b4a0bdf314610209578063c4d66de81461021a578063d88ff1f41461022d57600080fd5b80632ba21572116100e95780632ba2157214610186578063715018a6146101a857806379ba5097146101b05780637aee632d146101b85780638da5cb5b146101d857600080fd5b80630e32cb861461011b5780631cb6bb7e1461013057806323dc8d6414610143578063266e0a7f14610156575b600080fd5b61012e61012936600461226c565b6102ba565b005b61012e61013e3660046122d9565b6102ce565b61012e610151366004612344565b61041c565b6101696101643660046123d4565b610ab1565b6040516001600160a01b0390911681526020015b60405180910390f35b61019961019436600461226c565b610ae0565b60405161017d9392919061245d565b61012e610c9a565b61012e610cae565b6101cb6101c636600461226c565b610d25565b60405161017d91906124ed565b6033546001600160a01b0316610169565b6101fc6101f736600461226c565b610e1c565b60405161017d9190612548565b6097546001600160a01b0316610169565b61012e61022836600461226c565b61101f565b61023561113b565b60405161017d919061255b565b6065546001600160a01b0316610169565b6102666102613660046125bf565b6112dc565b60405190815260200161017d565b61012e61028236600461226c565b61149c565b61029a61029536600461226c565b61150d565b60405161017d9190612630565b61012e6102b536600461267d565b611583565b6102c26117ee565b6102cb81611848565b50565b61030c6040518060400160405280601b81526020017f736574506f6f6c4e616d6528616464726573732c737472696e67290000000000815250611906565b61031682826119a0565b6001600160a01b038316600090815260cc602052604081208054909190829061033e906126c9565b80601f016020809104026020016040519081016040528092919081815260200182805461036a906126c9565b80156103b75780601f1061038c576101008083540402835291602001916103b7565b820191906000526020600020905b81548152906001019060200180831161039a57829003601f168201915b509394508593506103cf925086915087905083612768565b50846001600160a01b03167fa01f2b0df2b143bfb23d4b696c103547a6bec8ca1f56e8e8a483611cb4e23a7e82868660405161040d9392919061284c565b60405180910390a25050505050565b61045a6040518060400160405280601981526020017f6164644d61726b6574284164644d61726b6574496e7075742900000000000000815250611906565b8051610465906119f1565b61047281608001516119f1565b60008160600151116104d75760405162461bcd60e51b815260206004820152602360248201527f506f6f6c52656769737472793a20696e697469616c537570706c79206973207a60448201526265726f60e81b60648201526084015b60405180910390fd5b60008160000151905060008190506000826001600160a01b0316635fe3b5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015610525573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105499190612872565b905060008190506000846001600160a01b0316636f307dc36040518163ffffffff1660e01b8152600401602060405180830381865afa158015610590573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105b49190612872565b6001600160a01b03848116600090815260cc602052604090206001015491925082911661062d5760405162461bcd60e51b815260206004820152602160248201527f506f6f6c52656769737472793a20506f6f6c206e6f74207265676973746572656044820152601960fa1b60648201526084016104ce565b6001600160a01b03848116600090815260cd60209081526040808320868516845290915290205416156106d65760405162461bcd60e51b8152602060048201526044602482018190527f506f6f6c52656769737472793a204d61726b657420616c726561647920616464908201527f656420666f7220617373657420636f6d7074726f6c6c657220636f6d62696e616064820152633a34b7b760e11b608482015260a4016104ce565b6040516332ad3e1360e21b81526001600160a01b03878116600483015284169063cab4f84c90602401600060405180830381600087803b15801561071957600080fd5b505af115801561072d573d6000803e3d6000fd5b5050505060208701516040808901519051635cc4fdeb60e01b81526001600160a01b0389811660048301526024820193909352604481019190915290841690635cc4fdeb90606401600060405180830381600087803b15801561078f57600080fd5b505af11580156107a3573d6000803e3d6000fd5b5060009250600191506107b39050565b6040519080825280602002602001820160405280156107dc578160200160208202803683370190505b50604080516001808252818301909252919250600091906020808301908036833750506040805160018082528183019092529293506000929150602080830190803683370190505090508960a001518360008151811061083e5761083e61288f565b6020026020010181815250508960c00151826000815181106108625761086261288f565b60200260200101818152505088816000815181106108825761088261288f565b6001600160a01b03928316602091820292909201015260405163344dabd160e21b81529087169063d136af44906108bf90849087906004016128a5565b600060405180830381600087803b1580156108d957600080fd5b505af11580156108ed573d6000803e3d6000fd5b505060405163186db48f60e01b81526001600160a01b038916925063186db48f915061091f90849086906004016128a5565b600060405180830381600087803b15801561093957600080fd5b505af115801561094d573d6000803e3d6000fd5b5050506001600160a01b03808916600081815260cd602090815260408083208b8616845282528083208054958f166001600160a01b031996871617905560ce82528220805460018101825590835290822001805490931690911790915560608c01519091506109bf9086903390611a18565b90506109d66001600160a01b0386168a6000611b1d565b6109ea6001600160a01b0386168a83611b1d565b60808b01516040516323323e0360e01b81526001600160a01b03918216600482015260248101839052908b16906323323e03906044016020604051808303816000875af1158015610a3f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a639190612929565b50886001600160a01b0316886001600160a01b03167f7772c85e68debdf74fad87834e2cc05fa763e74faf14de7096da30529065114260405160405180910390a35050505050505050505050565b6001600160a01b03808316600090815260cd602090815260408083208585168452909152902054165b92915050565b60c960205260009081526040902080548190610afb906126c9565b80601f0160208091040260200160405190810160405280929190818152602001828054610b27906126c9565b8015610b745780601f10610b4957610100808354040283529160200191610b74565b820191906000526020600020905b815481529060010190602001808311610b5757829003601f168201915b505050505090806001018054610b89906126c9565b80601f0160208091040260200160405190810160405280929190818152602001828054610bb5906126c9565b8015610c025780601f10610bd757610100808354040283529160200191610c02565b820191906000526020600020905b815481529060010190602001808311610be557829003601f168201915b505050505090806002018054610c17906126c9565b80601f0160208091040260200160405190810160405280929190818152602001828054610c43906126c9565b8015610c905780601f10610c6557610100808354040283529160200191610c90565b820191906000526020600020905b815481529060010190602001808311610c7357829003601f168201915b5050505050905083565b610ca26117ee565b610cac6000611be5565b565b60655433906001600160a01b03168114610d1c5760405162461bcd60e51b815260206004820152602960248201527f4f776e61626c6532537465703a2063616c6c6572206973206e6f7420746865206044820152683732bb9037bbb732b960b91b60648201526084016104ce565b6102cb81611be5565b610d2d612206565b6001600160a01b038216600090815260cc602052604090819020815160a08101909252805482908290610d5f906126c9565b80601f0160208091040260200160405190810160405280929190818152602001828054610d8b906126c9565b8015610dd85780601f10610dad57610100808354040283529160200191610dd8565b820191906000526020600020905b815481529060010190602001808311610dbb57829003601f168201915b505050918352505060018201546001600160a01b03908116602083015260028301541660408201526003820154606082015260049091015460809091015292915050565b610e4060405180606001604052806060815260200160608152602001606081525090565b6001600160a01b038216600090815260c9602052604090819020815160608101909252805482908290610e72906126c9565b80601f0160208091040260200160405190810160405280929190818152602001828054610e9e906126c9565b8015610eeb5780601f10610ec057610100808354040283529160200191610eeb565b820191906000526020600020905b815481529060010190602001808311610ece57829003601f168201915b50505050508152602001600182018054610f04906126c9565b80601f0160208091040260200160405190810160405280929190818152602001828054610f30906126c9565b8015610f7d5780601f10610f5257610100808354040283529160200191610f7d565b820191906000526020600020905b815481529060010190602001808311610f6057829003601f168201915b50505050508152602001600282018054610f96906126c9565b80601f0160208091040260200160405190810160405280929190818152602001828054610fc2906126c9565b801561100f5780601f10610fe45761010080835404028352916020019161100f565b820191906000526020600020905b815481529060010190602001808311610ff257829003601f168201915b5050505050815250509050919050565b600054610100900460ff161580801561103f5750600054600160ff909116105b806110595750303b158015611059575060005460ff166001145b6110bc5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b60648201526084016104ce565b6000805460ff1916600117905580156110df576000805461ff0019166101001790555b6110e7611bfe565b6110f082611c2d565b8015611137576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498906020015b60405180910390a15b5050565b60cb5460609060008167ffffffffffffffff81111561115c5761115c61232e565b60405190808252806020026020018201604052801561119557816020015b611182612206565b81526020019060019003908161117a5790505b50905060015b8281116112d557600081815260ca60209081526040808320546001600160a01b031680845260cc90925291829020825160a081019093528054919291829082906111e4906126c9565b80601f0160208091040260200160405190810160405280929190818152602001828054611210906126c9565b801561125d5780601f106112325761010080835404028352916020019161125d565b820191906000526020600020905b81548152906001019060200180831161124057829003601f168201915b50505091835250506001828101546001600160a01b03908116602084015260028401541660408301526003830154606083015260049092015460809091015284906112a89085612958565b815181106112b8576112b861288f565b602002602001018190525050806112ce9061296b565b905061119b565b5092915050565b60006112ff6040518060600160405280602f8152602001612d69602f9139611906565b611308856119f1565b611372856001600160a01b0316637dc0d1d06040518163ffffffff1660e01b8152600401602060405180830381865afa158015611349573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061136d9190612872565b6119f1565b600061137f888888611c54565b60405163091a474b60e11b8152600481018790529091506001600160a01b038716906312348e9690602401600060405180830381600087803b1580156113c457600080fd5b505af11580156113d8573d6000803e3d6000fd5b505060405163a843108160e01b8152600481018790526001600160a01b038916925063a84310819150602401600060405180830381600087803b15801561141e57600080fd5b505af1158015611432573d6000803e3d6000fd5b5050604051631482db1d60e21b8152600481018690526001600160a01b038916925063520b6c749150602401600060405180830381600087803b15801561147857600080fd5b505af115801561148c573d6000803e3d6000fd5b50929a9950505050505050505050565b6114a46117ee565b606580546001600160a01b0383166001600160a01b031990911681179091556114d56033546001600160a01b031690565b6001600160a01b03167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e2270060405160405180910390a350565b6001600160a01b038116600090815260ce602090815260409182902080548351818402810184019094528084526060939283018282801561157757602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311611559575b50505050509050919050565b6115a46040518060600160405280602d8152602001612d3c602d9139611906565b6001600160a01b038216600090815260c960205260408082208151606081019092528054829082906115d5906126c9565b80601f0160208091040260200160405190810160405280929190818152602001828054611601906126c9565b801561164e5780601f106116235761010080835404028352916020019161164e565b820191906000526020600020905b81548152906001019060200180831161163157829003601f168201915b50505050508152602001600182018054611667906126c9565b80601f0160208091040260200160405190810160405280929190818152602001828054611693906126c9565b80156116e05780601f106116b5576101008083540402835291602001916116e0565b820191906000526020600020905b8154815290600101906020018083116116c357829003601f168201915b505050505081526020016002820180546116f9906126c9565b80601f0160208091040260200160405190810160405280929190818152602001828054611725906126c9565b80156117725780601f1061174757610100808354040283529160200191611772565b820191906000526020600020905b81548152906001019060200180831161175557829003601f168201915b505050919092525050506001600160a01b038416600090815260c96020526040902090915082906117a382826129cb565b905050826001600160a01b03167f8f91f3b5d20b61744ed591c43346d4514ee5c2ffced5fc3795bb13c6f951814782846040516117e1929190612b0b565b60405180910390a2505050565b6033546001600160a01b03163314610cac5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064016104ce565b6001600160a01b0381166118ac5760405162461bcd60e51b815260206004820152602560248201527f696e76616c696420616365737320636f6e74726f6c206d616e61676572206164604482015264647265737360d81b60648201526084016104ce565b609780546001600160a01b038381166001600160a01b031983168117909355604080519190921680825260208201939093527f66fd58e82f7b31a2a5c30e0888f3093efe4e111b00cd2b0c31fe014601293aa0910161112e565b6097546040516318c5e8ab60e01b81526000916001600160a01b0316906318c5e8ab906119399033908690600401612b95565b602060405180830381865afa158015611956573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061197a9190612bb9565b90508061113757333083604051634a3fa29360e01b81526004016104ce93929190612bdb565b60648111156111375760405162461bcd60e51b815260206004820152601860248201527f506f6f6c2773206e616d6520697320746f6f206c61726765000000000000000060448201526064016104ce565b6001600160a01b0381166102cb576040516342bcdf7f60e11b815260040160405180910390fd5b6040516370a0823160e01b815230600482015260009081906001600160a01b038616906370a0823190602401602060405180830381865afa158015611a61573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a859190612929565b9050611a9c6001600160a01b038616853086611e3b565b6040516370a0823160e01b81523060048201526000906001600160a01b038716906370a0823190602401602060405180830381865afa158015611ae3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b079190612929565b9050611b138282612958565b9695505050505050565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b179052611b6e8482611e73565b611bdf576040516001600160a01b038416602482015260006044820152611bd590859063095ea7b360e01b906064015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152611f1a565b611bdf8482611f1a565b50505050565b606580546001600160a01b03191690556102cb81611ff4565b600054610100900460ff16611c255760405162461bcd60e51b81526004016104ce90612c07565b610cac612046565b600054610100900460ff166102c25760405162461bcd60e51b81526004016104ce90612c07565b6001600160a01b03808216600090815260cc602052604081206001810154919290911615611ce05760405162461bcd60e51b815260206004820152603360248201527f506f6f6c52656769737472793a20506f6f6c20616c7265616479206578697374604482015272399034b7103a3432903234b932b1ba37b93c9760691b60648201526084016104ce565b611cea85856119a0565b60cb60008154611cf99061296b565b9091555060cb546040805160c06020601f8901819004028201810190925260a081018781526000928291908a908a908190850183828082843760009201829052509385525050336020808501919091526001600160a01b038a1660408086018290524360608701524260809096019590955287845260ca825284842080546001600160a01b03191682179055835260cc90525020815191925082918190611da09082612c52565b5060208201516001820180546001600160a01b03199081166001600160a01b039384161790915560408085015160028501805490931690841617909155606084015160038401556080909301516004909201919091559051908616907f53ec2a1d9645c4631472dabcf6d255f5f2971baa64321235b1610d91c692928e90611e299084906124ed565b60405180910390a25095945050505050565b6040516001600160a01b0380851660248301528316604482015260648101829052611bdf9085906323b872dd60e01b90608401611b9e565b6000806000846001600160a01b031684604051611e909190612d0c565b6000604051808303816000865af19150503d8060008114611ecd576040519150601f19603f3d011682016040523d82523d6000602084013e611ed2565b606091505b5091509150818015611efc575080511580611efc575080806020019051810190611efc9190612bb9565b8015611f1157506001600160a01b0385163b15155b95945050505050565b6000611f6f826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166120769092919063ffffffff16565b9050805160001480611f90575080806020019051810190611f909190612bb9565b611fef5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b60648201526084016104ce565b505050565b603380546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b600054610100900460ff1661206d5760405162461bcd60e51b81526004016104ce90612c07565b610cac33611be5565b6060612085848460008561208d565b949350505050565b6060824710156120ee5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b60648201526084016104ce565b600080866001600160a01b0316858760405161210a9190612d0c565b60006040518083038185875af1925050503d8060008114612147576040519150601f19603f3d011682016040523d82523d6000602084013e61214c565b606091505b509150915061215d87838387612168565b979650505050505050565b606083156121d75782516000036121d0576001600160a01b0385163b6121d05760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016104ce565b5081612085565b61208583838151156121ec5781518083602001fd5b8060405162461bcd60e51b81526004016104ce9190612d28565b6040518060a001604052806060815260200160006001600160a01b0316815260200160006001600160a01b0316815260200160008152602001600081525090565b6001600160a01b03811681146102cb57600080fd5b803561226781612247565b919050565b60006020828403121561227e57600080fd5b813561228981612247565b9392505050565b60008083601f8401126122a257600080fd5b50813567ffffffffffffffff8111156122ba57600080fd5b6020830191508360208285010111156122d257600080fd5b9250929050565b6000806000604084860312156122ee57600080fd5b83356122f981612247565b9250602084013567ffffffffffffffff81111561231557600080fd5b61232186828701612290565b9497909650939450505050565b634e487b7160e01b600052604160045260246000fd5b600060e0828403121561235657600080fd5b60405160e0810181811067ffffffffffffffff821117156123795761237961232e565b6040526123858361225c565b81526020830135602082015260408301356040820152606083013560608201526123b16080840161225c565b608082015260a083013560a082015260c083013560c08201528091505092915050565b600080604083850312156123e757600080fd5b82356123f281612247565b9150602083013561240281612247565b809150509250929050565b60005b83811015612428578181015183820152602001612410565b50506000910152565b6000815180845261244981602086016020860161240d565b601f01601f19169290920160200192915050565b6060815260006124706060830186612431565b82810360208401526124828186612431565b90508281036040840152611b138185612431565b6000815160a084526124ab60a0850182612431565b9050602083015160018060a01b038082166020870152806040860151166040870152505060608301516060850152608083015160808501528091505092915050565b6020815260006122896020830184612496565b60008151606084526125156060850182612431565b90506020830151848203602086015261252e8282612431565b91505060408301518482036040860152611f118282612431565b6020815260006122896020830184612500565b600060208083016020845280855180835260408601915060408160051b87010192506020870160005b828110156125b257603f198886030184526125a0858351612496565b94509285019290850190600101612584565b5092979650505050505050565b60008060008060008060a087890312156125d857600080fd5b863567ffffffffffffffff8111156125ef57600080fd5b6125fb89828a01612290565b909750955050602087013561260f81612247565b95989497509495604081013595506060810135946080909101359350915050565b6020808252825182820181905260009190848201906040850190845b818110156126715783516001600160a01b03168352928401929184019160010161264c565b50909695505050505050565b6000806040838503121561269057600080fd5b823561269b81612247565b9150602083013567ffffffffffffffff8111156126b757600080fd5b83016060818603121561240257600080fd5b600181811c908216806126dd57607f821691505b6020821081036126fd57634e487b7160e01b600052602260045260246000fd5b50919050565b601f821115611fef576000816000526020600020601f850160051c8101602086101561272c5750805b601f850160051c820191505b8181101561274b57828155600101612738565b505050505050565b600019600383901b1c191660019190911b1790565b67ffffffffffffffff8311156127805761278061232e565b6127948361278e83546126c9565b83612703565b6000601f8411600181146127c257600085156127b05750838201355b6127ba8682612753565b84555061281c565b600083815260209020601f19861690835b828110156127f357868501358255602094850194600190920191016127d3565b50868210156128105760001960f88860031b161c19848701351681555b505060018560011b0183555b5050505050565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b60408152600061285f6040830186612431565b8281036020840152611b13818587612823565b60006020828403121561288457600080fd5b815161228981612247565b634e487b7160e01b600052603260045260246000fd5b604080825283519082018190526000906020906060840190828701845b828110156128e75781516001600160a01b0316845292840192908401906001016128c2565b5050508381038285015284518082528583019183019060005b8181101561291c57835183529284019291840191600101612900565b5090979650505050505050565b60006020828403121561293b57600080fd5b5051919050565b634e487b7160e01b600052601160045260246000fd5b81810381811115610ada57610ada612942565b60006001820161297d5761297d612942565b5060010190565b6000808335601e1984360301811261299b57600080fd5b83018035915067ffffffffffffffff8211156129b657600080fd5b6020019150368190038213156122d257600080fd5b6129d58283612984565b67ffffffffffffffff8111156129ed576129ed61232e565b612a01816129fb85546126c9565b85612703565b6000601f821160018114612a2f5760008315612a1d5750838201355b612a278482612753565b865550612a89565b600085815260209020601f19841690835b82811015612a605786850135825560209485019460019092019101612a40565b5084821015612a7d5760001960f88660031b161c19848701351681555b505060018360011b0185555b50505050612a9a6020830183612984565b612aa8818360018601612768565b5050612ab76040830183612984565b611bdf818360028601612768565b6000808335601e19843603018112612adc57600080fd5b830160208101925035905067ffffffffffffffff811115612afc57600080fd5b8036038213156122d257600080fd5b604081526000612b1e6040830185612500565b8281036020840152612b308485612ac5565b60608352612b42606084018284612823565b915050612b526020860186612ac5565b8383036020850152612b65838284612823565b92505050612b766040860186612ac5565b8383036040850152612b89838284612823565b98975050505050505050565b6001600160a01b038316815260406020820181905260009061208590830184612431565b600060208284031215612bcb57600080fd5b8151801515811461228957600080fd5b6001600160a01b03848116825283166020820152606060408201819052600090611f1190830184612431565b6020808252602b908201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960408201526a6e697469616c697a696e6760a81b606082015260800190565b815167ffffffffffffffff811115612c6c57612c6c61232e565b612c8081612c7a84546126c9565b84612703565b602080601f831160018114612caf5760008415612c9d5750858301515b612ca78582612753565b86555061274b565b600085815260208120601f198616915b82811015612cde57888601518255948401946001909101908401612cbf565b5085821015612cfc5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b60008251612d1e81846020870161240d565b9190910192915050565b602081526000612289602083018461243156fe757064617465506f6f6c4d6574616461746128616464726573732c56656e7573506f6f6c4d6574614461746129616464506f6f6c28737472696e672c616464726573732c75696e743235362c75696e743235362c75696e7432353629a264697066735822122034ad52241cc9e9ea0aeb98857c2557789aa6cc8170d34e822ecf91a04ec63ee864736f6c63430008190033", + "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106101165760003560e01c8063a3aefa2c116100a2578063e30c397811610071578063e30c397814610242578063eed873c214610253578063f2fde38b14610274578063f36dba3814610287578063ff94d958146102a757600080fd5b8063a3aefa2c146101e9578063b4a0bdf314610209578063c4d66de81461021a578063d88ff1f41461022d57600080fd5b80632ba21572116100e95780632ba2157214610186578063715018a6146101a857806379ba5097146101b05780637aee632d146101b85780638da5cb5b146101d857600080fd5b80630e32cb861461011b5780631cb6bb7e1461013057806323dc8d6414610143578063266e0a7f14610156575b600080fd5b61012e61012936600461226c565b6102ba565b005b61012e61013e3660046122d9565b6102ce565b61012e610151366004612344565b61041c565b6101696101643660046123d4565b610ab1565b6040516001600160a01b0390911681526020015b60405180910390f35b61019961019436600461226c565b610ae0565b60405161017d9392919061245d565b61012e610c9a565b61012e610cae565b6101cb6101c636600461226c565b610d25565b60405161017d91906124ed565b6033546001600160a01b0316610169565b6101fc6101f736600461226c565b610e1c565b60405161017d9190612548565b6097546001600160a01b0316610169565b61012e61022836600461226c565b61101f565b61023561113b565b60405161017d919061255b565b6065546001600160a01b0316610169565b6102666102613660046125bf565b6112dc565b60405190815260200161017d565b61012e61028236600461226c565b61149c565b61029a61029536600461226c565b61150d565b60405161017d9190612630565b61012e6102b536600461267d565b611583565b6102c26117ee565b6102cb81611848565b50565b61030c6040518060400160405280601b81526020017f736574506f6f6c4e616d6528616464726573732c737472696e67290000000000815250611906565b61031682826119a0565b6001600160a01b038316600090815260cc602052604081208054909190829061033e906126c9565b80601f016020809104026020016040519081016040528092919081815260200182805461036a906126c9565b80156103b75780601f1061038c576101008083540402835291602001916103b7565b820191906000526020600020905b81548152906001019060200180831161039a57829003601f168201915b509394508593506103cf925086915087905083612768565b50846001600160a01b03167fa01f2b0df2b143bfb23d4b696c103547a6bec8ca1f56e8e8a483611cb4e23a7e82868660405161040d9392919061284c565b60405180910390a25050505050565b61045a6040518060400160405280601981526020017f6164644d61726b6574284164644d61726b6574496e7075742900000000000000815250611906565b8051610465906119f1565b61047281608001516119f1565b60008160600151116104d75760405162461bcd60e51b815260206004820152602360248201527f506f6f6c52656769737472793a20696e697469616c537570706c79206973207a60448201526265726f60e81b60648201526084015b60405180910390fd5b60008160000151905060008190506000826001600160a01b0316635fe3b5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015610525573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105499190612872565b905060008190506000846001600160a01b0316636f307dc36040518163ffffffff1660e01b8152600401602060405180830381865afa158015610590573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105b49190612872565b6001600160a01b03848116600090815260cc602052604090206001015491925082911661062d5760405162461bcd60e51b815260206004820152602160248201527f506f6f6c52656769737472793a20506f6f6c206e6f74207265676973746572656044820152601960fa1b60648201526084016104ce565b6001600160a01b03848116600090815260cd60209081526040808320868516845290915290205416156106d65760405162461bcd60e51b8152602060048201526044602482018190527f506f6f6c52656769737472793a204d61726b657420616c726561647920616464908201527f656420666f7220617373657420636f6d7074726f6c6c657220636f6d62696e616064820152633a34b7b760e11b608482015260a4016104ce565b6040516332ad3e1360e21b81526001600160a01b03878116600483015284169063cab4f84c90602401600060405180830381600087803b15801561071957600080fd5b505af115801561072d573d6000803e3d6000fd5b5050505060208701516040808901519051635cc4fdeb60e01b81526001600160a01b0389811660048301526024820193909352604481019190915290841690635cc4fdeb90606401600060405180830381600087803b15801561078f57600080fd5b505af11580156107a3573d6000803e3d6000fd5b5060009250600191506107b39050565b6040519080825280602002602001820160405280156107dc578160200160208202803683370190505b50604080516001808252818301909252919250600091906020808301908036833750506040805160018082528183019092529293506000929150602080830190803683370190505090508960a001518360008151811061083e5761083e61288f565b6020026020010181815250508960c00151826000815181106108625761086261288f565b60200260200101818152505088816000815181106108825761088261288f565b6001600160a01b03928316602091820292909201015260405163344dabd160e21b81529087169063d136af44906108bf90849087906004016128a5565b600060405180830381600087803b1580156108d957600080fd5b505af11580156108ed573d6000803e3d6000fd5b505060405163186db48f60e01b81526001600160a01b038916925063186db48f915061091f90849086906004016128a5565b600060405180830381600087803b15801561093957600080fd5b505af115801561094d573d6000803e3d6000fd5b5050506001600160a01b03808916600081815260cd602090815260408083208b8616845282528083208054958f166001600160a01b031996871617905560ce82528220805460018101825590835290822001805490931690911790915560608c01519091506109bf9086903390611a18565b90506109d66001600160a01b0386168a6000611b1d565b6109ea6001600160a01b0386168a83611b1d565b60808b01516040516323323e0360e01b81526001600160a01b03918216600482015260248101839052908b16906323323e03906044016020604051808303816000875af1158015610a3f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a639190612929565b50886001600160a01b0316886001600160a01b03167f7772c85e68debdf74fad87834e2cc05fa763e74faf14de7096da30529065114260405160405180910390a35050505050505050505050565b6001600160a01b03808316600090815260cd602090815260408083208585168452909152902054165b92915050565b60c960205260009081526040902080548190610afb906126c9565b80601f0160208091040260200160405190810160405280929190818152602001828054610b27906126c9565b8015610b745780601f10610b4957610100808354040283529160200191610b74565b820191906000526020600020905b815481529060010190602001808311610b5757829003601f168201915b505050505090806001018054610b89906126c9565b80601f0160208091040260200160405190810160405280929190818152602001828054610bb5906126c9565b8015610c025780601f10610bd757610100808354040283529160200191610c02565b820191906000526020600020905b815481529060010190602001808311610be557829003601f168201915b505050505090806002018054610c17906126c9565b80601f0160208091040260200160405190810160405280929190818152602001828054610c43906126c9565b8015610c905780601f10610c6557610100808354040283529160200191610c90565b820191906000526020600020905b815481529060010190602001808311610c7357829003601f168201915b5050505050905083565b610ca26117ee565b610cac6000611be5565b565b60655433906001600160a01b03168114610d1c5760405162461bcd60e51b815260206004820152602960248201527f4f776e61626c6532537465703a2063616c6c6572206973206e6f7420746865206044820152683732bb9037bbb732b960b91b60648201526084016104ce565b6102cb81611be5565b610d2d612206565b6001600160a01b038216600090815260cc602052604090819020815160a08101909252805482908290610d5f906126c9565b80601f0160208091040260200160405190810160405280929190818152602001828054610d8b906126c9565b8015610dd85780601f10610dad57610100808354040283529160200191610dd8565b820191906000526020600020905b815481529060010190602001808311610dbb57829003601f168201915b505050918352505060018201546001600160a01b03908116602083015260028301541660408201526003820154606082015260049091015460809091015292915050565b610e4060405180606001604052806060815260200160608152602001606081525090565b6001600160a01b038216600090815260c9602052604090819020815160608101909252805482908290610e72906126c9565b80601f0160208091040260200160405190810160405280929190818152602001828054610e9e906126c9565b8015610eeb5780601f10610ec057610100808354040283529160200191610eeb565b820191906000526020600020905b815481529060010190602001808311610ece57829003601f168201915b50505050508152602001600182018054610f04906126c9565b80601f0160208091040260200160405190810160405280929190818152602001828054610f30906126c9565b8015610f7d5780601f10610f5257610100808354040283529160200191610f7d565b820191906000526020600020905b815481529060010190602001808311610f6057829003601f168201915b50505050508152602001600282018054610f96906126c9565b80601f0160208091040260200160405190810160405280929190818152602001828054610fc2906126c9565b801561100f5780601f10610fe45761010080835404028352916020019161100f565b820191906000526020600020905b815481529060010190602001808311610ff257829003601f168201915b5050505050815250509050919050565b600054610100900460ff161580801561103f5750600054600160ff909116105b806110595750303b158015611059575060005460ff166001145b6110bc5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b60648201526084016104ce565b6000805460ff1916600117905580156110df576000805461ff0019166101001790555b6110e7611bfe565b6110f082611c2d565b8015611137576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498906020015b60405180910390a15b5050565b60cb5460609060008167ffffffffffffffff81111561115c5761115c61232e565b60405190808252806020026020018201604052801561119557816020015b611182612206565b81526020019060019003908161117a5790505b50905060015b8281116112d557600081815260ca60209081526040808320546001600160a01b031680845260cc90925291829020825160a081019093528054919291829082906111e4906126c9565b80601f0160208091040260200160405190810160405280929190818152602001828054611210906126c9565b801561125d5780601f106112325761010080835404028352916020019161125d565b820191906000526020600020905b81548152906001019060200180831161124057829003601f168201915b50505091835250506001828101546001600160a01b03908116602084015260028401541660408301526003830154606083015260049092015460809091015284906112a89085612958565b815181106112b8576112b861288f565b602002602001018190525050806112ce9061296b565b905061119b565b5092915050565b60006112ff6040518060600160405280602f8152602001612d69602f9139611906565b611308856119f1565b611372856001600160a01b0316637dc0d1d06040518163ffffffff1660e01b8152600401602060405180830381865afa158015611349573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061136d9190612872565b6119f1565b600061137f888888611c54565b60405163091a474b60e11b8152600481018790529091506001600160a01b038716906312348e9690602401600060405180830381600087803b1580156113c457600080fd5b505af11580156113d8573d6000803e3d6000fd5b505060405163a843108160e01b8152600481018790526001600160a01b038916925063a84310819150602401600060405180830381600087803b15801561141e57600080fd5b505af1158015611432573d6000803e3d6000fd5b5050604051631482db1d60e21b8152600481018690526001600160a01b038916925063520b6c749150602401600060405180830381600087803b15801561147857600080fd5b505af115801561148c573d6000803e3d6000fd5b50929a9950505050505050505050565b6114a46117ee565b606580546001600160a01b0383166001600160a01b031990911681179091556114d56033546001600160a01b031690565b6001600160a01b03167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e2270060405160405180910390a350565b6001600160a01b038116600090815260ce602090815260409182902080548351818402810184019094528084526060939283018282801561157757602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311611559575b50505050509050919050565b6115a46040518060600160405280602d8152602001612d3c602d9139611906565b6001600160a01b038216600090815260c960205260408082208151606081019092528054829082906115d5906126c9565b80601f0160208091040260200160405190810160405280929190818152602001828054611601906126c9565b801561164e5780601f106116235761010080835404028352916020019161164e565b820191906000526020600020905b81548152906001019060200180831161163157829003601f168201915b50505050508152602001600182018054611667906126c9565b80601f0160208091040260200160405190810160405280929190818152602001828054611693906126c9565b80156116e05780601f106116b5576101008083540402835291602001916116e0565b820191906000526020600020905b8154815290600101906020018083116116c357829003601f168201915b505050505081526020016002820180546116f9906126c9565b80601f0160208091040260200160405190810160405280929190818152602001828054611725906126c9565b80156117725780601f1061174757610100808354040283529160200191611772565b820191906000526020600020905b81548152906001019060200180831161175557829003601f168201915b505050919092525050506001600160a01b038416600090815260c96020526040902090915082906117a382826129cb565b905050826001600160a01b03167f8f91f3b5d20b61744ed591c43346d4514ee5c2ffced5fc3795bb13c6f951814782846040516117e1929190612b0b565b60405180910390a2505050565b6033546001600160a01b03163314610cac5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064016104ce565b6001600160a01b0381166118ac5760405162461bcd60e51b815260206004820152602560248201527f696e76616c696420616365737320636f6e74726f6c206d616e61676572206164604482015264647265737360d81b60648201526084016104ce565b609780546001600160a01b038381166001600160a01b031983168117909355604080519190921680825260208201939093527f66fd58e82f7b31a2a5c30e0888f3093efe4e111b00cd2b0c31fe014601293aa0910161112e565b6097546040516318c5e8ab60e01b81526000916001600160a01b0316906318c5e8ab906119399033908690600401612b95565b602060405180830381865afa158015611956573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061197a9190612bb9565b90508061113757333083604051634a3fa29360e01b81526004016104ce93929190612bdb565b60648111156111375760405162461bcd60e51b815260206004820152601860248201527f506f6f6c2773206e616d6520697320746f6f206c61726765000000000000000060448201526064016104ce565b6001600160a01b0381166102cb576040516342bcdf7f60e11b815260040160405180910390fd5b6040516370a0823160e01b815230600482015260009081906001600160a01b038616906370a0823190602401602060405180830381865afa158015611a61573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a859190612929565b9050611a9c6001600160a01b038616853086611e3b565b6040516370a0823160e01b81523060048201526000906001600160a01b038716906370a0823190602401602060405180830381865afa158015611ae3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b079190612929565b9050611b138282612958565b9695505050505050565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b179052611b6e8482611e73565b611bdf576040516001600160a01b038416602482015260006044820152611bd590859063095ea7b360e01b906064015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152611f1a565b611bdf8482611f1a565b50505050565b606580546001600160a01b03191690556102cb81611ff4565b600054610100900460ff16611c255760405162461bcd60e51b81526004016104ce90612c07565b610cac612046565b600054610100900460ff166102c25760405162461bcd60e51b81526004016104ce90612c07565b6001600160a01b03808216600090815260cc602052604081206001810154919290911615611ce05760405162461bcd60e51b815260206004820152603360248201527f506f6f6c52656769737472793a20506f6f6c20616c7265616479206578697374604482015272399034b7103a3432903234b932b1ba37b93c9760691b60648201526084016104ce565b611cea85856119a0565b60cb60008154611cf99061296b565b9091555060cb546040805160c06020601f8901819004028201810190925260a081018781526000928291908a908a908190850183828082843760009201829052509385525050336020808501919091526001600160a01b038a1660408086018290524360608701524260809096019590955287845260ca825284842080546001600160a01b03191682179055835260cc90525020815191925082918190611da09082612c52565b5060208201516001820180546001600160a01b03199081166001600160a01b039384161790915560408085015160028501805490931690841617909155606084015160038401556080909301516004909201919091559051908616907f53ec2a1d9645c4631472dabcf6d255f5f2971baa64321235b1610d91c692928e90611e299084906124ed565b60405180910390a25095945050505050565b6040516001600160a01b0380851660248301528316604482015260648101829052611bdf9085906323b872dd60e01b90608401611b9e565b6000806000846001600160a01b031684604051611e909190612d0c565b6000604051808303816000865af19150503d8060008114611ecd576040519150601f19603f3d011682016040523d82523d6000602084013e611ed2565b606091505b5091509150818015611efc575080511580611efc575080806020019051810190611efc9190612bb9565b8015611f1157506001600160a01b0385163b15155b95945050505050565b6000611f6f826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166120769092919063ffffffff16565b9050805160001480611f90575080806020019051810190611f909190612bb9565b611fef5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b60648201526084016104ce565b505050565b603380546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b600054610100900460ff1661206d5760405162461bcd60e51b81526004016104ce90612c07565b610cac33611be5565b6060612085848460008561208d565b949350505050565b6060824710156120ee5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b60648201526084016104ce565b600080866001600160a01b0316858760405161210a9190612d0c565b60006040518083038185875af1925050503d8060008114612147576040519150601f19603f3d011682016040523d82523d6000602084013e61214c565b606091505b509150915061215d87838387612168565b979650505050505050565b606083156121d75782516000036121d0576001600160a01b0385163b6121d05760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016104ce565b5081612085565b61208583838151156121ec5781518083602001fd5b8060405162461bcd60e51b81526004016104ce9190612d28565b6040518060a001604052806060815260200160006001600160a01b0316815260200160006001600160a01b0316815260200160008152602001600081525090565b6001600160a01b03811681146102cb57600080fd5b803561226781612247565b919050565b60006020828403121561227e57600080fd5b813561228981612247565b9392505050565b60008083601f8401126122a257600080fd5b50813567ffffffffffffffff8111156122ba57600080fd5b6020830191508360208285010111156122d257600080fd5b9250929050565b6000806000604084860312156122ee57600080fd5b83356122f981612247565b9250602084013567ffffffffffffffff81111561231557600080fd5b61232186828701612290565b9497909650939450505050565b634e487b7160e01b600052604160045260246000fd5b600060e0828403121561235657600080fd5b60405160e0810181811067ffffffffffffffff821117156123795761237961232e565b6040526123858361225c565b81526020830135602082015260408301356040820152606083013560608201526123b16080840161225c565b608082015260a083013560a082015260c083013560c08201528091505092915050565b600080604083850312156123e757600080fd5b82356123f281612247565b9150602083013561240281612247565b809150509250929050565b60005b83811015612428578181015183820152602001612410565b50506000910152565b6000815180845261244981602086016020860161240d565b601f01601f19169290920160200192915050565b6060815260006124706060830186612431565b82810360208401526124828186612431565b90508281036040840152611b138185612431565b6000815160a084526124ab60a0850182612431565b9050602083015160018060a01b038082166020870152806040860151166040870152505060608301516060850152608083015160808501528091505092915050565b6020815260006122896020830184612496565b60008151606084526125156060850182612431565b90506020830151848203602086015261252e8282612431565b91505060408301518482036040860152611f118282612431565b6020815260006122896020830184612500565b600060208083016020845280855180835260408601915060408160051b87010192506020870160005b828110156125b257603f198886030184526125a0858351612496565b94509285019290850190600101612584565b5092979650505050505050565b60008060008060008060a087890312156125d857600080fd5b863567ffffffffffffffff8111156125ef57600080fd5b6125fb89828a01612290565b909750955050602087013561260f81612247565b95989497509495604081013595506060810135946080909101359350915050565b6020808252825182820181905260009190848201906040850190845b818110156126715783516001600160a01b03168352928401929184019160010161264c565b50909695505050505050565b6000806040838503121561269057600080fd5b823561269b81612247565b9150602083013567ffffffffffffffff8111156126b757600080fd5b83016060818603121561240257600080fd5b600181811c908216806126dd57607f821691505b6020821081036126fd57634e487b7160e01b600052602260045260246000fd5b50919050565b601f821115611fef576000816000526020600020601f850160051c8101602086101561272c5750805b601f850160051c820191505b8181101561274b57828155600101612738565b505050505050565b600019600383901b1c191660019190911b1790565b67ffffffffffffffff8311156127805761278061232e565b6127948361278e83546126c9565b83612703565b6000601f8411600181146127c257600085156127b05750838201355b6127ba8682612753565b84555061281c565b600083815260209020601f19861690835b828110156127f357868501358255602094850194600190920191016127d3565b50868210156128105760001960f88860031b161c19848701351681555b505060018560011b0183555b5050505050565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b60408152600061285f6040830186612431565b8281036020840152611b13818587612823565b60006020828403121561288457600080fd5b815161228981612247565b634e487b7160e01b600052603260045260246000fd5b604080825283519082018190526000906020906060840190828701845b828110156128e75781516001600160a01b0316845292840192908401906001016128c2565b5050508381038285015284518082528583019183019060005b8181101561291c57835183529284019291840191600101612900565b5090979650505050505050565b60006020828403121561293b57600080fd5b5051919050565b634e487b7160e01b600052601160045260246000fd5b81810381811115610ada57610ada612942565b60006001820161297d5761297d612942565b5060010190565b6000808335601e1984360301811261299b57600080fd5b83018035915067ffffffffffffffff8211156129b657600080fd5b6020019150368190038213156122d257600080fd5b6129d58283612984565b67ffffffffffffffff8111156129ed576129ed61232e565b612a01816129fb85546126c9565b85612703565b6000601f821160018114612a2f5760008315612a1d5750838201355b612a278482612753565b865550612a89565b600085815260209020601f19841690835b82811015612a605786850135825560209485019460019092019101612a40565b5084821015612a7d5760001960f88660031b161c19848701351681555b505060018360011b0185555b50505050612a9a6020830183612984565b612aa8818360018601612768565b5050612ab76040830183612984565b611bdf818360028601612768565b6000808335601e19843603018112612adc57600080fd5b830160208101925035905067ffffffffffffffff811115612afc57600080fd5b8036038213156122d257600080fd5b604081526000612b1e6040830185612500565b8281036020840152612b308485612ac5565b60608352612b42606084018284612823565b915050612b526020860186612ac5565b8383036020850152612b65838284612823565b92505050612b766040860186612ac5565b8383036040850152612b89838284612823565b98975050505050505050565b6001600160a01b038316815260406020820181905260009061208590830184612431565b600060208284031215612bcb57600080fd5b8151801515811461228957600080fd5b6001600160a01b03848116825283166020820152606060408201819052600090611f1190830184612431565b6020808252602b908201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960408201526a6e697469616c697a696e6760a81b606082015260800190565b815167ffffffffffffffff811115612c6c57612c6c61232e565b612c8081612c7a84546126c9565b84612703565b602080601f831160018114612caf5760008415612c9d5750858301515b612ca78582612753565b86555061274b565b600085815260208120601f198616915b82811015612cde57888601518255948401946001909101908401612cbf565b5085821015612cfc5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b60008251612d1e81846020870161240d565b9190910192915050565b602081526000612289602083018461243156fe757064617465506f6f6c4d6574616461746128616464726573732c56656e7573506f6f6c4d6574614461746129616464506f6f6c28737472696e672c616464726573732c75696e743235362c75696e743235362c75696e7432353629a264697066735822122034ad52241cc9e9ea0aeb98857c2557789aa6cc8170d34e822ecf91a04ec63ee864736f6c63430008190033", + "devdoc": { + "author": "Venus", + "events": { + "Initialized(uint8)": { + "details": "Triggered when the contract has been initialized or reinitialized." + } + }, + "kind": "dev", + "methods": { + "acceptOwnership()": { + "details": "The new owner accepts the ownership transfer." + }, + "addMarket((address,uint256,uint256,uint256,address,uint256,uint256))": { + "custom:error": "ZeroAddressNotAllowed is thrown when vToken address is zeroZeroAddressNotAllowed is thrown when vTokenReceiver address is zero", + "params": { + "input": "The structure describing the parameters for adding a market to a pool" + } + }, + "addPool(string,address,uint256,uint256,uint256)": { + "custom:error": "ZeroAddressNotAllowed is thrown when Comptroller address is zeroZeroAddressNotAllowed is thrown when price oracle address is zero", + "details": "Price oracle must be configured before adding a pool", + "params": { + "closeFactor": "The pool's close factor (scaled by 1e18)", + "comptroller": "Pool's Comptroller contract", + "liquidationIncentive": "The pool's liquidation incentive (scaled by 1e18)", + "minLiquidatableCollateral": "Minimal collateral for regular (non-batch) liquidations flow", + "name": "The name of the pool" + }, + "returns": { + "index": "The index of the registered Venus pool" + } + }, + "constructor": { + "custom:oz-upgrades-unsafe-allow": "constructor" + }, + "getAllPools()": { + "details": "This function is not designed to be called in a transaction: it is too gas-intensive", + "returns": { + "_0": "A list of all pools within PoolRegistry, with details for each pool" + } + }, + "getPoolByComptroller(address)": { + "params": { + "comptroller": "The comptroller proxy address associated to the pool" + }, + "returns": { + "_0": "Returns Venus pool" + } + }, + "getVenusPoolMetadata(address)": { + "params": { + "comptroller": "comptroller of Venus pool" + }, + "returns": { + "_0": "Returns Metadata of Venus pool" + } + }, + "initialize(address)": { + "params": { + "accessControlManager_": "AccessControlManager contract address" + } + }, + "owner()": { + "details": "Returns the address of the current owner." + }, + "pendingOwner()": { + "details": "Returns the address of the pending owner." + }, + "renounceOwnership()": { + "details": "Leaves the contract without owner. It will not be possible to call `onlyOwner` functions. Can only be called by the current owner. NOTE: Renouncing ownership will leave the contract without an owner, thereby disabling any functionality that is only available to the owner." + }, + "setAccessControlManager(address)": { + "custom:access": "Only Governance", + "custom:event": "Emits NewAccessControlManager event", + "details": "Admin function to set address of AccessControlManager", + "params": { + "accessControlManager_": "The new address of the AccessControlManager" + } + }, + "setPoolName(address,string)": { + "params": { + "comptroller": "Pool's Comptroller", + "name": "New pool name" + } + }, + "transferOwnership(address)": { + "details": "Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one. Can only be called by the current owner." + }, + "updatePoolMetadata(address,(string,string,string))": { + "params": { + "comptroller": "Pool's Comptroller", + "metadata_": "New pool metadata" + } + } + }, + "stateVariables": { + "_numberOfPools": { + "details": "Total number of pools created." + }, + "_poolByComptroller": { + "details": "Maps comptroller address to Venus pool Index." + }, + "_poolsByID": { + "details": "Maps pool ID to pool's comptroller address" + }, + "_supportedPools": { + "details": "Maps asset to list of supported pools." + }, + "_vTokens": { + "details": "Maps pool's comptroller address to asset to vToken." + } + }, + "title": "PoolRegistry", + "version": 1 + }, + "userdoc": { + "errors": { + "Unauthorized(address,address,string)": [ + { + "notice": "Thrown when the action is prohibited by AccessControlManager" + } + ], + "ZeroAddressNotAllowed()": [ + { + "notice": "Thrown if the supplied address is a zero address where it is not allowed" + } + ] + }, + "events": { + "MarketAdded(address,address)": { + "notice": "Emitted when a Market is added to the pool." + }, + "NewAccessControlManager(address,address)": { + "notice": "Emitted when access control manager contract address is changed" + }, + "PoolMetadataUpdated(address,(string,string,string),(string,string,string))": { + "notice": "Emitted when a pool metadata is updated." + }, + "PoolNameSet(address,string,string)": { + "notice": "Emitted when a pool name is set." + }, + "PoolRegistered(address,(string,address,address,uint256,uint256))": { + "notice": "Emitted when a new Venus pool is added to the directory." + } + }, + "kind": "user", + "methods": { + "accessControlManager()": { + "notice": "Returns the address of the access control manager contract" + }, + "addMarket((address,uint256,uint256,uint256,address,uint256,uint256))": { + "notice": "Add a market to an existing pool and then mint to provide initial supply" + }, + "addPool(string,address,uint256,uint256,uint256)": { + "notice": "Adds a new Venus pool to the directory" + }, + "getAllPools()": { + "notice": "Returns arrays of all Venus pools' data" + }, + "getPoolsSupportedByAsset(address)": { + "notice": "Get the addresss of the Pools supported that include a market for the provided asset" + }, + "getVTokenForAsset(address,address)": { + "notice": "Get the address of the VToken contract in the Pool where the underlying token is the provided asset" + }, + "initialize(address)": { + "notice": "Initializes the deployer to owner" + }, + "metadata(address)": { + "notice": "Maps pool's comptroller address to metadata." + }, + "setAccessControlManager(address)": { + "notice": "Sets the address of AccessControlManager" + }, + "setPoolName(address,string)": { + "notice": "Modify existing Venus pool name" + }, + "updatePoolMetadata(address,(string,string,string))": { + "notice": "Update metadata of an existing pool" + } + }, + "notice": "The Isolated Pools architecture centers around the `PoolRegistry` contract. The `PoolRegistry` maintains a directory of isolated lending pools and can perform actions like creating and registering new pools, adding new markets to existing pools, setting and updating the pool's required metadata, and providing the getter methods to get information on the pools. Isolated lending has three main components: PoolRegistry, pools, and markets. The PoolRegistry is responsible for managing pools. It can create new pools, update pool metadata and manage markets within pools. PoolRegistry contains getter methods to get the details of any existing pool like `getVTokenForAsset` and `getPoolsSupportedByAsset`. It also contains methods for updating pool metadata (`updatePoolMetadata`) and setting pool name (`setPoolName`). The directory of pools is managed through two mappings: `_poolByComptroller` which is a hashmap with the comptroller address as the key and `VenusPool` as the value and `_poolsByID` which is an array of comptroller addresses. Individual pools can be accessed by calling `getPoolByComptroller` with the pool's comptroller address. `_poolsByID` is used to iterate through all of the pools. PoolRegistry also contains a map of asset addresses called `_supportedPools` that maps to an array of assets suppored by each pool. This array of pools by asset is retrieved by calling `getPoolsSupportedByAsset`. PoolRegistry registers new isolated pools in the directory with the `createRegistryPool` method. Isolated pools are composed of independent markets with specific assets and custom risk management configurations according to their markets.", + "version": 1 + }, + "storageLayout": { + "storage": [ + { + "astId": 292, + "contract": "contracts/Pool/PoolRegistry.sol:PoolRegistry", + "label": "_initialized", + "offset": 0, + "slot": "0", + "type": "t_uint8" + }, + { + "astId": 295, + "contract": "contracts/Pool/PoolRegistry.sol:PoolRegistry", + "label": "_initializing", + "offset": 1, + "slot": "0", + "type": "t_bool" + }, + { + "astId": 1562, + "contract": "contracts/Pool/PoolRegistry.sol:PoolRegistry", + "label": "__gap", + "offset": 0, + "slot": "1", + "type": "t_array(t_uint256)50_storage" + }, + { + "astId": 164, + "contract": "contracts/Pool/PoolRegistry.sol:PoolRegistry", + "label": "_owner", + "offset": 0, + "slot": "51", + "type": "t_address" + }, + { + "astId": 284, + "contract": "contracts/Pool/PoolRegistry.sol:PoolRegistry", + "label": "__gap", + "offset": 0, + "slot": "52", + "type": "t_array(t_uint256)49_storage" + }, + { + "astId": 57, + "contract": "contracts/Pool/PoolRegistry.sol:PoolRegistry", + "label": "_pendingOwner", + "offset": 0, + "slot": "101", + "type": "t_address" + }, + { + "astId": 151, + "contract": "contracts/Pool/PoolRegistry.sol:PoolRegistry", + "label": "__gap", + "offset": 0, + "slot": "102", + "type": "t_array(t_uint256)49_storage" + }, + { + "astId": 7293, + "contract": "contracts/Pool/PoolRegistry.sol:PoolRegistry", + "label": "_accessControlManager", + "offset": 0, + "slot": "151", + "type": "t_contract(IAccessControlManagerV8)7478" + }, + { + "astId": 7298, + "contract": "contracts/Pool/PoolRegistry.sol:PoolRegistry", + "label": "__gap", + "offset": 0, + "slot": "152", + "type": "t_array(t_uint256)49_storage" + }, + { + "astId": 30121, + "contract": "contracts/Pool/PoolRegistry.sol:PoolRegistry", + "label": "metadata", + "offset": 0, + "slot": "201", + "type": "t_mapping(t_address,t_struct(VenusPoolMetaData)30867_storage)" + }, + { + "astId": 30126, + "contract": "contracts/Pool/PoolRegistry.sol:PoolRegistry", + "label": "_poolsByID", + "offset": 0, + "slot": "202", + "type": "t_mapping(t_uint256,t_address)" + }, + { + "astId": 30129, + "contract": "contracts/Pool/PoolRegistry.sol:PoolRegistry", + "label": "_numberOfPools", + "offset": 0, + "slot": "203", + "type": "t_uint256" + }, + { + "astId": 30135, + "contract": "contracts/Pool/PoolRegistry.sol:PoolRegistry", + "label": "_poolByComptroller", + "offset": 0, + "slot": "204", + "type": "t_mapping(t_address,t_struct(VenusPool)30859_storage)" + }, + { + "astId": 30142, + "contract": "contracts/Pool/PoolRegistry.sol:PoolRegistry", + "label": "_vTokens", + "offset": 0, + "slot": "205", + "type": "t_mapping(t_address,t_mapping(t_address,t_address))" + }, + { + "astId": 30148, + "contract": "contracts/Pool/PoolRegistry.sol:PoolRegistry", + "label": "_supportedPools", + "offset": 0, + "slot": "206", + "type": "t_mapping(t_address,t_array(t_address)dyn_storage)" + } + ], + "types": { + "t_address": { + "encoding": "inplace", + "label": "address", + "numberOfBytes": "20" + }, + "t_array(t_address)dyn_storage": { + "base": "t_address", + "encoding": "dynamic_array", + "label": "address[]", + "numberOfBytes": "32" + }, + "t_array(t_uint256)49_storage": { + "base": "t_uint256", + "encoding": "inplace", + "label": "uint256[49]", + "numberOfBytes": "1568" + }, + "t_array(t_uint256)50_storage": { + "base": "t_uint256", + "encoding": "inplace", + "label": "uint256[50]", + "numberOfBytes": "1600" + }, + "t_bool": { + "encoding": "inplace", + "label": "bool", + "numberOfBytes": "1" + }, + "t_contract(IAccessControlManagerV8)7478": { + "encoding": "inplace", + "label": "contract IAccessControlManagerV8", + "numberOfBytes": "20" + }, + "t_mapping(t_address,t_address)": { + "encoding": "mapping", + "key": "t_address", + "label": "mapping(address => address)", + "numberOfBytes": "32", + "value": "t_address" + }, + "t_mapping(t_address,t_array(t_address)dyn_storage)": { + "encoding": "mapping", + "key": "t_address", + "label": "mapping(address => address[])", + "numberOfBytes": "32", + "value": "t_array(t_address)dyn_storage" + }, + "t_mapping(t_address,t_mapping(t_address,t_address))": { + "encoding": "mapping", + "key": "t_address", + "label": "mapping(address => mapping(address => address))", + "numberOfBytes": "32", + "value": "t_mapping(t_address,t_address)" + }, + "t_mapping(t_address,t_struct(VenusPool)30859_storage)": { + "encoding": "mapping", + "key": "t_address", + "label": "mapping(address => struct PoolRegistryInterface.VenusPool)", + "numberOfBytes": "32", + "value": "t_struct(VenusPool)30859_storage" + }, + "t_mapping(t_address,t_struct(VenusPoolMetaData)30867_storage)": { + "encoding": "mapping", + "key": "t_address", + "label": "mapping(address => struct PoolRegistryInterface.VenusPoolMetaData)", + "numberOfBytes": "32", + "value": "t_struct(VenusPoolMetaData)30867_storage" + }, + "t_mapping(t_uint256,t_address)": { + "encoding": "mapping", + "key": "t_uint256", + "label": "mapping(uint256 => address)", + "numberOfBytes": "32", + "value": "t_address" + }, + "t_string_storage": { + "encoding": "bytes", + "label": "string", + "numberOfBytes": "32" + }, + "t_struct(VenusPool)30859_storage": { + "encoding": "inplace", + "label": "struct PoolRegistryInterface.VenusPool", + "members": [ + { + "astId": 30850, + "contract": "contracts/Pool/PoolRegistry.sol:PoolRegistry", + "label": "name", + "offset": 0, + "slot": "0", + "type": "t_string_storage" + }, + { + "astId": 30852, + "contract": "contracts/Pool/PoolRegistry.sol:PoolRegistry", + "label": "creator", + "offset": 0, + "slot": "1", + "type": "t_address" + }, + { + "astId": 30854, + "contract": "contracts/Pool/PoolRegistry.sol:PoolRegistry", + "label": "comptroller", + "offset": 0, + "slot": "2", + "type": "t_address" + }, + { + "astId": 30856, + "contract": "contracts/Pool/PoolRegistry.sol:PoolRegistry", + "label": "blockPosted", + "offset": 0, + "slot": "3", + "type": "t_uint256" + }, + { + "astId": 30858, + "contract": "contracts/Pool/PoolRegistry.sol:PoolRegistry", + "label": "timestampPosted", + "offset": 0, + "slot": "4", + "type": "t_uint256" + } + ], + "numberOfBytes": "160" + }, + "t_struct(VenusPoolMetaData)30867_storage": { + "encoding": "inplace", + "label": "struct PoolRegistryInterface.VenusPoolMetaData", + "members": [ + { + "astId": 30862, + "contract": "contracts/Pool/PoolRegistry.sol:PoolRegistry", + "label": "category", + "offset": 0, + "slot": "0", + "type": "t_string_storage" + }, + { + "astId": 30864, + "contract": "contracts/Pool/PoolRegistry.sol:PoolRegistry", + "label": "logoURL", + "offset": 0, + "slot": "1", + "type": "t_string_storage" + }, + { + "astId": 30866, + "contract": "contracts/Pool/PoolRegistry.sol:PoolRegistry", + "label": "description", + "offset": 0, + "slot": "2", + "type": "t_string_storage" + } + ], + "numberOfBytes": "96" + }, + "t_uint256": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + }, + "t_uint8": { + "encoding": "inplace", + "label": "uint8", + "numberOfBytes": "1" + } + } + } +} diff --git a/deployments/bsctestnet/SpokePoolRegistry_Proxy.json b/deployments/bsctestnet/SpokePoolRegistry_Proxy.json new file mode 100644 index 000000000..19e7ab803 --- /dev/null +++ b/deployments/bsctestnet/SpokePoolRegistry_Proxy.json @@ -0,0 +1,262 @@ +{ + "address": "0xeAA45288d804971e5a76f33559e629F5b2b1Cb8B", + "abi": [ + { + "inputs": [ + { + "internalType": "address", + "name": "_logic", + "type": "address" + }, + { + "internalType": "address", + "name": "admin_", + "type": "address" + }, + { + "internalType": "bytes", + "name": "_data", + "type": "bytes" + } + ], + "stateMutability": "payable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "previousAdmin", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "newAdmin", + "type": "address" + } + ], + "name": "AdminChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "beacon", + "type": "address" + } + ], + "name": "BeaconUpgraded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "implementation", + "type": "address" + } + ], + "name": "Upgraded", + "type": "event" + }, + { + "stateMutability": "payable", + "type": "fallback" + }, + { + "inputs": [], + "name": "admin", + "outputs": [ + { + "internalType": "address", + "name": "admin_", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "implementation", + "outputs": [ + { + "internalType": "address", + "name": "implementation_", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newImplementation", + "type": "address" + } + ], + "name": "upgradeTo", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newImplementation", + "type": "address" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "upgradeToAndCall", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "stateMutability": "payable", + "type": "receive" + } + ], + "transactionHash": "0x091063dfd1125f31dd14d7c31f494d352edd25c3f48f01869463f2ffe0624d9c", + "receipt": { + "to": null, + "from": "0x33C6476F88eeA28D7E7900F759B4597704Ef95B7", + "contractAddress": "0xeAA45288d804971e5a76f33559e629F5b2b1Cb8B", + "transactionIndex": 4, + "gasUsed": "605368", + "logsBloom": "0x00000000000000000000000000000000400000000000000000800000000000000004000000040000000000000000000000000000000000000000000000008000000000000000000000000000000002000001000002000000000000000000000000000000020000000000000000000800000000800000000000000000000000400000000000000000000000400000000010000000000080008000000000800000000000000040000000000000000400000000000000800000000000000000000000000020200000000000000000040000000000000400000000000000000020000000000000000000000000000000000000000800000200000000000000000000", + "blockHash": "0x1327761aa714fe841718f40431971a18c5f2a29187e3311b3ac4dca1092e7c61", + "transactionHash": "0x091063dfd1125f31dd14d7c31f494d352edd25c3f48f01869463f2ffe0624d9c", + "logs": [ + { + "transactionIndex": 4, + "blockNumber": 129844919, + "transactionHash": "0x091063dfd1125f31dd14d7c31f494d352edd25c3f48f01869463f2ffe0624d9c", + "address": "0xeAA45288d804971e5a76f33559e629F5b2b1Cb8B", + "topics": [ + "0xbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b", + "0x000000000000000000000000266e65b6d81802f2bafa0c07288a62891172dad8" + ], + "data": "0x", + "logIndex": 53, + "blockHash": "0x1327761aa714fe841718f40431971a18c5f2a29187e3311b3ac4dca1092e7c61" + }, + { + "transactionIndex": 4, + "blockNumber": 129844919, + "transactionHash": "0x091063dfd1125f31dd14d7c31f494d352edd25c3f48f01869463f2ffe0624d9c", + "address": "0xeAA45288d804971e5a76f33559e629F5b2b1Cb8B", + "topics": [ + "0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x00000000000000000000000033c6476f88eea28d7e7900f759b4597704ef95b7" + ], + "data": "0x", + "logIndex": 54, + "blockHash": "0x1327761aa714fe841718f40431971a18c5f2a29187e3311b3ac4dca1092e7c61" + }, + { + "transactionIndex": 4, + "blockNumber": 129844919, + "transactionHash": "0x091063dfd1125f31dd14d7c31f494d352edd25c3f48f01869463f2ffe0624d9c", + "address": "0xeAA45288d804971e5a76f33559e629F5b2b1Cb8B", + "topics": ["0x66fd58e82f7b31a2a5c30e0888f3093efe4e111b00cd2b0c31fe014601293aa0"], + "data": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000045f8a08f534f34a97187626e05d4b6648eeaa9aa", + "logIndex": 55, + "blockHash": "0x1327761aa714fe841718f40431971a18c5f2a29187e3311b3ac4dca1092e7c61" + }, + { + "transactionIndex": 4, + "blockNumber": 129844919, + "transactionHash": "0x091063dfd1125f31dd14d7c31f494d352edd25c3f48f01869463f2ffe0624d9c", + "address": "0xeAA45288d804971e5a76f33559e629F5b2b1Cb8B", + "topics": ["0x7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498"], + "data": "0x0000000000000000000000000000000000000000000000000000000000000001", + "logIndex": 56, + "blockHash": "0x1327761aa714fe841718f40431971a18c5f2a29187e3311b3ac4dca1092e7c61" + }, + { + "transactionIndex": 4, + "blockNumber": 129844919, + "transactionHash": "0x091063dfd1125f31dd14d7c31f494d352edd25c3f48f01869463f2ffe0624d9c", + "address": "0xeAA45288d804971e5a76f33559e629F5b2b1Cb8B", + "topics": ["0x7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f"], + "data": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000007877ffd62649b6a1557b55d4c20fcbab17344c91", + "logIndex": 57, + "blockHash": "0x1327761aa714fe841718f40431971a18c5f2a29187e3311b3ac4dca1092e7c61" + } + ], + "blockNumber": 129844919, + "cumulativeGasUsed": "6713259", + "status": 1, + "byzantium": true + }, + "args": [ + "0x266e65b6D81802F2BAFa0c07288A62891172DAD8", + "0x7877ffd62649b6a1557b55d4c20fcbab17344c91", + "0xc4d66de800000000000000000000000045f8a08f534f34a97187626e05d4b6648eeaa9aa" + ], + "numDeployments": 1, + "solcInputHash": "02cfe3974aa10474c22406a3f2870e89", + "metadata": "{\"compiler\":{\"version\":\"0.8.25+commit.b61c2a91\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_logic\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"admin_\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"_data\",\"type\":\"bytes\"}],\"stateMutability\":\"payable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"previousAdmin\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"newAdmin\",\"type\":\"address\"}],\"name\":\"AdminChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"beacon\",\"type\":\"address\"}],\"name\":\"BeaconUpgraded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"implementation\",\"type\":\"address\"}],\"name\":\"Upgraded\",\"type\":\"event\"},{\"stateMutability\":\"payable\",\"type\":\"fallback\"},{\"inputs\":[],\"name\":\"admin\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"admin_\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"implementation\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"implementation_\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newImplementation\",\"type\":\"address\"}],\"name\":\"upgradeTo\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newImplementation\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"upgradeToAndCall\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"stateMutability\":\"payable\",\"type\":\"receive\"}],\"devdoc\":{\"details\":\"This contract implements a proxy that is upgradeable by an admin. To avoid https://medium.com/nomic-labs-blog/malicious-backdoors-in-ethereum-proxies-62629adf3357[proxy selector clashing], which can potentially be used in an attack, this contract uses the https://blog.openzeppelin.com/the-transparent-proxy-pattern/[transparent proxy pattern]. This pattern implies two things that go hand in hand: 1. If any account other than the admin calls the proxy, the call will be forwarded to the implementation, even if that call matches one of the admin functions exposed by the proxy itself. 2. If the admin calls the proxy, it can access the admin functions, but its calls will never be forwarded to the implementation. If the admin tries to call a function on the implementation it will fail with an error that says \\\"admin cannot fallback to proxy target\\\". These properties mean that the admin account can only be used for admin actions like upgrading the proxy or changing the admin, so it's best if it's a dedicated account that is not used for anything else. This will avoid headaches due to sudden errors when trying to call a function from the proxy implementation. Our recommendation is for the dedicated account to be an instance of the {ProxyAdmin} contract. If set up this way, you should think of the `ProxyAdmin` instance as the real administrative interface of your proxy.\",\"events\":{\"AdminChanged(address,address)\":{\"details\":\"Emitted when the admin account has changed.\"},\"BeaconUpgraded(address)\":{\"details\":\"Emitted when the beacon is upgraded.\"},\"Upgraded(address)\":{\"details\":\"Emitted when the implementation is upgraded.\"}},\"kind\":\"dev\",\"methods\":{\"admin()\":{\"details\":\"Returns the current admin. NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyAdmin}. TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call. `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103`\"},\"constructor\":{\"details\":\"Initializes an upgradeable proxy managed by `_admin`, backed by the implementation at `_logic`, and optionally initialized with `_data` as explained in {ERC1967Proxy-constructor}.\"},\"implementation()\":{\"details\":\"Returns the current implementation. NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyImplementation}. TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call. `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc`\"},\"upgradeTo(address)\":{\"details\":\"Upgrade the implementation of the proxy. NOTE: Only the admin can call this function. See {ProxyAdmin-upgrade}.\"},\"upgradeToAndCall(address,bytes)\":{\"details\":\"Upgrade the implementation of the proxy, and then call a function from the new implementation as specified by `data`, which should be an encoded function call. This is useful to initialize new storage variables in the proxied contract. NOTE: Only the admin can call this function. See {ProxyAdmin-upgradeAndCall}.\"}},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"hardhat-deploy/solc_0.8/proxy/OptimizedTransparentUpgradeableProxy.sol\":\"OptimizedTransparentUpgradeableProxy\"},\"evmVersion\":\"paris\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\",\"useLiteralContent\":true},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[]},\"sources\":{\"hardhat-deploy/solc_0.8/openzeppelin/interfaces/draft-IERC1822.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts (last updated v4.5.0-rc.0) (interfaces/draft-IERC1822.sol)\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev ERC1822: Universal Upgradeable Proxy Standard (UUPS) documents a method for upgradeability through a simplified\\n * proxy whose upgrades are fully controlled by the current implementation.\\n */\\ninterface IERC1822Proxiable {\\n /**\\n * @dev Returns the storage slot that the proxiable contract assumes is being used to store the implementation\\n * address.\\n *\\n * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks\\n * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this\\n * function revert if invoked through a proxy.\\n */\\n function proxiableUUID() external view returns (bytes32);\\n}\\n\",\"keccak256\":\"0x93b4e21c931252739a1ec13ea31d3d35a5c068be3163ccab83e4d70c40355f03\",\"license\":\"MIT\"},\"hardhat-deploy/solc_0.8/openzeppelin/proxy/ERC1967/ERC1967Proxy.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts v4.4.1 (proxy/ERC1967/ERC1967Proxy.sol)\\n\\npragma solidity ^0.8.0;\\n\\nimport \\\"../Proxy.sol\\\";\\nimport \\\"./ERC1967Upgrade.sol\\\";\\n\\n/**\\n * @dev This contract implements an upgradeable proxy. It is upgradeable because calls are delegated to an\\n * implementation address that can be changed. This address is stored in storage in the location specified by\\n * https://eips.ethereum.org/EIPS/eip-1967[EIP1967], so that it doesn't conflict with the storage layout of the\\n * implementation behind the proxy.\\n */\\ncontract ERC1967Proxy is Proxy, ERC1967Upgrade {\\n /**\\n * @dev Initializes the upgradeable proxy with an initial implementation specified by `_logic`.\\n *\\n * If `_data` is nonempty, it's used as data in a delegate call to `_logic`. This will typically be an encoded\\n * function call, and allows initializating the storage of the proxy like a Solidity constructor.\\n */\\n constructor(address _logic, bytes memory _data) payable {\\n assert(_IMPLEMENTATION_SLOT == bytes32(uint256(keccak256(\\\"eip1967.proxy.implementation\\\")) - 1));\\n _upgradeToAndCall(_logic, _data, false);\\n }\\n\\n /**\\n * @dev Returns the current implementation address.\\n */\\n function _implementation() internal view virtual override returns (address impl) {\\n return ERC1967Upgrade._getImplementation();\\n }\\n}\\n\",\"keccak256\":\"0x6309f9f39dc6f4f45a24f296543867aa358e32946cd6b2874627a996d606b3a0\",\"license\":\"MIT\"},\"hardhat-deploy/solc_0.8/openzeppelin/proxy/ERC1967/ERC1967Upgrade.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts (last updated v4.5.0-rc.0) (proxy/ERC1967/ERC1967Upgrade.sol)\\n\\npragma solidity ^0.8.2;\\n\\nimport \\\"../beacon/IBeacon.sol\\\";\\nimport \\\"../../interfaces/draft-IERC1822.sol\\\";\\nimport \\\"../../utils/Address.sol\\\";\\nimport \\\"../../utils/StorageSlot.sol\\\";\\n\\n/**\\n * @dev This abstract contract provides getters and event emitting update functions for\\n * https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots.\\n *\\n * _Available since v4.1._\\n *\\n * @custom:oz-upgrades-unsafe-allow delegatecall\\n */\\nabstract contract ERC1967Upgrade {\\n // This is the keccak-256 hash of \\\"eip1967.proxy.rollback\\\" subtracted by 1\\n bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143;\\n\\n /**\\n * @dev Storage slot with the address of the current implementation.\\n * This is the keccak-256 hash of \\\"eip1967.proxy.implementation\\\" subtracted by 1, and is\\n * validated in the constructor.\\n */\\n bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;\\n\\n /**\\n * @dev Emitted when the implementation is upgraded.\\n */\\n event Upgraded(address indexed implementation);\\n\\n /**\\n * @dev Returns the current implementation address.\\n */\\n function _getImplementation() internal view returns (address) {\\n return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;\\n }\\n\\n /**\\n * @dev Stores a new address in the EIP1967 implementation slot.\\n */\\n function _setImplementation(address newImplementation) private {\\n require(Address.isContract(newImplementation), \\\"ERC1967: new implementation is not a contract\\\");\\n StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;\\n }\\n\\n /**\\n * @dev Perform implementation upgrade\\n *\\n * Emits an {Upgraded} event.\\n */\\n function _upgradeTo(address newImplementation) internal {\\n _setImplementation(newImplementation);\\n emit Upgraded(newImplementation);\\n }\\n\\n /**\\n * @dev Perform implementation upgrade with additional setup call.\\n *\\n * Emits an {Upgraded} event.\\n */\\n function _upgradeToAndCall(\\n address newImplementation,\\n bytes memory data,\\n bool forceCall\\n ) internal {\\n _upgradeTo(newImplementation);\\n if (data.length > 0 || forceCall) {\\n Address.functionDelegateCall(newImplementation, data);\\n }\\n }\\n\\n /**\\n * @dev Perform implementation upgrade with security checks for UUPS proxies, and additional setup call.\\n *\\n * Emits an {Upgraded} event.\\n */\\n function _upgradeToAndCallUUPS(\\n address newImplementation,\\n bytes memory data,\\n bool forceCall\\n ) internal {\\n // Upgrades from old implementations will perform a rollback test. This test requires the new\\n // implementation to upgrade back to the old, non-ERC1822 compliant, implementation. Removing\\n // this special case will break upgrade paths from old UUPS implementation to new ones.\\n if (StorageSlot.getBooleanSlot(_ROLLBACK_SLOT).value) {\\n _setImplementation(newImplementation);\\n } else {\\n try IERC1822Proxiable(newImplementation).proxiableUUID() returns (bytes32 slot) {\\n require(slot == _IMPLEMENTATION_SLOT, \\\"ERC1967Upgrade: unsupported proxiableUUID\\\");\\n } catch {\\n revert(\\\"ERC1967Upgrade: new implementation is not UUPS\\\");\\n }\\n _upgradeToAndCall(newImplementation, data, forceCall);\\n }\\n }\\n\\n /**\\n * @dev Storage slot with the admin of the contract.\\n * This is the keccak-256 hash of \\\"eip1967.proxy.admin\\\" subtracted by 1, and is\\n * validated in the constructor.\\n */\\n bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;\\n\\n /**\\n * @dev Emitted when the admin account has changed.\\n */\\n event AdminChanged(address previousAdmin, address newAdmin);\\n\\n /**\\n * @dev Returns the current admin.\\n */\\n function _getAdmin() internal view virtual returns (address) {\\n return StorageSlot.getAddressSlot(_ADMIN_SLOT).value;\\n }\\n\\n /**\\n * @dev Stores a new address in the EIP1967 admin slot.\\n */\\n function _setAdmin(address newAdmin) private {\\n require(newAdmin != address(0), \\\"ERC1967: new admin is the zero address\\\");\\n StorageSlot.getAddressSlot(_ADMIN_SLOT).value = newAdmin;\\n }\\n\\n /**\\n * @dev Changes the admin of the proxy.\\n *\\n * Emits an {AdminChanged} event.\\n */\\n function _changeAdmin(address newAdmin) internal {\\n emit AdminChanged(_getAdmin(), newAdmin);\\n _setAdmin(newAdmin);\\n }\\n\\n /**\\n * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.\\n * This is bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)) and is validated in the constructor.\\n */\\n bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;\\n\\n /**\\n * @dev Emitted when the beacon is upgraded.\\n */\\n event BeaconUpgraded(address indexed beacon);\\n\\n /**\\n * @dev Returns the current beacon.\\n */\\n function _getBeacon() internal view returns (address) {\\n return StorageSlot.getAddressSlot(_BEACON_SLOT).value;\\n }\\n\\n /**\\n * @dev Stores a new beacon in the EIP1967 beacon slot.\\n */\\n function _setBeacon(address newBeacon) private {\\n require(Address.isContract(newBeacon), \\\"ERC1967: new beacon is not a contract\\\");\\n require(Address.isContract(IBeacon(newBeacon).implementation()), \\\"ERC1967: beacon implementation is not a contract\\\");\\n StorageSlot.getAddressSlot(_BEACON_SLOT).value = newBeacon;\\n }\\n\\n /**\\n * @dev Perform beacon upgrade with additional setup call. Note: This upgrades the address of the beacon, it does\\n * not upgrade the implementation contained in the beacon (see {UpgradeableBeacon-_setImplementation} for that).\\n *\\n * Emits a {BeaconUpgraded} event.\\n */\\n function _upgradeBeaconToAndCall(\\n address newBeacon,\\n bytes memory data,\\n bool forceCall\\n ) internal {\\n _setBeacon(newBeacon);\\n emit BeaconUpgraded(newBeacon);\\n if (data.length > 0 || forceCall) {\\n Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data);\\n }\\n }\\n}\\n\",\"keccak256\":\"0x17668652127feebed0ce8d9431ef95ccc8c4292f03e3b8cf06c6ca16af396633\",\"license\":\"MIT\"},\"hardhat-deploy/solc_0.8/openzeppelin/proxy/Proxy.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts (last updated v4.5.0-rc.0) (proxy/Proxy.sol)\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM\\n * instruction `delegatecall`. We refer to the second contract as the _implementation_ behind the proxy, and it has to\\n * be specified by overriding the virtual {_implementation} function.\\n *\\n * Additionally, delegation to the implementation can be triggered manually through the {_fallback} function, or to a\\n * different contract through the {_delegate} function.\\n *\\n * The success and return data of the delegated call will be returned back to the caller of the proxy.\\n */\\nabstract contract Proxy {\\n /**\\n * @dev Delegates the current call to `implementation`.\\n *\\n * This function does not return to its internal call site, it will return directly to the external caller.\\n */\\n function _delegate(address implementation) internal virtual {\\n assembly {\\n // Copy msg.data. We take full control of memory in this inline assembly\\n // block because it will not return to Solidity code. We overwrite the\\n // Solidity scratch pad at memory position 0.\\n calldatacopy(0, 0, calldatasize())\\n\\n // Call the implementation.\\n // out and outsize are 0 because we don't know the size yet.\\n let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)\\n\\n // Copy the returned data.\\n returndatacopy(0, 0, returndatasize())\\n\\n switch result\\n // delegatecall returns 0 on error.\\n case 0 {\\n revert(0, returndatasize())\\n }\\n default {\\n return(0, returndatasize())\\n }\\n }\\n }\\n\\n /**\\n * @dev This is a virtual function that should be overriden so it returns the address to which the fallback function\\n * and {_fallback} should delegate.\\n */\\n function _implementation() internal view virtual returns (address);\\n\\n /**\\n * @dev Delegates the current call to the address returned by `_implementation()`.\\n *\\n * This function does not return to its internall call site, it will return directly to the external caller.\\n */\\n function _fallback() internal virtual {\\n _beforeFallback();\\n _delegate(_implementation());\\n }\\n\\n /**\\n * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other\\n * function in the contract matches the call data.\\n */\\n fallback() external payable virtual {\\n _fallback();\\n }\\n\\n /**\\n * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if call data\\n * is empty.\\n */\\n receive() external payable virtual {\\n _fallback();\\n }\\n\\n /**\\n * @dev Hook that is called before falling back to the implementation. Can happen as part of a manual `_fallback`\\n * call, or as part of the Solidity `fallback` or `receive` functions.\\n *\\n * If overriden should call `super._beforeFallback()`.\\n */\\n function _beforeFallback() internal virtual {}\\n}\\n\",\"keccak256\":\"0xd5d1fd16e9faff7fcb3a52e02a8d49156f42a38a03f07b5f1810c21c2149a8ab\",\"license\":\"MIT\"},\"hardhat-deploy/solc_0.8/openzeppelin/proxy/beacon/IBeacon.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts v4.4.1 (proxy/beacon/IBeacon.sol)\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev This is the interface that {BeaconProxy} expects of its beacon.\\n */\\ninterface IBeacon {\\n /**\\n * @dev Must return an address that can be used as a delegate call target.\\n *\\n * {BeaconProxy} will check that this address is a contract.\\n */\\n function implementation() external view returns (address);\\n}\\n\",\"keccak256\":\"0xd50a3421ac379ccb1be435fa646d66a65c986b4924f0849839f08692f39dde61\",\"license\":\"MIT\"},\"hardhat-deploy/solc_0.8/openzeppelin/utils/Address.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts (last updated v4.5.0-rc.0) (utils/Address.sol)\\n\\npragma solidity ^0.8.1;\\n\\n/**\\n * @dev Collection of functions related to the address type\\n */\\nlibrary Address {\\n /**\\n * @dev Returns true if `account` is a contract.\\n *\\n * [IMPORTANT]\\n * ====\\n * It is unsafe to assume that an address for which this function returns\\n * false is an externally-owned account (EOA) and not a contract.\\n *\\n * Among others, `isContract` will return false for the following\\n * types of addresses:\\n *\\n * - an externally-owned account\\n * - a contract in construction\\n * - an address where a contract will be created\\n * - an address where a contract lived, but was destroyed\\n * ====\\n *\\n * [IMPORTANT]\\n * ====\\n * You shouldn't rely on `isContract` to protect against flash loan attacks!\\n *\\n * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets\\n * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract\\n * constructor.\\n * ====\\n */\\n function isContract(address account) internal view returns (bool) {\\n // This method relies on extcodesize/address.code.length, which returns 0\\n // for contracts in construction, since the code is only stored at the end\\n // of the constructor execution.\\n\\n return account.code.length > 0;\\n }\\n\\n /**\\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\\n * `recipient`, forwarding all available gas and reverting on errors.\\n *\\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\\n * imposed by `transfer`, making them unable to receive funds via\\n * `transfer`. {sendValue} removes this limitation.\\n *\\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\\n *\\n * IMPORTANT: because control is transferred to `recipient`, care must be\\n * taken to not create reentrancy vulnerabilities. Consider using\\n * {ReentrancyGuard} or the\\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\\n */\\n function sendValue(address payable recipient, uint256 amount) internal {\\n require(address(this).balance >= amount, \\\"Address: insufficient balance\\\");\\n\\n (bool success, ) = recipient.call{value: amount}(\\\"\\\");\\n require(success, \\\"Address: unable to send value, recipient may have reverted\\\");\\n }\\n\\n /**\\n * @dev Performs a Solidity function call using a low level `call`. A\\n * plain `call` is an unsafe replacement for a function call: use this\\n * function instead.\\n *\\n * If `target` reverts with a revert reason, it is bubbled up by this\\n * function (like regular Solidity function calls).\\n *\\n * Returns the raw returned data. To convert to the expected return value,\\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\\n *\\n * Requirements:\\n *\\n * - `target` must be a contract.\\n * - calling `target` with `data` must not revert.\\n *\\n * _Available since v3.1._\\n */\\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\\n return functionCall(target, data, \\\"Address: low-level call failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\\n * `errorMessage` as a fallback revert reason when `target` reverts.\\n *\\n * _Available since v3.1._\\n */\\n function functionCall(\\n address target,\\n bytes memory data,\\n string memory errorMessage\\n ) internal returns (bytes memory) {\\n return functionCallWithValue(target, data, 0, errorMessage);\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\\n * but also transferring `value` wei to `target`.\\n *\\n * Requirements:\\n *\\n * - the calling contract must have an ETH balance of at least `value`.\\n * - the called Solidity function must be `payable`.\\n *\\n * _Available since v3.1._\\n */\\n function functionCallWithValue(\\n address target,\\n bytes memory data,\\n uint256 value\\n ) internal returns (bytes memory) {\\n return functionCallWithValue(target, data, value, \\\"Address: low-level call with value failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\\n * with `errorMessage` as a fallback revert reason when `target` reverts.\\n *\\n * _Available since v3.1._\\n */\\n function functionCallWithValue(\\n address target,\\n bytes memory data,\\n uint256 value,\\n string memory errorMessage\\n ) internal returns (bytes memory) {\\n require(address(this).balance >= value, \\\"Address: insufficient balance for call\\\");\\n require(isContract(target), \\\"Address: call to non-contract\\\");\\n\\n (bool success, bytes memory returndata) = target.call{value: value}(data);\\n return verifyCallResult(success, returndata, errorMessage);\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\\n * but performing a static call.\\n *\\n * _Available since v3.3._\\n */\\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\\n return functionStaticCall(target, data, \\\"Address: low-level static call failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\\n * but performing a static call.\\n *\\n * _Available since v3.3._\\n */\\n function functionStaticCall(\\n address target,\\n bytes memory data,\\n string memory errorMessage\\n ) internal view returns (bytes memory) {\\n require(isContract(target), \\\"Address: static call to non-contract\\\");\\n\\n (bool success, bytes memory returndata) = target.staticcall(data);\\n return verifyCallResult(success, returndata, errorMessage);\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\\n * but performing a delegate call.\\n *\\n * _Available since v3.4._\\n */\\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\\n return functionDelegateCall(target, data, \\\"Address: low-level delegate call failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\\n * but performing a delegate call.\\n *\\n * _Available since v3.4._\\n */\\n function functionDelegateCall(\\n address target,\\n bytes memory data,\\n string memory errorMessage\\n ) internal returns (bytes memory) {\\n require(isContract(target), \\\"Address: delegate call to non-contract\\\");\\n\\n (bool success, bytes memory returndata) = target.delegatecall(data);\\n return verifyCallResult(success, returndata, errorMessage);\\n }\\n\\n /**\\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\\n * revert reason using the provided one.\\n *\\n * _Available since v4.3._\\n */\\n function verifyCallResult(\\n bool success,\\n bytes memory returndata,\\n string memory errorMessage\\n ) internal pure returns (bytes memory) {\\n if (success) {\\n return returndata;\\n } else {\\n // Look for revert reason and bubble it up if present\\n if (returndata.length > 0) {\\n // The easiest way to bubble the revert reason is using memory via assembly\\n\\n assembly {\\n let returndata_size := mload(returndata)\\n revert(add(32, returndata), returndata_size)\\n }\\n } else {\\n revert(errorMessage);\\n }\\n }\\n }\\n}\\n\",\"keccak256\":\"0x3777e696b62134e6177440dbe6e6601c0c156a443f57167194b67e75527439de\",\"license\":\"MIT\"},\"hardhat-deploy/solc_0.8/openzeppelin/utils/StorageSlot.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts v4.4.1 (utils/StorageSlot.sol)\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev Library for reading and writing primitive types to specific storage slots.\\n *\\n * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.\\n * This library helps with reading and writing to such slots without the need for inline assembly.\\n *\\n * The functions in this library return Slot structs that contain a `value` member that can be used to read or write.\\n *\\n * Example usage to set ERC1967 implementation slot:\\n * ```\\n * contract ERC1967 {\\n * bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;\\n *\\n * function _getImplementation() internal view returns (address) {\\n * return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;\\n * }\\n *\\n * function _setImplementation(address newImplementation) internal {\\n * require(Address.isContract(newImplementation), \\\"ERC1967: new implementation is not a contract\\\");\\n * StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;\\n * }\\n * }\\n * ```\\n *\\n * _Available since v4.1 for `address`, `bool`, `bytes32`, and `uint256`._\\n */\\nlibrary StorageSlot {\\n struct AddressSlot {\\n address value;\\n }\\n\\n struct BooleanSlot {\\n bool value;\\n }\\n\\n struct Bytes32Slot {\\n bytes32 value;\\n }\\n\\n struct Uint256Slot {\\n uint256 value;\\n }\\n\\n /**\\n * @dev Returns an `AddressSlot` with member `value` located at `slot`.\\n */\\n function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {\\n assembly {\\n r.slot := slot\\n }\\n }\\n\\n /**\\n * @dev Returns an `BooleanSlot` with member `value` located at `slot`.\\n */\\n function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {\\n assembly {\\n r.slot := slot\\n }\\n }\\n\\n /**\\n * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.\\n */\\n function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {\\n assembly {\\n r.slot := slot\\n }\\n }\\n\\n /**\\n * @dev Returns an `Uint256Slot` with member `value` located at `slot`.\\n */\\n function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {\\n assembly {\\n r.slot := slot\\n }\\n }\\n}\\n\",\"keccak256\":\"0xfe1b7a9aa2a530a9e705b220e26cd584e2fbdc9602a3a1066032b12816b46aca\",\"license\":\"MIT\"},\"hardhat-deploy/solc_0.8/proxy/OptimizedTransparentUpgradeableProxy.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts v4.4.1 (proxy/transparent/TransparentUpgradeableProxy.sol)\\n\\npragma solidity ^0.8.0;\\n\\nimport \\\"../openzeppelin/proxy/ERC1967/ERC1967Proxy.sol\\\";\\n\\n/**\\n * @dev This contract implements a proxy that is upgradeable by an admin.\\n *\\n * To avoid https://medium.com/nomic-labs-blog/malicious-backdoors-in-ethereum-proxies-62629adf3357[proxy selector\\n * clashing], which can potentially be used in an attack, this contract uses the\\n * https://blog.openzeppelin.com/the-transparent-proxy-pattern/[transparent proxy pattern]. This pattern implies two\\n * things that go hand in hand:\\n *\\n * 1. If any account other than the admin calls the proxy, the call will be forwarded to the implementation, even if\\n * that call matches one of the admin functions exposed by the proxy itself.\\n * 2. If the admin calls the proxy, it can access the admin functions, but its calls will never be forwarded to the\\n * implementation. If the admin tries to call a function on the implementation it will fail with an error that says\\n * \\\"admin cannot fallback to proxy target\\\".\\n *\\n * These properties mean that the admin account can only be used for admin actions like upgrading the proxy or changing\\n * the admin, so it's best if it's a dedicated account that is not used for anything else. This will avoid headaches due\\n * to sudden errors when trying to call a function from the proxy implementation.\\n *\\n * Our recommendation is for the dedicated account to be an instance of the {ProxyAdmin} contract. If set up this way,\\n * you should think of the `ProxyAdmin` instance as the real administrative interface of your proxy.\\n */\\ncontract OptimizedTransparentUpgradeableProxy is ERC1967Proxy {\\n address internal immutable _ADMIN;\\n\\n /**\\n * @dev Initializes an upgradeable proxy managed by `_admin`, backed by the implementation at `_logic`, and\\n * optionally initialized with `_data` as explained in {ERC1967Proxy-constructor}.\\n */\\n constructor(\\n address _logic,\\n address admin_,\\n bytes memory _data\\n ) payable ERC1967Proxy(_logic, _data) {\\n assert(_ADMIN_SLOT == bytes32(uint256(keccak256(\\\"eip1967.proxy.admin\\\")) - 1));\\n _ADMIN = admin_;\\n\\n // still store it to work with EIP-1967\\n bytes32 slot = _ADMIN_SLOT;\\n // solhint-disable-next-line no-inline-assembly\\n assembly {\\n sstore(slot, admin_)\\n }\\n emit AdminChanged(address(0), admin_);\\n }\\n\\n /**\\n * @dev Modifier used internally that will delegate the call to the implementation unless the sender is the admin.\\n */\\n modifier ifAdmin() {\\n if (msg.sender == _getAdmin()) {\\n _;\\n } else {\\n _fallback();\\n }\\n }\\n\\n /**\\n * @dev Returns the current admin.\\n *\\n * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyAdmin}.\\n *\\n * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the\\n * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.\\n * `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103`\\n */\\n function admin() external ifAdmin returns (address admin_) {\\n admin_ = _getAdmin();\\n }\\n\\n /**\\n * @dev Returns the current implementation.\\n *\\n * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyImplementation}.\\n *\\n * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the\\n * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.\\n * `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc`\\n */\\n function implementation() external ifAdmin returns (address implementation_) {\\n implementation_ = _implementation();\\n }\\n\\n /**\\n * @dev Upgrade the implementation of the proxy.\\n *\\n * NOTE: Only the admin can call this function. See {ProxyAdmin-upgrade}.\\n */\\n function upgradeTo(address newImplementation) external ifAdmin {\\n _upgradeToAndCall(newImplementation, bytes(\\\"\\\"), false);\\n }\\n\\n /**\\n * @dev Upgrade the implementation of the proxy, and then call a function from the new implementation as specified\\n * by `data`, which should be an encoded function call. This is useful to initialize new storage variables in the\\n * proxied contract.\\n *\\n * NOTE: Only the admin can call this function. See {ProxyAdmin-upgradeAndCall}.\\n */\\n function upgradeToAndCall(address newImplementation, bytes calldata data) external payable ifAdmin {\\n _upgradeToAndCall(newImplementation, data, true);\\n }\\n\\n /**\\n * @dev Returns the current admin.\\n */\\n function _admin() internal view virtual returns (address) {\\n return _getAdmin();\\n }\\n\\n /**\\n * @dev Makes sure the admin cannot access the fallback function. See {Proxy-_beforeFallback}.\\n */\\n function _beforeFallback() internal virtual override {\\n require(msg.sender != _getAdmin(), \\\"TransparentUpgradeableProxy: admin cannot fallback to proxy target\\\");\\n super._beforeFallback();\\n }\\n\\n function _getAdmin() internal view virtual override returns (address) {\\n return _ADMIN;\\n }\\n}\\n\",\"keccak256\":\"0xa30117644e27fa5b49e162aae2f62b36c1aca02f801b8c594d46e2024963a534\",\"license\":\"MIT\"}},\"version\":1}", + "bytecode": "0x60a0604052604051610d1a380380610d1a833981016040819052610022916103d2565b828161004f60017f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbd6104a2565b600080516020610cd38339815191521461006b5761006b6104c3565b61007782826000610128565b506100a5905060017fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61046104a2565b600080516020610cb3833981519152146100c1576100c16104c3565b6001600160a01b0382166080819052600080516020610cb38339815191528381556040805160008152602081019390935290917f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f910160405180910390a150505050610528565b61013183610154565b60008251118061013e5750805b1561014f5761014d8383610194565b505b505050565b61015d816101c2565b6040516001600160a01b038216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b60606101b98383604051806060016040528060278152602001610cf360279139610263565b90505b92915050565b6001600160a01b0381163b6102345760405162461bcd60e51b815260206004820152602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201526c1bdd08184818dbdb9d1c9858dd609a1b60648201526084015b60405180910390fd5b600080516020610cd383398151915280546001600160a01b0319166001600160a01b0392909216919091179055565b60606001600160a01b0384163b6102cb5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f6044820152651b9d1c9858dd60d21b606482015260840161022b565b600080856001600160a01b0316856040516102e691906104d9565b600060405180830381855af49150503d8060008114610321576040519150601f19603f3d011682016040523d82523d6000602084013e610326565b606091505b509092509050610337828286610343565b925050505b9392505050565b6060831561035257508161033c565b8251156103625782518084602001fd5b8160405162461bcd60e51b815260040161022b91906104f5565b80516001600160a01b038116811461039357600080fd5b919050565b634e487b7160e01b600052604160045260246000fd5b60005b838110156103c95781810151838201526020016103b1565b50506000910152565b6000806000606084860312156103e757600080fd5b6103f08461037c565b92506103fe6020850161037c565b60408501519092506001600160401b038082111561041b57600080fd5b818601915086601f83011261042f57600080fd5b81518181111561044157610441610398565b604051601f8201601f19908116603f0116810190838211818310171561046957610469610398565b8160405282815289602084870101111561048257600080fd5b6104938360208301602088016103ae565b80955050505050509250925092565b818103818111156101bc57634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052600160045260246000fd5b600082516104eb8184602087016103ae565b9190910192915050565b60208152600082518060208401526105148160408501602087016103ae565b601f01601f19169190910160400192915050565b60805161074e6105656000396000818160ef01528181610145015281816101c701528181610211015281816102420152610266015261074e6000f3fe6080604052600436106100435760003560e01c80633659cfe61461005a5780634f1ef2861461007a5780635c60da1b1461008d578063f851a440146100be57610052565b36610052576100506100d3565b005b6100506100d3565b34801561006657600080fd5b506100506100753660046105e0565b6100ed565b6100506100883660046105fb565b610143565b34801561009957600080fd5b506100a26101c3565b6040516001600160a01b03909116815260200160405180910390f35b3480156100ca57600080fd5b506100a261020d565b6100db610264565b6100eb6100e6610312565b610345565b565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316330361013b5761013881604051806020016040528060008152506000610369565b50565b6101386100d3565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031633036101bb576101b68383838080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525060019250610369915050565b505050565b6101b66100d3565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03163303610202576101fd610312565b905090565b61020a6100d3565b90565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316330361020257507f000000000000000000000000000000000000000000000000000000000000000090565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031633036100eb5760405162461bcd60e51b815260206004820152604260248201527f5472616e73706172656e745570677261646561626c6550726f78793a2061646d60448201527f696e2063616e6e6f742066616c6c6261636b20746f2070726f78792074617267606482015261195d60f21b608482015260a4015b60405180910390fd5b60006101fd7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc546001600160a01b031690565b3660008037600080366000845af43d6000803e808015610364573d6000f35b3d6000fd5b61037283610394565b60008251118061037f5750805b156101b65761038e83836103d4565b50505050565b61039d81610400565b6040516001600160a01b038216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b60606103f983836040518060600160405280602781526020016106f2602791396104ae565b9392505050565b6001600160a01b0381163b61046d5760405162461bcd60e51b815260206004820152602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201526c1bdd08184818dbdb9d1c9858dd609a1b6064820152608401610309565b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc80546001600160a01b0319166001600160a01b0392909216919091179055565b60606001600160a01b0384163b6105165760405162461bcd60e51b815260206004820152602660248201527f416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f6044820152651b9d1c9858dd60d21b6064820152608401610309565b600080856001600160a01b03168560405161053191906106a2565b600060405180830381855af49150503d806000811461056c576040519150601f19603f3d011682016040523d82523d6000602084013e610571565b606091505b509150915061058182828661058b565b9695505050505050565b6060831561059a5750816103f9565b8251156105aa5782518084602001fd5b8160405162461bcd60e51b815260040161030991906106be565b80356001600160a01b03811681146105db57600080fd5b919050565b6000602082840312156105f257600080fd5b6103f9826105c4565b60008060006040848603121561061057600080fd5b610619846105c4565b9250602084013567ffffffffffffffff8082111561063657600080fd5b818601915086601f83011261064a57600080fd5b81358181111561065957600080fd5b87602082850101111561066b57600080fd5b6020830194508093505050509250925092565b60005b83811015610699578181015183820152602001610681565b50506000910152565b600082516106b481846020870161067e565b9190910192915050565b60208152600082518060208401526106dd81604085016020870161067e565b601f01601f1916919091016040019291505056fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a26469706673582212207502388c3ffe7f7efd00a218c70c5d02b728d9a5b26b11a37a02a8761f7f520764736f6c63430008190033b53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564", + "deployedBytecode": "0x6080604052600436106100435760003560e01c80633659cfe61461005a5780634f1ef2861461007a5780635c60da1b1461008d578063f851a440146100be57610052565b36610052576100506100d3565b005b6100506100d3565b34801561006657600080fd5b506100506100753660046105e0565b6100ed565b6100506100883660046105fb565b610143565b34801561009957600080fd5b506100a26101c3565b6040516001600160a01b03909116815260200160405180910390f35b3480156100ca57600080fd5b506100a261020d565b6100db610264565b6100eb6100e6610312565b610345565b565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316330361013b5761013881604051806020016040528060008152506000610369565b50565b6101386100d3565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031633036101bb576101b68383838080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525060019250610369915050565b505050565b6101b66100d3565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03163303610202576101fd610312565b905090565b61020a6100d3565b90565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316330361020257507f000000000000000000000000000000000000000000000000000000000000000090565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031633036100eb5760405162461bcd60e51b815260206004820152604260248201527f5472616e73706172656e745570677261646561626c6550726f78793a2061646d60448201527f696e2063616e6e6f742066616c6c6261636b20746f2070726f78792074617267606482015261195d60f21b608482015260a4015b60405180910390fd5b60006101fd7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc546001600160a01b031690565b3660008037600080366000845af43d6000803e808015610364573d6000f35b3d6000fd5b61037283610394565b60008251118061037f5750805b156101b65761038e83836103d4565b50505050565b61039d81610400565b6040516001600160a01b038216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b60606103f983836040518060600160405280602781526020016106f2602791396104ae565b9392505050565b6001600160a01b0381163b61046d5760405162461bcd60e51b815260206004820152602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201526c1bdd08184818dbdb9d1c9858dd609a1b6064820152608401610309565b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc80546001600160a01b0319166001600160a01b0392909216919091179055565b60606001600160a01b0384163b6105165760405162461bcd60e51b815260206004820152602660248201527f416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f6044820152651b9d1c9858dd60d21b6064820152608401610309565b600080856001600160a01b03168560405161053191906106a2565b600060405180830381855af49150503d806000811461056c576040519150601f19603f3d011682016040523d82523d6000602084013e610571565b606091505b509150915061058182828661058b565b9695505050505050565b6060831561059a5750816103f9565b8251156105aa5782518084602001fd5b8160405162461bcd60e51b815260040161030991906106be565b80356001600160a01b03811681146105db57600080fd5b919050565b6000602082840312156105f257600080fd5b6103f9826105c4565b60008060006040848603121561061057600080fd5b610619846105c4565b9250602084013567ffffffffffffffff8082111561063657600080fd5b818601915086601f83011261064a57600080fd5b81358181111561065957600080fd5b87602082850101111561066b57600080fd5b6020830194508093505050509250925092565b60005b83811015610699578181015183820152602001610681565b50506000910152565b600082516106b481846020870161067e565b9190910192915050565b60208152600082518060208401526106dd81604085016020870161067e565b601f01601f1916919091016040019291505056fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a26469706673582212207502388c3ffe7f7efd00a218c70c5d02b728d9a5b26b11a37a02a8761f7f520764736f6c63430008190033", + "devdoc": { + "details": "This contract implements a proxy that is upgradeable by an admin. To avoid https://medium.com/nomic-labs-blog/malicious-backdoors-in-ethereum-proxies-62629adf3357[proxy selector clashing], which can potentially be used in an attack, this contract uses the https://blog.openzeppelin.com/the-transparent-proxy-pattern/[transparent proxy pattern]. This pattern implies two things that go hand in hand: 1. If any account other than the admin calls the proxy, the call will be forwarded to the implementation, even if that call matches one of the admin functions exposed by the proxy itself. 2. If the admin calls the proxy, it can access the admin functions, but its calls will never be forwarded to the implementation. If the admin tries to call a function on the implementation it will fail with an error that says \"admin cannot fallback to proxy target\". These properties mean that the admin account can only be used for admin actions like upgrading the proxy or changing the admin, so it's best if it's a dedicated account that is not used for anything else. This will avoid headaches due to sudden errors when trying to call a function from the proxy implementation. Our recommendation is for the dedicated account to be an instance of the {ProxyAdmin} contract. If set up this way, you should think of the `ProxyAdmin` instance as the real administrative interface of your proxy.", + "events": { + "AdminChanged(address,address)": { + "details": "Emitted when the admin account has changed." + }, + "BeaconUpgraded(address)": { + "details": "Emitted when the beacon is upgraded." + }, + "Upgraded(address)": { + "details": "Emitted when the implementation is upgraded." + } + }, + "kind": "dev", + "methods": { + "admin()": { + "details": "Returns the current admin. NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyAdmin}. TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call. `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103`" + }, + "constructor": { + "details": "Initializes an upgradeable proxy managed by `_admin`, backed by the implementation at `_logic`, and optionally initialized with `_data` as explained in {ERC1967Proxy-constructor}." + }, + "implementation()": { + "details": "Returns the current implementation. NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyImplementation}. TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call. `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc`" + }, + "upgradeTo(address)": { + "details": "Upgrade the implementation of the proxy. NOTE: Only the admin can call this function. See {ProxyAdmin-upgrade}." + }, + "upgradeToAndCall(address,bytes)": { + "details": "Upgrade the implementation of the proxy, and then call a function from the new implementation as specified by `data`, which should be an encoded function call. This is useful to initialize new storage variables in the proxied contract. NOTE: Only the admin can call this function. See {ProxyAdmin-upgradeAndCall}." + } + }, + "version": 1 + }, + "userdoc": { + "kind": "user", + "methods": {}, + "version": 1 + }, + "storageLayout": { + "storage": [], + "types": null + } +} diff --git a/deployments/bsctestnet/SpokeVTokenBeacon.json b/deployments/bsctestnet/SpokeVTokenBeacon.json new file mode 100644 index 000000000..f95898a67 --- /dev/null +++ b/deployments/bsctestnet/SpokeVTokenBeacon.json @@ -0,0 +1,206 @@ +{ + "address": "0xB9c3b5A6f13FD5BEF51eE8B62ED3EA384a9d1B45", + "abi": [ + { + "inputs": [ + { + "internalType": "address", + "name": "implementation_", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "implementation", + "type": "address" + } + ], + "name": "Upgraded", + "type": "event" + }, + { + "inputs": [], + "name": "implementation", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newImplementation", + "type": "address" + } + ], + "name": "upgradeTo", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } + ], + "transactionHash": "0x8574f9afed1982596cc00f9afb4e484455ce48950547833a12f6fd22d609e98e", + "receipt": { + "to": null, + "from": "0x33C6476F88eeA28D7E7900F759B4597704Ef95B7", + "contractAddress": "0xB9c3b5A6f13FD5BEF51eE8B62ED3EA384a9d1B45", + "transactionIndex": 0, + "gasUsed": "288554", + "logsBloom": "0x00000000000000000000000000000000000000000000000000800000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000002000000000000000000000000000000020000000000000000000800000000000000000000000000000000400000000000000010000000000000000010000000000000000000000000000000000001000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0x76231ed3ccd536c762d85172ca51049adfe93cf198fe655c66e158684e210ae6", + "transactionHash": "0x8574f9afed1982596cc00f9afb4e484455ce48950547833a12f6fd22d609e98e", + "logs": [ + { + "transactionIndex": 0, + "blockNumber": 129845646, + "transactionHash": "0x8574f9afed1982596cc00f9afb4e484455ce48950547833a12f6fd22d609e98e", + "address": "0xB9c3b5A6f13FD5BEF51eE8B62ED3EA384a9d1B45", + "topics": [ + "0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x00000000000000000000000033c6476f88eea28d7e7900f759b4597704ef95b7" + ], + "data": "0x", + "logIndex": 0, + "blockHash": "0x76231ed3ccd536c762d85172ca51049adfe93cf198fe655c66e158684e210ae6" + } + ], + "blockNumber": 129845646, + "cumulativeGasUsed": "288554", + "status": 1, + "byzantium": true + }, + "args": ["0x0A2399b0fC9A5921D0d843f2960589c1300Ac601"], + "numDeployments": 1, + "solcInputHash": "02cfe3974aa10474c22406a3f2870e89", + "metadata": "{\"compiler\":{\"version\":\"0.8.25+commit.b61c2a91\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"implementation_\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousOwner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"implementation\",\"type\":\"address\"}],\"name\":\"Upgraded\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"implementation\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"renounceOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newImplementation\",\"type\":\"address\"}],\"name\":\"upgradeTo\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}],\"devdoc\":{\"details\":\"This contract is used in conjunction with one or more instances of {BeaconProxy} to determine their implementation contract, which is where they will delegate all function calls. An owner is able to change the implementation the beacon points to, thus upgrading the proxies that use this beacon.\",\"events\":{\"Upgraded(address)\":{\"details\":\"Emitted when the implementation returned by the beacon is changed.\"}},\"kind\":\"dev\",\"methods\":{\"constructor\":{\"details\":\"Sets the address of the initial implementation, and the deployer account as the owner who can upgrade the beacon.\"},\"implementation()\":{\"details\":\"Returns the current implementation address.\"},\"owner()\":{\"details\":\"Returns the address of the current owner.\"},\"renounceOwnership()\":{\"details\":\"Leaves the contract without owner. It will not be possible to call `onlyOwner` functions. Can only be called by the current owner. NOTE: Renouncing ownership will leave the contract without an owner, thereby disabling any functionality that is only available to the owner.\"},\"transferOwnership(address)\":{\"details\":\"Transfers ownership of the contract to a new account (`newOwner`). Can only be called by the current owner.\"},\"upgradeTo(address)\":{\"details\":\"Upgrades the beacon to a new implementation. Emits an {Upgraded} event. Requirements: - msg.sender must be the owner of the contract. - `newImplementation` must be a contract.\"}},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol\":\"UpgradeableBeacon\"},\"evmVersion\":\"paris\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\",\"useLiteralContent\":true},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[]},\"sources\":{\"@openzeppelin/contracts/access/Ownable.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)\\n\\npragma solidity ^0.8.0;\\n\\nimport \\\"../utils/Context.sol\\\";\\n\\n/**\\n * @dev Contract module which provides a basic access control mechanism, where\\n * there is an account (an owner) that can be granted exclusive access to\\n * specific functions.\\n *\\n * By default, the owner account will be the one that deploys the contract. This\\n * can later be changed with {transferOwnership}.\\n *\\n * This module is used through inheritance. It will make available the modifier\\n * `onlyOwner`, which can be applied to your functions to restrict their use to\\n * the owner.\\n */\\nabstract contract Ownable is Context {\\n address private _owner;\\n\\n event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\\n\\n /**\\n * @dev Initializes the contract setting the deployer as the initial owner.\\n */\\n constructor() {\\n _transferOwnership(_msgSender());\\n }\\n\\n /**\\n * @dev Throws if called by any account other than the owner.\\n */\\n modifier onlyOwner() {\\n _checkOwner();\\n _;\\n }\\n\\n /**\\n * @dev Returns the address of the current owner.\\n */\\n function owner() public view virtual returns (address) {\\n return _owner;\\n }\\n\\n /**\\n * @dev Throws if the sender is not the owner.\\n */\\n function _checkOwner() internal view virtual {\\n require(owner() == _msgSender(), \\\"Ownable: caller is not the owner\\\");\\n }\\n\\n /**\\n * @dev Leaves the contract without owner. It will not be possible to call\\n * `onlyOwner` functions. Can only be called by the current owner.\\n *\\n * NOTE: Renouncing ownership will leave the contract without an owner,\\n * thereby disabling any functionality that is only available to the owner.\\n */\\n function renounceOwnership() public virtual onlyOwner {\\n _transferOwnership(address(0));\\n }\\n\\n /**\\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\\n * Can only be called by the current owner.\\n */\\n function transferOwnership(address newOwner) public virtual onlyOwner {\\n require(newOwner != address(0), \\\"Ownable: new owner is the zero address\\\");\\n _transferOwnership(newOwner);\\n }\\n\\n /**\\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\\n * Internal function without access restriction.\\n */\\n function _transferOwnership(address newOwner) internal virtual {\\n address oldOwner = _owner;\\n _owner = newOwner;\\n emit OwnershipTransferred(oldOwner, newOwner);\\n }\\n}\\n\",\"keccak256\":\"0xba43b97fba0d32eb4254f6a5a297b39a19a247082a02d6e69349e071e2946218\",\"license\":\"MIT\"},\"@openzeppelin/contracts/proxy/beacon/IBeacon.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts v4.4.1 (proxy/beacon/IBeacon.sol)\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev This is the interface that {BeaconProxy} expects of its beacon.\\n */\\ninterface IBeacon {\\n /**\\n * @dev Must return an address that can be used as a delegate call target.\\n *\\n * {BeaconProxy} will check that this address is a contract.\\n */\\n function implementation() external view returns (address);\\n}\\n\",\"keccak256\":\"0xd50a3421ac379ccb1be435fa646d66a65c986b4924f0849839f08692f39dde61\",\"license\":\"MIT\"},\"@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts v4.4.1 (proxy/beacon/UpgradeableBeacon.sol)\\n\\npragma solidity ^0.8.0;\\n\\nimport \\\"./IBeacon.sol\\\";\\nimport \\\"../../access/Ownable.sol\\\";\\nimport \\\"../../utils/Address.sol\\\";\\n\\n/**\\n * @dev This contract is used in conjunction with one or more instances of {BeaconProxy} to determine their\\n * implementation contract, which is where they will delegate all function calls.\\n *\\n * An owner is able to change the implementation the beacon points to, thus upgrading the proxies that use this beacon.\\n */\\ncontract UpgradeableBeacon is IBeacon, Ownable {\\n address private _implementation;\\n\\n /**\\n * @dev Emitted when the implementation returned by the beacon is changed.\\n */\\n event Upgraded(address indexed implementation);\\n\\n /**\\n * @dev Sets the address of the initial implementation, and the deployer account as the owner who can upgrade the\\n * beacon.\\n */\\n constructor(address implementation_) {\\n _setImplementation(implementation_);\\n }\\n\\n /**\\n * @dev Returns the current implementation address.\\n */\\n function implementation() public view virtual override returns (address) {\\n return _implementation;\\n }\\n\\n /**\\n * @dev Upgrades the beacon to a new implementation.\\n *\\n * Emits an {Upgraded} event.\\n *\\n * Requirements:\\n *\\n * - msg.sender must be the owner of the contract.\\n * - `newImplementation` must be a contract.\\n */\\n function upgradeTo(address newImplementation) public virtual onlyOwner {\\n _setImplementation(newImplementation);\\n emit Upgraded(newImplementation);\\n }\\n\\n /**\\n * @dev Sets the implementation contract address for this beacon\\n *\\n * Requirements:\\n *\\n * - `newImplementation` must be a contract.\\n */\\n function _setImplementation(address newImplementation) private {\\n require(Address.isContract(newImplementation), \\\"UpgradeableBeacon: implementation is not a contract\\\");\\n _implementation = newImplementation;\\n }\\n}\\n\",\"keccak256\":\"0x6ec71aef5659f3f74011169948d2fcda8c6599be5bb38f986380a8737f96cc0f\",\"license\":\"MIT\"},\"@openzeppelin/contracts/utils/Address.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)\\n\\npragma solidity ^0.8.1;\\n\\n/**\\n * @dev Collection of functions related to the address type\\n */\\nlibrary Address {\\n /**\\n * @dev Returns true if `account` is a contract.\\n *\\n * [IMPORTANT]\\n * ====\\n * It is unsafe to assume that an address for which this function returns\\n * false is an externally-owned account (EOA) and not a contract.\\n *\\n * Among others, `isContract` will return false for the following\\n * types of addresses:\\n *\\n * - an externally-owned account\\n * - a contract in construction\\n * - an address where a contract will be created\\n * - an address where a contract lived, but was destroyed\\n *\\n * Furthermore, `isContract` will also return true if the target contract within\\n * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,\\n * which only has an effect at the end of a transaction.\\n * ====\\n *\\n * [IMPORTANT]\\n * ====\\n * You shouldn't rely on `isContract` to protect against flash loan attacks!\\n *\\n * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets\\n * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract\\n * constructor.\\n * ====\\n */\\n function isContract(address account) internal view returns (bool) {\\n // This method relies on extcodesize/address.code.length, which returns 0\\n // for contracts in construction, since the code is only stored at the end\\n // of the constructor execution.\\n\\n return account.code.length > 0;\\n }\\n\\n /**\\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\\n * `recipient`, forwarding all available gas and reverting on errors.\\n *\\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\\n * imposed by `transfer`, making them unable to receive funds via\\n * `transfer`. {sendValue} removes this limitation.\\n *\\n * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].\\n *\\n * IMPORTANT: because control is transferred to `recipient`, care must be\\n * taken to not create reentrancy vulnerabilities. Consider using\\n * {ReentrancyGuard} or the\\n * https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\\n */\\n function sendValue(address payable recipient, uint256 amount) internal {\\n require(address(this).balance >= amount, \\\"Address: insufficient balance\\\");\\n\\n (bool success, ) = recipient.call{value: amount}(\\\"\\\");\\n require(success, \\\"Address: unable to send value, recipient may have reverted\\\");\\n }\\n\\n /**\\n * @dev Performs a Solidity function call using a low level `call`. A\\n * plain `call` is an unsafe replacement for a function call: use this\\n * function instead.\\n *\\n * If `target` reverts with a revert reason, it is bubbled up by this\\n * function (like regular Solidity function calls).\\n *\\n * Returns the raw returned data. To convert to the expected return value,\\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\\n *\\n * Requirements:\\n *\\n * - `target` must be a contract.\\n * - calling `target` with `data` must not revert.\\n *\\n * _Available since v3.1._\\n */\\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\\n return functionCallWithValue(target, data, 0, \\\"Address: low-level call failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\\n * `errorMessage` as a fallback revert reason when `target` reverts.\\n *\\n * _Available since v3.1._\\n */\\n function functionCall(\\n address target,\\n bytes memory data,\\n string memory errorMessage\\n ) internal returns (bytes memory) {\\n return functionCallWithValue(target, data, 0, errorMessage);\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\\n * but also transferring `value` wei to `target`.\\n *\\n * Requirements:\\n *\\n * - the calling contract must have an ETH balance of at least `value`.\\n * - the called Solidity function must be `payable`.\\n *\\n * _Available since v3.1._\\n */\\n function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {\\n return functionCallWithValue(target, data, value, \\\"Address: low-level call with value failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\\n * with `errorMessage` as a fallback revert reason when `target` reverts.\\n *\\n * _Available since v3.1._\\n */\\n function functionCallWithValue(\\n address target,\\n bytes memory data,\\n uint256 value,\\n string memory errorMessage\\n ) internal returns (bytes memory) {\\n require(address(this).balance >= value, \\\"Address: insufficient balance for call\\\");\\n (bool success, bytes memory returndata) = target.call{value: value}(data);\\n return verifyCallResultFromTarget(target, success, returndata, errorMessage);\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\\n * but performing a static call.\\n *\\n * _Available since v3.3._\\n */\\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\\n return functionStaticCall(target, data, \\\"Address: low-level static call failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\\n * but performing a static call.\\n *\\n * _Available since v3.3._\\n */\\n function functionStaticCall(\\n address target,\\n bytes memory data,\\n string memory errorMessage\\n ) internal view returns (bytes memory) {\\n (bool success, bytes memory returndata) = target.staticcall(data);\\n return verifyCallResultFromTarget(target, success, returndata, errorMessage);\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\\n * but performing a delegate call.\\n *\\n * _Available since v3.4._\\n */\\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\\n return functionDelegateCall(target, data, \\\"Address: low-level delegate call failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\\n * but performing a delegate call.\\n *\\n * _Available since v3.4._\\n */\\n function functionDelegateCall(\\n address target,\\n bytes memory data,\\n string memory errorMessage\\n ) internal returns (bytes memory) {\\n (bool success, bytes memory returndata) = target.delegatecall(data);\\n return verifyCallResultFromTarget(target, success, returndata, errorMessage);\\n }\\n\\n /**\\n * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling\\n * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.\\n *\\n * _Available since v4.8._\\n */\\n function verifyCallResultFromTarget(\\n address target,\\n bool success,\\n bytes memory returndata,\\n string memory errorMessage\\n ) internal view returns (bytes memory) {\\n if (success) {\\n if (returndata.length == 0) {\\n // only check isContract if the call was successful and the return data is empty\\n // otherwise we already know that it was a contract\\n require(isContract(target), \\\"Address: call to non-contract\\\");\\n }\\n return returndata;\\n } else {\\n _revert(returndata, errorMessage);\\n }\\n }\\n\\n /**\\n * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the\\n * revert reason or using the provided one.\\n *\\n * _Available since v4.3._\\n */\\n function verifyCallResult(\\n bool success,\\n bytes memory returndata,\\n string memory errorMessage\\n ) internal pure returns (bytes memory) {\\n if (success) {\\n return returndata;\\n } else {\\n _revert(returndata, errorMessage);\\n }\\n }\\n\\n function _revert(bytes memory returndata, string memory errorMessage) private pure {\\n // Look for revert reason and bubble it up if present\\n if (returndata.length > 0) {\\n // The easiest way to bubble the revert reason is using memory via assembly\\n /// @solidity memory-safe-assembly\\n assembly {\\n let returndata_size := mload(returndata)\\n revert(add(32, returndata), returndata_size)\\n }\\n } else {\\n revert(errorMessage);\\n }\\n }\\n}\\n\",\"keccak256\":\"0x006dd67219697fe68d7fbfdea512e7c4cb64a43565ed86171d67e844982da6fa\",\"license\":\"MIT\"},\"@openzeppelin/contracts/utils/Context.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts (last updated v4.9.4) (utils/Context.sol)\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev Provides information about the current execution context, including the\\n * sender of the transaction and its data. While these are generally available\\n * via msg.sender and msg.data, they should not be accessed in such a direct\\n * manner, since when dealing with meta-transactions the account sending and\\n * paying for execution may not be the actual sender (as far as an application\\n * is concerned).\\n *\\n * This contract is only required for intermediate, library-like contracts.\\n */\\nabstract contract Context {\\n function _msgSender() internal view virtual returns (address) {\\n return msg.sender;\\n }\\n\\n function _msgData() internal view virtual returns (bytes calldata) {\\n return msg.data;\\n }\\n\\n function _contextSuffixLength() internal view virtual returns (uint256) {\\n return 0;\\n }\\n}\\n\",\"keccak256\":\"0xa92e4fa126feb6907daa0513ddd816b2eb91f30a808de54f63c17d0e162c3439\",\"license\":\"MIT\"}},\"version\":1}", + "bytecode": "0x608060405234801561001057600080fd5b506040516104be3803806104be83398101604081905261002f9161013a565b61003833610047565b61004181610097565b5061016a565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6001600160a01b0381163b6101185760405162461bcd60e51b815260206004820152603360248201527f5570677261646561626c65426561636f6e3a20696d706c656d656e746174696f60448201527f6e206973206e6f74206120636f6e747261637400000000000000000000000000606482015260840160405180910390fd5b600180546001600160a01b0319166001600160a01b0392909216919091179055565b60006020828403121561014c57600080fd5b81516001600160a01b038116811461016357600080fd5b9392505050565b610345806101796000396000f3fe608060405234801561001057600080fd5b50600436106100575760003560e01c80633659cfe61461005c5780635c60da1b14610071578063715018a61461009a5780638da5cb5b146100a2578063f2fde38b146100b3575b600080fd5b61006f61006a3660046102df565b6100c6565b005b6001546001600160a01b03165b6040516001600160a01b03909116815260200160405180910390f35b61006f61010e565b6000546001600160a01b031661007e565b61006f6100c13660046102df565b610122565b6100ce6101a0565b6100d7816101fa565b6040516001600160a01b038216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b6101166101a0565b610120600061028f565b565b61012a6101a0565b6001600160a01b0381166101945760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b60648201526084015b60405180910390fd5b61019d8161028f565b50565b6000546001600160a01b031633146101205760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161018b565b6001600160a01b0381163b61026d5760405162461bcd60e51b815260206004820152603360248201527f5570677261646561626c65426561636f6e3a20696d706c656d656e746174696f6044820152721b881a5cc81b9bdd08184818dbdb9d1c9858dd606a1b606482015260840161018b565b600180546001600160a01b0319166001600160a01b0392909216919091179055565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6000602082840312156102f157600080fd5b81356001600160a01b038116811461030857600080fd5b939250505056fea2646970667358221220d545fd9e5dad1533895a65b3a05d1e6f1c1c47d5577fa2c59eec7f8e53dae96b64736f6c63430008190033", + "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106100575760003560e01c80633659cfe61461005c5780635c60da1b14610071578063715018a61461009a5780638da5cb5b146100a2578063f2fde38b146100b3575b600080fd5b61006f61006a3660046102df565b6100c6565b005b6001546001600160a01b03165b6040516001600160a01b03909116815260200160405180910390f35b61006f61010e565b6000546001600160a01b031661007e565b61006f6100c13660046102df565b610122565b6100ce6101a0565b6100d7816101fa565b6040516001600160a01b038216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b6101166101a0565b610120600061028f565b565b61012a6101a0565b6001600160a01b0381166101945760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b60648201526084015b60405180910390fd5b61019d8161028f565b50565b6000546001600160a01b031633146101205760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161018b565b6001600160a01b0381163b61026d5760405162461bcd60e51b815260206004820152603360248201527f5570677261646561626c65426561636f6e3a20696d706c656d656e746174696f6044820152721b881a5cc81b9bdd08184818dbdb9d1c9858dd606a1b606482015260840161018b565b600180546001600160a01b0319166001600160a01b0392909216919091179055565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6000602082840312156102f157600080fd5b81356001600160a01b038116811461030857600080fd5b939250505056fea2646970667358221220d545fd9e5dad1533895a65b3a05d1e6f1c1c47d5577fa2c59eec7f8e53dae96b64736f6c63430008190033", + "devdoc": { + "details": "This contract is used in conjunction with one or more instances of {BeaconProxy} to determine their implementation contract, which is where they will delegate all function calls. An owner is able to change the implementation the beacon points to, thus upgrading the proxies that use this beacon.", + "events": { + "Upgraded(address)": { + "details": "Emitted when the implementation returned by the beacon is changed." + } + }, + "kind": "dev", + "methods": { + "constructor": { + "details": "Sets the address of the initial implementation, and the deployer account as the owner who can upgrade the beacon." + }, + "implementation()": { + "details": "Returns the current implementation address." + }, + "owner()": { + "details": "Returns the address of the current owner." + }, + "renounceOwnership()": { + "details": "Leaves the contract without owner. It will not be possible to call `onlyOwner` functions. Can only be called by the current owner. NOTE: Renouncing ownership will leave the contract without an owner, thereby disabling any functionality that is only available to the owner." + }, + "transferOwnership(address)": { + "details": "Transfers ownership of the contract to a new account (`newOwner`). Can only be called by the current owner." + }, + "upgradeTo(address)": { + "details": "Upgrades the beacon to a new implementation. Emits an {Upgraded} event. Requirements: - msg.sender must be the owner of the contract. - `newImplementation` must be a contract." + } + }, + "version": 1 + }, + "userdoc": { + "kind": "user", + "methods": {}, + "version": 1 + }, + "storageLayout": { + "storage": [ + { + "astId": 3501, + "contract": "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol:UpgradeableBeacon", + "label": "_owner", + "offset": 0, + "slot": "0", + "type": "t_address" + }, + { + "astId": 4164, + "contract": "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol:UpgradeableBeacon", + "label": "_implementation", + "offset": 0, + "slot": "1", + "type": "t_address" + } + ], + "types": { + "t_address": { + "encoding": "inplace", + "label": "address", + "numberOfBytes": "20" + } + } + } +} diff --git a/deployments/bsctestnet/SpokeVTokenImpl.json b/deployments/bsctestnet/SpokeVTokenImpl.json new file mode 100644 index 000000000..d93c69cad --- /dev/null +++ b/deployments/bsctestnet/SpokeVTokenImpl.json @@ -0,0 +1,3336 @@ +{ + "address": "0x0A2399b0fC9A5921D0d843f2960589c1300Ac601", + "abi": [ + { + "inputs": [ + { + "internalType": "bool", + "name": "timeBased_", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "blocksPerYear_", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxBorrowRateMantissa_", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "actualAddAmount", + "type": "uint256" + } + ], + "name": "AddReservesFactorFreshCheck", + "type": "error" + }, + { + "inputs": [], + "name": "BorrowCashNotAvailable", + "type": "error" + }, + { + "inputs": [], + "name": "BorrowFreshnessCheck", + "type": "error" + }, + { + "inputs": [], + "name": "DelegateNotApproved", + "type": "error" + }, + { + "inputs": [], + "name": "ForceLiquidateBorrowUnauthorized", + "type": "error" + }, + { + "inputs": [], + "name": "HealBorrowUnauthorized", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidBlocksPerYear", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidTimeBasedConfiguration", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "errorCode", + "type": "uint256" + } + ], + "name": "LiquidateAccrueCollateralInterestFailed", + "type": "error" + }, + { + "inputs": [], + "name": "LiquidateCloseAmountIsUintMax", + "type": "error" + }, + { + "inputs": [], + "name": "LiquidateCloseAmountIsZero", + "type": "error" + }, + { + "inputs": [], + "name": "LiquidateCollateralFreshnessCheck", + "type": "error" + }, + { + "inputs": [], + "name": "LiquidateFreshnessCheck", + "type": "error" + }, + { + "inputs": [], + "name": "LiquidateLiquidatorIsBorrower", + "type": "error" + }, + { + "inputs": [], + "name": "LiquidateSeizeLiquidatorIsBorrower", + "type": "error" + }, + { + "inputs": [], + "name": "MintFreshnessCheck", + "type": "error" + }, + { + "inputs": [], + "name": "ProtocolSeizeShareTooBig", + "type": "error" + }, + { + "inputs": [], + "name": "RedeemFreshnessCheck", + "type": "error" + }, + { + "inputs": [], + "name": "RedeemTransferOutNotPossible", + "type": "error" + }, + { + "inputs": [], + "name": "ReduceReservesCashNotAvailable", + "type": "error" + }, + { + "inputs": [], + "name": "ReduceReservesCashValidation", + "type": "error" + }, + { + "inputs": [], + "name": "ReduceReservesFreshCheck", + "type": "error" + }, + { + "inputs": [], + "name": "RepayBorrowFreshnessCheck", + "type": "error" + }, + { + "inputs": [], + "name": "SetInterestRateModelFreshCheck", + "type": "error" + }, + { + "inputs": [], + "name": "SetReserveFactorBoundsCheck", + "type": "error" + }, + { + "inputs": [], + "name": "SetReserveFactorFreshCheck", + "type": "error" + }, + { + "inputs": [], + "name": "TransferNotAllowed", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "address", + "name": "calledContract", + "type": "address" + }, + { + "internalType": "string", + "name": "methodSignature", + "type": "string" + } + ], + "name": "Unauthorized", + "type": "error" + }, + { + "inputs": [], + "name": "ZeroAddressNotAllowed", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "cashPrior", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "interestAccumulated", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "borrowIndex", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "totalBorrows", + "type": "uint256" + } + ], + "name": "AccrueInterest", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "borrower", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "badDebtDelta", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "badDebtOld", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "badDebtNew", + "type": "uint256" + } + ], + "name": "BadDebtIncreased", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "badDebtOld", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "badDebtNew", + "type": "uint256" + } + ], + "name": "BadDebtRecovered", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "borrower", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "borrowAmount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "accountBorrows", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "totalBorrows", + "type": "uint256" + } + ], + "name": "Borrow", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "oldInternalCash", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newInternalCash", + "type": "uint256" + } + ], + "name": "CashSynced", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "payer", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "borrower", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "repayAmount", + "type": "uint256" + } + ], + "name": "HealBorrow", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint8", + "name": "version", + "type": "uint8" + } + ], + "name": "Initialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "liquidator", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "borrower", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "repayAmount", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "vTokenCollateral", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "seizeTokens", + "type": "uint256" + } + ], + "name": "LiquidateBorrow", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "minter", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "mintAmount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "mintTokens", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "accountBalance", + "type": "uint256" + } + ], + "name": "Mint", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "oldAccessControlManager", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "newAccessControlManager", + "type": "address" + } + ], + "name": "NewAccessControlManager", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "contract ComptrollerInterface", + "name": "oldComptroller", + "type": "address" + }, + { + "indexed": true, + "internalType": "contract ComptrollerInterface", + "name": "newComptroller", + "type": "address" + } + ], + "name": "NewComptroller", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "contract InterestRateModel", + "name": "oldInterestRateModel", + "type": "address" + }, + { + "indexed": true, + "internalType": "contract InterestRateModel", + "name": "newInterestRateModel", + "type": "address" + } + ], + "name": "NewMarketInterestRateModel", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "oldProtocolSeizeShareMantissa", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newProtocolSeizeShareMantissa", + "type": "uint256" + } + ], + "name": "NewProtocolSeizeShare", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "oldProtocolShareReserve", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newProtocolShareReserve", + "type": "address" + } + ], + "name": "NewProtocolShareReserve", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "oldReduceReservesBlockOrTimestampDelta", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newReduceReservesBlockOrTimestampDelta", + "type": "uint256" + } + ], + "name": "NewReduceReservesBlockDelta", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "oldReserveFactorMantissa", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newReserveFactorMantissa", + "type": "uint256" + } + ], + "name": "NewReserveFactor", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "oldShortfall", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newShortfall", + "type": "address" + } + ], + "name": "NewShortfallContract", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferStarted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "ProtocolSeize", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "redeemer", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "redeemAmount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "redeemTokens", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "accountBalance", + "type": "uint256" + } + ], + "name": "Redeem", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "payer", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "borrower", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "repayAmount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "accountBorrows", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "totalBorrows", + "type": "uint256" + } + ], + "name": "RepayBorrow", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "benefactor", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "addAmount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newTotalReserves", + "type": "uint256" + } + ], + "name": "ReservesAdded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "protocolShareReserve", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "reduceAmount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newTotalReserves", + "type": "uint256" + } + ], + "name": "SpreadReservesReduced", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "token", + "type": "address" + } + ], + "name": "SweepToken", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "inputs": [], + "name": "NO_ERROR", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "accessControlManager", + "outputs": [ + { + "internalType": "contract IAccessControlManagerV8", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "accrualBlockNumber", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "accrueInterest", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "addAmount", + "type": "uint256" + } + ], + "name": "addReserves", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "badDebt", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "recoveredAmount_", + "type": "uint256" + } + ], + "name": "badDebtRecovered", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "balanceOfUnderlying", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "blocksOrSecondsPerYear", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "borrowAmount", + "type": "uint256" + } + ], + "name": "borrow", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "borrowBalanceCurrent", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "borrowBalanceStored", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "borrower", + "type": "address" + }, + { + "internalType": "uint256", + "name": "borrowAmount", + "type": "uint256" + } + ], + "name": "borrowBehalf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "borrowIndex", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "borrowRatePerBlock", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "comptroller", + "outputs": [ + { + "internalType": "contract ComptrollerInterface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "subtractedValue", + "type": "uint256" + } + ], + "name": "decreaseAllowance", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "exchangeRateCurrent", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "exchangeRateStored", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "liquidator", + "type": "address" + }, + { + "internalType": "address", + "name": "borrower", + "type": "address" + }, + { + "internalType": "uint256", + "name": "repayAmount", + "type": "uint256" + }, + { + "internalType": "contract VTokenInterface", + "name": "vTokenCollateral", + "type": "address" + }, + { + "internalType": "bool", + "name": "skipLiquidityCheck", + "type": "bool" + } + ], + "name": "forceLiquidateBorrow", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "getAccountSnapshot", + "outputs": [ + { + "internalType": "uint256", + "name": "error", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "vTokenBalance", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "borrowBalance", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "exchangeRate", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getBlockNumberOrTimestamp", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getCash", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "payer", + "type": "address" + }, + { + "internalType": "address", + "name": "borrower", + "type": "address" + }, + { + "internalType": "uint256", + "name": "repayAmount", + "type": "uint256" + } + ], + "name": "healBorrow", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "addedValue", + "type": "uint256" + } + ], + "name": "increaseAllowance", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "underlying_", + "type": "address" + }, + { + "internalType": "contract ComptrollerInterface", + "name": "comptroller_", + "type": "address" + }, + { + "internalType": "contract InterestRateModel", + "name": "interestRateModel_", + "type": "address" + }, + { + "internalType": "uint256", + "name": "initialExchangeRateMantissa_", + "type": "uint256" + }, + { + "internalType": "string", + "name": "name_", + "type": "string" + }, + { + "internalType": "string", + "name": "symbol_", + "type": "string" + }, + { + "internalType": "uint8", + "name": "decimals_", + "type": "uint8" + }, + { + "internalType": "address", + "name": "admin_", + "type": "address" + }, + { + "internalType": "address", + "name": "accessControlManager_", + "type": "address" + }, + { + "components": [ + { + "internalType": "address", + "name": "shortfall", + "type": "address" + }, + { + "internalType": "address payable", + "name": "protocolShareReserve", + "type": "address" + } + ], + "internalType": "struct VTokenInterface.RiskManagementInit", + "name": "riskManagement", + "type": "tuple" + }, + { + "internalType": "uint256", + "name": "reserveFactorMantissa_", + "type": "uint256" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "interestRateModel", + "outputs": [ + { + "internalType": "contract InterestRateModel", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "internalCash", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "isTimeBased", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "isVToken", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "borrower", + "type": "address" + }, + { + "internalType": "uint256", + "name": "repayAmount", + "type": "uint256" + }, + { + "internalType": "contract VTokenInterface", + "name": "vTokenCollateral", + "type": "address" + } + ], + "name": "liquidateBorrow", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "mintAmount", + "type": "uint256" + } + ], + "name": "mint", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "minter", + "type": "address" + }, + { + "internalType": "uint256", + "name": "mintAmount", + "type": "uint256" + } + ], + "name": "mintBehalf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pendingOwner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "protocolSeizeShareMantissa", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "protocolShareReserve", + "outputs": [ + { + "internalType": "address payable", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "redeemTokens", + "type": "uint256" + } + ], + "name": "redeem", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "redeemer", + "type": "address" + }, + { + "internalType": "uint256", + "name": "redeemTokens", + "type": "uint256" + } + ], + "name": "redeemBehalf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "redeemAmount", + "type": "uint256" + } + ], + "name": "redeemUnderlying", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "redeemer", + "type": "address" + }, + { + "internalType": "uint256", + "name": "redeemAmount", + "type": "uint256" + } + ], + "name": "redeemUnderlyingBehalf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "reduceAmount", + "type": "uint256" + } + ], + "name": "reduceReserves", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "reduceReservesBlockDelta", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "reduceReservesBlockNumber", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "repayAmount", + "type": "uint256" + } + ], + "name": "repayBorrow", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "borrower", + "type": "address" + }, + { + "internalType": "uint256", + "name": "repayAmount", + "type": "uint256" + } + ], + "name": "repayBorrowBehalf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "reserveFactorMantissa", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "liquidator", + "type": "address" + }, + { + "internalType": "address", + "name": "borrower", + "type": "address" + }, + { + "internalType": "uint256", + "name": "seizeTokens", + "type": "uint256" + } + ], + "name": "seize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "accessControlManager_", + "type": "address" + } + ], + "name": "setAccessControlManager", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract InterestRateModel", + "name": "newInterestRateModel", + "type": "address" + } + ], + "name": "setInterestRateModel", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "newProtocolSeizeShareMantissa_", + "type": "uint256" + } + ], + "name": "setProtocolSeizeShare", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address payable", + "name": "protocolShareReserve_", + "type": "address" + } + ], + "name": "setProtocolShareReserve", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_newReduceReservesBlockOrTimestampDelta", + "type": "uint256" + } + ], + "name": "setReduceReservesBlockDelta", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "newReserveFactorMantissa", + "type": "uint256" + } + ], + "name": "setReserveFactor", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "shortfall_", + "type": "address" + } + ], + "name": "setShortfallContract", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "shortfall", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "supplyRatePerBlock", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IERC20Upgradeable", + "name": "token", + "type": "address" + } + ], + "name": "sweepToken", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "syncCash", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "totalBorrows", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalBorrowsCurrent", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "totalReserves", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "dst", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "src", + "type": "address" + }, + { + "internalType": "address", + "name": "dst", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "underlying", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + } + ], + "transactionHash": "0xefd7cb07c4a9df81582f0489ec383c0a3d24887674ae4cb78e4f245d08ab09b1", + "receipt": { + "to": null, + "from": "0x33C6476F88eeA28D7E7900F759B4597704Ef95B7", + "contractAddress": "0x0A2399b0fC9A5921D0d843f2960589c1300Ac601", + "transactionIndex": 0, + "gasUsed": "4497662", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000020000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0x7cde47f075bf3061e1dac33da1570a2710285168bb4429609701bdb5418aadc8", + "transactionHash": "0xefd7cb07c4a9df81582f0489ec383c0a3d24887674ae4cb78e4f245d08ab09b1", + "logs": [ + { + "transactionIndex": 0, + "blockNumber": 129845530, + "transactionHash": "0xefd7cb07c4a9df81582f0489ec383c0a3d24887674ae4cb78e4f245d08ab09b1", + "address": "0x0A2399b0fC9A5921D0d843f2960589c1300Ac601", + "topics": ["0x7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498"], + "data": "0x00000000000000000000000000000000000000000000000000000000000000ff", + "logIndex": 0, + "blockHash": "0x7cde47f075bf3061e1dac33da1570a2710285168bb4429609701bdb5418aadc8" + } + ], + "blockNumber": 129845530, + "cumulativeGasUsed": "4497662", + "status": 1, + "byzantium": true + }, + "args": [false, 70080000, "5000000000000"], + "numDeployments": 1, + "solcInputHash": "02cfe3974aa10474c22406a3f2870e89", + "metadata": "{\"compiler\":{\"version\":\"0.8.25+commit.b61c2a91\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[{\"internalType\":\"bool\",\"name\":\"timeBased_\",\"type\":\"bool\"},{\"internalType\":\"uint256\",\"name\":\"blocksPerYear_\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxBorrowRateMantissa_\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"actualAddAmount\",\"type\":\"uint256\"}],\"name\":\"AddReservesFactorFreshCheck\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"BorrowCashNotAvailable\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"BorrowFreshnessCheck\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"DelegateNotApproved\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ForceLiquidateBorrowUnauthorized\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"HealBorrowUnauthorized\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidBlocksPerYear\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidTimeBasedConfiguration\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"errorCode\",\"type\":\"uint256\"}],\"name\":\"LiquidateAccrueCollateralInterestFailed\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"LiquidateCloseAmountIsUintMax\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"LiquidateCloseAmountIsZero\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"LiquidateCollateralFreshnessCheck\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"LiquidateFreshnessCheck\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"LiquidateLiquidatorIsBorrower\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"LiquidateSeizeLiquidatorIsBorrower\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"MintFreshnessCheck\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ProtocolSeizeShareTooBig\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RedeemFreshnessCheck\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RedeemTransferOutNotPossible\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ReduceReservesCashNotAvailable\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ReduceReservesCashValidation\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ReduceReservesFreshCheck\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RepayBorrowFreshnessCheck\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"SetInterestRateModelFreshCheck\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"SetReserveFactorBoundsCheck\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"SetReserveFactorFreshCheck\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"TransferNotAllowed\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"calledContract\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"methodSignature\",\"type\":\"string\"}],\"name\":\"Unauthorized\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ZeroAddressNotAllowed\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"cashPrior\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"interestAccumulated\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"borrowIndex\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"totalBorrows\",\"type\":\"uint256\"}],\"name\":\"AccrueInterest\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"Approval\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"borrower\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"badDebtDelta\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"badDebtOld\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"badDebtNew\",\"type\":\"uint256\"}],\"name\":\"BadDebtIncreased\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"badDebtOld\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"badDebtNew\",\"type\":\"uint256\"}],\"name\":\"BadDebtRecovered\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"borrower\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"borrowAmount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"accountBorrows\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"totalBorrows\",\"type\":\"uint256\"}],\"name\":\"Borrow\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"oldInternalCash\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newInternalCash\",\"type\":\"uint256\"}],\"name\":\"CashSynced\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"payer\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"borrower\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"repayAmount\",\"type\":\"uint256\"}],\"name\":\"HealBorrow\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"version\",\"type\":\"uint8\"}],\"name\":\"Initialized\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"liquidator\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"borrower\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"repayAmount\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"vTokenCollateral\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"seizeTokens\",\"type\":\"uint256\"}],\"name\":\"LiquidateBorrow\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"minter\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"mintAmount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"mintTokens\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"accountBalance\",\"type\":\"uint256\"}],\"name\":\"Mint\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"oldAccessControlManager\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"newAccessControlManager\",\"type\":\"address\"}],\"name\":\"NewAccessControlManager\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"contract ComptrollerInterface\",\"name\":\"oldComptroller\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"contract ComptrollerInterface\",\"name\":\"newComptroller\",\"type\":\"address\"}],\"name\":\"NewComptroller\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"contract InterestRateModel\",\"name\":\"oldInterestRateModel\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"contract InterestRateModel\",\"name\":\"newInterestRateModel\",\"type\":\"address\"}],\"name\":\"NewMarketInterestRateModel\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"oldProtocolSeizeShareMantissa\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newProtocolSeizeShareMantissa\",\"type\":\"uint256\"}],\"name\":\"NewProtocolSeizeShare\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"oldProtocolShareReserve\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newProtocolShareReserve\",\"type\":\"address\"}],\"name\":\"NewProtocolShareReserve\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"oldReduceReservesBlockOrTimestampDelta\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newReduceReservesBlockOrTimestampDelta\",\"type\":\"uint256\"}],\"name\":\"NewReduceReservesBlockDelta\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"oldReserveFactorMantissa\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newReserveFactorMantissa\",\"type\":\"uint256\"}],\"name\":\"NewReserveFactor\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"oldShortfall\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newShortfall\",\"type\":\"address\"}],\"name\":\"NewShortfallContract\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousOwner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"OwnershipTransferStarted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousOwner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"ProtocolSeize\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"redeemer\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"redeemAmount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"redeemTokens\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"accountBalance\",\"type\":\"uint256\"}],\"name\":\"Redeem\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"payer\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"borrower\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"repayAmount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"accountBorrows\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"totalBorrows\",\"type\":\"uint256\"}],\"name\":\"RepayBorrow\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"benefactor\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"addAmount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newTotalReserves\",\"type\":\"uint256\"}],\"name\":\"ReservesAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"protocolShareReserve\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"reduceAmount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newTotalReserves\",\"type\":\"uint256\"}],\"name\":\"SpreadReservesReduced\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"SweepToken\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"Transfer\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"NO_ERROR\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"accessControlManager\",\"outputs\":[{\"internalType\":\"contract IAccessControlManagerV8\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"accrualBlockNumber\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"accrueInterest\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"addAmount\",\"type\":\"uint256\"}],\"name\":\"addReserves\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"}],\"name\":\"allowance\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"approve\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"badDebt\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"recoveredAmount_\",\"type\":\"uint256\"}],\"name\":\"badDebtRecovered\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"balanceOf\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"balanceOfUnderlying\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"blocksOrSecondsPerYear\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"borrowAmount\",\"type\":\"uint256\"}],\"name\":\"borrow\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"borrowBalanceCurrent\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"borrowBalanceStored\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"borrower\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"borrowAmount\",\"type\":\"uint256\"}],\"name\":\"borrowBehalf\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"borrowIndex\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"borrowRatePerBlock\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"comptroller\",\"outputs\":[{\"internalType\":\"contract ComptrollerInterface\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"decimals\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"subtractedValue\",\"type\":\"uint256\"}],\"name\":\"decreaseAllowance\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"exchangeRateCurrent\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"exchangeRateStored\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"liquidator\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"borrower\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"repayAmount\",\"type\":\"uint256\"},{\"internalType\":\"contract VTokenInterface\",\"name\":\"vTokenCollateral\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"skipLiquidityCheck\",\"type\":\"bool\"}],\"name\":\"forceLiquidateBorrow\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"getAccountSnapshot\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"error\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"vTokenBalance\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"borrowBalance\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"exchangeRate\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getBlockNumberOrTimestamp\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getCash\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"payer\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"borrower\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"repayAmount\",\"type\":\"uint256\"}],\"name\":\"healBorrow\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"addedValue\",\"type\":\"uint256\"}],\"name\":\"increaseAllowance\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"underlying_\",\"type\":\"address\"},{\"internalType\":\"contract ComptrollerInterface\",\"name\":\"comptroller_\",\"type\":\"address\"},{\"internalType\":\"contract InterestRateModel\",\"name\":\"interestRateModel_\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"initialExchangeRateMantissa_\",\"type\":\"uint256\"},{\"internalType\":\"string\",\"name\":\"name_\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"symbol_\",\"type\":\"string\"},{\"internalType\":\"uint8\",\"name\":\"decimals_\",\"type\":\"uint8\"},{\"internalType\":\"address\",\"name\":\"admin_\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"accessControlManager_\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"shortfall\",\"type\":\"address\"},{\"internalType\":\"address payable\",\"name\":\"protocolShareReserve\",\"type\":\"address\"}],\"internalType\":\"struct VTokenInterface.RiskManagementInit\",\"name\":\"riskManagement\",\"type\":\"tuple\"},{\"internalType\":\"uint256\",\"name\":\"reserveFactorMantissa_\",\"type\":\"uint256\"}],\"name\":\"initialize\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"interestRateModel\",\"outputs\":[{\"internalType\":\"contract InterestRateModel\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"internalCash\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"isTimeBased\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"isVToken\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"borrower\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"repayAmount\",\"type\":\"uint256\"},{\"internalType\":\"contract VTokenInterface\",\"name\":\"vTokenCollateral\",\"type\":\"address\"}],\"name\":\"liquidateBorrow\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"mintAmount\",\"type\":\"uint256\"}],\"name\":\"mint\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"minter\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"mintAmount\",\"type\":\"uint256\"}],\"name\":\"mintBehalf\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"name\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"pendingOwner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"protocolSeizeShareMantissa\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"protocolShareReserve\",\"outputs\":[{\"internalType\":\"address payable\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"redeemTokens\",\"type\":\"uint256\"}],\"name\":\"redeem\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"redeemer\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"redeemTokens\",\"type\":\"uint256\"}],\"name\":\"redeemBehalf\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"redeemAmount\",\"type\":\"uint256\"}],\"name\":\"redeemUnderlying\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"redeemer\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"redeemAmount\",\"type\":\"uint256\"}],\"name\":\"redeemUnderlyingBehalf\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"reduceAmount\",\"type\":\"uint256\"}],\"name\":\"reduceReserves\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"reduceReservesBlockDelta\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"reduceReservesBlockNumber\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"renounceOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"repayAmount\",\"type\":\"uint256\"}],\"name\":\"repayBorrow\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"borrower\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"repayAmount\",\"type\":\"uint256\"}],\"name\":\"repayBorrowBehalf\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"reserveFactorMantissa\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"liquidator\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"borrower\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"seizeTokens\",\"type\":\"uint256\"}],\"name\":\"seize\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"accessControlManager_\",\"type\":\"address\"}],\"name\":\"setAccessControlManager\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract InterestRateModel\",\"name\":\"newInterestRateModel\",\"type\":\"address\"}],\"name\":\"setInterestRateModel\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"newProtocolSeizeShareMantissa_\",\"type\":\"uint256\"}],\"name\":\"setProtocolSeizeShare\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address payable\",\"name\":\"protocolShareReserve_\",\"type\":\"address\"}],\"name\":\"setProtocolShareReserve\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_newReduceReservesBlockOrTimestampDelta\",\"type\":\"uint256\"}],\"name\":\"setReduceReservesBlockDelta\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"newReserveFactorMantissa\",\"type\":\"uint256\"}],\"name\":\"setReserveFactor\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"shortfall_\",\"type\":\"address\"}],\"name\":\"setShortfallContract\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"shortfall\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"supplyRatePerBlock\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contract IERC20Upgradeable\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"sweepToken\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"symbol\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"syncCash\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"totalBorrows\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"totalBorrowsCurrent\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"totalReserves\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"totalSupply\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"dst\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"transfer\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"src\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"dst\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"transferFrom\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"underlying\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"}],\"devdoc\":{\"author\":\"Venus\",\"events\":{\"BadDebtIncreased(address,uint256,uint256,uint256)\":{\"params\":{\"badDebtDelta\":\"amount of new bad debt recorded\",\"badDebtNew\":\"new bad debt value\",\"badDebtOld\":\"previous bad debt value\",\"borrower\":\"borrower to \\\"forgive\\\"\"}},\"BadDebtRecovered(uint256,uint256)\":{\"params\":{\"badDebtNew\":\"new bad debt value\",\"badDebtOld\":\"previous bad debt value\"}},\"Initialized(uint8)\":{\"details\":\"Triggered when the contract has been initialized or reinitialized.\"}},\"kind\":\"dev\",\"methods\":{\"acceptOwnership()\":{\"details\":\"The new owner accepts the ownership transfer.\"},\"accrueInterest()\":{\"custom:access\":\"Not restricted\",\"custom:event\":\"Emits AccrueInterest event on success\",\"details\":\"This calculates interest accrued from the last checkpointed slot(block or second) up to the current slot(block or second) and writes new checkpoint to storage and reduce spread reserves to protocol share reserve if currentSlot - reduceReservesBlockNumber >= slotDelta\",\"returns\":{\"_0\":\"Always NO_ERROR\"}},\"addReserves(uint256)\":{\"custom:access\":\"Not restricted\",\"custom:event\":\"Emits ReservesAdded event; may emit AccrueInterest\",\"params\":{\"addAmount\":\"The amount of underlying token to add as reserves\"}},\"allowance(address,address)\":{\"params\":{\"owner\":\"The address of the account which owns the tokens to be spent\",\"spender\":\"The address of the account which may transfer tokens\"},\"returns\":{\"_0\":\"amount The number of tokens allowed to be spent (type(uint256).max means infinite)\"}},\"approve(address,uint256)\":{\"custom:access\":\"Not restricted\",\"custom:error\":\"ZeroAddressNotAllowed is thrown when spender address is zero\",\"custom:event\":\"Emits Approval event\",\"details\":\"This will overwrite the approval amount for `spender` and is subject to issues noted [here](https://eips.ethereum.org/EIPS/eip-20#approve)\",\"params\":{\"amount\":\"The number of tokens that are approved (uint256.max means infinite)\",\"spender\":\"The address of the account which may transfer tokens\"},\"returns\":{\"_0\":\"success Whether or not the approval succeeded\"}},\"badDebtRecovered(uint256)\":{\"custom:access\":\"Only Shortfall contract\",\"custom:event\":\"Emits BadDebtRecovered event\",\"details\":\"Called only when bad debt is recovered from auction. Updates internal cash balance.\",\"params\":{\"recoveredAmount_\":\"The amount of bad debt recovered\"}},\"balanceOf(address)\":{\"params\":{\"owner\":\"The address of the account to query\"},\"returns\":{\"_0\":\"amount The number of tokens owned by `owner`\"}},\"balanceOfUnderlying(address)\":{\"details\":\"This also accrues interest in a transaction\",\"params\":{\"owner\":\"The address of the account to query\"},\"returns\":{\"_0\":\"amount The amount of underlying owned by `owner`\"}},\"borrow(uint256)\":{\"custom:access\":\"Not restricted\",\"custom:error\":\"BorrowCashNotAvailable is thrown when the protocol has insufficient cash\",\"custom:event\":\"Emits Borrow event; may emit AccrueInterest\",\"params\":{\"borrowAmount\":\"The amount of the underlying asset to borrow\"},\"returns\":{\"_0\":\"error Always NO_ERROR for compatibility with Venus core tooling\"}},\"borrowBalanceCurrent(address)\":{\"params\":{\"account\":\"The address whose balance should be calculated after updating borrowIndex\"},\"returns\":{\"_0\":\"borrowBalance The calculated balance\"}},\"borrowBalanceStored(address)\":{\"params\":{\"account\":\"The address whose balance should be calculated\"},\"returns\":{\"_0\":\"borrowBalance The calculated balance\"}},\"borrowBehalf(address,uint256)\":{\"custom:access\":\"Not restricted\",\"custom:error\":\"DelegateNotApproved is thrown if caller is not approved delegateBorrowCashNotAvailable is thrown when the protocol has insufficient cash\",\"custom:event\":\"Emits Borrow event; may emit AccrueInterest\",\"params\":{\"borrowAmount\":\"The amount of the underlying asset to borrow\",\"borrower\":\"The borrower, on behalf of whom to borrow\"},\"returns\":{\"_0\":\"error Always NO_ERROR for compatibility with Venus core tooling\"}},\"borrowRatePerBlock()\":{\"returns\":{\"_0\":\"rate The borrow interest rate per slot(block or second), scaled by 1e18\"}},\"constructor\":{\"custom:oz-upgrades-unsafe-allow\":\"constructor\",\"params\":{\"blocksPerYear_\":\"The number of blocks per year\",\"maxBorrowRateMantissa_\":\"The maximum value of borrowing rate mantissa\",\"timeBased_\":\"A boolean indicating whether the contract is based on time or block.\"}},\"decreaseAllowance(address,uint256)\":{\"custom:access\":\"Not restricted\",\"custom:error\":\"ZeroAddressNotAllowed is thrown when spender address is zero\",\"custom:event\":\"Emits Approval event\",\"params\":{\"spender\":\"The address of the account which may transfer tokens\",\"subtractedValue\":\"The number of tokens to remove from total approval\"},\"returns\":{\"_0\":\"success Whether or not the approval succeeded\"}},\"exchangeRateCurrent()\":{\"returns\":{\"_0\":\"exchangeRate Calculated exchange rate scaled by 1e18\"}},\"exchangeRateStored()\":{\"details\":\"This function does not accrue interest before calculating the exchange rate\",\"returns\":{\"_0\":\"exchangeRate Calculated exchange rate scaled by 1e18\"}},\"forceLiquidateBorrow(address,address,uint256,address,bool)\":{\"custom:access\":\"Only Comptroller\",\"custom:error\":\"ForceLiquidateBorrowUnauthorized is thrown when the request does not come from ComptrollerLiquidateAccrueCollateralInterestFailed is thrown when it is not possible to accrue interest on the collateral vTokenLiquidateCollateralFreshnessCheck is thrown when interest has not been accrued on the collateral vTokenLiquidateLiquidatorIsBorrower is thrown when trying to liquidate selfLiquidateCloseAmountIsZero is thrown when repayment amount is zeroLiquidateCloseAmountIsUintMax is thrown when repayment amount is UINT_MAX\",\"custom:event\":\"Emits LiquidateBorrow event; may emit AccrueInterest\",\"params\":{\"borrower\":\"The borrower of this vToken to be liquidated\",\"liquidator\":\"The address repaying the borrow and seizing collateral\",\"repayAmount\":\"The amount of the underlying borrowed asset to repay\",\"skipLiquidityCheck\":\"If set to true, allows to liquidate up to 100% of the borrow regardless of the account liquidity\",\"vTokenCollateral\":\"The market in which to seize collateral from the borrower\"}},\"getAccountSnapshot(address)\":{\"details\":\"This is used by comptroller to more efficiently perform liquidity checks.\",\"params\":{\"account\":\"Address of the account to snapshot\"},\"returns\":{\"borrowBalance\":\"Amount owed in terms of underlying\",\"error\":\"Always NO_ERROR for compatibility with Venus core tooling\",\"exchangeRate\":\"Stored exchange rate\",\"vTokenBalance\":\"User's balance of vTokens\"}},\"getBlockNumberOrTimestamp()\":{\"details\":\"Function to simply retrieve block number or block timestamp\",\"returns\":{\"_0\":\"Current block number or block timestamp\"}},\"getCash()\":{\"returns\":{\"_0\":\"cash The quantity of underlying asset tracked internally by this contract\"}},\"healBorrow(address,address,uint256)\":{\"custom:access\":\"Only Comptroller\",\"custom:error\":\"HealBorrowUnauthorized is thrown when the request does not come from Comptroller\",\"custom:event\":\"Emits RepayBorrow, BadDebtIncreased events; may emit AccrueInterest\",\"details\":\"This function does not call any Comptroller hooks (like \\\"healAllowed\\\"), because we assume the Comptroller does all the necessary checks before calling this function.\",\"params\":{\"borrower\":\"account to heal\",\"payer\":\"account who repays the debt\",\"repayAmount\":\"amount to repay\"}},\"increaseAllowance(address,uint256)\":{\"custom:access\":\"Not restricted\",\"custom:error\":\"ZeroAddressNotAllowed is thrown when spender address is zero\",\"custom:event\":\"Emits Approval event\",\"params\":{\"addedValue\":\"The number of additional tokens spender can transfer\",\"spender\":\"The address of the account which may transfer tokens\"},\"returns\":{\"_0\":\"success Whether or not the approval succeeded\"}},\"initialize(address,address,address,uint256,string,string,uint8,address,address,(address,address),uint256)\":{\"custom:error\":\"ZeroAddressNotAllowed is thrown when admin address is zeroZeroAddressNotAllowed is thrown when shortfall contract address is zeroZeroAddressNotAllowed is thrown when protocol share reserve address is zero\",\"params\":{\"accessControlManager_\":\"AccessControlManager contract address\",\"admin_\":\"Address of the administrator of this token\",\"comptroller_\":\"The address of the Comptroller\",\"decimals_\":\"ERC-20 decimal precision of this token\",\"initialExchangeRateMantissa_\":\"The initial exchange rate, scaled by 1e18\",\"interestRateModel_\":\"The address of the interest rate model\",\"name_\":\"ERC-20 name of this token\",\"reserveFactorMantissa_\":\"Percentage of borrow interest that goes to reserves (from 0 to 1e18)\",\"riskManagement\":\"Addresses of risk & income related contracts\",\"symbol_\":\"ERC-20 symbol of this token\",\"underlying_\":\"The address of the underlying asset\"}},\"isVToken()\":{\"returns\":{\"_0\":\"Always true\"}},\"liquidateBorrow(address,uint256,address)\":{\"custom:access\":\"Not restricted\",\"custom:error\":\"LiquidateAccrueCollateralInterestFailed is thrown when it is not possible to accrue interest on the collateral vTokenLiquidateCollateralFreshnessCheck is thrown when interest has not been accrued on the collateral vTokenLiquidateLiquidatorIsBorrower is thrown when trying to liquidate selfLiquidateCloseAmountIsZero is thrown when repayment amount is zeroLiquidateCloseAmountIsUintMax is thrown when repayment amount is UINT_MAX\",\"custom:event\":\"Emits LiquidateBorrow event; may emit AccrueInterest\",\"params\":{\"borrower\":\"The borrower of this vToken to be liquidated\",\"repayAmount\":\"The amount of the underlying borrowed asset to repay\",\"vTokenCollateral\":\"The market in which to seize collateral from the borrower\"},\"returns\":{\"_0\":\"error Always NO_ERROR for compatibility with Venus core tooling\"}},\"mint(uint256)\":{\"custom:access\":\"Not restricted\",\"custom:event\":\"Emits Mint and Transfer events; may emit AccrueInterest\",\"details\":\"Accrues interest whether or not the operation succeeds, unless reverted\",\"params\":{\"mintAmount\":\"The amount of the underlying asset to supply\"},\"returns\":{\"_0\":\"error Always NO_ERROR for compatibility with Venus core tooling\"}},\"mintBehalf(address,uint256)\":{\"custom:access\":\"Not restricted\",\"custom:error\":\"ZeroAddressNotAllowed is thrown when minter address is zero\",\"custom:event\":\"Emits Mint and Transfer events; may emit AccrueInterest\",\"details\":\"Accrues interest whether or not the operation succeeds, unless reverted\",\"params\":{\"mintAmount\":\"The amount of the underlying asset to supply\",\"minter\":\"User whom the supply will be attributed to\"},\"returns\":{\"_0\":\"error Always NO_ERROR for compatibility with Venus core tooling\"}},\"owner()\":{\"details\":\"Returns the address of the current owner.\"},\"pendingOwner()\":{\"details\":\"Returns the address of the pending owner.\"},\"redeem(uint256)\":{\"custom:access\":\"Not restricted\",\"custom:error\":\"RedeemTransferOutNotPossible is thrown when the protocol has insufficient cash\",\"custom:event\":\"Emits Redeem and Transfer events; may emit AccrueInterest\",\"details\":\"Accrues interest whether or not the operation succeeds, unless reverted\",\"params\":{\"redeemTokens\":\"The number of vTokens to redeem into underlying\"},\"returns\":{\"_0\":\"error Always NO_ERROR for compatibility with Venus core tooling\"}},\"redeemBehalf(address,uint256)\":{\"custom:access\":\"Not restricted\",\"custom:error\":\"InsufficientRedeemApproval is thrown when sender is not approved by the redeemer for the given amountRedeemTransferOutNotPossible is thrown when the protocol has insufficient cash\",\"custom:event\":\"Emits Redeem and Transfer events; may emit AccrueInterest\",\"details\":\"Accrues interest whether or not the operation succeeds, unless reverted\",\"params\":{\"redeemTokens\":\"The number of vTokens to redeem into underlying\",\"redeemer\":\"The user on behalf of whom to redeem\"},\"returns\":{\"_0\":\"error Always NO_ERROR for compatibility with Venus core tooling\"}},\"redeemUnderlying(uint256)\":{\"details\":\"Accrues interest whether or not the operation succeeds, unless reverted\",\"params\":{\"redeemAmount\":\"The amount of underlying to receive from redeeming vTokens\"},\"returns\":{\"_0\":\"error Always NO_ERROR for compatibility with Venus core tooling\"}},\"redeemUnderlyingBehalf(address,uint256)\":{\"custom:access\":\"Not restricted\",\"custom:error\":\"InsufficientRedeemApproval is thrown when sender is not approved by the redeemer for the given amount\",\"custom:event\":\"Emits Redeem and Transfer events; may emit AccrueInterest\",\"details\":\"Accrues interest whether or not the operation succeeds, unless reverted\",\"params\":{\"redeemAmount\":\"The amount of underlying to receive from redeeming vTokens\",\"redeemer\":\", on behalf of whom to redeem\"},\"returns\":{\"_0\":\"error Always NO_ERROR for compatibility with Venus core tooling\"}},\"reduceReserves(uint256)\":{\"custom:access\":\"Not restricted\",\"custom:error\":\"ReduceReservesCashNotAvailable is thrown when the vToken does not have sufficient cashReduceReservesCashValidation is thrown when trying to withdraw more cash than the reserves have\",\"custom:event\":\"Emits ReservesReduced event; may emit AccrueInterest\",\"details\":\"Gracefully return if reserves already reduced in accrueInterest\",\"params\":{\"reduceAmount\":\"Amount of reduction to reserves\"}},\"renounceOwnership()\":{\"details\":\"Leaves the contract without owner. It will not be possible to call `onlyOwner` functions. Can only be called by the current owner. NOTE: Renouncing ownership will leave the contract without an owner, thereby disabling any functionality that is only available to the owner.\"},\"repayBorrow(uint256)\":{\"custom:access\":\"Not restricted\",\"custom:event\":\"Emits RepayBorrow event; may emit AccrueInterest\",\"params\":{\"repayAmount\":\"The amount to repay, or type(uint256).max for the full outstanding amount\"},\"returns\":{\"_0\":\"error Always NO_ERROR for compatibility with Venus core tooling\"}},\"repayBorrowBehalf(address,uint256)\":{\"custom:access\":\"Not restricted\",\"custom:event\":\"Emits RepayBorrow event; may emit AccrueInterest\",\"params\":{\"borrower\":\"the account with the debt being payed off\",\"repayAmount\":\"The amount to repay, or type(uint256).max for the full outstanding amount\"},\"returns\":{\"_0\":\"error Always NO_ERROR for compatibility with Venus core tooling\"}},\"seize(address,address,uint256)\":{\"custom:access\":\"Not restricted\",\"custom:error\":\"LiquidateSeizeLiquidatorIsBorrower is thrown when trying to liquidate self\",\"custom:event\":\"Emits Transfer, ReservesAdded events\",\"details\":\"Will fail unless called by another vToken during the process of liquidation. It's absolutely critical to use msg.sender as the borrowed vToken and not a parameter.\",\"params\":{\"borrower\":\"The account having collateral seized\",\"liquidator\":\"The account receiving seized collateral\",\"seizeTokens\":\"The number of vTokens to seize\"}},\"setAccessControlManager(address)\":{\"custom:access\":\"Only Governance\",\"custom:event\":\"Emits NewAccessControlManager event\",\"details\":\"Admin function to set address of AccessControlManager\",\"params\":{\"accessControlManager_\":\"The new address of the AccessControlManager\"}},\"setInterestRateModel(address)\":{\"custom:access\":\"Controlled by AccessControlManager\",\"custom:error\":\"Unauthorized error is thrown when the call is not authorized by AccessControlManager\",\"custom:event\":\"Emits NewMarketInterestRateModel event; may emit AccrueInterest\",\"details\":\"Admin function to accrue interest and update the interest rate model\",\"params\":{\"newInterestRateModel\":\"the new interest rate model to use\"}},\"setProtocolSeizeShare(uint256)\":{\"custom:access\":\"Controlled by AccessControlManager\",\"custom:error\":\"Unauthorized error is thrown when the call is not authorized by AccessControlManagerProtocolSeizeShareTooBig is thrown when the new seize share is too high\",\"custom:event\":\"Emits NewProtocolSeizeShare event on success\",\"details\":\"must be equal or less than liquidation incentive - 1\",\"params\":{\"newProtocolSeizeShareMantissa_\":\"new protocol share mantissa\"}},\"setProtocolShareReserve(address)\":{\"custom:access\":\"Only Governance\",\"custom:error\":\"ZeroAddressNotAllowed is thrown when protocol share reserve address is zero\",\"params\":{\"protocolShareReserve_\":\"The address of the protocol share reserve contract\"}},\"setReduceReservesBlockDelta(uint256)\":{\"custom:access\":\"Only Governance\",\"params\":{\"_newReduceReservesBlockOrTimestampDelta\":\"slot(block or second) difference value\"}},\"setReserveFactor(uint256)\":{\"custom:access\":\"Controlled by AccessControlManager\",\"custom:error\":\"Unauthorized error is thrown when the call is not authorized by AccessControlManagerSetReserveFactorBoundsCheck is thrown when the new reserve factor is too high\",\"custom:event\":\"Emits NewReserveFactor event; may emit AccrueInterest\",\"details\":\"Admin function to accrue interest and set a new reserve factor\",\"params\":{\"newReserveFactorMantissa\":\"New reserve factor (from 0 to 1e18)\"}},\"setShortfallContract(address)\":{\"custom:access\":\"Only Governance\",\"custom:error\":\"ZeroAddressNotAllowed is thrown when shortfall contract address is zero\",\"params\":{\"shortfall_\":\"The address of the shortfall contract\"}},\"supplyRatePerBlock()\":{\"returns\":{\"_0\":\"rate The supply interest rate per slot(block or second), scaled by 1e18\"}},\"sweepToken(address)\":{\"custom:access\":\"Only Governance\",\"params\":{\"token\":\"The address of the ERC-20 token to sweep\"}},\"syncCash()\":{\"custom:access\":\"Controlled by AccessControlManager\",\"custom:event\":\"Emits CashSynced\",\"details\":\"Used for one-time migration after upgrade to initialize internalCash\"},\"totalBorrowsCurrent()\":{\"returns\":{\"_0\":\"totalBorrows The total borrows with interest\"}},\"transfer(address,uint256)\":{\"custom:access\":\"Not restricted\",\"custom:error\":\"TransferNotAllowed is thrown if trying to transfer to self\",\"custom:event\":\"Emits Transfer event on success\",\"params\":{\"amount\":\"The number of tokens to transfer\",\"dst\":\"The address of the destination account\"},\"returns\":{\"_0\":\"success True if the transfer succeeded, reverts otherwise\"}},\"transferFrom(address,address,uint256)\":{\"custom:access\":\"Not restricted\",\"custom:error\":\"TransferNotAllowed is thrown if trying to transfer to self\",\"custom:event\":\"Emits Transfer event on success\",\"params\":{\"amount\":\"The number of tokens to transfer\",\"dst\":\"The address of the destination account\",\"src\":\"The address of the source account\"},\"returns\":{\"_0\":\"success True if the transfer succeeded, reverts otherwise\"}},\"transferOwnership(address)\":{\"details\":\"Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one. Can only be called by the current owner.\"}},\"stateVariables\":{\"MAX_BORROW_RATE_MANTISSA\":{\"custom:oz-upgrades-unsafe-allow\":\"state-variable-immutable\"}},\"title\":\"VToken\",\"version\":1},\"userdoc\":{\"errors\":{\"InvalidBlocksPerYear()\":[{\"notice\":\"Thrown when blocks per year is invalid\"}],\"InvalidTimeBasedConfiguration()\":[{\"notice\":\"Thrown when time based but blocks per year is provided\"}],\"Unauthorized(address,address,string)\":[{\"notice\":\"Thrown when the action is prohibited by AccessControlManager\"}],\"ZeroAddressNotAllowed()\":[{\"notice\":\"Thrown if the supplied address is a zero address where it is not allowed\"}]},\"events\":{\"AccrueInterest(uint256,uint256,uint256,uint256)\":{\"notice\":\"Event emitted when interest is accrued\"},\"Approval(address,address,uint256)\":{\"notice\":\"EIP20 Approval event\"},\"BadDebtIncreased(address,uint256,uint256,uint256)\":{\"notice\":\"Event emitted when bad debt is accumulated on a market\"},\"BadDebtRecovered(uint256,uint256)\":{\"notice\":\"Event emitted when bad debt is recovered via an auction\"},\"Borrow(address,uint256,uint256,uint256)\":{\"notice\":\"Event emitted when underlying is borrowed\"},\"CashSynced(uint256,uint256)\":{\"notice\":\"Event emitted when internalCash is synced with actual token balance\"},\"HealBorrow(address,address,uint256)\":{\"notice\":\"Event emitted when healing the borrow\"},\"LiquidateBorrow(address,address,uint256,address,uint256)\":{\"notice\":\"Event emitted when a borrow is liquidated\"},\"Mint(address,uint256,uint256,uint256)\":{\"notice\":\"Event emitted when tokens are minted\"},\"NewAccessControlManager(address,address)\":{\"notice\":\"Emitted when access control manager contract address is changed\"},\"NewComptroller(address,address)\":{\"notice\":\"Event emitted when comptroller is changed\"},\"NewMarketInterestRateModel(address,address)\":{\"notice\":\"Event emitted when interestRateModel is changed\"},\"NewProtocolSeizeShare(uint256,uint256)\":{\"notice\":\"Event emitted when protocol seize share is changed\"},\"NewProtocolShareReserve(address,address)\":{\"notice\":\"Event emitted when protocol share reserve contract address is changed\"},\"NewReduceReservesBlockDelta(uint256,uint256)\":{\"notice\":\"Event emitted when reduce reserves slot (block or second) delta is changed\"},\"NewReserveFactor(uint256,uint256)\":{\"notice\":\"Event emitted when the reserve factor is changed\"},\"NewShortfallContract(address,address)\":{\"notice\":\"Event emitted when shortfall contract address is changed\"},\"ProtocolSeize(address,address,uint256)\":{\"notice\":\"Event emitted when liquidation reserves are reduced\"},\"Redeem(address,uint256,uint256,uint256)\":{\"notice\":\"Event emitted when tokens are redeemed\"},\"RepayBorrow(address,address,uint256,uint256,uint256)\":{\"notice\":\"Event emitted when a borrow is repaid\"},\"ReservesAdded(address,uint256,uint256)\":{\"notice\":\"Event emitted when the reserves are added\"},\"SpreadReservesReduced(address,uint256,uint256)\":{\"notice\":\"Event emitted when the spread reserves are reduced\"},\"SweepToken(address)\":{\"notice\":\"Event emitted when tokens are swept\"},\"Transfer(address,address,uint256)\":{\"notice\":\"EIP20 Transfer event\"}},\"kind\":\"user\",\"methods\":{\"accessControlManager()\":{\"notice\":\"Returns the address of the access control manager contract\"},\"accrualBlockNumber()\":{\"notice\":\"Slot(block or second) number that interest was last accrued at\"},\"accrueInterest()\":{\"notice\":\"Applies accrued interest to total borrows and reserves\"},\"addReserves(uint256)\":{\"notice\":\"The sender adds to reserves.\"},\"allowance(address,address)\":{\"notice\":\"Get the current allowance from `owner` for `spender`\"},\"approve(address,uint256)\":{\"notice\":\"Approve `spender` to transfer up to `amount` from `src`\"},\"badDebt()\":{\"notice\":\"Total bad debt of the market\"},\"badDebtRecovered(uint256)\":{\"notice\":\"Updates bad debt\"},\"balanceOf(address)\":{\"notice\":\"Get the token balance of the `owner`\"},\"balanceOfUnderlying(address)\":{\"notice\":\"Get the underlying balance of the `owner`\"},\"blocksOrSecondsPerYear()\":{\"notice\":\"Stores blocksPerYear if isTimeBased is true else secondsPerYear is stored\"},\"borrow(uint256)\":{\"notice\":\"Sender borrows assets from the protocol to their own address\"},\"borrowBalanceCurrent(address)\":{\"notice\":\"Accrue interest to updated borrowIndex and then calculate account's borrow balance using the updated borrowIndex\"},\"borrowBalanceStored(address)\":{\"notice\":\"Return the borrow balance of account based on stored data\"},\"borrowBehalf(address,uint256)\":{\"notice\":\"Sender borrows assets on behalf of some other address. This function is only available for senders, explicitly marked as delegates of the borrower using `comptroller.updateDelegate`\"},\"borrowIndex()\":{\"notice\":\"Accumulator of the total earned interest rate since the opening of the market\"},\"borrowRatePerBlock()\":{\"notice\":\"Returns the current per slot(block or second) borrow interest rate for this vToken\"},\"comptroller()\":{\"notice\":\"Contract which oversees inter-vToken operations\"},\"decimals()\":{\"notice\":\"EIP-20 token decimals for this token\"},\"decreaseAllowance(address,uint256)\":{\"notice\":\"Decreases approval for `spender`\"},\"exchangeRateCurrent()\":{\"notice\":\"Accrue interest then return the up-to-date exchange rate\"},\"exchangeRateStored()\":{\"notice\":\"Calculates the exchange rate from the underlying to the VToken\"},\"forceLiquidateBorrow(address,address,uint256,address,bool)\":{\"notice\":\"The extended version of liquidations, callable only by Comptroller. May skip the close factor check. The collateral seized is transferred to the liquidator.\"},\"getAccountSnapshot(address)\":{\"notice\":\"Get a snapshot of the account's balances, and the cached exchange rate\"},\"getCash()\":{\"notice\":\"Get the internally tracked cash balance of this vToken in the underlying asset\"},\"healBorrow(address,address,uint256)\":{\"notice\":\"Repays a certain amount of debt, treats the rest of the borrow as bad debt, essentially \\\"forgiving\\\" the borrower. Healing is a situation that should rarely happen. However, some pools may list risky assets or be configured improperly \\u2013 we want to still handle such cases gracefully. We assume that Comptroller does the seizing, so this function is only available to Comptroller.\"},\"increaseAllowance(address,uint256)\":{\"notice\":\"Increase approval for `spender`\"},\"initialize(address,address,address,uint256,string,string,uint8,address,address,(address,address),uint256)\":{\"notice\":\"Construct a new money market\"},\"interestRateModel()\":{\"notice\":\"Model which tells what the current interest rate should be\"},\"internalCash()\":{\"notice\":\"Tracked internal cash balance, immune to direct token transfers (donation attacks)\"},\"isTimeBased()\":{\"notice\":\"Acknowledges if a contract is time based or not\"},\"isVToken()\":{\"notice\":\"Indicator that this is a VToken contract (for inspection)\"},\"liquidateBorrow(address,uint256,address)\":{\"notice\":\"The sender liquidates the borrowers collateral. The collateral seized is transferred to the liquidator.\"},\"mint(uint256)\":{\"notice\":\"Sender supplies assets into the market and receives vTokens in exchange\"},\"mintBehalf(address,uint256)\":{\"notice\":\"Sender calls on-behalf of minter. minter supplies assets into the market and receives vTokens in exchange\"},\"name()\":{\"notice\":\"EIP-20 token name for this token\"},\"protocolSeizeShareMantissa()\":{\"notice\":\"Share of seized collateral that is added to reserves\"},\"protocolShareReserve()\":{\"notice\":\"Protocol share Reserve contract address\"},\"redeem(uint256)\":{\"notice\":\"Sender redeems vTokens in exchange for the underlying asset\"},\"redeemBehalf(address,uint256)\":{\"notice\":\"Sender redeems assets on behalf of some other address. This function is only available for senders, explicitly marked as delegates of the supplier using `comptroller.updateDelegate`\"},\"redeemUnderlying(uint256)\":{\"notice\":\"Sender redeems vTokens in exchange for a specified amount of underlying asset\"},\"redeemUnderlyingBehalf(address,uint256)\":{\"notice\":\"Sender redeems underlying assets on behalf of some other address. This function is only available for senders, explicitly marked as delegates of the supplier using `comptroller.updateDelegate`\"},\"reduceReserves(uint256)\":{\"notice\":\"Accrues interest and reduces reserves by transferring to the protocol reserve contract\"},\"reduceReservesBlockDelta()\":{\"notice\":\"delta slot (block or second) after which reserves will be reduced\"},\"reduceReservesBlockNumber()\":{\"notice\":\"last slot (block or second) number at which reserves were reduced\"},\"repayBorrow(uint256)\":{\"notice\":\"Sender repays their own borrow\"},\"repayBorrowBehalf(address,uint256)\":{\"notice\":\"Sender repays a borrow belonging to borrower\"},\"reserveFactorMantissa()\":{\"notice\":\"Fraction of interest currently set aside for reserves\"},\"seize(address,address,uint256)\":{\"notice\":\"Transfers collateral tokens (this market) to the liquidator.\"},\"setAccessControlManager(address)\":{\"notice\":\"Sets the address of AccessControlManager\"},\"setInterestRateModel(address)\":{\"notice\":\"accrues interest and updates the interest rate model using _setInterestRateModelFresh\"},\"setProtocolSeizeShare(uint256)\":{\"notice\":\"sets protocol share accumulated from liquidations\"},\"setProtocolShareReserve(address)\":{\"notice\":\"Sets protocol share reserve contract address\"},\"setReduceReservesBlockDelta(uint256)\":{\"notice\":\"A public function to set new threshold of slot(block or second) difference after which funds will be sent to the protocol share reserve\"},\"setReserveFactor(uint256)\":{\"notice\":\"accrues interest and sets a new reserve factor for the protocol using _setReserveFactorFresh\"},\"setShortfallContract(address)\":{\"notice\":\"Sets shortfall contract address\"},\"shortfall()\":{\"notice\":\"Storage of Shortfall contract address\"},\"supplyRatePerBlock()\":{\"notice\":\"Returns the current per-slot(block or second) supply interest rate for this v\"},\"sweepToken(address)\":{\"notice\":\"A public function to sweep accidental ERC-20 transfers to this contract. Tokens are sent to admin (timelock)\"},\"symbol()\":{\"notice\":\"EIP-20 token symbol for this token\"},\"syncCash()\":{\"notice\":\"Sync internalCash with the actual underlying token balance\"},\"totalBorrows()\":{\"notice\":\"Total amount of outstanding borrows of the underlying in this market\"},\"totalBorrowsCurrent()\":{\"notice\":\"Returns the current total borrows plus accrued interest\"},\"totalReserves()\":{\"notice\":\"Total amount of reserves of the underlying held in this market\"},\"totalSupply()\":{\"notice\":\"Total number of tokens in circulation\"},\"transfer(address,uint256)\":{\"notice\":\"Transfer `amount` tokens from `msg.sender` to `dst`\"},\"transferFrom(address,address,uint256)\":{\"notice\":\"Transfer `amount` tokens from `src` to `dst`\"},\"underlying()\":{\"notice\":\"Underlying asset for this VToken\"}},\"notice\":\"Each asset that is supported by a pool is integrated through an instance of the `VToken` contract. As outlined in the protocol overview, each isolated pool creates its own `vToken` corresponding to an asset. Within a given pool, each included `vToken` is referred to as a market of the pool. The main actions a user regularly interacts with in a market are: - mint/redeem of vTokens; - transfer of vTokens; - borrow/repay a loan on an underlying asset; - liquidate a borrow or liquidate/heal an account. A user supplies the underlying asset to a pool by minting `vTokens`, where the corresponding `vToken` amount is determined by the `exchangeRate`. The `exchangeRate` will change over time, dependent on a number of factors, some of which accrue interest. Additionally, once users have minted `vToken` in a pool, they can borrow any asset in the isolated pool by using their `vToken` as collateral. In order to borrow an asset or use a `vToken` as collateral, the user must be entered into each corresponding market (else, the `vToken` will not be considered collateral for a borrow). Note that a user may borrow up to a portion of their collateral determined by the market\\u2019s collateral factor. However, if their borrowed amount exceeds an amount calculated using the market\\u2019s corresponding liquidation threshold, the borrow is eligible for liquidation. When a user repays a borrow, they must also pay off interest accrued on the borrow. The Venus protocol includes unique mechanisms for healing an account and liquidating an account. These actions are performed in the `Comptroller` and consider all borrows and collateral for which a given account is entered within a market. These functions may only be called on an account with a total collateral amount that is no larger than a universal `minLiquidatableCollateral` value, which is used for all markets within a `Comptroller`. Both functions settle all of an account\\u2019s borrows, but `healAccount()` may add `badDebt` to a vToken. For more detail, see the description of `healAccount()` and `liquidateAccount()` in the `Comptroller` summary section below.\",\"version\":1}},\"settings\":{\"compilationTarget\":{\"contracts/VToken.sol\":\"VToken\"},\"evmVersion\":\"paris\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\",\"useLiteralContent\":true},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[]},\"sources\":{\"@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable2Step.sol)\\n\\npragma solidity ^0.8.0;\\n\\nimport \\\"./OwnableUpgradeable.sol\\\";\\nimport {Initializable} from \\\"../proxy/utils/Initializable.sol\\\";\\n\\n/**\\n * @dev Contract module which provides access control mechanism, where\\n * there is an account (an owner) that can be granted exclusive access to\\n * specific functions.\\n *\\n * By default, the owner account will be the one that deploys the contract. This\\n * can later be changed with {transferOwnership} and {acceptOwnership}.\\n *\\n * This module is used through inheritance. It will make available all functions\\n * from parent (Ownable).\\n */\\nabstract contract Ownable2StepUpgradeable is Initializable, OwnableUpgradeable {\\n address private _pendingOwner;\\n\\n event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner);\\n\\n function __Ownable2Step_init() internal onlyInitializing {\\n __Ownable_init_unchained();\\n }\\n\\n function __Ownable2Step_init_unchained() internal onlyInitializing {\\n }\\n /**\\n * @dev Returns the address of the pending owner.\\n */\\n function pendingOwner() public view virtual returns (address) {\\n return _pendingOwner;\\n }\\n\\n /**\\n * @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one.\\n * Can only be called by the current owner.\\n */\\n function transferOwnership(address newOwner) public virtual override onlyOwner {\\n _pendingOwner = newOwner;\\n emit OwnershipTransferStarted(owner(), newOwner);\\n }\\n\\n /**\\n * @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner.\\n * Internal function without access restriction.\\n */\\n function _transferOwnership(address newOwner) internal virtual override {\\n delete _pendingOwner;\\n super._transferOwnership(newOwner);\\n }\\n\\n /**\\n * @dev The new owner accepts the ownership transfer.\\n */\\n function acceptOwnership() public virtual {\\n address sender = _msgSender();\\n require(pendingOwner() == sender, \\\"Ownable2Step: caller is not the new owner\\\");\\n _transferOwnership(sender);\\n }\\n\\n /**\\n * @dev This empty reserved space is put in place to allow future versions to add new\\n * variables without shifting down storage in the inheritance chain.\\n * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\\n */\\n uint256[49] private __gap;\\n}\\n\",\"keccak256\":\"0x9140dabc466abab21b48b72dbda26736b1183a310d0e677d3719d201df026510\",\"license\":\"MIT\"},\"@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)\\n\\npragma solidity ^0.8.0;\\n\\nimport \\\"../utils/ContextUpgradeable.sol\\\";\\nimport {Initializable} from \\\"../proxy/utils/Initializable.sol\\\";\\n\\n/**\\n * @dev Contract module which provides a basic access control mechanism, where\\n * there is an account (an owner) that can be granted exclusive access to\\n * specific functions.\\n *\\n * By default, the owner account will be the one that deploys the contract. This\\n * can later be changed with {transferOwnership}.\\n *\\n * This module is used through inheritance. It will make available the modifier\\n * `onlyOwner`, which can be applied to your functions to restrict their use to\\n * the owner.\\n */\\nabstract contract OwnableUpgradeable is Initializable, ContextUpgradeable {\\n address private _owner;\\n\\n event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\\n\\n /**\\n * @dev Initializes the contract setting the deployer as the initial owner.\\n */\\n function __Ownable_init() internal onlyInitializing {\\n __Ownable_init_unchained();\\n }\\n\\n function __Ownable_init_unchained() internal onlyInitializing {\\n _transferOwnership(_msgSender());\\n }\\n\\n /**\\n * @dev Throws if called by any account other than the owner.\\n */\\n modifier onlyOwner() {\\n _checkOwner();\\n _;\\n }\\n\\n /**\\n * @dev Returns the address of the current owner.\\n */\\n function owner() public view virtual returns (address) {\\n return _owner;\\n }\\n\\n /**\\n * @dev Throws if the sender is not the owner.\\n */\\n function _checkOwner() internal view virtual {\\n require(owner() == _msgSender(), \\\"Ownable: caller is not the owner\\\");\\n }\\n\\n /**\\n * @dev Leaves the contract without owner. It will not be possible to call\\n * `onlyOwner` functions. Can only be called by the current owner.\\n *\\n * NOTE: Renouncing ownership will leave the contract without an owner,\\n * thereby disabling any functionality that is only available to the owner.\\n */\\n function renounceOwnership() public virtual onlyOwner {\\n _transferOwnership(address(0));\\n }\\n\\n /**\\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\\n * Can only be called by the current owner.\\n */\\n function transferOwnership(address newOwner) public virtual onlyOwner {\\n require(newOwner != address(0), \\\"Ownable: new owner is the zero address\\\");\\n _transferOwnership(newOwner);\\n }\\n\\n /**\\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\\n * Internal function without access restriction.\\n */\\n function _transferOwnership(address newOwner) internal virtual {\\n address oldOwner = _owner;\\n _owner = newOwner;\\n emit OwnershipTransferred(oldOwner, newOwner);\\n }\\n\\n /**\\n * @dev This empty reserved space is put in place to allow future versions to add new\\n * variables without shifting down storage in the inheritance chain.\\n * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\\n */\\n uint256[49] private __gap;\\n}\\n\",\"keccak256\":\"0x359a1ab89b46b9aba7bcad3fb651924baf4893d15153049b9976b0fc9be1358e\",\"license\":\"MIT\"},\"@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts (last updated v4.9.0) (proxy/utils/Initializable.sol)\\n\\npragma solidity ^0.8.2;\\n\\nimport \\\"../../utils/AddressUpgradeable.sol\\\";\\n\\n/**\\n * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed\\n * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an\\n * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer\\n * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.\\n *\\n * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be\\n * reused. This mechanism prevents re-execution of each \\\"step\\\" but allows the creation of new initialization steps in\\n * case an upgrade adds a module that needs to be initialized.\\n *\\n * For example:\\n *\\n * [.hljs-theme-light.nopadding]\\n * ```solidity\\n * contract MyToken is ERC20Upgradeable {\\n * function initialize() initializer public {\\n * __ERC20_init(\\\"MyToken\\\", \\\"MTK\\\");\\n * }\\n * }\\n *\\n * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {\\n * function initializeV2() reinitializer(2) public {\\n * __ERC20Permit_init(\\\"MyToken\\\");\\n * }\\n * }\\n * ```\\n *\\n * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as\\n * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.\\n *\\n * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure\\n * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.\\n *\\n * [CAUTION]\\n * ====\\n * Avoid leaving a contract uninitialized.\\n *\\n * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation\\n * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke\\n * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:\\n *\\n * [.hljs-theme-light.nopadding]\\n * ```\\n * /// @custom:oz-upgrades-unsafe-allow constructor\\n * constructor() {\\n * _disableInitializers();\\n * }\\n * ```\\n * ====\\n */\\nabstract contract Initializable {\\n /**\\n * @dev Indicates that the contract has been initialized.\\n * @custom:oz-retyped-from bool\\n */\\n uint8 private _initialized;\\n\\n /**\\n * @dev Indicates that the contract is in the process of being initialized.\\n */\\n bool private _initializing;\\n\\n /**\\n * @dev Triggered when the contract has been initialized or reinitialized.\\n */\\n event Initialized(uint8 version);\\n\\n /**\\n * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,\\n * `onlyInitializing` functions can be used to initialize parent contracts.\\n *\\n * Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a\\n * constructor.\\n *\\n * Emits an {Initialized} event.\\n */\\n modifier initializer() {\\n bool isTopLevelCall = !_initializing;\\n require(\\n (isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1),\\n \\\"Initializable: contract is already initialized\\\"\\n );\\n _initialized = 1;\\n if (isTopLevelCall) {\\n _initializing = true;\\n }\\n _;\\n if (isTopLevelCall) {\\n _initializing = false;\\n emit Initialized(1);\\n }\\n }\\n\\n /**\\n * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the\\n * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be\\n * used to initialize parent contracts.\\n *\\n * A reinitializer may be used after the original initialization step. This is essential to configure modules that\\n * are added through upgrades and that require initialization.\\n *\\n * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`\\n * cannot be nested. If one is invoked in the context of another, execution will revert.\\n *\\n * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in\\n * a contract, executing them in the right order is up to the developer or operator.\\n *\\n * WARNING: setting the version to 255 will prevent any future reinitialization.\\n *\\n * Emits an {Initialized} event.\\n */\\n modifier reinitializer(uint8 version) {\\n require(!_initializing && _initialized < version, \\\"Initializable: contract is already initialized\\\");\\n _initialized = version;\\n _initializing = true;\\n _;\\n _initializing = false;\\n emit Initialized(version);\\n }\\n\\n /**\\n * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the\\n * {initializer} and {reinitializer} modifiers, directly or indirectly.\\n */\\n modifier onlyInitializing() {\\n require(_initializing, \\\"Initializable: contract is not initializing\\\");\\n _;\\n }\\n\\n /**\\n * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.\\n * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized\\n * to any version. It is recommended to use this to lock implementation contracts that are designed to be called\\n * through proxies.\\n *\\n * Emits an {Initialized} event the first time it is successfully executed.\\n */\\n function _disableInitializers() internal virtual {\\n require(!_initializing, \\\"Initializable: contract is initializing\\\");\\n if (_initialized != type(uint8).max) {\\n _initialized = type(uint8).max;\\n emit Initialized(type(uint8).max);\\n }\\n }\\n\\n /**\\n * @dev Returns the highest version that has been initialized. See {reinitializer}.\\n */\\n function _getInitializedVersion() internal view returns (uint8) {\\n return _initialized;\\n }\\n\\n /**\\n * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.\\n */\\n function _isInitializing() internal view returns (bool) {\\n return _initializing;\\n }\\n}\\n\",\"keccak256\":\"0x89be10e757d242e9b18d5a32c9fbe2019f6d63052bbe46397a430a1d60d7f794\",\"license\":\"MIT\"},\"@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev Interface of the ERC20 standard as defined in the EIP.\\n */\\ninterface IERC20Upgradeable {\\n /**\\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\\n * another (`to`).\\n *\\n * Note that `value` may be zero.\\n */\\n event Transfer(address indexed from, address indexed to, uint256 value);\\n\\n /**\\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\\n * a call to {approve}. `value` is the new allowance.\\n */\\n event Approval(address indexed owner, address indexed spender, uint256 value);\\n\\n /**\\n * @dev Returns the amount of tokens in existence.\\n */\\n function totalSupply() external view returns (uint256);\\n\\n /**\\n * @dev Returns the amount of tokens owned by `account`.\\n */\\n function balanceOf(address account) external view returns (uint256);\\n\\n /**\\n * @dev Moves `amount` tokens from the caller's account to `to`.\\n *\\n * Returns a boolean value indicating whether the operation succeeded.\\n *\\n * Emits a {Transfer} event.\\n */\\n function transfer(address to, uint256 amount) external returns (bool);\\n\\n /**\\n * @dev Returns the remaining number of tokens that `spender` will be\\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\\n * zero by default.\\n *\\n * This value changes when {approve} or {transferFrom} are called.\\n */\\n function allowance(address owner, address spender) external view returns (uint256);\\n\\n /**\\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\\n *\\n * Returns a boolean value indicating whether the operation succeeded.\\n *\\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\\n * that someone may use both the old and the new allowance by unfortunate\\n * transaction ordering. One possible solution to mitigate this race\\n * condition is to first reduce the spender's allowance to 0 and set the\\n * desired value afterwards:\\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\\n *\\n * Emits an {Approval} event.\\n */\\n function approve(address spender, uint256 amount) external returns (bool);\\n\\n /**\\n * @dev Moves `amount` tokens from `from` to `to` using the\\n * allowance mechanism. `amount` is then deducted from the caller's\\n * allowance.\\n *\\n * Returns a boolean value indicating whether the operation succeeded.\\n *\\n * Emits a {Transfer} event.\\n */\\n function transferFrom(address from, address to, uint256 amount) external returns (bool);\\n}\\n\",\"keccak256\":\"0x0e1f0f5f62f67a881cd1a9597acbc0a5e4071f3c2c10449a183b922ae7272e3f\",\"license\":\"MIT\"},\"@openzeppelin/contracts-upgradeable/token/ERC20/extensions/IERC20PermitUpgradeable.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts (last updated v4.9.4) (token/ERC20/extensions/IERC20Permit.sol)\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in\\n * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].\\n *\\n * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by\\n * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't\\n * need to send a transaction, and thus is not required to hold Ether at all.\\n *\\n * ==== Security Considerations\\n *\\n * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature\\n * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be\\n * considered as an intention to spend the allowance in any specific way. The second is that because permits have\\n * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should\\n * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be\\n * generally recommended is:\\n *\\n * ```solidity\\n * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {\\n * try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}\\n * doThing(..., value);\\n * }\\n *\\n * function doThing(..., uint256 value) public {\\n * token.safeTransferFrom(msg.sender, address(this), value);\\n * ...\\n * }\\n * ```\\n *\\n * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of\\n * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also\\n * {SafeERC20-safeTransferFrom}).\\n *\\n * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so\\n * contracts should have entry points that don't rely on permit.\\n */\\ninterface IERC20PermitUpgradeable {\\n /**\\n * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,\\n * given ``owner``'s signed approval.\\n *\\n * IMPORTANT: The same issues {IERC20-approve} has related to transaction\\n * ordering also apply here.\\n *\\n * Emits an {Approval} event.\\n *\\n * Requirements:\\n *\\n * - `spender` cannot be the zero address.\\n * - `deadline` must be a timestamp in the future.\\n * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`\\n * over the EIP712-formatted function arguments.\\n * - the signature must use ``owner``'s current nonce (see {nonces}).\\n *\\n * For more information on the signature format, see the\\n * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP\\n * section].\\n *\\n * CAUTION: See Security Considerations above.\\n */\\n function permit(\\n address owner,\\n address spender,\\n uint256 value,\\n uint256 deadline,\\n uint8 v,\\n bytes32 r,\\n bytes32 s\\n ) external;\\n\\n /**\\n * @dev Returns the current nonce for `owner`. This value must be\\n * included whenever a signature is generated for {permit}.\\n *\\n * Every successful call to {permit} increases ``owner``'s nonce by one. This\\n * prevents a signature from being used multiple times.\\n */\\n function nonces(address owner) external view returns (uint256);\\n\\n /**\\n * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.\\n */\\n // solhint-disable-next-line func-name-mixedcase\\n function DOMAIN_SEPARATOR() external view returns (bytes32);\\n}\\n\",\"keccak256\":\"0x07e881de3b9f6d2c07909f193f24b96c7fe4ea60013260f3f25aecd8bab3c2f8\",\"license\":\"MIT\"},\"@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts (last updated v4.9.3) (token/ERC20/utils/SafeERC20.sol)\\n\\npragma solidity ^0.8.0;\\n\\nimport \\\"../IERC20Upgradeable.sol\\\";\\nimport \\\"../extensions/IERC20PermitUpgradeable.sol\\\";\\nimport \\\"../../../utils/AddressUpgradeable.sol\\\";\\n\\n/**\\n * @title SafeERC20\\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\\n * contract returns false). Tokens that return no value (and instead revert or\\n * throw on failure) are also supported, non-reverting calls are assumed to be\\n * successful.\\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\\n */\\nlibrary SafeERC20Upgradeable {\\n using AddressUpgradeable for address;\\n\\n /**\\n * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,\\n * non-reverting calls are assumed to be successful.\\n */\\n function safeTransfer(IERC20Upgradeable token, address to, uint256 value) internal {\\n _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\\n }\\n\\n /**\\n * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the\\n * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.\\n */\\n function safeTransferFrom(IERC20Upgradeable token, address from, address to, uint256 value) internal {\\n _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\\n }\\n\\n /**\\n * @dev Deprecated. This function has issues similar to the ones found in\\n * {IERC20-approve}, and its usage is discouraged.\\n *\\n * Whenever possible, use {safeIncreaseAllowance} and\\n * {safeDecreaseAllowance} instead.\\n */\\n function safeApprove(IERC20Upgradeable token, address spender, uint256 value) internal {\\n // safeApprove should only be called when setting an initial allowance,\\n // or when resetting it to zero. To increase and decrease it, use\\n // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\\n require(\\n (value == 0) || (token.allowance(address(this), spender) == 0),\\n \\\"SafeERC20: approve from non-zero to non-zero allowance\\\"\\n );\\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\\n }\\n\\n /**\\n * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,\\n * non-reverting calls are assumed to be successful.\\n */\\n function safeIncreaseAllowance(IERC20Upgradeable token, address spender, uint256 value) internal {\\n uint256 oldAllowance = token.allowance(address(this), spender);\\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));\\n }\\n\\n /**\\n * @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,\\n * non-reverting calls are assumed to be successful.\\n */\\n function safeDecreaseAllowance(IERC20Upgradeable token, address spender, uint256 value) internal {\\n unchecked {\\n uint256 oldAllowance = token.allowance(address(this), spender);\\n require(oldAllowance >= value, \\\"SafeERC20: decreased allowance below zero\\\");\\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));\\n }\\n }\\n\\n /**\\n * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,\\n * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval\\n * to be set to zero before setting it to a non-zero value, such as USDT.\\n */\\n function forceApprove(IERC20Upgradeable token, address spender, uint256 value) internal {\\n bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);\\n\\n if (!_callOptionalReturnBool(token, approvalCall)) {\\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));\\n _callOptionalReturn(token, approvalCall);\\n }\\n }\\n\\n /**\\n * @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.\\n * Revert on invalid signature.\\n */\\n function safePermit(\\n IERC20PermitUpgradeable token,\\n address owner,\\n address spender,\\n uint256 value,\\n uint256 deadline,\\n uint8 v,\\n bytes32 r,\\n bytes32 s\\n ) internal {\\n uint256 nonceBefore = token.nonces(owner);\\n token.permit(owner, spender, value, deadline, v, r, s);\\n uint256 nonceAfter = token.nonces(owner);\\n require(nonceAfter == nonceBefore + 1, \\\"SafeERC20: permit did not succeed\\\");\\n }\\n\\n /**\\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\\n * on the return value: the return value is optional (but if data is returned, it must not be false).\\n * @param token The token targeted by the call.\\n * @param data The call data (encoded using abi.encode or one of its variants).\\n */\\n function _callOptionalReturn(IERC20Upgradeable token, bytes memory data) private {\\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\\n // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that\\n // the target address contains contract code and also asserts for success in the low-level call.\\n\\n bytes memory returndata = address(token).functionCall(data, \\\"SafeERC20: low-level call failed\\\");\\n require(returndata.length == 0 || abi.decode(returndata, (bool)), \\\"SafeERC20: ERC20 operation did not succeed\\\");\\n }\\n\\n /**\\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\\n * on the return value: the return value is optional (but if data is returned, it must not be false).\\n * @param token The token targeted by the call.\\n * @param data The call data (encoded using abi.encode or one of its variants).\\n *\\n * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.\\n */\\n function _callOptionalReturnBool(IERC20Upgradeable token, bytes memory data) private returns (bool) {\\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\\n // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false\\n // and not revert is the subcall reverts.\\n\\n (bool success, bytes memory returndata) = address(token).call(data);\\n return\\n success && (returndata.length == 0 || abi.decode(returndata, (bool))) && AddressUpgradeable.isContract(address(token));\\n }\\n}\\n\",\"keccak256\":\"0x23b997be73d3dd46885262704f0f8cfc7273fdadfe303d37969a9561373972b5\",\"license\":\"MIT\"},\"@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)\\n\\npragma solidity ^0.8.1;\\n\\n/**\\n * @dev Collection of functions related to the address type\\n */\\nlibrary AddressUpgradeable {\\n /**\\n * @dev Returns true if `account` is a contract.\\n *\\n * [IMPORTANT]\\n * ====\\n * It is unsafe to assume that an address for which this function returns\\n * false is an externally-owned account (EOA) and not a contract.\\n *\\n * Among others, `isContract` will return false for the following\\n * types of addresses:\\n *\\n * - an externally-owned account\\n * - a contract in construction\\n * - an address where a contract will be created\\n * - an address where a contract lived, but was destroyed\\n *\\n * Furthermore, `isContract` will also return true if the target contract within\\n * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,\\n * which only has an effect at the end of a transaction.\\n * ====\\n *\\n * [IMPORTANT]\\n * ====\\n * You shouldn't rely on `isContract` to protect against flash loan attacks!\\n *\\n * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets\\n * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract\\n * constructor.\\n * ====\\n */\\n function isContract(address account) internal view returns (bool) {\\n // This method relies on extcodesize/address.code.length, which returns 0\\n // for contracts in construction, since the code is only stored at the end\\n // of the constructor execution.\\n\\n return account.code.length > 0;\\n }\\n\\n /**\\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\\n * `recipient`, forwarding all available gas and reverting on errors.\\n *\\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\\n * imposed by `transfer`, making them unable to receive funds via\\n * `transfer`. {sendValue} removes this limitation.\\n *\\n * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].\\n *\\n * IMPORTANT: because control is transferred to `recipient`, care must be\\n * taken to not create reentrancy vulnerabilities. Consider using\\n * {ReentrancyGuard} or the\\n * https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\\n */\\n function sendValue(address payable recipient, uint256 amount) internal {\\n require(address(this).balance >= amount, \\\"Address: insufficient balance\\\");\\n\\n (bool success, ) = recipient.call{value: amount}(\\\"\\\");\\n require(success, \\\"Address: unable to send value, recipient may have reverted\\\");\\n }\\n\\n /**\\n * @dev Performs a Solidity function call using a low level `call`. A\\n * plain `call` is an unsafe replacement for a function call: use this\\n * function instead.\\n *\\n * If `target` reverts with a revert reason, it is bubbled up by this\\n * function (like regular Solidity function calls).\\n *\\n * Returns the raw returned data. To convert to the expected return value,\\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\\n *\\n * Requirements:\\n *\\n * - `target` must be a contract.\\n * - calling `target` with `data` must not revert.\\n *\\n * _Available since v3.1._\\n */\\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\\n return functionCallWithValue(target, data, 0, \\\"Address: low-level call failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\\n * `errorMessage` as a fallback revert reason when `target` reverts.\\n *\\n * _Available since v3.1._\\n */\\n function functionCall(\\n address target,\\n bytes memory data,\\n string memory errorMessage\\n ) internal returns (bytes memory) {\\n return functionCallWithValue(target, data, 0, errorMessage);\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\\n * but also transferring `value` wei to `target`.\\n *\\n * Requirements:\\n *\\n * - the calling contract must have an ETH balance of at least `value`.\\n * - the called Solidity function must be `payable`.\\n *\\n * _Available since v3.1._\\n */\\n function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {\\n return functionCallWithValue(target, data, value, \\\"Address: low-level call with value failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\\n * with `errorMessage` as a fallback revert reason when `target` reverts.\\n *\\n * _Available since v3.1._\\n */\\n function functionCallWithValue(\\n address target,\\n bytes memory data,\\n uint256 value,\\n string memory errorMessage\\n ) internal returns (bytes memory) {\\n require(address(this).balance >= value, \\\"Address: insufficient balance for call\\\");\\n (bool success, bytes memory returndata) = target.call{value: value}(data);\\n return verifyCallResultFromTarget(target, success, returndata, errorMessage);\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\\n * but performing a static call.\\n *\\n * _Available since v3.3._\\n */\\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\\n return functionStaticCall(target, data, \\\"Address: low-level static call failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\\n * but performing a static call.\\n *\\n * _Available since v3.3._\\n */\\n function functionStaticCall(\\n address target,\\n bytes memory data,\\n string memory errorMessage\\n ) internal view returns (bytes memory) {\\n (bool success, bytes memory returndata) = target.staticcall(data);\\n return verifyCallResultFromTarget(target, success, returndata, errorMessage);\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\\n * but performing a delegate call.\\n *\\n * _Available since v3.4._\\n */\\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\\n return functionDelegateCall(target, data, \\\"Address: low-level delegate call failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\\n * but performing a delegate call.\\n *\\n * _Available since v3.4._\\n */\\n function functionDelegateCall(\\n address target,\\n bytes memory data,\\n string memory errorMessage\\n ) internal returns (bytes memory) {\\n (bool success, bytes memory returndata) = target.delegatecall(data);\\n return verifyCallResultFromTarget(target, success, returndata, errorMessage);\\n }\\n\\n /**\\n * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling\\n * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.\\n *\\n * _Available since v4.8._\\n */\\n function verifyCallResultFromTarget(\\n address target,\\n bool success,\\n bytes memory returndata,\\n string memory errorMessage\\n ) internal view returns (bytes memory) {\\n if (success) {\\n if (returndata.length == 0) {\\n // only check isContract if the call was successful and the return data is empty\\n // otherwise we already know that it was a contract\\n require(isContract(target), \\\"Address: call to non-contract\\\");\\n }\\n return returndata;\\n } else {\\n _revert(returndata, errorMessage);\\n }\\n }\\n\\n /**\\n * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the\\n * revert reason or using the provided one.\\n *\\n * _Available since v4.3._\\n */\\n function verifyCallResult(\\n bool success,\\n bytes memory returndata,\\n string memory errorMessage\\n ) internal pure returns (bytes memory) {\\n if (success) {\\n return returndata;\\n } else {\\n _revert(returndata, errorMessage);\\n }\\n }\\n\\n function _revert(bytes memory returndata, string memory errorMessage) private pure {\\n // Look for revert reason and bubble it up if present\\n if (returndata.length > 0) {\\n // The easiest way to bubble the revert reason is using memory via assembly\\n /// @solidity memory-safe-assembly\\n assembly {\\n let returndata_size := mload(returndata)\\n revert(add(32, returndata), returndata_size)\\n }\\n } else {\\n revert(errorMessage);\\n }\\n }\\n}\\n\",\"keccak256\":\"0x9c80f545915582e63fe206c6ce27cbe85a86fc10b9cd2a0e8c9488fb7c2ee422\",\"license\":\"MIT\"},\"@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts (last updated v4.9.4) (utils/Context.sol)\\n\\npragma solidity ^0.8.0;\\nimport {Initializable} from \\\"../proxy/utils/Initializable.sol\\\";\\n\\n/**\\n * @dev Provides information about the current execution context, including the\\n * sender of the transaction and its data. While these are generally available\\n * via msg.sender and msg.data, they should not be accessed in such a direct\\n * manner, since when dealing with meta-transactions the account sending and\\n * paying for execution may not be the actual sender (as far as an application\\n * is concerned).\\n *\\n * This contract is only required for intermediate, library-like contracts.\\n */\\nabstract contract ContextUpgradeable is Initializable {\\n function __Context_init() internal onlyInitializing {\\n }\\n\\n function __Context_init_unchained() internal onlyInitializing {\\n }\\n function _msgSender() internal view virtual returns (address) {\\n return msg.sender;\\n }\\n\\n function _msgData() internal view virtual returns (bytes calldata) {\\n return msg.data;\\n }\\n\\n function _contextSuffixLength() internal view virtual returns (uint256) {\\n return 0;\\n }\\n\\n /**\\n * @dev This empty reserved space is put in place to allow future versions to add new\\n * variables without shifting down storage in the inheritance chain.\\n * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\\n */\\n uint256[50] private __gap;\\n}\\n\",\"keccak256\":\"0x75097e35253e7fb282ee4d7f27a80eaacfa759923185bf17302a89cbc059c5ef\",\"license\":\"MIT\"},\"@openzeppelin/contracts/access/IAccessControl.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev External interface of AccessControl declared to support ERC165 detection.\\n */\\ninterface IAccessControl {\\n /**\\n * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`\\n *\\n * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite\\n * {RoleAdminChanged} not being emitted signaling this.\\n *\\n * _Available since v3.1._\\n */\\n event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);\\n\\n /**\\n * @dev Emitted when `account` is granted `role`.\\n *\\n * `sender` is the account that originated the contract call, an admin role\\n * bearer except when using {AccessControl-_setupRole}.\\n */\\n event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);\\n\\n /**\\n * @dev Emitted when `account` is revoked `role`.\\n *\\n * `sender` is the account that originated the contract call:\\n * - if using `revokeRole`, it is the admin role bearer\\n * - if using `renounceRole`, it is the role bearer (i.e. `account`)\\n */\\n event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);\\n\\n /**\\n * @dev Returns `true` if `account` has been granted `role`.\\n */\\n function hasRole(bytes32 role, address account) external view returns (bool);\\n\\n /**\\n * @dev Returns the admin role that controls `role`. See {grantRole} and\\n * {revokeRole}.\\n *\\n * To change a role's admin, use {AccessControl-_setRoleAdmin}.\\n */\\n function getRoleAdmin(bytes32 role) external view returns (bytes32);\\n\\n /**\\n * @dev Grants `role` to `account`.\\n *\\n * If `account` had not been already granted `role`, emits a {RoleGranted}\\n * event.\\n *\\n * Requirements:\\n *\\n * - the caller must have ``role``'s admin role.\\n */\\n function grantRole(bytes32 role, address account) external;\\n\\n /**\\n * @dev Revokes `role` from `account`.\\n *\\n * If `account` had been granted `role`, emits a {RoleRevoked} event.\\n *\\n * Requirements:\\n *\\n * - the caller must have ``role``'s admin role.\\n */\\n function revokeRole(bytes32 role, address account) external;\\n\\n /**\\n * @dev Revokes `role` from the calling account.\\n *\\n * Roles are often managed via {grantRole} and {revokeRole}: this function's\\n * purpose is to provide a mechanism for accounts to lose their privileges\\n * if they are compromised (such as when a trusted device is misplaced).\\n *\\n * If the calling account had been granted `role`, emits a {RoleRevoked}\\n * event.\\n *\\n * Requirements:\\n *\\n * - the caller must be `account`.\\n */\\n function renounceRole(bytes32 role, address account) external;\\n}\\n\",\"keccak256\":\"0x59ce320a585d7e1f163cd70390a0ef2ff9cec832e2aa544293a00692465a7a57\",\"license\":\"MIT\"},\"@venusprotocol/governance-contracts/contracts/Governance/AccessControlledV8.sol\":{\"content\":\"// SPDX-License-Identifier: BSD-3-Clause\\npragma solidity 0.8.25;\\n\\nimport \\\"@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol\\\";\\nimport \\\"@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol\\\";\\n\\nimport \\\"./IAccessControlManagerV8.sol\\\";\\n\\n/**\\n * @title AccessControlledV8\\n * @author Venus\\n * @notice This contract is helper between access control manager and actual contract. This contract further inherited by other contract (using solidity 0.8.13)\\n * to integrate access controlled mechanism. It provides initialise methods and verifying access methods.\\n */\\nabstract contract AccessControlledV8 is Initializable, Ownable2StepUpgradeable {\\n /// @notice Access control manager contract\\n IAccessControlManagerV8 internal _accessControlManager;\\n\\n /**\\n * @dev This empty reserved space is put in place to allow future versions to add new\\n * variables without shifting down storage in the inheritance chain.\\n * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\\n */\\n uint256[49] private __gap;\\n\\n /// @notice Emitted when access control manager contract address is changed\\n event NewAccessControlManager(address oldAccessControlManager, address newAccessControlManager);\\n\\n /// @notice Thrown when the action is prohibited by AccessControlManager\\n error Unauthorized(address sender, address calledContract, string methodSignature);\\n\\n function __AccessControlled_init(address accessControlManager_) internal onlyInitializing {\\n __Ownable2Step_init();\\n __AccessControlled_init_unchained(accessControlManager_);\\n }\\n\\n function __AccessControlled_init_unchained(address accessControlManager_) internal onlyInitializing {\\n _setAccessControlManager(accessControlManager_);\\n }\\n\\n /**\\n * @notice Sets the address of AccessControlManager\\n * @dev Admin function to set address of AccessControlManager\\n * @param accessControlManager_ The new address of the AccessControlManager\\n * @custom:event Emits NewAccessControlManager event\\n * @custom:access Only Governance\\n */\\n function setAccessControlManager(address accessControlManager_) external onlyOwner {\\n _setAccessControlManager(accessControlManager_);\\n }\\n\\n /**\\n * @notice Returns the address of the access control manager contract\\n */\\n function accessControlManager() external view returns (IAccessControlManagerV8) {\\n return _accessControlManager;\\n }\\n\\n /**\\n * @dev Internal function to set address of AccessControlManager\\n * @param accessControlManager_ The new address of the AccessControlManager\\n */\\n function _setAccessControlManager(address accessControlManager_) internal {\\n require(address(accessControlManager_) != address(0), \\\"invalid acess control manager address\\\");\\n address oldAccessControlManager = address(_accessControlManager);\\n _accessControlManager = IAccessControlManagerV8(accessControlManager_);\\n emit NewAccessControlManager(oldAccessControlManager, accessControlManager_);\\n }\\n\\n /**\\n * @notice Reverts if the call is not allowed by AccessControlManager\\n * @param signature Method signature\\n */\\n function _checkAccessAllowed(string memory signature) internal view {\\n bool isAllowedToCall = _accessControlManager.isAllowedToCall(msg.sender, signature);\\n\\n if (!isAllowedToCall) {\\n revert Unauthorized(msg.sender, address(this), signature);\\n }\\n }\\n}\\n\",\"keccak256\":\"0xfe0fd441c84ac907cabc88db69ef04f6d7532d770c7e6a1dfe6e7d6305debb49\",\"license\":\"BSD-3-Clause\"},\"@venusprotocol/governance-contracts/contracts/Governance/IAccessControlManagerV8.sol\":{\"content\":\"// SPDX-License-Identifier: BSD-3-Clause\\npragma solidity ^0.8.25;\\n\\nimport \\\"@openzeppelin/contracts/access/IAccessControl.sol\\\";\\n\\n/**\\n * @title IAccessControlManagerV8\\n * @author Venus\\n * @notice Interface implemented by the `AccessControlManagerV8` contract.\\n */\\ninterface IAccessControlManagerV8 is IAccessControl {\\n function giveCallPermission(address contractAddress, string calldata functionSig, address accountToPermit) external;\\n\\n function revokeCallPermission(\\n address contractAddress,\\n string calldata functionSig,\\n address accountToRevoke\\n ) external;\\n\\n function isAllowedToCall(address account, string calldata functionSig) external view returns (bool);\\n\\n function hasPermission(\\n address account,\\n address contractAddress,\\n string calldata functionSig\\n ) external view returns (bool);\\n}\\n\",\"keccak256\":\"0xaa29b098440d0b3a131c5ecdf25ce548790c1b5ac7bf9b5c0264b6af6f7a1e0b\",\"license\":\"BSD-3-Clause\"},\"@venusprotocol/oracle/contracts/interfaces/OracleInterface.sol\":{\"content\":\"// SPDX-License-Identifier: BSD-3-Clause\\npragma solidity ^0.8.25;\\n\\ninterface OracleInterface {\\n function getPrice(address asset) external view returns (uint256);\\n}\\n\\ninterface ResilientOracleInterface is OracleInterface {\\n function updatePrice(address vToken) external;\\n\\n function updateAssetPrice(address asset) external;\\n\\n function getUnderlyingPrice(address vToken) external view returns (uint256);\\n}\\n\\ninterface BoundValidatorInterface {\\n function validatePriceWithAnchorPrice(\\n address asset,\\n uint256 reporterPrice,\\n uint256 anchorPrice\\n ) external view returns (bool);\\n}\\n\",\"keccak256\":\"0xd3bbb7c9eef19e8f467342df6034ef95399a00964646fb8c82b438968ae3a8c0\",\"license\":\"BSD-3-Clause\"},\"@venusprotocol/protocol-reserve/contracts/Interfaces/IProtocolShareReserve.sol\":{\"content\":\"// SPDX-License-Identifier: BSD-3-Clause\\npragma solidity ^0.8.25;\\n\\ninterface IProtocolShareReserve {\\n /// @notice it represents the type of vToken income\\n enum IncomeType {\\n SPREAD,\\n LIQUIDATION,\\n ERC4626_WRAPPER_REWARDS,\\n FLASHLOAN\\n }\\n\\n function updateAssetsState(\\n address comptroller,\\n address asset,\\n IncomeType incomeType\\n ) external;\\n}\\n\",\"keccak256\":\"0x0f341bbef8f12885d79b7acaad7aaf11b84809e8f6b059ef4efc011c8f6c39a5\",\"license\":\"BSD-3-Clause\"},\"@venusprotocol/solidity-utilities/contracts/TimeManagerV8.sol\":{\"content\":\"// SPDX-License-Identifier: BSD-3-Clause\\npragma solidity 0.8.25;\\n\\nimport { SECONDS_PER_YEAR } from \\\"./constants.sol\\\";\\n\\nabstract contract TimeManagerV8 {\\n /// @notice Stores blocksPerYear if isTimeBased is true else secondsPerYear is stored\\n /// @custom:oz-upgrades-unsafe-allow state-variable-immutable\\n uint256 public immutable blocksOrSecondsPerYear;\\n\\n /// @notice Acknowledges if a contract is time based or not\\n /// @custom:oz-upgrades-unsafe-allow state-variable-immutable\\n bool public immutable isTimeBased;\\n\\n /// @notice Stores the current block timestamp or block number depending on isTimeBased\\n /// @custom:oz-upgrades-unsafe-allow state-variable-immutable\\n function() view returns (uint256) private immutable _getCurrentSlot;\\n\\n /**\\n * @dev This empty reserved space is put in place to allow future versions to add new\\n * variables without shifting down storage in the inheritance chain\\n * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\\n */\\n uint256[48] private __gap;\\n\\n /// @notice Thrown when blocks per year is invalid\\n error InvalidBlocksPerYear();\\n\\n /// @notice Thrown when time based but blocks per year is provided\\n error InvalidTimeBasedConfiguration();\\n\\n /**\\n * @param timeBased_ A boolean indicating whether the contract is based on time or block\\n * If timeBased is true than blocksPerYear_ param is ignored as blocksOrSecondsPerYear is set to SECONDS_PER_YEAR\\n * @param blocksPerYear_ The number of blocks per year\\n * @custom:error InvalidBlocksPerYear is thrown if blocksPerYear entered is zero and timeBased is false\\n * @custom:error InvalidTimeBasedConfiguration is thrown if blocksPerYear entered is non zero and timeBased is true\\n * @custom:oz-upgrades-unsafe-allow constructor\\n */\\n constructor(bool timeBased_, uint256 blocksPerYear_) {\\n if (!timeBased_ && blocksPerYear_ == 0) {\\n revert InvalidBlocksPerYear();\\n }\\n\\n if (timeBased_ && blocksPerYear_ != 0) {\\n revert InvalidTimeBasedConfiguration();\\n }\\n\\n isTimeBased = timeBased_;\\n blocksOrSecondsPerYear = timeBased_ ? SECONDS_PER_YEAR : blocksPerYear_;\\n _getCurrentSlot = timeBased_ ? _getBlockTimestamp : _getBlockNumber;\\n }\\n\\n /**\\n * @dev Function to simply retrieve block number or block timestamp\\n * @return Current block number or block timestamp\\n */\\n function getBlockNumberOrTimestamp() public view virtual returns (uint256) {\\n return _getCurrentSlot();\\n }\\n\\n /**\\n * @dev Returns the current timestamp in seconds\\n * @return The current timestamp\\n */\\n function _getBlockTimestamp() private view returns (uint256) {\\n return block.timestamp;\\n }\\n\\n /**\\n * @dev Returns the current block number\\n * @return The current block number\\n */\\n function _getBlockNumber() private view returns (uint256) {\\n return block.number;\\n }\\n}\\n\",\"keccak256\":\"0x57a2bbb9b8e02b1c0a5c0e305fef1328a22db56c3d4b148c362010a6e767243c\",\"license\":\"BSD-3-Clause\"},\"@venusprotocol/solidity-utilities/contracts/constants.sol\":{\"content\":\"// SPDX-License-Identifier: BSD-3-Clause\\npragma solidity ^0.8.25;\\n\\n/// @dev Base unit for computations, usually used in scaling (multiplications, divisions)\\nuint256 constant EXP_SCALE = 1e18;\\n\\n/// @dev A unit (literal one) in EXP_SCALE, usually used in additions/subtractions\\nuint256 constant MANTISSA_ONE = EXP_SCALE;\\n\\n/// @dev The approximate number of seconds per year\\nuint256 constant SECONDS_PER_YEAR = 31_536_000;\\n\",\"keccak256\":\"0x14de93ead464da249af31bea0e3bcfb62ec693bea3475fb4d90f055ac81dc5eb\",\"license\":\"BSD-3-Clause\"},\"@venusprotocol/venus-protocol/contracts/Tokens/Prime/Interfaces/IPrime.sol\":{\"content\":\"// SPDX-License-Identifier: BSD-3-Clause\\npragma solidity ^0.8.25;\\n\\nimport { PrimeStorageV1 } from \\\"../PrimeStorage.sol\\\";\\n\\n/**\\n * @title IPrime\\n * @author Venus\\n * @notice Interface for Prime Token\\n */\\ninterface IPrime {\\n struct APRInfo {\\n // supply APR of the user in BPS\\n uint256 supplyAPR;\\n // borrow APR of the user in BPS\\n uint256 borrowAPR;\\n // total score of the market\\n uint256 totalScore;\\n // score of the user\\n uint256 userScore;\\n // capped XVS balance of the user\\n uint256 xvsBalanceForScore;\\n // capital of the user\\n uint256 capital;\\n // capped supply of the user\\n uint256 cappedSupply;\\n // capped borrow of the user\\n uint256 cappedBorrow;\\n // capped supply of user in USD\\n uint256 supplyCapUSD;\\n // capped borrow of user in USD\\n uint256 borrowCapUSD;\\n }\\n\\n struct Capital {\\n // capital of the user\\n uint256 capital;\\n // capped supply of the user\\n uint256 cappedSupply;\\n // capped borrow of the user\\n uint256 cappedBorrow;\\n // capped supply of user in USD\\n uint256 supplyCapUSD;\\n // capped borrow of user in USD\\n uint256 borrowCapUSD;\\n }\\n\\n /**\\n * @notice Returns boosted pending interest accrued for a user for all markets\\n * @param user the account for which to get the accrued interests\\n * @return pendingRewards the number of underlying tokens accrued by the user for all markets\\n */\\n function getPendingRewards(address user) external returns (PrimeStorageV1.PendingReward[] memory pendingRewards);\\n\\n /**\\n * @notice Update total score of multiple users and market\\n * @param users accounts for which we need to update score\\n */\\n function updateScores(address[] memory users) external;\\n\\n /**\\n * @notice Update value of alpha\\n * @param _alphaNumerator numerator of alpha. If alpha is 0.5 then numerator is 1\\n * @param _alphaDenominator denominator of alpha. If alpha is 0.5 then denominator is 2\\n */\\n function updateAlpha(uint128 _alphaNumerator, uint128 _alphaDenominator) external;\\n\\n /**\\n * @notice Update multipliers for a market\\n * @param market address of the market vToken\\n * @param supplyMultiplier new supply multiplier for the market, scaled by 1e18\\n * @param borrowMultiplier new borrow multiplier for the market, scaled by 1e18\\n */\\n function updateMultipliers(address market, uint256 supplyMultiplier, uint256 borrowMultiplier) external;\\n\\n /**\\n * @notice Add a market to prime program\\n * @param comptroller address of the comptroller\\n * @param market address of the market vToken\\n * @param supplyMultiplier the multiplier for supply cap. It should be converted to 1e18\\n * @param borrowMultiplier the multiplier for borrow cap. It should be converted to 1e18\\n */\\n function addMarket(\\n address comptroller,\\n address market,\\n uint256 supplyMultiplier,\\n uint256 borrowMultiplier\\n ) external;\\n\\n /**\\n * @notice Set limits for total tokens that can be minted\\n * @param _irrevocableLimit total number of irrevocable tokens that can be minted\\n * @param _revocableLimit total number of revocable tokens that can be minted\\n */\\n function setLimit(uint256 _irrevocableLimit, uint256 _revocableLimit) external;\\n\\n /**\\n * @notice Directly issue prime tokens to users\\n * @param isIrrevocable are the tokens being issued\\n * @param users list of address to issue tokens to\\n */\\n function issue(bool isIrrevocable, address[] calldata users) external;\\n\\n /**\\n * @notice Executed by XVSVault whenever user's XVSVault balance changes\\n * @param user the account address whose balance was updated\\n */\\n function xvsUpdated(address user) external;\\n\\n /**\\n * @notice accrues interest and updates score for an user for a specific market\\n * @param user the account address for which to accrue interest and update score\\n * @param market the market for which to accrue interest and update score\\n */\\n function accrueInterestAndUpdateScore(address user, address market) external;\\n\\n /**\\n * @notice For claiming prime token when staking period is completed\\n */\\n function claim() external;\\n\\n /**\\n * @notice For burning any prime token\\n * @param user the account address for which the prime token will be burned\\n */\\n function burn(address user) external;\\n\\n /**\\n * @notice To pause or unpause claiming of interest\\n */\\n function togglePause() external;\\n\\n /**\\n * @notice For user to claim boosted yield\\n * @param vToken the market for which claim the accrued interest\\n * @return amount the amount of tokens transferred to the user\\n */\\n function claimInterest(address vToken) external returns (uint256);\\n\\n /**\\n * @notice For user to claim boosted yield\\n * @param vToken the market for which claim the accrued interest\\n * @param user the user for which to claim the accrued interest\\n * @return amount the amount of tokens transferred to the user\\n */\\n function claimInterest(address vToken, address user) external returns (uint256);\\n\\n /**\\n * @notice Distributes income from market since last distribution\\n * @param vToken the market for which to distribute the income\\n */\\n function accrueInterest(address vToken) external;\\n\\n /**\\n * @notice Returns boosted interest accrued for a user\\n * @param vToken the market for which to fetch the accrued interest\\n * @param user the account for which to get the accrued interest\\n * @return interestAccrued the number of underlying tokens accrued by the user since the last accrual\\n */\\n function getInterestAccrued(address vToken, address user) external returns (uint256);\\n\\n /**\\n * @notice Retrieves an array of all available markets\\n * @return an array of addresses representing all available markets\\n */\\n function getAllMarkets() external view returns (address[] memory);\\n\\n /**\\n * @notice fetch the numbers of seconds remaining for staking period to complete\\n * @param user the account address for which we are checking the remaining time\\n * @return timeRemaining the number of seconds the user needs to wait to claim prime token\\n */\\n function claimTimeRemaining(address user) external view returns (uint256);\\n\\n /**\\n * @notice Returns supply and borrow APR for user for a given market\\n * @param market the market for which to fetch the APR\\n * @param user the account for which to get the APR\\n * @return aprInfo APR information for the user for the given market\\n */\\n function calculateAPR(address market, address user) external view returns (APRInfo memory aprInfo);\\n\\n /**\\n * @notice Returns supply and borrow APR for estimated supply, borrow and XVS staked\\n * @param market the market for which to fetch the APR\\n * @param user the account for which to get the APR\\n * @param borrow hypothetical borrow amount\\n * @param supply hypothetical supply amount\\n * @param xvsStaked hypothetical staked XVS amount\\n * @return aprInfo APR information for the user for the given market\\n */\\n function estimateAPR(\\n address market,\\n address user,\\n uint256 borrow,\\n uint256 supply,\\n uint256 xvsStaked\\n ) external view returns (APRInfo memory aprInfo);\\n\\n /**\\n * @notice the total income that's going to be distributed in a year to prime token holders\\n * @param vToken the market for which to fetch the total income that's going to distributed in a year\\n * @return amount the total income\\n */\\n function incomeDistributionYearly(address vToken) external view returns (uint256 amount);\\n\\n /**\\n * @notice Returns if user is a prime holder\\n * @return isPrimeHolder true if user is a prime holder\\n */\\n function isUserPrimeHolder(address user) external view returns (bool);\\n\\n /**\\n * @notice Set the limit for the loops can iterate to avoid the DOS\\n * @param loopsLimit Number of loops limit\\n */\\n function setMaxLoopsLimit(uint256 loopsLimit) external;\\n\\n /**\\n * @notice Update staked at timestamp for multiple users\\n * @param users accounts for which we need to update staked at timestamp\\n * @param timestamps new staked at timestamp for the users\\n */\\n function setStakedAt(address[] calldata users, uint256[] calldata timestamps) external;\\n}\\n\",\"keccak256\":\"0xd112f60183416ad796093b4a83a80ddc1b8b655965f02ae55ca82e2a0c68f97d\",\"license\":\"BSD-3-Clause\"},\"@venusprotocol/venus-protocol/contracts/Tokens/Prime/PrimeStorage.sol\":{\"content\":\"// SPDX-License-Identifier: BSD-3-Clause\\npragma solidity 0.8.25;\\n\\nimport { ResilientOracleInterface } from \\\"@venusprotocol/oracle/contracts/interfaces/OracleInterface.sol\\\";\\n\\n/**\\n * @title PrimeStorageV1\\n * @author Venus\\n * @notice Storage for Prime Token\\n */\\ncontract PrimeStorageV1 {\\n struct Token {\\n bool exists;\\n bool isIrrevocable;\\n }\\n\\n struct Market {\\n uint256 supplyMultiplier;\\n uint256 borrowMultiplier;\\n uint256 rewardIndex;\\n uint256 sumOfMembersScore;\\n bool exists;\\n }\\n\\n struct Interest {\\n uint256 accrued;\\n uint256 score;\\n uint256 rewardIndex;\\n }\\n\\n struct PendingReward {\\n address vToken;\\n address rewardToken;\\n uint256 amount;\\n }\\n\\n /// @notice Base unit for computations, usually used in scaling (multiplications, divisions)\\n uint256 internal constant EXP_SCALE = 1e18;\\n\\n /// @notice maximum BPS = 100%\\n uint256 internal constant MAXIMUM_BPS = 1e4;\\n\\n /// @notice Mapping to get prime token's metadata\\n mapping(address => Token) public tokens;\\n\\n /// @notice Tracks total irrevocable tokens minted\\n uint256 public totalIrrevocable;\\n\\n /// @notice Tracks total revocable tokens minted\\n uint256 public totalRevocable;\\n\\n /// @notice Indicates maximum revocable tokens that can be minted\\n uint256 public revocableLimit;\\n\\n /// @notice Indicates maximum irrevocable tokens that can be minted\\n uint256 public irrevocableLimit;\\n\\n /// @notice Tracks when prime token eligible users started staking for claiming prime token\\n mapping(address => uint256) public stakedAt;\\n\\n /// @notice vToken to market configuration\\n mapping(address => Market) public markets;\\n\\n /// @notice vToken to user to user index\\n mapping(address => mapping(address => Interest)) public interests;\\n\\n /// @notice A list of boosted markets\\n address[] internal _allMarkets;\\n\\n /// @notice numerator of alpha. Ex: if alpha is 0.5 then this will be 1\\n uint128 public alphaNumerator;\\n\\n /// @notice denominator of alpha. Ex: if alpha is 0.5 then this will be 2\\n uint128 public alphaDenominator;\\n\\n /// @notice address of XVS vault\\n address public xvsVault;\\n\\n /// @notice address of XVS vault reward token\\n address public xvsVaultRewardToken;\\n\\n /// @notice address of XVS vault pool id\\n uint256 public xvsVaultPoolId;\\n\\n /// @notice mapping to check if a account's score was updated in the round\\n mapping(uint256 => mapping(address => bool)) public isScoreUpdated;\\n\\n /// @notice unique id for next round\\n uint256 public nextScoreUpdateRoundId;\\n\\n /// @notice total number of accounts whose score needs to be updated\\n uint256 public totalScoreUpdatesRequired;\\n\\n /// @notice total number of accounts whose score is yet to be updated\\n uint256 public pendingScoreUpdates;\\n\\n /// @notice mapping used to find if an asset is part of prime markets\\n mapping(address => address) public vTokenForAsset;\\n\\n /// @notice Address of core pool comptroller contract\\n address internal corePoolComptroller;\\n\\n /// @notice unreleased income from PLP that's already distributed to prime holders\\n /// @dev mapping of asset address => amount\\n mapping(address => uint256) public unreleasedPLPIncome;\\n\\n /// @notice The address of PLP contract\\n address public primeLiquidityProvider;\\n\\n /// @notice The address of ResilientOracle contract\\n ResilientOracleInterface public oracle;\\n\\n /// @notice The address of PoolRegistry contract\\n address public poolRegistry;\\n\\n /// @dev This empty reserved space is put in place to allow future versions to add new\\n /// variables without shifting down storage in the inheritance chain.\\n uint256[26] private __gap;\\n}\\n\",\"keccak256\":\"0x5285c875114db2ea2be0b81b65722d5761806217022db733323c4e03365a95e7\",\"license\":\"BSD-3-Clause\"},\"contracts/Comptroller.sol\":{\"content\":\"// SPDX-License-Identifier: BSD-3-Clause\\npragma solidity 0.8.25;\\n\\nimport { Ownable2StepUpgradeable } from \\\"@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol\\\";\\nimport { ResilientOracleInterface } from \\\"@venusprotocol/oracle/contracts/interfaces/OracleInterface.sol\\\";\\nimport { AccessControlledV8 } from \\\"@venusprotocol/governance-contracts/contracts/Governance/AccessControlledV8.sol\\\";\\nimport { IPrime } from \\\"@venusprotocol/venus-protocol/contracts/Tokens/Prime/Interfaces/IPrime.sol\\\";\\n\\nimport { ComptrollerInterface, Action } from \\\"./ComptrollerInterface.sol\\\";\\nimport { ComptrollerStorage } from \\\"./ComptrollerStorage.sol\\\";\\nimport { ExponentialNoError } from \\\"./ExponentialNoError.sol\\\";\\nimport { VToken } from \\\"./VToken.sol\\\";\\nimport { RewardsDistributor } from \\\"./Rewards/RewardsDistributor.sol\\\";\\nimport { MaxLoopsLimitHelper } from \\\"./MaxLoopsLimitHelper.sol\\\";\\nimport { ensureNonzeroAddress } from \\\"./lib/validators.sol\\\";\\n\\n/**\\n * @title Comptroller\\n * @author Venus\\n * @notice The Comptroller is designed to provide checks for all minting, redeeming, transferring, borrowing, lending, repaying, liquidating,\\n * and seizing done by the `vToken` contract. Each pool has one `Comptroller` checking these interactions across markets. When a user interacts\\n * with a given market by one of these main actions, a call is made to a corresponding hook in the associated `Comptroller`, which either allows\\n * or reverts the transaction. These hooks also update supply and borrow rewards as they are called. The comptroller holds the logic for assessing\\n * liquidity snapshots of an account via the collateral factor and liquidation threshold. This check determines the collateral needed for a borrow,\\n * as well as how much of a borrow may be liquidated. A user may borrow a portion of their collateral with the maximum amount determined by the\\n * markets collateral factor. However, if their borrowed amount exceeds an amount calculated using the market\\u2019s corresponding liquidation threshold,\\n * the borrow is eligible for liquidation.\\n *\\n * The `Comptroller` also includes two functions `liquidateAccount()` and `healAccount()`, which are meant to handle accounts that do not exceed\\n * the `minLiquidatableCollateral` for the `Comptroller`:\\n *\\n * - `healAccount()`: This function is called to seize all of a given user\\u2019s collateral, requiring the `msg.sender` repay a certain percentage\\n * of the debt calculated by `collateral/(borrows*liquidationIncentive)`. The function can only be called if the calculated percentage does not exceed\\n * 100%, because otherwise no `badDebt` would be created and `liquidateAccount()` should be used instead. The difference in the actual amount of debt\\n * and debt paid off is recorded as `badDebt` for each market, which can then be auctioned off for the risk reserves of the associated pool.\\n * - `liquidateAccount()`: This function can only be called if the collateral seized will cover all borrows of an account, as well as the liquidation\\n * incentive. Otherwise, the pool will incur bad debt, in which case the function `healAccount()` should be used instead. This function skips the logic\\n * verifying that the repay amount does not exceed the close factor.\\n */\\ncontract Comptroller is\\n Ownable2StepUpgradeable,\\n AccessControlledV8,\\n ComptrollerStorage,\\n ComptrollerInterface,\\n ExponentialNoError,\\n MaxLoopsLimitHelper\\n{\\n // PoolRegistry, immutable to save on gas\\n /// @custom:oz-upgrades-unsafe-allow state-variable-immutable\\n address public immutable poolRegistry;\\n\\n /// @notice Emitted when an account enters a market\\n event MarketEntered(VToken indexed vToken, address indexed account);\\n\\n /// @notice Emitted when an account exits a market\\n event MarketExited(VToken indexed vToken, address indexed account);\\n\\n /// @notice Emitted when close factor is changed by admin\\n event NewCloseFactor(uint256 oldCloseFactorMantissa, uint256 newCloseFactorMantissa);\\n\\n /// @notice Emitted when a collateral factor is changed by admin\\n event NewCollateralFactor(VToken vToken, uint256 oldCollateralFactorMantissa, uint256 newCollateralFactorMantissa);\\n\\n /// @notice Emitted when liquidation threshold is changed by admin\\n event NewLiquidationThreshold(\\n VToken vToken,\\n uint256 oldLiquidationThresholdMantissa,\\n uint256 newLiquidationThresholdMantissa\\n );\\n\\n /// @notice Emitted when liquidation incentive is changed by admin\\n event NewLiquidationIncentive(uint256 oldLiquidationIncentiveMantissa, uint256 newLiquidationIncentiveMantissa);\\n\\n /// @notice Emitted when price oracle is changed\\n event NewPriceOracle(ResilientOracleInterface oldPriceOracle, ResilientOracleInterface newPriceOracle);\\n\\n /// @notice Emitted when an action is paused on a market\\n event ActionPausedMarket(VToken vToken, Action action, bool pauseState);\\n\\n /// @notice Emitted when borrow cap for a vToken is changed\\n event NewBorrowCap(VToken indexed vToken, uint256 newBorrowCap);\\n\\n /// @notice Emitted when the collateral threshold (in USD) for non-batch liquidations is changed\\n event NewMinLiquidatableCollateral(uint256 oldMinLiquidatableCollateral, uint256 newMinLiquidatableCollateral);\\n\\n /// @notice Emitted when supply cap for a vToken is changed\\n event NewSupplyCap(VToken indexed vToken, uint256 newSupplyCap);\\n\\n /// @notice Emitted when a rewards distributor is added\\n event NewRewardsDistributor(address indexed rewardsDistributor, address indexed rewardToken);\\n\\n /// @notice Emitted when a market is supported\\n event MarketSupported(VToken vToken);\\n\\n /// @notice Emitted when prime token contract address is changed\\n event NewPrimeToken(IPrime oldPrimeToken, IPrime newPrimeToken);\\n\\n /// @notice Emitted when forced liquidation is enabled or disabled for a market\\n event IsForcedLiquidationEnabledUpdated(address indexed vToken, bool enable);\\n\\n /// @notice Emitted when a market is unlisted\\n event MarketUnlisted(address indexed vToken);\\n /// @notice Emitted when the borrowing or redeeming delegate rights are updated for an account\\n event DelegateUpdated(address indexed approver, address indexed delegate, bool approved);\\n\\n /// @notice Thrown when collateral factor exceeds the upper bound\\n error InvalidCollateralFactor();\\n\\n /// @notice Thrown when liquidation threshold exceeds the collateral factor\\n error InvalidLiquidationThreshold();\\n\\n /// @notice Thrown when the action is only available to specific sender, but the real sender was different\\n error UnexpectedSender(address expectedSender, address actualSender);\\n\\n /// @notice Thrown when the oracle returns an invalid price for some asset\\n error PriceError(address vToken);\\n\\n /// @notice Thrown if VToken unexpectedly returned a nonzero error code while trying to get account snapshot\\n error SnapshotError(address vToken, address user);\\n\\n /// @notice Thrown when the market is not listed\\n error MarketNotListed(address market);\\n\\n /// @notice Thrown when a market has an unexpected comptroller\\n error ComptrollerMismatch();\\n\\n /// @notice Thrown when user is not member of market\\n error MarketNotCollateral(address vToken, address user);\\n\\n /// @notice Thrown when borrow action is not paused\\n error BorrowActionNotPaused();\\n\\n /// @notice Thrown when mint action is not paused\\n error MintActionNotPaused();\\n\\n /// @notice Thrown when redeem action is not paused\\n error RedeemActionNotPaused();\\n\\n /// @notice Thrown when repay action is not paused\\n error RepayActionNotPaused();\\n\\n /// @notice Thrown when seize action is not paused\\n error SeizeActionNotPaused();\\n\\n /// @notice Thrown when exit market action is not paused\\n error ExitMarketActionNotPaused();\\n\\n /// @notice Thrown when transfer action is not paused\\n error TransferActionNotPaused();\\n\\n /// @notice Thrown when enter market action is not paused\\n error EnterMarketActionNotPaused();\\n\\n /// @notice Thrown when liquidate action is not paused\\n error LiquidateActionNotPaused();\\n\\n /// @notice Thrown when borrow cap is not zero\\n error BorrowCapIsNotZero();\\n\\n /// @notice Thrown when supply cap is not zero\\n error SupplyCapIsNotZero();\\n\\n /// @notice Thrown when collateral factor is not zero\\n error CollateralFactorIsNotZero();\\n\\n /**\\n * @notice Thrown during the liquidation if user's total collateral amount is lower than\\n * a predefined threshold. In this case only batch liquidations (either liquidateAccount\\n * or healAccount) are available.\\n */\\n error MinimalCollateralViolated(uint256 expectedGreaterThan, uint256 actual);\\n error CollateralExceedsThreshold(uint256 expectedLessThanOrEqualTo, uint256 actual);\\n error InsufficientCollateral(uint256 collateralToSeize, uint256 availableCollateral);\\n\\n /// @notice Thrown when the account doesn't have enough liquidity to redeem or borrow\\n error InsufficientLiquidity();\\n\\n /// @notice Thrown when trying to liquidate a healthy account\\n error InsufficientShortfall();\\n\\n /// @notice Thrown when trying to repay more than allowed by close factor\\n error TooMuchRepay();\\n\\n /// @notice Thrown if the user is trying to exit a market in which they have an outstanding debt\\n error NonzeroBorrowBalance();\\n\\n /// @notice Thrown when trying to perform an action that is paused\\n error ActionPaused(address market, Action action);\\n\\n /// @notice Thrown when trying to add a market that is already listed\\n error MarketAlreadyListed(address market);\\n\\n /// @notice Thrown if the supply cap is exceeded\\n error SupplyCapExceeded(address market, uint256 cap);\\n\\n /// @notice Thrown if the borrow cap is exceeded\\n error BorrowCapExceeded(address market, uint256 cap);\\n\\n /// @notice Thrown if delegate approval status is already set to the requested value\\n error DelegationStatusUnchanged();\\n\\n /// @param poolRegistry_ Pool registry address\\n /// @custom:oz-upgrades-unsafe-allow constructor\\n /// @custom:error ZeroAddressNotAllowed is thrown when pool registry address is zero\\n constructor(address poolRegistry_) {\\n ensureNonzeroAddress(poolRegistry_);\\n\\n poolRegistry = poolRegistry_;\\n _disableInitializers();\\n }\\n\\n /**\\n * @param loopLimit Limit for the loops can iterate to avoid the DOS\\n * @param accessControlManager Access control manager contract address\\n */\\n function initialize(uint256 loopLimit, address accessControlManager) external initializer {\\n __Ownable2Step_init();\\n __AccessControlled_init_unchained(accessControlManager);\\n\\n _setMaxLoopsLimit(loopLimit);\\n }\\n\\n /**\\n * @notice Add assets to be included in account liquidity calculation; enabling them to be used as collateral\\n * @param vTokens The list of addresses of the vToken markets to be enabled\\n * @return errors An array of NO_ERROR for compatibility with Venus core tooling\\n * @custom:event MarketEntered is emitted for each market on success\\n * @custom:error ActionPaused error is thrown if entering any of the markets is paused\\n * @custom:error MarketNotListed error is thrown if any of the markets is not listed\\n * @custom:access Not restricted\\n */\\n function enterMarkets(address[] memory vTokens) external override returns (uint256[] memory) {\\n uint256 len = vTokens.length;\\n\\n uint256[] memory results = new uint256[](len);\\n for (uint256 i; i < len; ++i) {\\n VToken vToken = VToken(vTokens[i]);\\n\\n _addToMarket(vToken, msg.sender);\\n results[i] = NO_ERROR;\\n }\\n\\n return results;\\n }\\n\\n /**\\n * @notice Unlist a market by setting isListed to false\\n * @dev Checks if all actions are paused, borrow/supply caps is set to 0 and collateral factor is to 0.\\n * @param market The address of the market (token) to unlist\\n * @return uint256 Always NO_ERROR for compatibility with Venus core tooling\\n * @custom:event MarketUnlisted is emitted on success\\n * @custom:error MarketNotListed error is thrown when the market is not listed\\n * @custom:error BorrowActionNotPaused error is thrown if borrow action is not paused\\n * @custom:error MintActionNotPaused error is thrown if mint action is not paused\\n * @custom:error RedeemActionNotPaused error is thrown if redeem action is not paused\\n * @custom:error RepayActionNotPaused error is thrown if repay action is not paused\\n * @custom:error EnterMarketActionNotPaused error is thrown if enter market action is not paused\\n * @custom:error LiquidateActionNotPaused error is thrown if liquidate action is not paused\\n * @custom:error BorrowCapIsNotZero error is thrown if borrow cap is not zero\\n * @custom:error SupplyCapIsNotZero error is thrown if supply cap is not zero\\n * @custom:error CollateralFactorIsNotZero error is thrown if collateral factor is not zero\\n */\\n function unlistMarket(address market) external returns (uint256) {\\n _checkAccessAllowed(\\\"unlistMarket(address)\\\");\\n\\n Market storage _market = markets[market];\\n\\n if (!_market.isListed) {\\n revert MarketNotListed(market);\\n }\\n\\n if (!actionPaused(market, Action.BORROW)) {\\n revert BorrowActionNotPaused();\\n }\\n\\n if (!actionPaused(market, Action.MINT)) {\\n revert MintActionNotPaused();\\n }\\n\\n if (!actionPaused(market, Action.REDEEM)) {\\n revert RedeemActionNotPaused();\\n }\\n\\n if (!actionPaused(market, Action.REPAY)) {\\n revert RepayActionNotPaused();\\n }\\n\\n if (!actionPaused(market, Action.SEIZE)) {\\n revert SeizeActionNotPaused();\\n }\\n\\n if (!actionPaused(market, Action.ENTER_MARKET)) {\\n revert EnterMarketActionNotPaused();\\n }\\n\\n if (!actionPaused(market, Action.LIQUIDATE)) {\\n revert LiquidateActionNotPaused();\\n }\\n\\n if (!actionPaused(market, Action.TRANSFER)) {\\n revert TransferActionNotPaused();\\n }\\n\\n if (!actionPaused(market, Action.EXIT_MARKET)) {\\n revert ExitMarketActionNotPaused();\\n }\\n\\n if (borrowCaps[market] != 0) {\\n revert BorrowCapIsNotZero();\\n }\\n\\n if (supplyCaps[market] != 0) {\\n revert SupplyCapIsNotZero();\\n }\\n\\n if (_market.collateralFactorMantissa != 0) {\\n revert CollateralFactorIsNotZero();\\n }\\n\\n _market.isListed = false;\\n emit MarketUnlisted(market);\\n\\n return NO_ERROR;\\n }\\n\\n /**\\n * @notice Grants or revokes the borrowing or redeeming delegate rights to / from an account\\n * If allowed, the delegate will be able to borrow funds on behalf of the sender\\n * Upon a delegated borrow, the delegate will receive the funds, and the borrower\\n * will see the debt on their account\\n * Upon a delegated redeem, the delegate will receive the redeemed amount and the approver\\n * will see a deduction in his vToken balance\\n * @param delegate The address to update the rights for\\n * @param approved Whether to grant (true) or revoke (false) the borrowing or redeeming rights\\n * @custom:event DelegateUpdated emits on success\\n * @custom:error ZeroAddressNotAllowed is thrown when delegate address is zero\\n * @custom:error DelegationStatusUnchanged is thrown if approval status is already set to the requested value\\n * @custom:access Not restricted\\n */\\n function updateDelegate(address delegate, bool approved) external {\\n ensureNonzeroAddress(delegate);\\n if (approvedDelegates[msg.sender][delegate] == approved) {\\n revert DelegationStatusUnchanged();\\n }\\n\\n approvedDelegates[msg.sender][delegate] = approved;\\n emit DelegateUpdated(msg.sender, delegate, approved);\\n }\\n\\n /**\\n * @notice Removes asset from sender's account liquidity calculation; disabling them as collateral\\n * @dev Sender must not have an outstanding borrow balance in the asset,\\n * or be providing necessary collateral for an outstanding borrow.\\n * @param vTokenAddress The address of the asset to be removed\\n * @return error Always NO_ERROR for compatibility with Venus core tooling\\n * @custom:event MarketExited is emitted on success\\n * @custom:error ActionPaused error is thrown if exiting the market is paused\\n * @custom:error NonzeroBorrowBalance error is thrown if the user has an outstanding borrow in this market\\n * @custom:error MarketNotListed error is thrown when the market is not listed\\n * @custom:error InsufficientLiquidity error is thrown if exiting the market would lead to user's insolvency\\n * @custom:error SnapshotError is thrown if some vToken fails to return the account's supply and borrows\\n * @custom:error PriceError is thrown if the oracle returns an incorrect price for some asset\\n * @custom:access Not restricted\\n */\\n function exitMarket(address vTokenAddress) external override returns (uint256) {\\n _checkActionPauseState(vTokenAddress, Action.EXIT_MARKET);\\n VToken vToken = VToken(vTokenAddress);\\n /* Get sender tokensHeld and amountOwed underlying from the vToken */\\n (uint256 tokensHeld, uint256 amountOwed, ) = _safeGetAccountSnapshot(vToken, msg.sender);\\n\\n /* Fail if the sender has a borrow balance */\\n if (amountOwed != 0) {\\n revert NonzeroBorrowBalance();\\n }\\n\\n /* Fail if the sender is not permitted to redeem all of their tokens */\\n _checkRedeemAllowed(vTokenAddress, msg.sender, tokensHeld);\\n\\n Market storage marketToExit = markets[address(vToken)];\\n\\n /* Return true if the sender is not already \\u2018in\\u2019 the market */\\n if (!marketToExit.accountMembership[msg.sender]) {\\n return NO_ERROR;\\n }\\n\\n /* Set vToken account membership to false */\\n delete marketToExit.accountMembership[msg.sender];\\n\\n /* Delete vToken from the account\\u2019s list of assets */\\n // load into memory for faster iteration\\n VToken[] memory userAssetList = accountAssets[msg.sender];\\n uint256 len = userAssetList.length;\\n\\n uint256 assetIndex = len;\\n for (uint256 i; i < len; ++i) {\\n if (userAssetList[i] == vToken) {\\n assetIndex = i;\\n break;\\n }\\n }\\n\\n // We *must* have found the asset in the list or our redundant data structure is broken\\n assert(assetIndex < len);\\n\\n // copy last item in list to location of item to be removed, reduce length by 1\\n VToken[] storage storedList = accountAssets[msg.sender];\\n storedList[assetIndex] = storedList[storedList.length - 1];\\n storedList.pop();\\n\\n emit MarketExited(vToken, msg.sender);\\n\\n return NO_ERROR;\\n }\\n\\n /*** Policy Hooks ***/\\n\\n /**\\n * @notice Checks if the account should be allowed to mint tokens in the given market\\n * @param vToken The market to verify the mint against\\n * @param minter The account which would get the minted tokens\\n * @param mintAmount The amount of underlying being supplied to the market in exchange for tokens\\n * @custom:error ActionPaused error is thrown if supplying to this market is paused\\n * @custom:error MarketNotListed error is thrown when the market is not listed\\n * @custom:error SupplyCapExceeded error is thrown if the total supply exceeds the cap after minting\\n * @custom:access Not restricted\\n */\\n function preMintHook(address vToken, address minter, uint256 mintAmount) external override {\\n _checkActionPauseState(vToken, Action.MINT);\\n\\n if (!markets[vToken].isListed) {\\n revert MarketNotListed(address(vToken));\\n }\\n\\n uint256 supplyCap = supplyCaps[vToken];\\n // Skipping the cap check for uncapped coins to save some gas\\n if (supplyCap != type(uint256).max) {\\n uint256 vTokenSupply = VToken(vToken).totalSupply();\\n Exp memory exchangeRate = Exp({ mantissa: VToken(vToken).exchangeRateStored() });\\n uint256 nextTotalSupply = mul_ScalarTruncateAddUInt(exchangeRate, vTokenSupply, mintAmount);\\n if (nextTotalSupply > supplyCap) {\\n revert SupplyCapExceeded(vToken, supplyCap);\\n }\\n }\\n\\n // Keep the flywheel moving\\n uint256 rewardDistributorsCount = rewardsDistributors.length;\\n\\n for (uint256 i; i < rewardDistributorsCount; ++i) {\\n RewardsDistributor rewardsDistributor = rewardsDistributors[i];\\n rewardsDistributor.updateRewardTokenSupplyIndex(vToken);\\n rewardsDistributor.distributeSupplierRewardToken(vToken, minter);\\n }\\n }\\n\\n /**\\n * @notice Validates mint, accrues interest and updates score in prime. Reverts on rejection. May emit logs.\\n * @param vToken Asset being minted\\n * @param minter The address minting the tokens\\n * @param actualMintAmount The amount of the underlying asset being minted\\n * @param mintTokens The number of tokens being minted\\n */\\n // solhint-disable-next-line no-unused-vars\\n function mintVerify(address vToken, address minter, uint256 actualMintAmount, uint256 mintTokens) external {\\n if (address(prime) != address(0)) {\\n prime.accrueInterestAndUpdateScore(minter, vToken);\\n }\\n }\\n\\n /**\\n * @notice Checks if the account should be allowed to redeem tokens in the given market\\n * @param vToken The market to verify the redeem against\\n * @param redeemer The account which would redeem the tokens\\n * @param redeemTokens The number of vTokens to exchange for the underlying asset in the market\\n * @custom:error ActionPaused error is thrown if withdrawals are paused in this market\\n * @custom:error MarketNotListed error is thrown when the market is not listed\\n * @custom:error InsufficientLiquidity error is thrown if the withdrawal would lead to user's insolvency\\n * @custom:error SnapshotError is thrown if some vToken fails to return the account's supply and borrows\\n * @custom:error PriceError is thrown if the oracle returns an incorrect price for some asset\\n * @custom:access Not restricted\\n */\\n function preRedeemHook(address vToken, address redeemer, uint256 redeemTokens) external override {\\n _checkActionPauseState(vToken, Action.REDEEM);\\n\\n _checkRedeemAllowed(vToken, redeemer, redeemTokens);\\n\\n // Keep the flywheel moving\\n uint256 rewardDistributorsCount = rewardsDistributors.length;\\n\\n for (uint256 i; i < rewardDistributorsCount; ++i) {\\n RewardsDistributor rewardsDistributor = rewardsDistributors[i];\\n rewardsDistributor.updateRewardTokenSupplyIndex(vToken);\\n rewardsDistributor.distributeSupplierRewardToken(vToken, redeemer);\\n }\\n }\\n\\n /**\\n * @notice Validates redeem, accrues interest and updates score in prime. Reverts on rejection. May emit logs.\\n * @param vToken Asset being redeemed\\n * @param redeemer The address redeeming the tokens\\n * @param redeemAmount The amount of the underlying asset being redeemed\\n * @param redeemTokens The number of tokens being redeemed\\n */\\n function redeemVerify(address vToken, address redeemer, uint256 redeemAmount, uint256 redeemTokens) external {\\n if (address(prime) != address(0)) {\\n prime.accrueInterestAndUpdateScore(redeemer, vToken);\\n }\\n }\\n\\n /**\\n * @notice Validates repayBorrow, accrues interest and updates score in prime. Reverts on rejection. May emit logs.\\n * @param vToken Asset being repaid\\n * @param payer The address repaying the borrow\\n * @param borrower The address of the borrower\\n * @param actualRepayAmount The amount of underlying being repaid\\n */\\n function repayBorrowVerify(\\n address vToken,\\n address payer, // solhint-disable-line no-unused-vars\\n address borrower,\\n uint256 actualRepayAmount, // solhint-disable-line no-unused-vars\\n uint256 borrowerIndex // solhint-disable-line no-unused-vars\\n ) external {\\n if (address(prime) != address(0)) {\\n prime.accrueInterestAndUpdateScore(borrower, vToken);\\n }\\n }\\n\\n /**\\n * @notice Validates liquidateBorrow, accrues interest and updates score in prime. Reverts on rejection. May emit logs.\\n * @param vTokenBorrowed Asset which was borrowed by the borrower\\n * @param vTokenCollateral Asset which was used as collateral and will be seized\\n * @param liquidator The address repaying the borrow and seizing the collateral\\n * @param borrower The address of the borrower\\n * @param actualRepayAmount The amount of underlying being repaid\\n * @param seizeTokens The amount of collateral token that will be seized\\n */\\n function liquidateBorrowVerify(\\n address vTokenBorrowed,\\n address vTokenCollateral, // solhint-disable-line no-unused-vars\\n address liquidator,\\n address borrower,\\n uint256 actualRepayAmount, // solhint-disable-line no-unused-vars\\n uint256 seizeTokens // solhint-disable-line no-unused-vars\\n ) external {\\n if (address(prime) != address(0)) {\\n prime.accrueInterestAndUpdateScore(borrower, vTokenBorrowed);\\n prime.accrueInterestAndUpdateScore(liquidator, vTokenBorrowed);\\n }\\n }\\n\\n /**\\n * @notice Validates seize, accrues interest and updates score in prime. Reverts on rejection. May emit logs.\\n * @param vTokenCollateral Asset which was used as collateral and will be seized\\n * @param vTokenBorrowed Asset which was borrowed by the borrower\\n * @param liquidator The address repaying the borrow and seizing the collateral\\n * @param borrower The address of the borrower\\n * @param seizeTokens The number of collateral tokens to seize\\n */\\n function seizeVerify(\\n address vTokenCollateral,\\n address vTokenBorrowed, // solhint-disable-line no-unused-vars\\n address liquidator,\\n address borrower,\\n uint256 seizeTokens // solhint-disable-line no-unused-vars\\n ) external {\\n if (address(prime) != address(0)) {\\n prime.accrueInterestAndUpdateScore(borrower, vTokenCollateral);\\n prime.accrueInterestAndUpdateScore(liquidator, vTokenCollateral);\\n }\\n }\\n\\n /**\\n * @notice Validates transfer, accrues interest and updates score in prime. Reverts on rejection. May emit logs.\\n * @param vToken Asset being transferred\\n * @param src The account which sources the tokens\\n * @param dst The account which receives the tokens\\n * @param transferTokens The number of vTokens to transfer\\n */\\n // solhint-disable-next-line no-unused-vars\\n function transferVerify(address vToken, address src, address dst, uint256 transferTokens) external {\\n if (address(prime) != address(0)) {\\n prime.accrueInterestAndUpdateScore(src, vToken);\\n prime.accrueInterestAndUpdateScore(dst, vToken);\\n }\\n }\\n\\n /**\\n * @notice Checks if the account should be allowed to borrow the underlying asset of the given market\\n * @param vToken The market to verify the borrow against\\n * @param borrower The account which would borrow the asset\\n * @param borrowAmount The amount of underlying the account would borrow\\n * @custom:error ActionPaused error is thrown if borrowing is paused in this market\\n * @custom:error MarketNotListed error is thrown when the market is not listed\\n * @custom:error InsufficientLiquidity error is thrown if there is not enough collateral to borrow\\n * @custom:error BorrowCapExceeded is thrown if the borrow cap will be exceeded should this borrow succeed\\n * @custom:error SnapshotError is thrown if some vToken fails to return the account's supply and borrows\\n * @custom:error PriceError is thrown if the oracle returns an incorrect price for some asset\\n * @custom:access Not restricted if vToken is enabled as collateral, otherwise only vToken\\n */\\n /// disable-eslint\\n function preBorrowHook(address vToken, address borrower, uint256 borrowAmount) external override {\\n _checkActionPauseState(vToken, Action.BORROW);\\n\\n if (!markets[vToken].isListed) {\\n revert MarketNotListed(address(vToken));\\n }\\n\\n if (!markets[vToken].accountMembership[borrower]) {\\n // only vTokens may call borrowAllowed if borrower not in market\\n _checkSenderIs(vToken);\\n\\n // attempt to add borrower to the market or revert\\n _addToMarket(VToken(msg.sender), borrower);\\n }\\n\\n // Update the prices of tokens\\n updatePrices(borrower);\\n\\n if (oracle.getUnderlyingPrice(vToken) == 0) {\\n revert PriceError(address(vToken));\\n }\\n\\n uint256 borrowCap = borrowCaps[vToken];\\n // Skipping the cap check for uncapped coins to save some gas\\n if (borrowCap != type(uint256).max) {\\n uint256 totalBorrows = VToken(vToken).totalBorrows();\\n uint256 badDebt = VToken(vToken).badDebt();\\n uint256 nextTotalBorrows = totalBorrows + borrowAmount + badDebt;\\n if (nextTotalBorrows > borrowCap) {\\n revert BorrowCapExceeded(vToken, borrowCap);\\n }\\n }\\n\\n AccountLiquiditySnapshot memory snapshot = _getHypotheticalLiquiditySnapshot(\\n borrower,\\n VToken(vToken),\\n 0,\\n borrowAmount,\\n _getCollateralFactor\\n );\\n\\n if (snapshot.shortfall > 0) {\\n revert InsufficientLiquidity();\\n }\\n\\n Exp memory borrowIndex = Exp({ mantissa: VToken(vToken).borrowIndex() });\\n\\n // Keep the flywheel moving\\n uint256 rewardDistributorsCount = rewardsDistributors.length;\\n\\n for (uint256 i; i < rewardDistributorsCount; ++i) {\\n RewardsDistributor rewardsDistributor = rewardsDistributors[i];\\n rewardsDistributor.updateRewardTokenBorrowIndex(vToken, borrowIndex);\\n rewardsDistributor.distributeBorrowerRewardToken(vToken, borrower, borrowIndex);\\n }\\n }\\n\\n /**\\n * @notice Validates borrow, accrues interest and updates score in prime. Reverts on rejection. May emit logs.\\n * @param vToken Asset whose underlying is being borrowed\\n * @param borrower The address borrowing the underlying\\n * @param borrowAmount The amount of the underlying asset requested to borrow\\n */\\n // solhint-disable-next-line no-unused-vars\\n function borrowVerify(address vToken, address borrower, uint256 borrowAmount) external {\\n if (address(prime) != address(0)) {\\n prime.accrueInterestAndUpdateScore(borrower, vToken);\\n }\\n }\\n\\n /**\\n * @notice Checks if the account should be allowed to repay a borrow in the given market\\n * @param vToken The market to verify the repay against\\n * @param borrower The account which would borrowed the asset\\n * @custom:error ActionPaused error is thrown if repayments are paused in this market\\n * @custom:error MarketNotListed error is thrown when the market is not listed\\n * @custom:access Not restricted\\n */\\n function preRepayHook(address vToken, address borrower) external override {\\n _checkActionPauseState(vToken, Action.REPAY);\\n\\n oracle.updatePrice(vToken);\\n\\n if (!markets[vToken].isListed) {\\n revert MarketNotListed(address(vToken));\\n }\\n\\n // Keep the flywheel moving\\n uint256 rewardDistributorsCount = rewardsDistributors.length;\\n\\n for (uint256 i; i < rewardDistributorsCount; ++i) {\\n Exp memory borrowIndex = Exp({ mantissa: VToken(vToken).borrowIndex() });\\n RewardsDistributor rewardsDistributor = rewardsDistributors[i];\\n rewardsDistributor.updateRewardTokenBorrowIndex(vToken, borrowIndex);\\n rewardsDistributor.distributeBorrowerRewardToken(vToken, borrower, borrowIndex);\\n }\\n }\\n\\n /**\\n * @notice Checks if the liquidation should be allowed to occur\\n * @param vTokenBorrowed Asset which was borrowed by the borrower\\n * @param vTokenCollateral Asset which was used as collateral and will be seized\\n * @param borrower The address of the borrower\\n * @param repayAmount The amount of underlying being repaid\\n * @param skipLiquidityCheck Allows the borrow to be liquidated regardless of the account liquidity\\n * @custom:error ActionPaused error is thrown if liquidations are paused in this market\\n * @custom:error MarketNotListed error is thrown if either collateral or borrowed token is not listed\\n * @custom:error TooMuchRepay error is thrown if the liquidator is trying to repay more than allowed by close factor\\n * @custom:error MinimalCollateralViolated is thrown if the users' total collateral is lower than the threshold for non-batch liquidations\\n * @custom:error InsufficientShortfall is thrown when trying to liquidate a healthy account\\n * @custom:error SnapshotError is thrown if some vToken fails to return the account's supply and borrows\\n * @custom:error PriceError is thrown if the oracle returns an incorrect price for some asset\\n */\\n function preLiquidateHook(\\n address vTokenBorrowed,\\n address vTokenCollateral,\\n address borrower,\\n uint256 repayAmount,\\n bool skipLiquidityCheck\\n ) external override {\\n // Pause Action.LIQUIDATE on BORROWED TOKEN to prevent liquidating it.\\n // If we want to pause liquidating to vTokenCollateral, we should pause\\n // Action.SEIZE on it\\n _checkActionPauseState(vTokenBorrowed, Action.LIQUIDATE);\\n\\n // Update the prices of tokens\\n updatePrices(borrower);\\n\\n if (!markets[vTokenBorrowed].isListed) {\\n revert MarketNotListed(address(vTokenBorrowed));\\n }\\n if (!markets[vTokenCollateral].isListed) {\\n revert MarketNotListed(address(vTokenCollateral));\\n }\\n\\n uint256 borrowBalance = VToken(vTokenBorrowed).borrowBalanceStored(borrower);\\n\\n /* Allow accounts to be liquidated if it is a forced liquidation */\\n if (skipLiquidityCheck || isForcedLiquidationEnabled[vTokenBorrowed]) {\\n if (repayAmount > borrowBalance) {\\n revert TooMuchRepay();\\n }\\n return;\\n }\\n\\n /* The borrower must have shortfall and collateral > threshold in order to be liquidatable */\\n AccountLiquiditySnapshot memory snapshot = _getCurrentLiquiditySnapshot(borrower, _getLiquidationThreshold);\\n\\n if (snapshot.totalCollateral <= minLiquidatableCollateral) {\\n /* The liquidator should use either liquidateAccount or healAccount */\\n revert MinimalCollateralViolated(minLiquidatableCollateral, snapshot.totalCollateral);\\n }\\n\\n if (snapshot.shortfall == 0) {\\n revert InsufficientShortfall();\\n }\\n\\n /* The liquidator may not repay more than what is allowed by the closeFactor */\\n uint256 maxClose = mul_ScalarTruncate(Exp({ mantissa: closeFactorMantissa }), borrowBalance);\\n if (repayAmount > maxClose) {\\n revert TooMuchRepay();\\n }\\n }\\n\\n /**\\n * @notice Checks if the seizing of assets should be allowed to occur\\n * @param vTokenCollateral Asset which was used as collateral and will be seized\\n * @param seizerContract Contract that tries to seize the asset (either borrowed vToken or Comptroller)\\n * @param liquidator The address repaying the borrow and seizing the collateral\\n * @param borrower The address of the borrower\\n * @custom:error ActionPaused error is thrown if seizing this type of collateral is paused\\n * @custom:error MarketNotListed error is thrown if either collateral or borrowed token is not listed\\n * @custom:error ComptrollerMismatch error is when seizer contract or seized asset belong to different pools\\n * @custom:access Not restricted\\n */\\n function preSeizeHook(\\n address vTokenCollateral,\\n address seizerContract,\\n address liquidator,\\n address borrower\\n ) external override {\\n // Pause Action.SEIZE on COLLATERAL to prevent seizing it.\\n // If we want to pause liquidating vTokenBorrowed, we should pause\\n // Action.LIQUIDATE on it\\n _checkActionPauseState(vTokenCollateral, Action.SEIZE);\\n\\n Market storage market = markets[vTokenCollateral];\\n\\n if (!market.isListed) {\\n revert MarketNotListed(vTokenCollateral);\\n }\\n\\n if (seizerContract == address(this)) {\\n // If Comptroller is the seizer, just check if collateral's comptroller\\n // is equal to the current address\\n if (address(VToken(vTokenCollateral).comptroller()) != address(this)) {\\n revert ComptrollerMismatch();\\n }\\n } else {\\n // If the seizer is not the Comptroller, check that the seizer is a\\n // listed market, and that the markets' comptrollers match\\n if (!markets[seizerContract].isListed) {\\n revert MarketNotListed(seizerContract);\\n }\\n if (VToken(vTokenCollateral).comptroller() != VToken(seizerContract).comptroller()) {\\n revert ComptrollerMismatch();\\n }\\n }\\n\\n if (!market.accountMembership[borrower]) {\\n revert MarketNotCollateral(vTokenCollateral, borrower);\\n }\\n\\n // Keep the flywheel moving\\n uint256 rewardDistributorsCount = rewardsDistributors.length;\\n\\n for (uint256 i; i < rewardDistributorsCount; ++i) {\\n RewardsDistributor rewardsDistributor = rewardsDistributors[i];\\n rewardsDistributor.updateRewardTokenSupplyIndex(vTokenCollateral);\\n rewardsDistributor.distributeSupplierRewardToken(vTokenCollateral, borrower);\\n rewardsDistributor.distributeSupplierRewardToken(vTokenCollateral, liquidator);\\n }\\n }\\n\\n /**\\n * @notice Checks if the account should be allowed to transfer tokens in the given market\\n * @param vToken The market to verify the transfer against\\n * @param src The account which sources the tokens\\n * @param dst The account which receives the tokens\\n * @param transferTokens The number of vTokens to transfer\\n * @custom:error ActionPaused error is thrown if withdrawals are paused in this market\\n * @custom:error MarketNotListed error is thrown when the market is not listed\\n * @custom:error InsufficientLiquidity error is thrown if the withdrawal would lead to user's insolvency\\n * @custom:error SnapshotError is thrown if some vToken fails to return the account's supply and borrows\\n * @custom:error PriceError is thrown if the oracle returns an incorrect price for some asset\\n * @custom:access Not restricted\\n */\\n function preTransferHook(address vToken, address src, address dst, uint256 transferTokens) external override {\\n _checkActionPauseState(vToken, Action.TRANSFER);\\n\\n // Currently the only consideration is whether or not\\n // the src is allowed to redeem this many tokens\\n _checkRedeemAllowed(vToken, src, transferTokens);\\n\\n // Keep the flywheel moving\\n uint256 rewardDistributorsCount = rewardsDistributors.length;\\n\\n for (uint256 i; i < rewardDistributorsCount; ++i) {\\n RewardsDistributor rewardsDistributor = rewardsDistributors[i];\\n rewardsDistributor.updateRewardTokenSupplyIndex(vToken);\\n rewardsDistributor.distributeSupplierRewardToken(vToken, src);\\n rewardsDistributor.distributeSupplierRewardToken(vToken, dst);\\n }\\n }\\n\\n /*** Pool-level operations ***/\\n\\n /**\\n * @notice Seizes all the remaining collateral, makes msg.sender repay the existing\\n * borrows, and treats the rest of the debt as bad debt (for each market).\\n * The sender has to repay a certain percentage of the debt, computed as\\n * collateral / (borrows * liquidationIncentive).\\n * @param user account to heal\\n * @custom:error CollateralExceedsThreshold error is thrown when the collateral is too big for healing\\n * @custom:error SnapshotError is thrown if some vToken fails to return the account's supply and borrows\\n * @custom:error PriceError is thrown if the oracle returns an incorrect price for some asset\\n * @custom:access Not restricted\\n */\\n function healAccount(address user) external {\\n VToken[] memory userAssets = getAssetsIn(user);\\n uint256 userAssetsCount = userAssets.length;\\n\\n address liquidator = msg.sender;\\n {\\n ResilientOracleInterface oracle_ = oracle;\\n // We need all user's markets to be fresh for the computations to be correct\\n for (uint256 i; i < userAssetsCount; ++i) {\\n userAssets[i].accrueInterest();\\n oracle_.updatePrice(address(userAssets[i]));\\n }\\n }\\n\\n AccountLiquiditySnapshot memory snapshot = _getCurrentLiquiditySnapshot(user, _getLiquidationThreshold);\\n\\n if (snapshot.totalCollateral > minLiquidatableCollateral) {\\n revert CollateralExceedsThreshold(minLiquidatableCollateral, snapshot.totalCollateral);\\n }\\n\\n if (snapshot.shortfall == 0) {\\n revert InsufficientShortfall();\\n }\\n\\n // percentage = collateral / (borrows * liquidation incentive)\\n Exp memory collateral = Exp({ mantissa: snapshot.totalCollateral });\\n Exp memory scaledBorrows = mul_(\\n Exp({ mantissa: snapshot.borrows }),\\n Exp({ mantissa: liquidationIncentiveMantissa })\\n );\\n\\n Exp memory percentage = div_(collateral, scaledBorrows);\\n if (lessThanExp(Exp({ mantissa: MANTISSA_ONE }), percentage)) {\\n revert CollateralExceedsThreshold(scaledBorrows.mantissa, collateral.mantissa);\\n }\\n\\n for (uint256 i; i < userAssetsCount; ++i) {\\n VToken market = userAssets[i];\\n\\n (uint256 tokens, uint256 borrowBalance, ) = _safeGetAccountSnapshot(market, user);\\n uint256 repaymentAmount = mul_ScalarTruncate(percentage, borrowBalance);\\n\\n // Seize the entire collateral\\n if (tokens != 0) {\\n market.seize(liquidator, user, tokens);\\n }\\n // Repay a certain percentage of the borrow, forgive the rest\\n if (borrowBalance != 0) {\\n market.healBorrow(liquidator, user, repaymentAmount);\\n }\\n }\\n }\\n\\n /**\\n * @notice Liquidates all borrows of the borrower. Callable only if the collateral is less than\\n * a predefined threshold, and the account collateral can be seized to cover all borrows. If\\n * the collateral is higher than the threshold, use regular liquidations. If the collateral is\\n * below the threshold, and the account is insolvent, use healAccount.\\n * @param borrower the borrower address\\n * @param orders an array of liquidation orders\\n * @custom:error CollateralExceedsThreshold error is thrown when the collateral is too big for a batch liquidation\\n * @custom:error InsufficientCollateral error is thrown when there is not enough collateral to cover the debt\\n * @custom:error SnapshotError is thrown if some vToken fails to return the account's supply and borrows\\n * @custom:error PriceError is thrown if the oracle returns an incorrect price for some asset\\n * @custom:access Not restricted\\n */\\n function liquidateAccount(address borrower, LiquidationOrder[] calldata orders) external {\\n // We will accrue interest and update the oracle prices later during the liquidation\\n\\n AccountLiquiditySnapshot memory snapshot = _getCurrentLiquiditySnapshot(borrower, _getLiquidationThreshold);\\n\\n if (snapshot.totalCollateral > minLiquidatableCollateral) {\\n // You should use the regular vToken.liquidateBorrow(...) call\\n revert CollateralExceedsThreshold(minLiquidatableCollateral, snapshot.totalCollateral);\\n }\\n\\n uint256 collateralToSeize = mul_ScalarTruncate(\\n Exp({ mantissa: liquidationIncentiveMantissa }),\\n snapshot.borrows\\n );\\n if (collateralToSeize >= snapshot.totalCollateral) {\\n // There is not enough collateral to seize. Use healBorrow to repay some part of the borrow\\n // and record bad debt.\\n revert InsufficientCollateral(collateralToSeize, snapshot.totalCollateral);\\n }\\n\\n if (snapshot.shortfall == 0) {\\n revert InsufficientShortfall();\\n }\\n\\n uint256 ordersCount = orders.length;\\n\\n _ensureMaxLoops(ordersCount / 2);\\n\\n for (uint256 i; i < ordersCount; ++i) {\\n if (!markets[address(orders[i].vTokenBorrowed)].isListed) {\\n revert MarketNotListed(address(orders[i].vTokenBorrowed));\\n }\\n if (!markets[address(orders[i].vTokenCollateral)].isListed) {\\n revert MarketNotListed(address(orders[i].vTokenCollateral));\\n }\\n\\n LiquidationOrder calldata order = orders[i];\\n order.vTokenBorrowed.forceLiquidateBorrow(\\n msg.sender,\\n borrower,\\n order.repayAmount,\\n order.vTokenCollateral,\\n true\\n );\\n }\\n\\n VToken[] memory borrowMarkets = getAssetsIn(borrower);\\n uint256 marketsCount = borrowMarkets.length;\\n\\n for (uint256 i; i < marketsCount; ++i) {\\n (, uint256 borrowBalance, ) = _safeGetAccountSnapshot(borrowMarkets[i], borrower);\\n require(borrowBalance == 0, \\\"Nonzero borrow balance after liquidation\\\");\\n }\\n }\\n\\n /**\\n * @notice Sets the closeFactor to use when liquidating borrows\\n * @param newCloseFactorMantissa New close factor, scaled by 1e18\\n * @custom:event Emits NewCloseFactor on success\\n * @custom:access Controlled by AccessControlManager\\n */\\n function setCloseFactor(uint256 newCloseFactorMantissa) external {\\n _checkAccessAllowed(\\\"setCloseFactor(uint256)\\\");\\n require(MAX_CLOSE_FACTOR_MANTISSA >= newCloseFactorMantissa, \\\"Close factor greater than maximum close factor\\\");\\n require(MIN_CLOSE_FACTOR_MANTISSA <= newCloseFactorMantissa, \\\"Close factor smaller than minimum close factor\\\");\\n\\n uint256 oldCloseFactorMantissa = closeFactorMantissa;\\n closeFactorMantissa = newCloseFactorMantissa;\\n emit NewCloseFactor(oldCloseFactorMantissa, newCloseFactorMantissa);\\n }\\n\\n /**\\n * @notice Sets the collateralFactor for a market\\n * @dev This function is restricted by the AccessControlManager\\n * @param vToken The market to set the factor on\\n * @param newCollateralFactorMantissa The new collateral factor, scaled by 1e18\\n * @param newLiquidationThresholdMantissa The new liquidation threshold, scaled by 1e18\\n * @custom:event Emits NewCollateralFactor when collateral factor is updated\\n * and NewLiquidationThreshold when liquidation threshold is updated\\n * @custom:error MarketNotListed error is thrown when the market is not listed\\n * @custom:error InvalidCollateralFactor error is thrown when collateral factor is too high\\n * @custom:error InvalidLiquidationThreshold error is thrown when liquidation threshold is lower than collateral factor\\n * @custom:error PriceError is thrown when the oracle returns an invalid price for the asset\\n * @custom:access Controlled by AccessControlManager\\n */\\n function setCollateralFactor(\\n VToken vToken,\\n uint256 newCollateralFactorMantissa,\\n uint256 newLiquidationThresholdMantissa\\n ) external {\\n _checkAccessAllowed(\\\"setCollateralFactor(address,uint256,uint256)\\\");\\n\\n // Verify market is listed\\n Market storage market = markets[address(vToken)];\\n if (!market.isListed) {\\n revert MarketNotListed(address(vToken));\\n }\\n\\n // Check collateral factor <= 0.9\\n if (newCollateralFactorMantissa > MAX_COLLATERAL_FACTOR_MANTISSA) {\\n revert InvalidCollateralFactor();\\n }\\n\\n // Ensure that liquidation threshold <= 1\\n if (newLiquidationThresholdMantissa > MANTISSA_ONE) {\\n revert InvalidLiquidationThreshold();\\n }\\n\\n // Ensure that liquidation threshold >= CF\\n if (newLiquidationThresholdMantissa < newCollateralFactorMantissa) {\\n revert InvalidLiquidationThreshold();\\n }\\n\\n // If collateral factor != 0, fail if price == 0\\n if (newCollateralFactorMantissa != 0 && oracle.getUnderlyingPrice(address(vToken)) == 0) {\\n revert PriceError(address(vToken));\\n }\\n\\n uint256 oldCollateralFactorMantissa = market.collateralFactorMantissa;\\n if (newCollateralFactorMantissa != oldCollateralFactorMantissa) {\\n market.collateralFactorMantissa = newCollateralFactorMantissa;\\n emit NewCollateralFactor(vToken, oldCollateralFactorMantissa, newCollateralFactorMantissa);\\n }\\n\\n uint256 oldLiquidationThresholdMantissa = market.liquidationThresholdMantissa;\\n if (newLiquidationThresholdMantissa != oldLiquidationThresholdMantissa) {\\n market.liquidationThresholdMantissa = newLiquidationThresholdMantissa;\\n emit NewLiquidationThreshold(vToken, oldLiquidationThresholdMantissa, newLiquidationThresholdMantissa);\\n }\\n }\\n\\n /**\\n * @notice Sets liquidationIncentive\\n * @dev This function is restricted by the AccessControlManager\\n * @param newLiquidationIncentiveMantissa New liquidationIncentive scaled by 1e18\\n * @custom:event Emits NewLiquidationIncentive on success\\n * @custom:access Controlled by AccessControlManager\\n */\\n function setLiquidationIncentive(uint256 newLiquidationIncentiveMantissa) external {\\n require(newLiquidationIncentiveMantissa >= MANTISSA_ONE, \\\"liquidation incentive should be greater than 1e18\\\");\\n\\n _checkAccessAllowed(\\\"setLiquidationIncentive(uint256)\\\");\\n\\n // Save current value for use in log\\n uint256 oldLiquidationIncentiveMantissa = liquidationIncentiveMantissa;\\n\\n // Set liquidation incentive to new incentive\\n liquidationIncentiveMantissa = newLiquidationIncentiveMantissa;\\n\\n // Emit event with old incentive, new incentive\\n emit NewLiquidationIncentive(oldLiquidationIncentiveMantissa, newLiquidationIncentiveMantissa);\\n }\\n\\n /**\\n * @notice Add the market to the markets mapping and set it as listed\\n * @dev Only callable by the PoolRegistry\\n * @param vToken The address of the market (token) to list\\n * @custom:error MarketAlreadyListed is thrown if the market is already listed in this pool\\n * @custom:access Only PoolRegistry\\n */\\n function supportMarket(VToken vToken) external {\\n _checkSenderIs(poolRegistry);\\n\\n if (markets[address(vToken)].isListed) {\\n revert MarketAlreadyListed(address(vToken));\\n }\\n\\n require(vToken.isVToken(), \\\"Comptroller: Invalid vToken\\\"); // Sanity check to make sure its really a VToken\\n\\n Market storage newMarket = markets[address(vToken)];\\n newMarket.isListed = true;\\n newMarket.collateralFactorMantissa = 0;\\n newMarket.liquidationThresholdMantissa = 0;\\n\\n _addMarket(address(vToken));\\n\\n uint256 rewardDistributorsCount = rewardsDistributors.length;\\n\\n for (uint256 i; i < rewardDistributorsCount; ++i) {\\n rewardsDistributors[i].initializeMarket(address(vToken));\\n }\\n\\n emit MarketSupported(vToken);\\n }\\n\\n /**\\n * @notice Set the given borrow caps for the given vToken markets. Borrowing that brings total borrows to or above borrow cap will revert.\\n * @dev This function is restricted by the AccessControlManager\\n * @dev A borrow cap of type(uint256).max corresponds to unlimited borrowing.\\n * @dev Borrow caps smaller than the current total borrows are accepted. This way, new borrows will not be allowed\\n until the total borrows amount goes below the new borrow cap\\n * @param vTokens The addresses of the markets (tokens) to change the borrow caps for\\n * @param newBorrowCaps The new borrow cap values in underlying to be set. A value of type(uint256).max corresponds to unlimited borrowing.\\n * @custom:access Controlled by AccessControlManager\\n */\\n function setMarketBorrowCaps(VToken[] calldata vTokens, uint256[] calldata newBorrowCaps) external {\\n _checkAccessAllowed(\\\"setMarketBorrowCaps(address[],uint256[])\\\");\\n\\n uint256 numMarkets = vTokens.length;\\n uint256 numBorrowCaps = newBorrowCaps.length;\\n\\n require(numMarkets != 0 && numMarkets == numBorrowCaps, \\\"invalid input\\\");\\n\\n _ensureMaxLoops(numMarkets);\\n\\n for (uint256 i; i < numMarkets; ++i) {\\n borrowCaps[address(vTokens[i])] = newBorrowCaps[i];\\n emit NewBorrowCap(vTokens[i], newBorrowCaps[i]);\\n }\\n }\\n\\n /**\\n * @notice Set the given supply caps for the given vToken markets. Supply that brings total Supply to or above supply cap will revert.\\n * @dev This function is restricted by the AccessControlManager\\n * @dev A supply cap of type(uint256).max corresponds to unlimited supply.\\n * @dev Supply caps smaller than the current total supplies are accepted. This way, new supplies will not be allowed\\n until the total supplies amount goes below the new supply cap\\n * @param vTokens The addresses of the markets (tokens) to change the supply caps for\\n * @param newSupplyCaps The new supply cap values in underlying to be set. A value of type(uint256).max corresponds to unlimited supply.\\n * @custom:access Controlled by AccessControlManager\\n */\\n function setMarketSupplyCaps(VToken[] calldata vTokens, uint256[] calldata newSupplyCaps) external {\\n _checkAccessAllowed(\\\"setMarketSupplyCaps(address[],uint256[])\\\");\\n uint256 vTokensCount = vTokens.length;\\n\\n require(vTokensCount != 0, \\\"invalid number of markets\\\");\\n require(vTokensCount == newSupplyCaps.length, \\\"invalid number of markets\\\");\\n\\n _ensureMaxLoops(vTokensCount);\\n\\n for (uint256 i; i < vTokensCount; ++i) {\\n supplyCaps[address(vTokens[i])] = newSupplyCaps[i];\\n emit NewSupplyCap(vTokens[i], newSupplyCaps[i]);\\n }\\n }\\n\\n /**\\n * @notice Pause/unpause specified actions\\n * @dev This function is restricted by the AccessControlManager\\n * @param marketsList Markets to pause/unpause the actions on\\n * @param actionsList List of action ids to pause/unpause\\n * @param paused The new paused state (true=paused, false=unpaused)\\n * @custom:access Controlled by AccessControlManager\\n */\\n function setActionsPaused(VToken[] calldata marketsList, Action[] calldata actionsList, bool paused) external {\\n _checkAccessAllowed(\\\"setActionsPaused(address[],uint256[],bool)\\\");\\n\\n uint256 marketsCount = marketsList.length;\\n uint256 actionsCount = actionsList.length;\\n\\n _ensureMaxLoops(marketsCount * actionsCount);\\n\\n for (uint256 marketIdx; marketIdx < marketsCount; ++marketIdx) {\\n for (uint256 actionIdx; actionIdx < actionsCount; ++actionIdx) {\\n _setActionPaused(address(marketsList[marketIdx]), actionsList[actionIdx], paused);\\n }\\n }\\n }\\n\\n /**\\n * @notice Set the given collateral threshold for non-batch liquidations. Regular liquidations\\n * will fail if the collateral amount is less than this threshold. Liquidators should use batch\\n * operations like liquidateAccount or healAccount.\\n * @dev This function is restricted by the AccessControlManager\\n * @param newMinLiquidatableCollateral The new min liquidatable collateral (in USD).\\n * @custom:access Controlled by AccessControlManager\\n */\\n function setMinLiquidatableCollateral(uint256 newMinLiquidatableCollateral) external {\\n _checkAccessAllowed(\\\"setMinLiquidatableCollateral(uint256)\\\");\\n\\n uint256 oldMinLiquidatableCollateral = minLiquidatableCollateral;\\n minLiquidatableCollateral = newMinLiquidatableCollateral;\\n emit NewMinLiquidatableCollateral(oldMinLiquidatableCollateral, newMinLiquidatableCollateral);\\n }\\n\\n /**\\n * @notice Add a new RewardsDistributor and initialize it with all markets. We can add several RewardsDistributor\\n * contracts with the same rewardToken, and there could be overlaping among them considering the last reward slot (block or second)\\n * @dev Only callable by the admin\\n * @param _rewardsDistributor Address of the RewardDistributor contract to add\\n * @custom:access Only Governance\\n * @custom:event Emits NewRewardsDistributor with distributor address\\n */\\n function addRewardsDistributor(RewardsDistributor _rewardsDistributor) external onlyOwner {\\n require(!rewardsDistributorExists[address(_rewardsDistributor)], \\\"already exists\\\");\\n\\n uint256 rewardsDistributorsLen = rewardsDistributors.length;\\n _ensureMaxLoops(rewardsDistributorsLen + 1);\\n\\n rewardsDistributors.push(_rewardsDistributor);\\n rewardsDistributorExists[address(_rewardsDistributor)] = true;\\n\\n uint256 marketsCount = allMarkets.length;\\n\\n for (uint256 i; i < marketsCount; ++i) {\\n _rewardsDistributor.initializeMarket(address(allMarkets[i]));\\n }\\n\\n emit NewRewardsDistributor(address(_rewardsDistributor), address(_rewardsDistributor.rewardToken()));\\n }\\n\\n /**\\n * @notice Sets a new price oracle for the Comptroller\\n * @dev Only callable by the admin\\n * @param newOracle Address of the new price oracle to set\\n * @custom:event Emits NewPriceOracle on success\\n * @custom:error ZeroAddressNotAllowed is thrown when the new oracle address is zero\\n */\\n function setPriceOracle(ResilientOracleInterface newOracle) external onlyOwner {\\n ensureNonzeroAddress(address(newOracle));\\n\\n ResilientOracleInterface oldOracle = oracle;\\n oracle = newOracle;\\n emit NewPriceOracle(oldOracle, newOracle);\\n }\\n\\n /**\\n * @notice Set the for loop iteration limit to avoid DOS\\n * @param limit Limit for the max loops can execute at a time\\n */\\n function setMaxLoopsLimit(uint256 limit) external onlyOwner {\\n _setMaxLoopsLimit(limit);\\n }\\n\\n /**\\n * @notice Sets the prime token contract for the comptroller\\n * @param _prime Address of the Prime contract\\n */\\n function setPrimeToken(IPrime _prime) external onlyOwner {\\n ensureNonzeroAddress(address(_prime));\\n\\n emit NewPrimeToken(prime, _prime);\\n prime = _prime;\\n }\\n\\n /**\\n * @notice Enables forced liquidations for a market. If forced liquidation is enabled,\\n * borrows in the market may be liquidated regardless of the account liquidity\\n * @param vTokenBorrowed Borrowed vToken\\n * @param enable Whether to enable forced liquidations\\n */\\n function setForcedLiquidation(address vTokenBorrowed, bool enable) external {\\n _checkAccessAllowed(\\\"setForcedLiquidation(address,bool)\\\");\\n ensureNonzeroAddress(vTokenBorrowed);\\n\\n if (!markets[vTokenBorrowed].isListed) {\\n revert MarketNotListed(vTokenBorrowed);\\n }\\n\\n isForcedLiquidationEnabled[vTokenBorrowed] = enable;\\n emit IsForcedLiquidationEnabledUpdated(vTokenBorrowed, enable);\\n }\\n\\n /**\\n * @notice Determine the current account liquidity with respect to liquidation threshold requirements\\n * @dev The interface of this function is intentionally kept compatible with Compound and Venus Core\\n * @param account The account get liquidity for\\n * @return error Always NO_ERROR for compatibility with Venus core tooling\\n * @return liquidity Account liquidity in excess of liquidation threshold requirements,\\n * @return shortfall Account shortfall below liquidation threshold requirements\\n */\\n function getAccountLiquidity(\\n address account\\n ) external view returns (uint256 error, uint256 liquidity, uint256 shortfall) {\\n AccountLiquiditySnapshot memory snapshot = _getCurrentLiquiditySnapshot(account, _getLiquidationThreshold);\\n return (NO_ERROR, snapshot.liquidity, snapshot.shortfall);\\n }\\n\\n /**\\n * @notice Determine the current account liquidity with respect to collateral requirements\\n * @dev The interface of this function is intentionally kept compatible with Compound and Venus Core\\n * @param account The account get liquidity for\\n * @return error Always NO_ERROR for compatibility with Venus core tooling\\n * @return liquidity Account liquidity in excess of collateral requirements,\\n * @return shortfall Account shortfall below collateral requirements\\n */\\n function getBorrowingPower(\\n address account\\n ) external view returns (uint256 error, uint256 liquidity, uint256 shortfall) {\\n AccountLiquiditySnapshot memory snapshot = _getCurrentLiquiditySnapshot(account, _getCollateralFactor);\\n return (NO_ERROR, snapshot.liquidity, snapshot.shortfall);\\n }\\n\\n /**\\n * @notice Determine what the account liquidity would be if the given amounts were redeemed/borrowed\\n * @dev The interface of this function is intentionally kept compatible with Compound and Venus Core\\n * @param vTokenModify The market to hypothetically redeem/borrow in\\n * @param account The account to determine liquidity for\\n * @param redeemTokens The number of tokens to hypothetically redeem\\n * @param borrowAmount The amount of underlying to hypothetically borrow\\n * @return error Always NO_ERROR for compatibility with Venus core tooling\\n * @return liquidity Hypothetical account liquidity in excess of collateral requirements,\\n * @return shortfall Hypothetical account shortfall below collateral requirements\\n */\\n function getHypotheticalAccountLiquidity(\\n address account,\\n address vTokenModify,\\n uint256 redeemTokens,\\n uint256 borrowAmount\\n ) external view returns (uint256 error, uint256 liquidity, uint256 shortfall) {\\n AccountLiquiditySnapshot memory snapshot = _getHypotheticalLiquiditySnapshot(\\n account,\\n VToken(vTokenModify),\\n redeemTokens,\\n borrowAmount,\\n _getCollateralFactor\\n );\\n return (NO_ERROR, snapshot.liquidity, snapshot.shortfall);\\n }\\n\\n /**\\n * @notice Return all of the markets\\n * @dev The automatic getter may be used to access an individual market.\\n * @return markets The list of market addresses\\n */\\n function getAllMarkets() external view override returns (VToken[] memory) {\\n return allMarkets;\\n }\\n\\n /**\\n * @notice Check if a market is marked as listed (active)\\n * @param vToken vToken Address for the market to check\\n * @return listed True if listed otherwise false\\n */\\n function isMarketListed(VToken vToken) external view returns (bool) {\\n return markets[address(vToken)].isListed;\\n }\\n\\n /*** Assets You Are In ***/\\n\\n /**\\n * @notice Returns whether the given account is entered in a given market\\n * @param account The address of the account to check\\n * @param vToken The vToken to check\\n * @return True if the account is in the market specified, otherwise false.\\n */\\n function checkMembership(address account, VToken vToken) external view returns (bool) {\\n return markets[address(vToken)].accountMembership[account];\\n }\\n\\n /**\\n * @notice Calculate number of tokens of collateral asset to seize given an underlying amount\\n * @dev Used in liquidation (called in vToken.liquidateBorrowFresh)\\n * @param vTokenBorrowed The address of the borrowed vToken\\n * @param vTokenCollateral The address of the collateral vToken\\n * @param actualRepayAmount The amount of vTokenBorrowed underlying to convert into vTokenCollateral tokens\\n * @return error Always NO_ERROR for compatibility with Venus core tooling\\n * @return tokensToSeize Number of vTokenCollateral tokens to be seized in a liquidation\\n * @custom:error PriceError if the oracle returns an invalid price\\n */\\n function liquidateCalculateSeizeTokens(\\n address vTokenBorrowed,\\n address vTokenCollateral,\\n uint256 actualRepayAmount\\n ) external view override returns (uint256 error, uint256 tokensToSeize) {\\n /* Read oracle prices for borrowed and collateral markets */\\n uint256 priceBorrowedMantissa = _safeGetUnderlyingPrice(VToken(vTokenBorrowed));\\n uint256 priceCollateralMantissa = _safeGetUnderlyingPrice(VToken(vTokenCollateral));\\n\\n /*\\n * Get the exchange rate and calculate the number of collateral tokens to seize:\\n * seizeAmount = actualRepayAmount * liquidationIncentive * priceBorrowed / priceCollateral\\n * seizeTokens = seizeAmount / exchangeRate\\n * = actualRepayAmount * (liquidationIncentive * priceBorrowed) / (priceCollateral * exchangeRate)\\n */\\n uint256 exchangeRateMantissa = VToken(vTokenCollateral).exchangeRateStored(); // Note: reverts on error\\n uint256 seizeTokens;\\n Exp memory numerator;\\n Exp memory denominator;\\n Exp memory ratio;\\n\\n numerator = mul_(Exp({ mantissa: liquidationIncentiveMantissa }), Exp({ mantissa: priceBorrowedMantissa }));\\n denominator = mul_(Exp({ mantissa: priceCollateralMantissa }), Exp({ mantissa: exchangeRateMantissa }));\\n ratio = div_(numerator, denominator);\\n\\n seizeTokens = mul_ScalarTruncate(ratio, actualRepayAmount);\\n\\n return (NO_ERROR, seizeTokens);\\n }\\n\\n /**\\n * @notice Returns reward speed given a vToken\\n * @param vToken The vToken to get the reward speeds for\\n * @return rewardSpeeds Array of total supply and borrow speeds and reward token for all reward distributors\\n */\\n function getRewardsByMarket(address vToken) external view returns (RewardSpeeds[] memory rewardSpeeds) {\\n uint256 rewardsDistributorsLength = rewardsDistributors.length;\\n rewardSpeeds = new RewardSpeeds[](rewardsDistributorsLength);\\n for (uint256 i; i < rewardsDistributorsLength; ++i) {\\n RewardsDistributor rewardsDistributor = rewardsDistributors[i];\\n address rewardToken = address(rewardsDistributor.rewardToken());\\n rewardSpeeds[i] = RewardSpeeds({\\n rewardToken: rewardToken,\\n supplySpeed: rewardsDistributor.rewardTokenSupplySpeeds(vToken),\\n borrowSpeed: rewardsDistributor.rewardTokenBorrowSpeeds(vToken)\\n });\\n }\\n return rewardSpeeds;\\n }\\n\\n /**\\n * @notice Return all reward distributors for this pool\\n * @return Array of RewardDistributor addresses\\n */\\n function getRewardDistributors() external view returns (RewardsDistributor[] memory) {\\n return rewardsDistributors;\\n }\\n\\n /**\\n * @notice A marker method that returns true for a valid Comptroller contract\\n * @return Always true\\n */\\n function isComptroller() external pure override returns (bool) {\\n return true;\\n }\\n\\n /**\\n * @notice Update the prices of all the tokens associated with the provided account\\n * @param account Address of the account to get associated tokens with\\n */\\n function updatePrices(address account) public {\\n VToken[] memory vTokens = getAssetsIn(account);\\n uint256 vTokensCount = vTokens.length;\\n\\n ResilientOracleInterface oracle_ = oracle;\\n\\n for (uint256 i; i < vTokensCount; ++i) {\\n oracle_.updatePrice(address(vTokens[i]));\\n }\\n }\\n\\n /**\\n * @notice Checks if a certain action is paused on a market\\n * @param market vToken address\\n * @param action Action to check\\n * @return paused True if the action is paused otherwise false\\n */\\n function actionPaused(address market, Action action) public view returns (bool) {\\n return _actionPaused[market][action];\\n }\\n\\n /**\\n * @notice Returns the assets an account has entered\\n * @param account The address of the account to pull assets for\\n * @return A list with the assets the account has entered\\n */\\n function getAssetsIn(address account) public view returns (VToken[] memory) {\\n uint256 len;\\n VToken[] memory _accountAssets = accountAssets[account];\\n uint256 _accountAssetsLength = _accountAssets.length;\\n\\n VToken[] memory assetsIn = new VToken[](_accountAssetsLength);\\n\\n for (uint256 i; i < _accountAssetsLength; ++i) {\\n Market storage market = markets[address(_accountAssets[i])];\\n if (market.isListed) {\\n assetsIn[len] = _accountAssets[i];\\n ++len;\\n }\\n }\\n\\n assembly {\\n mstore(assetsIn, len)\\n }\\n\\n return assetsIn;\\n }\\n\\n /**\\n * @notice Add the market to the borrower's \\\"assets in\\\" for liquidity calculations\\n * @param vToken The market to enter\\n * @param borrower The address of the account to modify\\n */\\n function _addToMarket(VToken vToken, address borrower) internal {\\n _checkActionPauseState(address(vToken), Action.ENTER_MARKET);\\n Market storage marketToJoin = markets[address(vToken)];\\n\\n if (!marketToJoin.isListed) {\\n revert MarketNotListed(address(vToken));\\n }\\n\\n if (marketToJoin.accountMembership[borrower]) {\\n // already joined\\n return;\\n }\\n\\n // survived the gauntlet, add to list\\n // NOTE: we store these somewhat redundantly as a significant optimization\\n // this avoids having to iterate through the list for the most common use cases\\n // that is, only when we need to perform liquidity checks\\n // and not whenever we want to check if an account is in a particular market\\n marketToJoin.accountMembership[borrower] = true;\\n accountAssets[borrower].push(vToken);\\n\\n emit MarketEntered(vToken, borrower);\\n }\\n\\n /**\\n * @notice Internal function to validate that a market hasn't already been added\\n * and if it hasn't adds it\\n * @param vToken The market to support\\n */\\n function _addMarket(address vToken) internal {\\n uint256 marketsCount = allMarkets.length;\\n\\n for (uint256 i; i < marketsCount; ++i) {\\n if (allMarkets[i] == VToken(vToken)) {\\n revert MarketAlreadyListed(vToken);\\n }\\n }\\n allMarkets.push(VToken(vToken));\\n marketsCount = allMarkets.length;\\n _ensureMaxLoops(marketsCount);\\n }\\n\\n /**\\n * @dev Pause/unpause an action on a market\\n * @param market Market to pause/unpause the action on\\n * @param action Action id to pause/unpause\\n * @param paused The new paused state (true=paused, false=unpaused)\\n */\\n function _setActionPaused(address market, Action action, bool paused) internal {\\n require(markets[market].isListed, \\\"cannot pause a market that is not listed\\\");\\n _actionPaused[market][action] = paused;\\n emit ActionPausedMarket(VToken(market), action, paused);\\n }\\n\\n /**\\n * @dev Internal function to check that vTokens can be safely redeemed for the underlying asset.\\n * @param vToken Address of the vTokens to redeem\\n * @param redeemer Account redeeming the tokens\\n * @param redeemTokens The number of tokens to redeem\\n */\\n function _checkRedeemAllowed(address vToken, address redeemer, uint256 redeemTokens) internal {\\n Market storage market = markets[vToken];\\n\\n if (!market.isListed) {\\n revert MarketNotListed(address(vToken));\\n }\\n\\n /* If the redeemer is not 'in' the market, then we can bypass the liquidity check */\\n if (!market.accountMembership[redeemer]) {\\n return;\\n }\\n\\n // Update the prices of tokens\\n updatePrices(redeemer);\\n\\n /* Otherwise, perform a hypothetical liquidity check to guard against shortfall */\\n AccountLiquiditySnapshot memory snapshot = _getHypotheticalLiquiditySnapshot(\\n redeemer,\\n VToken(vToken),\\n redeemTokens,\\n 0,\\n _getCollateralFactor\\n );\\n if (snapshot.shortfall > 0) {\\n revert InsufficientLiquidity();\\n }\\n }\\n\\n /**\\n * @notice Get the total collateral, weighted collateral, borrow balance, liquidity, shortfall\\n * @param account The account to get the snapshot for\\n * @param weight The function to compute the weight of the collateral \\u2013\\u00a0either collateral factor or\\n * liquidation threshold. Accepts the address of the vToken and returns the weight as Exp.\\n * @dev Note that we calculate the exchangeRateStored for each collateral vToken using stored data,\\n * without calculating accumulated interest.\\n * @return snapshot Account liquidity snapshot\\n */\\n function _getCurrentLiquiditySnapshot(\\n address account,\\n function(VToken) internal view returns (Exp memory) weight\\n ) internal view returns (AccountLiquiditySnapshot memory snapshot) {\\n return _getHypotheticalLiquiditySnapshot(account, VToken(address(0)), 0, 0, weight);\\n }\\n\\n /**\\n * @notice Determine what the supply/borrow balances would be if the given amounts were redeemed/borrowed\\n * @param vTokenModify The market to hypothetically redeem/borrow in\\n * @param account The account to determine liquidity for\\n * @param redeemTokens The number of tokens to hypothetically redeem\\n * @param borrowAmount The amount of underlying to hypothetically borrow\\n * @param weight The function to compute the weight of the collateral \\u2013\\u00a0either collateral factor or\\n liquidation threshold. Accepts the address of the VToken and returns the weight\\n * @dev Note that we calculate the exchangeRateStored for each collateral vToken using stored data,\\n * without calculating accumulated interest.\\n * @return snapshot Account liquidity snapshot\\n */\\n function _getHypotheticalLiquiditySnapshot(\\n address account,\\n VToken vTokenModify,\\n uint256 redeemTokens,\\n uint256 borrowAmount,\\n function(VToken) internal view returns (Exp memory) weight\\n ) internal view returns (AccountLiquiditySnapshot memory snapshot) {\\n // For each asset the account is in\\n VToken[] memory assets = getAssetsIn(account);\\n uint256 assetsCount = assets.length;\\n\\n for (uint256 i; i < assetsCount; ++i) {\\n VToken asset = assets[i];\\n\\n // Read the balances and exchange rate from the vToken\\n (uint256 vTokenBalance, uint256 borrowBalance, uint256 exchangeRateMantissa) = _safeGetAccountSnapshot(\\n asset,\\n account\\n );\\n\\n // Get the normalized price of the asset\\n Exp memory oraclePrice = Exp({ mantissa: _safeGetUnderlyingPrice(asset) });\\n\\n // Pre-compute conversion factors from vTokens -> usd\\n Exp memory vTokenPrice = mul_(Exp({ mantissa: exchangeRateMantissa }), oraclePrice);\\n Exp memory weightedVTokenPrice = mul_(weight(asset), vTokenPrice);\\n\\n // weightedCollateral += weightedVTokenPrice * vTokenBalance\\n snapshot.weightedCollateral = mul_ScalarTruncateAddUInt(\\n weightedVTokenPrice,\\n vTokenBalance,\\n snapshot.weightedCollateral\\n );\\n\\n // totalCollateral += vTokenPrice * vTokenBalance\\n snapshot.totalCollateral = mul_ScalarTruncateAddUInt(vTokenPrice, vTokenBalance, snapshot.totalCollateral);\\n\\n // borrows += oraclePrice * borrowBalance\\n snapshot.borrows = mul_ScalarTruncateAddUInt(oraclePrice, borrowBalance, snapshot.borrows);\\n\\n // Calculate effects of interacting with vTokenModify\\n if (asset == vTokenModify) {\\n // redeem effect\\n // effects += tokensToDenom * redeemTokens\\n snapshot.effects = mul_ScalarTruncateAddUInt(weightedVTokenPrice, redeemTokens, snapshot.effects);\\n\\n // borrow effect\\n // effects += oraclePrice * borrowAmount\\n snapshot.effects = mul_ScalarTruncateAddUInt(oraclePrice, borrowAmount, snapshot.effects);\\n }\\n }\\n\\n uint256 borrowPlusEffects = snapshot.borrows + snapshot.effects;\\n // These are safe, as the underflow condition is checked first\\n unchecked {\\n if (snapshot.weightedCollateral > borrowPlusEffects) {\\n snapshot.liquidity = snapshot.weightedCollateral - borrowPlusEffects;\\n snapshot.shortfall = 0;\\n } else {\\n snapshot.liquidity = 0;\\n snapshot.shortfall = borrowPlusEffects - snapshot.weightedCollateral;\\n }\\n }\\n\\n return snapshot;\\n }\\n\\n /**\\n * @dev Retrieves price from oracle for an asset and checks it is nonzero\\n * @param asset Address for asset to query price\\n * @return Underlying price\\n */\\n function _safeGetUnderlyingPrice(VToken asset) internal view returns (uint256) {\\n uint256 oraclePriceMantissa = oracle.getUnderlyingPrice(address(asset));\\n if (oraclePriceMantissa == 0) {\\n revert PriceError(address(asset));\\n }\\n return oraclePriceMantissa;\\n }\\n\\n /**\\n * @dev Return collateral factor for a market\\n * @param asset Address for asset\\n * @return Collateral factor as exponential\\n */\\n function _getCollateralFactor(VToken asset) internal view returns (Exp memory) {\\n return Exp({ mantissa: markets[address(asset)].collateralFactorMantissa });\\n }\\n\\n /**\\n * @dev Retrieves liquidation threshold for a market as an exponential\\n * @param asset Address for asset to liquidation threshold\\n * @return Liquidation threshold as exponential\\n */\\n function _getLiquidationThreshold(VToken asset) internal view returns (Exp memory) {\\n return Exp({ mantissa: markets[address(asset)].liquidationThresholdMantissa });\\n }\\n\\n /**\\n * @dev Returns supply and borrow balances of user in vToken, reverts on failure\\n * @param vToken Market to query\\n * @param user Account address\\n * @return vTokenBalance Balance of vTokens, the same as vToken.balanceOf(user)\\n * @return borrowBalance Borrowed amount, including the interest\\n * @return exchangeRateMantissa Stored exchange rate\\n */\\n function _safeGetAccountSnapshot(\\n VToken vToken,\\n address user\\n ) internal view returns (uint256 vTokenBalance, uint256 borrowBalance, uint256 exchangeRateMantissa) {\\n uint256 err;\\n (err, vTokenBalance, borrowBalance, exchangeRateMantissa) = vToken.getAccountSnapshot(user);\\n if (err != 0) {\\n revert SnapshotError(address(vToken), user);\\n }\\n return (vTokenBalance, borrowBalance, exchangeRateMantissa);\\n }\\n\\n /// @notice Reverts if the call is not from expectedSender\\n /// @param expectedSender Expected transaction sender\\n function _checkSenderIs(address expectedSender) internal view {\\n if (msg.sender != expectedSender) {\\n revert UnexpectedSender(expectedSender, msg.sender);\\n }\\n }\\n\\n /// @notice Reverts if a certain action is paused on a market\\n /// @param market Market to check\\n /// @param action Action to check\\n function _checkActionPauseState(address market, Action action) private view {\\n if (actionPaused(market, action)) {\\n revert ActionPaused(market, action);\\n }\\n }\\n}\\n\",\"keccak256\":\"0x7c6e1c6264e4681f82a9ac1bcd9155197a930033291ee5561ad97a56006f5e9c\",\"license\":\"BSD-3-Clause\"},\"contracts/ComptrollerInterface.sol\":{\"content\":\"// SPDX-License-Identifier: BSD-3-Clause\\npragma solidity ^0.8.25;\\n\\nimport { ResilientOracleInterface } from \\\"@venusprotocol/oracle/contracts/interfaces/OracleInterface.sol\\\";\\n\\nimport { VToken } from \\\"./VToken.sol\\\";\\nimport { RewardsDistributor } from \\\"./Rewards/RewardsDistributor.sol\\\";\\n\\nenum Action {\\n MINT,\\n REDEEM,\\n BORROW,\\n REPAY,\\n SEIZE,\\n LIQUIDATE,\\n TRANSFER,\\n ENTER_MARKET,\\n EXIT_MARKET\\n}\\n\\n/**\\n * @title ComptrollerInterface\\n * @author Venus\\n * @notice Interface implemented by the `Comptroller` contract.\\n */\\ninterface ComptrollerInterface {\\n /*** Assets You Are In ***/\\n\\n function enterMarkets(address[] calldata vTokens) external returns (uint256[] memory);\\n\\n function exitMarket(address vToken) external returns (uint256);\\n\\n /*** Policy Hooks ***/\\n\\n function preMintHook(address vToken, address minter, uint256 mintAmount) external;\\n\\n function preRedeemHook(address vToken, address redeemer, uint256 redeemTokens) external;\\n\\n function preBorrowHook(address vToken, address borrower, uint256 borrowAmount) external;\\n\\n function preRepayHook(address vToken, address borrower) external;\\n\\n function preLiquidateHook(\\n address vTokenBorrowed,\\n address vTokenCollateral,\\n address borrower,\\n uint256 repayAmount,\\n bool skipLiquidityCheck\\n ) external;\\n\\n function preSeizeHook(\\n address vTokenCollateral,\\n address vTokenBorrowed,\\n address liquidator,\\n address borrower\\n ) external;\\n\\n function borrowVerify(address vToken, address borrower, uint borrowAmount) external;\\n\\n function mintVerify(address vToken, address minter, uint mintAmount, uint mintTokens) external;\\n\\n function redeemVerify(address vToken, address redeemer, uint redeemAmount, uint redeemTokens) external;\\n\\n function repayBorrowVerify(\\n address vToken,\\n address payer,\\n address borrower,\\n uint repayAmount,\\n uint borrowerIndex\\n ) external;\\n\\n function liquidateBorrowVerify(\\n address vTokenBorrowed,\\n address vTokenCollateral,\\n address liquidator,\\n address borrower,\\n uint repayAmount,\\n uint seizeTokens\\n ) external;\\n\\n function seizeVerify(\\n address vTokenCollateral,\\n address vTokenBorrowed,\\n address liquidator,\\n address borrower,\\n uint seizeTokens\\n ) external;\\n\\n function transferVerify(address vToken, address src, address dst, uint transferTokens) external;\\n\\n function preTransferHook(address vToken, address src, address dst, uint256 transferTokens) external;\\n\\n function isComptroller() external view returns (bool);\\n\\n /*** Liquidity/Liquidation Calculations ***/\\n\\n function liquidateCalculateSeizeTokens(\\n address vTokenBorrowed,\\n address vTokenCollateral,\\n uint256 repayAmount\\n ) external view returns (uint256, uint256);\\n\\n function getAllMarkets() external view returns (VToken[] memory);\\n\\n function actionPaused(address market, Action action) external view returns (bool);\\n}\\n\\n/**\\n * @title ComptrollerViewInterface\\n * @author Venus\\n * @notice Interface implemented by the `Comptroller` contract, including only some util view functions.\\n */\\ninterface ComptrollerViewInterface {\\n function markets(address) external view returns (bool, uint256);\\n\\n function oracle() external view returns (ResilientOracleInterface);\\n\\n function getAssetsIn(address) external view returns (VToken[] memory);\\n\\n function closeFactorMantissa() external view returns (uint256);\\n\\n function liquidationIncentiveMantissa() external view returns (uint256);\\n\\n function minLiquidatableCollateral() external view returns (uint256);\\n\\n function getRewardDistributors() external view returns (RewardsDistributor[] memory);\\n\\n function getAllMarkets() external view returns (VToken[] memory);\\n\\n function borrowCaps(address) external view returns (uint256);\\n\\n function supplyCaps(address) external view returns (uint256);\\n\\n function approvedDelegates(address user, address delegate) external view returns (bool);\\n}\\n\",\"keccak256\":\"0xcbf7f9977472650c61345b7cbde23e81f626e1f8863bab4c6ce3dacfc7604919\",\"license\":\"BSD-3-Clause\"},\"contracts/ComptrollerStorage.sol\":{\"content\":\"// SPDX-License-Identifier: BSD-3-Clause\\npragma solidity 0.8.25;\\n\\nimport { ResilientOracleInterface } from \\\"@venusprotocol/oracle/contracts/interfaces/OracleInterface.sol\\\";\\n\\nimport { VToken } from \\\"./VToken.sol\\\";\\nimport { RewardsDistributor } from \\\"./Rewards/RewardsDistributor.sol\\\";\\nimport { IPrime } from \\\"@venusprotocol/venus-protocol/contracts/Tokens/Prime/Interfaces/IPrime.sol\\\";\\nimport { Action } from \\\"./ComptrollerInterface.sol\\\";\\n\\n/**\\n * @title ComptrollerStorage\\n * @author Venus\\n * @notice Storage layout for the `Comptroller` contract.\\n */\\ncontract ComptrollerStorage {\\n struct LiquidationOrder {\\n VToken vTokenCollateral;\\n VToken vTokenBorrowed;\\n uint256 repayAmount;\\n }\\n\\n struct AccountLiquiditySnapshot {\\n uint256 totalCollateral;\\n uint256 weightedCollateral;\\n uint256 borrows;\\n uint256 effects;\\n uint256 liquidity;\\n uint256 shortfall;\\n }\\n\\n struct RewardSpeeds {\\n address rewardToken;\\n uint256 supplySpeed;\\n uint256 borrowSpeed;\\n }\\n\\n struct Market {\\n // Whether or not this market is listed\\n bool isListed;\\n // Multiplier representing the most one can borrow against their collateral in this market.\\n // For instance, 0.9 to allow borrowing 90% of collateral value.\\n // Must be between 0 and 1, and stored as a mantissa.\\n uint256 collateralFactorMantissa;\\n // Multiplier representing the collateralization after which the borrow is eligible\\n // for liquidation. For instance, 0.8 liquidate when the borrow is 80% of collateral\\n // value. Must be between 0 and collateral factor, stored as a mantissa.\\n uint256 liquidationThresholdMantissa;\\n // Per-market mapping of \\\"accounts in this asset\\\"\\n mapping(address => bool) accountMembership;\\n }\\n\\n /**\\n * @notice Oracle which gives the price of any given asset\\n */\\n ResilientOracleInterface public oracle;\\n\\n /**\\n * @notice Multiplier used to calculate the maximum repayAmount when liquidating a borrow\\n */\\n uint256 public closeFactorMantissa;\\n\\n /**\\n * @notice Multiplier representing the discount on collateral that a liquidator receives\\n */\\n uint256 public liquidationIncentiveMantissa;\\n\\n /**\\n * @notice Per-account mapping of \\\"assets you are in\\\"\\n */\\n mapping(address => VToken[]) public accountAssets;\\n\\n /**\\n * @notice Official mapping of vTokens -> Market metadata\\n * @dev Used e.g. to determine if a market is supported\\n */\\n mapping(address => Market) public markets;\\n\\n /// @notice A list of all markets\\n VToken[] public allMarkets;\\n\\n /// @notice Borrow caps enforced by borrowAllowed for each vToken address. Defaults to zero which restricts borrowing.\\n mapping(address => uint256) public borrowCaps;\\n\\n /// @notice Minimal collateral required for regular (non-batch) liquidations\\n uint256 public minLiquidatableCollateral;\\n\\n /// @notice Supply caps enforced by mintAllowed for each vToken address. Defaults to zero which corresponds to minting not allowed\\n mapping(address => uint256) public supplyCaps;\\n\\n /// @notice True if a certain action is paused on a certain market\\n mapping(address => mapping(Action => bool)) internal _actionPaused;\\n\\n // List of Reward Distributors added\\n RewardsDistributor[] internal rewardsDistributors;\\n\\n // Used to check if rewards distributor is added\\n mapping(address => bool) internal rewardsDistributorExists;\\n\\n /// @notice Flag indicating whether forced liquidation enabled for a market\\n mapping(address => bool) public isForcedLiquidationEnabled;\\n\\n uint256 internal constant NO_ERROR = 0;\\n\\n // closeFactorMantissa must be strictly greater than this value\\n uint256 internal constant MIN_CLOSE_FACTOR_MANTISSA = 0.05e18; // 0.05\\n\\n // closeFactorMantissa must not exceed this value\\n uint256 internal constant MAX_CLOSE_FACTOR_MANTISSA = 0.9e18; // 0.9\\n\\n // No collateralFactorMantissa may exceed this value\\n uint256 internal constant MAX_COLLATERAL_FACTOR_MANTISSA = 0.95e18; // 0.95\\n\\n /// Prime token address\\n IPrime public prime;\\n\\n /// @notice Whether the delegate is allowed to borrow or redeem on behalf of the user\\n //mapping(address user => mapping (address delegate => bool approved)) public approvedDelegates;\\n mapping(address => mapping(address => bool)) public approvedDelegates;\\n\\n /**\\n * @dev This empty reserved space is put in place to allow future versions to add new\\n * variables without shifting down storage in the inheritance chain.\\n * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\\n */\\n uint256[47] private __gap;\\n}\\n\",\"keccak256\":\"0x4df44cb65ac152bac2be4b4545430e703005a74a55b72e144100b16421bd8bfd\",\"license\":\"BSD-3-Clause\"},\"contracts/ErrorReporter.sol\":{\"content\":\"// SPDX-License-Identifier: BSD-3-Clause\\npragma solidity 0.8.25;\\n\\n/**\\n * @title TokenErrorReporter\\n * @author Venus\\n * @notice Errors that can be thrown by the `VToken` contract.\\n */\\ncontract TokenErrorReporter {\\n uint256 public constant NO_ERROR = 0; // support legacy return codes\\n\\n error TransferNotAllowed();\\n\\n error MintFreshnessCheck();\\n\\n error RedeemFreshnessCheck();\\n error RedeemTransferOutNotPossible();\\n\\n error BorrowFreshnessCheck();\\n error BorrowCashNotAvailable();\\n error DelegateNotApproved();\\n\\n error RepayBorrowFreshnessCheck();\\n\\n error HealBorrowUnauthorized();\\n error ForceLiquidateBorrowUnauthorized();\\n\\n error LiquidateFreshnessCheck();\\n error LiquidateCollateralFreshnessCheck();\\n error LiquidateAccrueCollateralInterestFailed(uint256 errorCode);\\n error LiquidateLiquidatorIsBorrower();\\n error LiquidateCloseAmountIsZero();\\n error LiquidateCloseAmountIsUintMax();\\n\\n error LiquidateSeizeLiquidatorIsBorrower();\\n\\n error ProtocolSeizeShareTooBig();\\n\\n error SetReserveFactorFreshCheck();\\n error SetReserveFactorBoundsCheck();\\n\\n error AddReservesFactorFreshCheck(uint256 actualAddAmount);\\n\\n error ReduceReservesFreshCheck();\\n error ReduceReservesCashNotAvailable();\\n error ReduceReservesCashValidation();\\n\\n error SetInterestRateModelFreshCheck();\\n}\\n\",\"keccak256\":\"0x7a7ced1caaac2d9242659ebd50b99f308c725ce958ac9e11f7ada404b1d97a7b\",\"license\":\"BSD-3-Clause\"},\"contracts/ExponentialNoError.sol\":{\"content\":\"// SPDX-License-Identifier: BSD-3-Clause\\npragma solidity 0.8.25;\\n\\nimport { EXP_SCALE as EXP_SCALE_, MANTISSA_ONE as MANTISSA_ONE_ } from \\\"./lib/constants.sol\\\";\\n\\n/**\\n * @title Exponential module for storing fixed-precision decimals\\n * @author Compound\\n * @notice Exp is a struct which stores decimals with a fixed precision of 18 decimal places.\\n * Thus, if we wanted to store the 5.1, mantissa would store 5.1e18. That is:\\n * `Exp({mantissa: 5100000000000000000})`.\\n */\\ncontract ExponentialNoError {\\n struct Exp {\\n uint256 mantissa;\\n }\\n\\n struct Double {\\n uint256 mantissa;\\n }\\n\\n uint256 internal constant EXP_SCALE = EXP_SCALE_;\\n uint256 internal constant DOUBLE_SCALE = 1e36;\\n uint256 internal constant HALF_EXP_SCALE = EXP_SCALE / 2;\\n uint256 internal constant MANTISSA_ONE = MANTISSA_ONE_;\\n\\n /**\\n * @dev Truncates the given exp to a whole number value.\\n * For example, truncate(Exp{mantissa: 15 * EXP_SCALE}) = 15\\n */\\n function truncate(Exp memory exp) internal pure returns (uint256) {\\n // Note: We are not using careful math here as we're performing a division that cannot fail\\n return exp.mantissa / EXP_SCALE;\\n }\\n\\n /**\\n * @dev Multiply an Exp by a scalar, then truncate to return an unsigned integer.\\n */\\n // solhint-disable-next-line func-name-mixedcase\\n function mul_ScalarTruncate(Exp memory a, uint256 scalar) internal pure returns (uint256) {\\n Exp memory product = mul_(a, scalar);\\n return truncate(product);\\n }\\n\\n /**\\n * @dev Multiply an Exp by a scalar, truncate, then add an to an unsigned integer, returning an unsigned integer.\\n */\\n // solhint-disable-next-line func-name-mixedcase\\n function mul_ScalarTruncateAddUInt(Exp memory a, uint256 scalar, uint256 addend) internal pure returns (uint256) {\\n Exp memory product = mul_(a, scalar);\\n return add_(truncate(product), addend);\\n }\\n\\n /**\\n * @dev Checks if first Exp is less than second Exp.\\n */\\n function lessThanExp(Exp memory left, Exp memory right) internal pure returns (bool) {\\n return left.mantissa < right.mantissa;\\n }\\n\\n function safe224(uint256 n, string memory errorMessage) internal pure returns (uint224) {\\n require(n <= type(uint224).max, errorMessage);\\n return uint224(n);\\n }\\n\\n function safe32(uint256 n, string memory errorMessage) internal pure returns (uint32) {\\n require(n <= type(uint32).max, errorMessage);\\n return uint32(n);\\n }\\n\\n function add_(Exp memory a, Exp memory b) internal pure returns (Exp memory) {\\n return Exp({ mantissa: add_(a.mantissa, b.mantissa) });\\n }\\n\\n function add_(Double memory a, Double memory b) internal pure returns (Double memory) {\\n return Double({ mantissa: add_(a.mantissa, b.mantissa) });\\n }\\n\\n function add_(uint256 a, uint256 b) internal pure returns (uint256) {\\n return a + b;\\n }\\n\\n function sub_(Exp memory a, Exp memory b) internal pure returns (Exp memory) {\\n return Exp({ mantissa: sub_(a.mantissa, b.mantissa) });\\n }\\n\\n function sub_(Double memory a, Double memory b) internal pure returns (Double memory) {\\n return Double({ mantissa: sub_(a.mantissa, b.mantissa) });\\n }\\n\\n function sub_(uint256 a, uint256 b) internal pure returns (uint256) {\\n return a - b;\\n }\\n\\n function mul_(Exp memory a, Exp memory b) internal pure returns (Exp memory) {\\n return Exp({ mantissa: mul_(a.mantissa, b.mantissa) / EXP_SCALE });\\n }\\n\\n function mul_(Exp memory a, uint256 b) internal pure returns (Exp memory) {\\n return Exp({ mantissa: mul_(a.mantissa, b) });\\n }\\n\\n function mul_(uint256 a, Exp memory b) internal pure returns (uint256) {\\n return mul_(a, b.mantissa) / EXP_SCALE;\\n }\\n\\n function mul_(Double memory a, Double memory b) internal pure returns (Double memory) {\\n return Double({ mantissa: mul_(a.mantissa, b.mantissa) / DOUBLE_SCALE });\\n }\\n\\n function mul_(Double memory a, uint256 b) internal pure returns (Double memory) {\\n return Double({ mantissa: mul_(a.mantissa, b) });\\n }\\n\\n function mul_(uint256 a, Double memory b) internal pure returns (uint256) {\\n return mul_(a, b.mantissa) / DOUBLE_SCALE;\\n }\\n\\n function mul_(uint256 a, uint256 b) internal pure returns (uint256) {\\n return a * b;\\n }\\n\\n function div_(Exp memory a, Exp memory b) internal pure returns (Exp memory) {\\n return Exp({ mantissa: div_(mul_(a.mantissa, EXP_SCALE), b.mantissa) });\\n }\\n\\n function div_(Exp memory a, uint256 b) internal pure returns (Exp memory) {\\n return Exp({ mantissa: div_(a.mantissa, b) });\\n }\\n\\n function div_(uint256 a, Exp memory b) internal pure returns (uint256) {\\n return div_(mul_(a, EXP_SCALE), b.mantissa);\\n }\\n\\n function div_(Double memory a, Double memory b) internal pure returns (Double memory) {\\n return Double({ mantissa: div_(mul_(a.mantissa, DOUBLE_SCALE), b.mantissa) });\\n }\\n\\n function div_(Double memory a, uint256 b) internal pure returns (Double memory) {\\n return Double({ mantissa: div_(a.mantissa, b) });\\n }\\n\\n function div_(uint256 a, Double memory b) internal pure returns (uint256) {\\n return div_(mul_(a, DOUBLE_SCALE), b.mantissa);\\n }\\n\\n function div_(uint256 a, uint256 b) internal pure returns (uint256) {\\n return a / b;\\n }\\n\\n function fraction(uint256 a, uint256 b) internal pure returns (Double memory) {\\n return Double({ mantissa: div_(mul_(a, DOUBLE_SCALE), b) });\\n }\\n}\\n\",\"keccak256\":\"0x8afbe8a24fe3539c124e718681ed3dcebd24c40dd53095786c0155998213b9b0\",\"license\":\"BSD-3-Clause\"},\"contracts/InterestRateModel.sol\":{\"content\":\"// SPDX-License-Identifier: BSD-3-Clause\\npragma solidity 0.8.25;\\n\\n/**\\n * @title Compound's InterestRateModel Interface\\n * @author Compound\\n */\\nabstract contract InterestRateModel {\\n /**\\n * @notice Calculates the current borrow interest rate per slot (block or second)\\n * @param cash The total amount of cash the market has\\n * @param borrows The total amount of borrows the market has outstanding\\n * @param reserves The total amount of reserves the market has\\n * @param badDebt The amount of badDebt in the market\\n * @return The borrow rate percentage per slot (block or second) as a mantissa (scaled by EXP_SCALE)\\n */\\n function getBorrowRate(\\n uint256 cash,\\n uint256 borrows,\\n uint256 reserves,\\n uint256 badDebt\\n ) external view virtual returns (uint256);\\n\\n /**\\n * @notice Calculates the current supply interest rate per slot (block or second)\\n * @param cash The total amount of cash the market has\\n * @param borrows The total amount of borrows the market has outstanding\\n * @param reserves The total amount of reserves the market has\\n * @param reserveFactorMantissa The current reserve factor the market has\\n * @param badDebt The amount of badDebt in the market\\n * @return The supply rate percentage per slot (block or second) as a mantissa (scaled by EXP_SCALE)\\n */\\n function getSupplyRate(\\n uint256 cash,\\n uint256 borrows,\\n uint256 reserves,\\n uint256 reserveFactorMantissa,\\n uint256 badDebt\\n ) external view virtual returns (uint256);\\n\\n /**\\n * @notice Indicator that this is an InterestRateModel contract (for inspection)\\n * @return Always true\\n */\\n function isInterestRateModel() external pure virtual returns (bool) {\\n return true;\\n }\\n}\\n\",\"keccak256\":\"0xc4fda1ab75ebe4b187b707c4f10c58780f343cf343c537f641dc75d3cd28ab51\",\"license\":\"BSD-3-Clause\"},\"contracts/MaxLoopsLimitHelper.sol\":{\"content\":\"// SPDX-License-Identifier: BSD-3-Clause\\npragma solidity 0.8.25;\\n\\n/**\\n * @title MaxLoopsLimitHelper\\n * @author Venus\\n * @notice Abstract contract used to avoid collection with too many items that would generate gas errors and DoS.\\n */\\nabstract contract MaxLoopsLimitHelper {\\n // Limit for the loops to avoid the DOS\\n uint256 public maxLoopsLimit;\\n\\n /**\\n * @dev This empty reserved space is put in place to allow future versions to add new\\n * variables without shifting down storage in the inheritance chain.\\n * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\\n */\\n uint256[49] private __gap;\\n\\n /// @notice Emitted when max loops limit is set\\n event MaxLoopsLimitUpdated(uint256 oldMaxLoopsLimit, uint256 newmaxLoopsLimit);\\n\\n /// @notice Thrown an error on maxLoopsLimit exceeds for any loop\\n error MaxLoopsLimitExceeded(uint256 loopsLimit, uint256 requiredLoops);\\n\\n /**\\n * @notice Set the limit for the loops can iterate to avoid the DOS\\n * @param limit Limit for the max loops can execute at a time\\n */\\n function _setMaxLoopsLimit(uint256 limit) internal {\\n require(limit > maxLoopsLimit, \\\"Comptroller: Invalid maxLoopsLimit\\\");\\n\\n uint256 oldMaxLoopsLimit = maxLoopsLimit;\\n maxLoopsLimit = limit;\\n\\n emit MaxLoopsLimitUpdated(oldMaxLoopsLimit, limit);\\n }\\n\\n /**\\n * @notice Compare the maxLoopsLimit with number of the times loop iterate\\n * @param len Length of the loops iterate\\n * @custom:error MaxLoopsLimitExceeded error is thrown when loops length exceeds maxLoopsLimit\\n */\\n function _ensureMaxLoops(uint256 len) internal view {\\n if (len > maxLoopsLimit) {\\n revert MaxLoopsLimitExceeded(maxLoopsLimit, len);\\n }\\n }\\n}\\n\",\"keccak256\":\"0x4c25e30635485d162177effa384eee51768b0141a567a0da16ff6ad673274166\",\"license\":\"BSD-3-Clause\"},\"contracts/Rewards/RewardsDistributor.sol\":{\"content\":\"// SPDX-License-Identifier: BSD-3-Clause\\npragma solidity 0.8.25;\\n\\nimport { Ownable2StepUpgradeable } from \\\"@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol\\\";\\nimport { IERC20Upgradeable } from \\\"@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol\\\";\\nimport { SafeERC20Upgradeable } from \\\"@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol\\\";\\nimport { AccessControlledV8 } from \\\"@venusprotocol/governance-contracts/contracts/Governance/AccessControlledV8.sol\\\";\\nimport { TimeManagerV8 } from \\\"@venusprotocol/solidity-utilities/contracts/TimeManagerV8.sol\\\";\\n\\nimport { ExponentialNoError } from \\\"../ExponentialNoError.sol\\\";\\nimport { VToken } from \\\"../VToken.sol\\\";\\nimport { Comptroller } from \\\"../Comptroller.sol\\\";\\nimport { MaxLoopsLimitHelper } from \\\"../MaxLoopsLimitHelper.sol\\\";\\nimport { RewardsDistributorStorage } from \\\"./RewardsDistributorStorage.sol\\\";\\n\\n/**\\n * @title `RewardsDistributor`\\n * @author Venus\\n * @notice Contract used to configure, track and distribute rewards to users based on their actions (borrows and supplies) in the protocol.\\n * Users can receive additional rewards through a `RewardsDistributor`. Each `RewardsDistributor` proxy is initialized with a specific reward\\n * token and `Comptroller`, which can then distribute the reward token to users that supply or borrow in the associated pool.\\n * Authorized users can set the reward token borrow and supply speeds for each market in the pool. This sets a fixed amount of reward\\n * token to be released each slot (block or second) for borrowers and suppliers, which is distributed based on a user\\u2019s percentage of the borrows or supplies\\n * respectively. The owner can also set up reward distributions to contributor addresses (distinct from suppliers and borrowers) by setting\\n * their contributor reward token speed, which similarly allocates a fixed amount of reward token per slot (block or second).\\n *\\n * The owner has the ability to transfer any amount of reward tokens held by the contract to any other address. Rewards are not distributed\\n * automatically and must be claimed by a user calling `claimRewardToken()`. Users should be aware that it is up to the owner and other centralized\\n * entities to ensure that the `RewardsDistributor` holds enough tokens to distribute the accumulated rewards of users and contributors.\\n */\\ncontract RewardsDistributor is\\n ExponentialNoError,\\n Ownable2StepUpgradeable,\\n AccessControlledV8,\\n MaxLoopsLimitHelper,\\n RewardsDistributorStorage,\\n TimeManagerV8\\n{\\n using SafeERC20Upgradeable for IERC20Upgradeable;\\n\\n /// @notice The initial REWARD TOKEN index for a market\\n uint224 public constant INITIAL_INDEX = 1e36;\\n\\n /// @notice Emitted when REWARD TOKEN is distributed to a supplier\\n event DistributedSupplierRewardToken(\\n VToken indexed vToken,\\n address indexed supplier,\\n uint256 rewardTokenDelta,\\n uint256 rewardTokenTotal,\\n uint256 rewardTokenSupplyIndex\\n );\\n\\n /// @notice Emitted when REWARD TOKEN is distributed to a borrower\\n event DistributedBorrowerRewardToken(\\n VToken indexed vToken,\\n address indexed borrower,\\n uint256 rewardTokenDelta,\\n uint256 rewardTokenTotal,\\n uint256 rewardTokenBorrowIndex\\n );\\n\\n /// @notice Emitted when a new supply-side REWARD TOKEN speed is calculated for a market\\n event RewardTokenSupplySpeedUpdated(VToken indexed vToken, uint256 newSpeed);\\n\\n /// @notice Emitted when a new borrow-side REWARD TOKEN speed is calculated for a market\\n event RewardTokenBorrowSpeedUpdated(VToken indexed vToken, uint256 newSpeed);\\n\\n /// @notice Emitted when REWARD TOKEN is granted by admin\\n event RewardTokenGranted(address indexed recipient, uint256 amount);\\n\\n /// @notice Emitted when a new REWARD TOKEN speed is set for a contributor\\n event ContributorRewardTokenSpeedUpdated(address indexed contributor, uint256 newSpeed);\\n\\n /// @notice Emitted when a market is initialized\\n event MarketInitialized(address indexed vToken);\\n\\n /// @notice Emitted when a reward token supply index is updated\\n event RewardTokenSupplyIndexUpdated(address indexed vToken);\\n\\n /// @notice Emitted when a reward token borrow index is updated\\n event RewardTokenBorrowIndexUpdated(address indexed vToken, Exp marketBorrowIndex);\\n\\n /// @notice Emitted when a reward for contributor is updated\\n event ContributorRewardsUpdated(address indexed contributor, uint256 rewardAccrued);\\n\\n /// @notice Emitted when a reward token last rewarding block for supply is updated\\n event SupplyLastRewardingBlockUpdated(address indexed vToken, uint32 newBlock);\\n\\n /// @notice Emitted when a reward token last rewarding block for borrow is updated\\n event BorrowLastRewardingBlockUpdated(address indexed vToken, uint32 newBlock);\\n\\n /// @notice Emitted when a reward token last rewarding timestamp for supply is updated\\n event SupplyLastRewardingBlockTimestampUpdated(address indexed vToken, uint256 newTimestamp);\\n\\n /// @notice Emitted when a reward token last rewarding timestamp for borrow is updated\\n event BorrowLastRewardingBlockTimestampUpdated(address indexed vToken, uint256 newTimestamp);\\n\\n modifier onlyComptroller() {\\n require(address(comptroller) == msg.sender, \\\"Only comptroller can call this function\\\");\\n _;\\n }\\n\\n /**\\n * @param timeBased_ A boolean indicating whether the contract is based on time or block.\\n * @param blocksPerYear_ The number of blocks per year\\n * @custom:oz-upgrades-unsafe-allow constructor\\n */\\n constructor(bool timeBased_, uint256 blocksPerYear_) TimeManagerV8(timeBased_, blocksPerYear_) {\\n // Note that the contract is upgradeable. Use initialize() or reinitializers\\n // to set the state variables.\\n _disableInitializers();\\n }\\n\\n /**\\n * @notice RewardsDistributor initializer\\n * @dev Initializes the deployer to owner\\n * @param comptroller_ Comptroller to attach the reward distributor to\\n * @param rewardToken_ Reward token to distribute\\n * @param loopsLimit_ Maximum number of iterations for the loops in this contract\\n * @param accessControlManager_ AccessControlManager contract address\\n */\\n function initialize(\\n Comptroller comptroller_,\\n IERC20Upgradeable rewardToken_,\\n uint256 loopsLimit_,\\n address accessControlManager_\\n ) external initializer {\\n comptroller = comptroller_;\\n rewardToken = rewardToken_;\\n __Ownable2Step_init();\\n __AccessControlled_init_unchained(accessControlManager_);\\n\\n _setMaxLoopsLimit(loopsLimit_);\\n }\\n\\n /**\\n * @notice Initializes the market state for a specific vToken\\n * @param vToken The address of the vToken to be initialized\\n * @custom:event MarketInitialized emits on success\\n * @custom:access Only Comptroller\\n */\\n function initializeMarket(address vToken) external onlyComptroller {\\n uint256 blockNumberOrTimestamp = getBlockNumberOrTimestamp();\\n\\n isTimeBased\\n ? _initializeMarketTimestampBased(vToken, blockNumberOrTimestamp)\\n : _initializeMarketBlockBased(vToken, safe32(blockNumberOrTimestamp, \\\"block number exceeds 32 bits\\\"));\\n\\n emit MarketInitialized(vToken);\\n }\\n\\n /*** Reward Token Distribution ***/\\n\\n /**\\n * @notice Calculate reward token accrued by a borrower and possibly transfer it to them\\n * Borrowers will begin to accrue after the first interaction with the protocol.\\n * @dev This function should only be called when the user has a borrow position in the market\\n * (e.g. Comptroller.preBorrowHook, and Comptroller.preRepayHook)\\n * We avoid an external call to check if they are in the market to save gas because this function is called in many places\\n * @param vToken The market in which the borrower is interacting\\n * @param borrower The address of the borrower to distribute REWARD TOKEN to\\n * @param marketBorrowIndex The current global borrow index of vToken\\n */\\n function distributeBorrowerRewardToken(\\n address vToken,\\n address borrower,\\n Exp memory marketBorrowIndex\\n ) external onlyComptroller {\\n _distributeBorrowerRewardToken(vToken, borrower, marketBorrowIndex);\\n }\\n\\n function updateRewardTokenSupplyIndex(address vToken) external onlyComptroller {\\n _updateRewardTokenSupplyIndex(vToken);\\n }\\n\\n /**\\n * @notice Transfer REWARD TOKEN to the recipient\\n * @dev Note: If there is not enough REWARD TOKEN, we do not perform the transfer all\\n * @param recipient The address of the recipient to transfer REWARD TOKEN to\\n * @param amount The amount of REWARD TOKEN to (possibly) transfer\\n */\\n function grantRewardToken(address recipient, uint256 amount) external onlyOwner {\\n uint256 amountLeft = _grantRewardToken(recipient, amount);\\n require(amountLeft == 0, \\\"insufficient rewardToken for grant\\\");\\n emit RewardTokenGranted(recipient, amount);\\n }\\n\\n function updateRewardTokenBorrowIndex(address vToken, Exp memory marketBorrowIndex) external onlyComptroller {\\n _updateRewardTokenBorrowIndex(vToken, marketBorrowIndex);\\n }\\n\\n /**\\n * @notice Set REWARD TOKEN borrow and supply speeds for the specified markets\\n * @param vTokens The markets whose REWARD TOKEN speed to update\\n * @param supplySpeeds New supply-side REWARD TOKEN speed for the corresponding market\\n * @param borrowSpeeds New borrow-side REWARD TOKEN speed for the corresponding market\\n */\\n function setRewardTokenSpeeds(\\n VToken[] memory vTokens,\\n uint256[] memory supplySpeeds,\\n uint256[] memory borrowSpeeds\\n ) external {\\n _checkAccessAllowed(\\\"setRewardTokenSpeeds(address[],uint256[],uint256[])\\\");\\n uint256 numTokens = vTokens.length;\\n require(numTokens == supplySpeeds.length && numTokens == borrowSpeeds.length, \\\"invalid setRewardTokenSpeeds\\\");\\n\\n for (uint256 i; i < numTokens; ++i) {\\n _setRewardTokenSpeed(vTokens[i], supplySpeeds[i], borrowSpeeds[i]);\\n }\\n }\\n\\n /**\\n * @notice Set REWARD TOKEN last rewarding block for the specified markets, used when contract is block based\\n * @param vTokens The markets whose REWARD TOKEN last rewarding block to update\\n * @param supplyLastRewardingBlocks New supply-side REWARD TOKEN last rewarding block for the corresponding market\\n * @param borrowLastRewardingBlocks New borrow-side REWARD TOKEN last rewarding block for the corresponding market\\n */\\n function setLastRewardingBlocks(\\n VToken[] calldata vTokens,\\n uint32[] calldata supplyLastRewardingBlocks,\\n uint32[] calldata borrowLastRewardingBlocks\\n ) external {\\n _checkAccessAllowed(\\\"setLastRewardingBlocks(address[],uint32[],uint32[])\\\");\\n require(!isTimeBased, \\\"Block-based operation only\\\");\\n\\n uint256 numTokens = vTokens.length;\\n require(\\n numTokens == supplyLastRewardingBlocks.length && numTokens == borrowLastRewardingBlocks.length,\\n \\\"RewardsDistributor::setLastRewardingBlocks invalid input\\\"\\n );\\n\\n for (uint256 i; i < numTokens; ) {\\n _setLastRewardingBlock(vTokens[i], supplyLastRewardingBlocks[i], borrowLastRewardingBlocks[i]);\\n unchecked {\\n ++i;\\n }\\n }\\n }\\n\\n /**\\n * @notice Set REWARD TOKEN last rewarding block timestamp for the specified markets, used when contract is time based\\n * @param vTokens The markets whose REWARD TOKEN last rewarding block to update\\n * @param supplyLastRewardingBlockTimestamps New supply-side REWARD TOKEN last rewarding block timestamp for the corresponding market\\n * @param borrowLastRewardingBlockTimestamps New borrow-side REWARD TOKEN last rewarding block timestamp for the corresponding market\\n */\\n function setLastRewardingBlockTimestamps(\\n VToken[] calldata vTokens,\\n uint256[] calldata supplyLastRewardingBlockTimestamps,\\n uint256[] calldata borrowLastRewardingBlockTimestamps\\n ) external {\\n _checkAccessAllowed(\\\"setLastRewardingBlockTimestamps(address[],uint256[],uint256[])\\\");\\n require(isTimeBased, \\\"Time-based operation only\\\");\\n\\n uint256 numTokens = vTokens.length;\\n require(\\n numTokens == supplyLastRewardingBlockTimestamps.length &&\\n numTokens == borrowLastRewardingBlockTimestamps.length,\\n \\\"RewardsDistributor::setLastRewardingBlockTimestamps invalid input\\\"\\n );\\n\\n for (uint256 i; i < numTokens; ) {\\n _setLastRewardingBlockTimestamp(\\n vTokens[i],\\n supplyLastRewardingBlockTimestamps[i],\\n borrowLastRewardingBlockTimestamps[i]\\n );\\n unchecked {\\n ++i;\\n }\\n }\\n }\\n\\n /**\\n * @notice Set REWARD TOKEN speed for a single contributor\\n * @param contributor The contributor whose REWARD TOKEN speed to update\\n * @param rewardTokenSpeed New REWARD TOKEN speed for contributor\\n */\\n function setContributorRewardTokenSpeed(address contributor, uint256 rewardTokenSpeed) external onlyOwner {\\n // note that REWARD TOKEN speed could be set to 0 to halt liquidity rewards for a contributor\\n updateContributorRewards(contributor);\\n if (rewardTokenSpeed == 0) {\\n // release storage\\n delete lastContributorBlock[contributor];\\n } else {\\n lastContributorBlock[contributor] = getBlockNumberOrTimestamp();\\n }\\n rewardTokenContributorSpeeds[contributor] = rewardTokenSpeed;\\n\\n emit ContributorRewardTokenSpeedUpdated(contributor, rewardTokenSpeed);\\n }\\n\\n function distributeSupplierRewardToken(address vToken, address supplier) external onlyComptroller {\\n _distributeSupplierRewardToken(vToken, supplier);\\n }\\n\\n /**\\n * @notice Claim all the rewardToken accrued by holder in all markets\\n * @param holder The address to claim REWARD TOKEN for\\n */\\n function claimRewardToken(address holder) external {\\n return claimRewardToken(holder, comptroller.getAllMarkets());\\n }\\n\\n /**\\n * @notice Set the limit for the loops can iterate to avoid the DOS\\n * @param limit Limit for the max loops can execute at a time\\n */\\n function setMaxLoopsLimit(uint256 limit) external onlyOwner {\\n _setMaxLoopsLimit(limit);\\n }\\n\\n /**\\n * @notice Calculate additional accrued REWARD TOKEN for a contributor since last accrual\\n * @param contributor The address to calculate contributor rewards for\\n */\\n function updateContributorRewards(address contributor) public {\\n uint256 rewardTokenSpeed = rewardTokenContributorSpeeds[contributor];\\n uint256 blockNumberOrTimestamp = getBlockNumberOrTimestamp();\\n uint256 deltaBlocksOrTimestamp = sub_(blockNumberOrTimestamp, lastContributorBlock[contributor]);\\n if (deltaBlocksOrTimestamp > 0 && rewardTokenSpeed > 0) {\\n uint256 newAccrued = mul_(deltaBlocksOrTimestamp, rewardTokenSpeed);\\n uint256 contributorAccrued = add_(rewardTokenAccrued[contributor], newAccrued);\\n\\n rewardTokenAccrued[contributor] = contributorAccrued;\\n lastContributorBlock[contributor] = blockNumberOrTimestamp;\\n\\n emit ContributorRewardsUpdated(contributor, rewardTokenAccrued[contributor]);\\n }\\n }\\n\\n /**\\n * @notice Claim all the rewardToken accrued by holder in the specified markets\\n * @param holder The address to claim REWARD TOKEN for\\n * @param vTokens The list of markets to claim REWARD TOKEN in\\n */\\n function claimRewardToken(address holder, VToken[] memory vTokens) public {\\n uint256 vTokensCount = vTokens.length;\\n\\n _ensureMaxLoops(vTokensCount);\\n\\n for (uint256 i; i < vTokensCount; ++i) {\\n VToken vToken = vTokens[i];\\n require(comptroller.isMarketListed(vToken), \\\"market must be listed\\\");\\n Exp memory borrowIndex = Exp({ mantissa: vToken.borrowIndex() });\\n _updateRewardTokenBorrowIndex(address(vToken), borrowIndex);\\n _distributeBorrowerRewardToken(address(vToken), holder, borrowIndex);\\n _updateRewardTokenSupplyIndex(address(vToken));\\n _distributeSupplierRewardToken(address(vToken), holder);\\n }\\n rewardTokenAccrued[holder] = _grantRewardToken(holder, rewardTokenAccrued[holder]);\\n }\\n\\n /**\\n * @notice Set REWARD TOKEN last rewarding block for a single market.\\n * @param vToken market's whose reward token last rewarding block to be updated\\n * @param supplyLastRewardingBlock New supply-side REWARD TOKEN last rewarding block for market\\n * @param borrowLastRewardingBlock New borrow-side REWARD TOKEN last rewarding block for market\\n */\\n function _setLastRewardingBlock(\\n VToken vToken,\\n uint32 supplyLastRewardingBlock,\\n uint32 borrowLastRewardingBlock\\n ) internal {\\n require(comptroller.isMarketListed(vToken), \\\"rewardToken market is not listed\\\");\\n\\n uint256 blockNumber = getBlockNumberOrTimestamp();\\n\\n require(supplyLastRewardingBlock > blockNumber, \\\"setting last rewarding block in the past is not allowed\\\");\\n require(borrowLastRewardingBlock > blockNumber, \\\"setting last rewarding block in the past is not allowed\\\");\\n\\n uint32 currentSupplyLastRewardingBlock = rewardTokenSupplyState[address(vToken)].lastRewardingBlock;\\n uint32 currentBorrowLastRewardingBlock = rewardTokenBorrowState[address(vToken)].lastRewardingBlock;\\n\\n require(\\n currentSupplyLastRewardingBlock == 0 || currentSupplyLastRewardingBlock > blockNumber,\\n \\\"this RewardsDistributor is already locked\\\"\\n );\\n require(\\n currentBorrowLastRewardingBlock == 0 || currentBorrowLastRewardingBlock > blockNumber,\\n \\\"this RewardsDistributor is already locked\\\"\\n );\\n\\n if (currentSupplyLastRewardingBlock != supplyLastRewardingBlock) {\\n rewardTokenSupplyState[address(vToken)].lastRewardingBlock = supplyLastRewardingBlock;\\n emit SupplyLastRewardingBlockUpdated(address(vToken), supplyLastRewardingBlock);\\n }\\n\\n if (currentBorrowLastRewardingBlock != borrowLastRewardingBlock) {\\n rewardTokenBorrowState[address(vToken)].lastRewardingBlock = borrowLastRewardingBlock;\\n emit BorrowLastRewardingBlockUpdated(address(vToken), borrowLastRewardingBlock);\\n }\\n }\\n\\n /**\\n * @notice Set REWARD TOKEN last rewarding timestamp for a single market.\\n * @param vToken market's whose reward token last rewarding timestamp to be updated\\n * @param supplyLastRewardingBlockTimestamp New supply-side REWARD TOKEN last rewarding timestamp for market\\n * @param borrowLastRewardingBlockTimestamp New borrow-side REWARD TOKEN last rewarding timestamp for market\\n */\\n function _setLastRewardingBlockTimestamp(\\n VToken vToken,\\n uint256 supplyLastRewardingBlockTimestamp,\\n uint256 borrowLastRewardingBlockTimestamp\\n ) internal {\\n require(comptroller.isMarketListed(vToken), \\\"rewardToken market is not listed\\\");\\n\\n uint256 blockTimestamp = getBlockNumberOrTimestamp();\\n\\n require(\\n supplyLastRewardingBlockTimestamp > blockTimestamp,\\n \\\"setting last rewarding timestamp in the past is not allowed\\\"\\n );\\n require(\\n borrowLastRewardingBlockTimestamp > blockTimestamp,\\n \\\"setting last rewarding timestamp in the past is not allowed\\\"\\n );\\n\\n uint256 currentSupplyLastRewardingBlockTimestamp = rewardTokenSupplyStateTimeBased[address(vToken)]\\n .lastRewardingTimestamp;\\n uint256 currentBorrowLastRewardingBlockTimestamp = rewardTokenBorrowStateTimeBased[address(vToken)]\\n .lastRewardingTimestamp;\\n\\n require(\\n currentSupplyLastRewardingBlockTimestamp == 0 || currentSupplyLastRewardingBlockTimestamp > blockTimestamp,\\n \\\"this RewardsDistributor is already locked\\\"\\n );\\n require(\\n currentBorrowLastRewardingBlockTimestamp == 0 || currentBorrowLastRewardingBlockTimestamp > blockTimestamp,\\n \\\"this RewardsDistributor is already locked\\\"\\n );\\n\\n if (currentSupplyLastRewardingBlockTimestamp != supplyLastRewardingBlockTimestamp) {\\n rewardTokenSupplyStateTimeBased[address(vToken)].lastRewardingTimestamp = supplyLastRewardingBlockTimestamp;\\n emit SupplyLastRewardingBlockTimestampUpdated(address(vToken), supplyLastRewardingBlockTimestamp);\\n }\\n\\n if (currentBorrowLastRewardingBlockTimestamp != borrowLastRewardingBlockTimestamp) {\\n rewardTokenBorrowStateTimeBased[address(vToken)].lastRewardingTimestamp = borrowLastRewardingBlockTimestamp;\\n emit BorrowLastRewardingBlockTimestampUpdated(address(vToken), borrowLastRewardingBlockTimestamp);\\n }\\n }\\n\\n /**\\n * @notice Set REWARD TOKEN speed for a single market.\\n * @param vToken market's whose reward token rate to be updated\\n * @param supplySpeed New supply-side REWARD TOKEN speed for market\\n * @param borrowSpeed New borrow-side REWARD TOKEN speed for market\\n */\\n function _setRewardTokenSpeed(VToken vToken, uint256 supplySpeed, uint256 borrowSpeed) internal {\\n require(comptroller.isMarketListed(vToken), \\\"rewardToken market is not listed\\\");\\n\\n if (rewardTokenSupplySpeeds[address(vToken)] != supplySpeed) {\\n // Supply speed updated so let's update supply state to ensure that\\n // 1. REWARD TOKEN accrued properly for the old speed, and\\n // 2. REWARD TOKEN accrued at the new speed starts after this block.\\n _updateRewardTokenSupplyIndex(address(vToken));\\n\\n // Update speed and emit event\\n rewardTokenSupplySpeeds[address(vToken)] = supplySpeed;\\n emit RewardTokenSupplySpeedUpdated(vToken, supplySpeed);\\n }\\n\\n if (rewardTokenBorrowSpeeds[address(vToken)] != borrowSpeed) {\\n // Borrow speed updated so let's update borrow state to ensure that\\n // 1. REWARD TOKEN accrued properly for the old speed, and\\n // 2. REWARD TOKEN accrued at the new speed starts after this block.\\n Exp memory borrowIndex = Exp({ mantissa: vToken.borrowIndex() });\\n _updateRewardTokenBorrowIndex(address(vToken), borrowIndex);\\n\\n // Update speed and emit event\\n rewardTokenBorrowSpeeds[address(vToken)] = borrowSpeed;\\n emit RewardTokenBorrowSpeedUpdated(vToken, borrowSpeed);\\n }\\n }\\n\\n /**\\n * @notice Calculate REWARD TOKEN accrued by a supplier and possibly transfer it to them.\\n * @param vToken The market in which the supplier is interacting\\n * @param supplier The address of the supplier to distribute REWARD TOKEN to\\n */\\n function _distributeSupplierRewardToken(address vToken, address supplier) internal {\\n RewardToken storage supplyState = rewardTokenSupplyState[vToken];\\n TimeBasedRewardToken storage supplyStateTimeBased = rewardTokenSupplyStateTimeBased[vToken];\\n\\n uint256 supplyIndex = isTimeBased ? supplyStateTimeBased.index : supplyState.index;\\n uint256 supplierIndex = rewardTokenSupplierIndex[vToken][supplier];\\n\\n // Update supplier's index to the current index since we are distributing accrued REWARD TOKEN\\n rewardTokenSupplierIndex[vToken][supplier] = supplyIndex;\\n\\n if (supplierIndex == 0 && supplyIndex >= INITIAL_INDEX) {\\n // Covers the case where users supplied tokens before the market's supply state index was set.\\n // Rewards the user with REWARD TOKEN accrued from the start of when supplier rewards were first\\n // set for the market.\\n supplierIndex = INITIAL_INDEX;\\n }\\n\\n // Calculate change in the cumulative sum of the REWARD TOKEN per vToken accrued\\n Double memory deltaIndex = Double({ mantissa: sub_(supplyIndex, supplierIndex) });\\n\\n uint256 supplierTokens = VToken(vToken).balanceOf(supplier);\\n\\n // Calculate REWARD TOKEN accrued: vTokenAmount * accruedPerVToken\\n uint256 supplierDelta = mul_(supplierTokens, deltaIndex);\\n\\n uint256 supplierAccrued = add_(rewardTokenAccrued[supplier], supplierDelta);\\n rewardTokenAccrued[supplier] = supplierAccrued;\\n\\n emit DistributedSupplierRewardToken(VToken(vToken), supplier, supplierDelta, supplierAccrued, supplyIndex);\\n }\\n\\n /**\\n * @notice Calculate reward token accrued by a borrower and possibly transfer it to them.\\n * @param vToken The market in which the borrower is interacting\\n * @param borrower The address of the borrower to distribute REWARD TOKEN to\\n * @param marketBorrowIndex The current global borrow index of vToken\\n */\\n function _distributeBorrowerRewardToken(address vToken, address borrower, Exp memory marketBorrowIndex) internal {\\n RewardToken storage borrowState = rewardTokenBorrowState[vToken];\\n TimeBasedRewardToken storage borrowStateTimeBased = rewardTokenBorrowStateTimeBased[vToken];\\n\\n uint256 borrowIndex = isTimeBased ? borrowStateTimeBased.index : borrowState.index;\\n uint256 borrowerIndex = rewardTokenBorrowerIndex[vToken][borrower];\\n\\n // Update borrowers's index to the current index since we are distributing accrued REWARD TOKEN\\n rewardTokenBorrowerIndex[vToken][borrower] = borrowIndex;\\n\\n if (borrowerIndex == 0 && borrowIndex >= INITIAL_INDEX) {\\n // Covers the case where users borrowed tokens before the market's borrow state index was set.\\n // Rewards the user with REWARD TOKEN accrued from the start of when borrower rewards were first\\n // set for the market.\\n borrowerIndex = INITIAL_INDEX;\\n }\\n\\n // Calculate change in the cumulative sum of the REWARD TOKEN per borrowed unit accrued\\n Double memory deltaIndex = Double({ mantissa: sub_(borrowIndex, borrowerIndex) });\\n\\n uint256 borrowerAmount = div_(VToken(vToken).borrowBalanceStored(borrower), marketBorrowIndex);\\n\\n // Calculate REWARD TOKEN accrued: vTokenAmount * accruedPerBorrowedUnit\\n if (borrowerAmount != 0) {\\n uint256 borrowerDelta = mul_(borrowerAmount, deltaIndex);\\n\\n uint256 borrowerAccrued = add_(rewardTokenAccrued[borrower], borrowerDelta);\\n rewardTokenAccrued[borrower] = borrowerAccrued;\\n\\n emit DistributedBorrowerRewardToken(VToken(vToken), borrower, borrowerDelta, borrowerAccrued, borrowIndex);\\n }\\n }\\n\\n /**\\n * @notice Transfer REWARD TOKEN to the user.\\n * @dev Note: If there is not enough REWARD TOKEN, we do not perform the transfer all.\\n * @param user The address of the user to transfer REWARD TOKEN to\\n * @param amount The amount of REWARD TOKEN to (possibly) transfer\\n * @return The amount of REWARD TOKEN which was NOT transferred to the user\\n */\\n function _grantRewardToken(address user, uint256 amount) internal returns (uint256) {\\n uint256 rewardTokenRemaining = rewardToken.balanceOf(address(this));\\n if (amount > 0 && amount <= rewardTokenRemaining) {\\n rewardToken.safeTransfer(user, amount);\\n return 0;\\n }\\n return amount;\\n }\\n\\n /**\\n * @notice Accrue REWARD TOKEN to the market by updating the supply index\\n * @param vToken The market whose supply index to update\\n * @dev Index is a cumulative sum of the REWARD TOKEN per vToken accrued\\n */\\n function _updateRewardTokenSupplyIndex(address vToken) internal {\\n RewardToken storage supplyState = rewardTokenSupplyState[vToken];\\n TimeBasedRewardToken storage supplyStateTimeBased = rewardTokenSupplyStateTimeBased[vToken];\\n\\n uint256 supplySpeed = rewardTokenSupplySpeeds[vToken];\\n uint256 blockNumberOrTimestamp = getBlockNumberOrTimestamp();\\n\\n if (!isTimeBased) {\\n safe32(blockNumberOrTimestamp, \\\"block number exceeds 32 bits\\\");\\n }\\n\\n uint256 lastRewardingBlockOrTimestamp = isTimeBased\\n ? supplyStateTimeBased.lastRewardingTimestamp\\n : uint256(supplyState.lastRewardingBlock);\\n\\n if (lastRewardingBlockOrTimestamp > 0 && blockNumberOrTimestamp > lastRewardingBlockOrTimestamp) {\\n blockNumberOrTimestamp = lastRewardingBlockOrTimestamp;\\n }\\n\\n uint256 deltaBlocksOrTimestamp = sub_(\\n blockNumberOrTimestamp,\\n (isTimeBased ? supplyStateTimeBased.timestamp : uint256(supplyState.block))\\n );\\n if (deltaBlocksOrTimestamp > 0 && supplySpeed > 0) {\\n uint256 supplyTokens = VToken(vToken).totalSupply();\\n uint256 accruedSinceUpdate = mul_(deltaBlocksOrTimestamp, supplySpeed);\\n Double memory ratio = supplyTokens > 0\\n ? fraction(accruedSinceUpdate, supplyTokens)\\n : Double({ mantissa: 0 });\\n uint224 supplyIndex = isTimeBased ? supplyStateTimeBased.index : supplyState.index;\\n uint224 index = safe224(\\n add_(Double({ mantissa: supplyIndex }), ratio).mantissa,\\n \\\"new index exceeds 224 bits\\\"\\n );\\n\\n if (isTimeBased) {\\n supplyStateTimeBased.index = index;\\n supplyStateTimeBased.timestamp = blockNumberOrTimestamp;\\n } else {\\n supplyState.index = index;\\n supplyState.block = uint32(blockNumberOrTimestamp);\\n }\\n } else if (deltaBlocksOrTimestamp > 0) {\\n isTimeBased ? supplyStateTimeBased.timestamp = blockNumberOrTimestamp : supplyState.block = uint32(\\n blockNumberOrTimestamp\\n );\\n }\\n\\n emit RewardTokenSupplyIndexUpdated(vToken);\\n }\\n\\n /**\\n * @notice Accrue REWARD TOKEN to the market by updating the borrow index\\n * @param vToken The market whose borrow index to update\\n * @param marketBorrowIndex The current global borrow index of vToken\\n * @dev Index is a cumulative sum of the REWARD TOKEN per vToken accrued\\n */\\n function _updateRewardTokenBorrowIndex(address vToken, Exp memory marketBorrowIndex) internal {\\n RewardToken storage borrowState = rewardTokenBorrowState[vToken];\\n TimeBasedRewardToken storage borrowStateTimeBased = rewardTokenBorrowStateTimeBased[vToken];\\n\\n uint256 borrowSpeed = rewardTokenBorrowSpeeds[vToken];\\n uint256 blockNumberOrTimestamp = getBlockNumberOrTimestamp();\\n\\n if (!isTimeBased) {\\n safe32(blockNumberOrTimestamp, \\\"block number exceeds 32 bits\\\");\\n }\\n\\n uint256 lastRewardingBlockOrTimestamp = isTimeBased\\n ? borrowStateTimeBased.lastRewardingTimestamp\\n : uint256(borrowState.lastRewardingBlock);\\n\\n if (lastRewardingBlockOrTimestamp > 0 && blockNumberOrTimestamp > lastRewardingBlockOrTimestamp) {\\n blockNumberOrTimestamp = lastRewardingBlockOrTimestamp;\\n }\\n\\n uint256 deltaBlocksOrTimestamp = sub_(\\n blockNumberOrTimestamp,\\n (isTimeBased ? borrowStateTimeBased.timestamp : uint256(borrowState.block))\\n );\\n if (deltaBlocksOrTimestamp > 0 && borrowSpeed > 0) {\\n uint256 borrowAmount = div_(VToken(vToken).totalBorrows(), marketBorrowIndex);\\n uint256 accruedSinceUpdate = mul_(deltaBlocksOrTimestamp, borrowSpeed);\\n Double memory ratio = borrowAmount > 0\\n ? fraction(accruedSinceUpdate, borrowAmount)\\n : Double({ mantissa: 0 });\\n uint224 borrowIndex = isTimeBased ? borrowStateTimeBased.index : borrowState.index;\\n uint224 index = safe224(\\n add_(Double({ mantissa: borrowIndex }), ratio).mantissa,\\n \\\"new index exceeds 224 bits\\\"\\n );\\n\\n if (isTimeBased) {\\n borrowStateTimeBased.index = index;\\n borrowStateTimeBased.timestamp = blockNumberOrTimestamp;\\n } else {\\n borrowState.index = index;\\n borrowState.block = uint32(blockNumberOrTimestamp);\\n }\\n } else if (deltaBlocksOrTimestamp > 0) {\\n if (isTimeBased) {\\n borrowStateTimeBased.timestamp = blockNumberOrTimestamp;\\n } else {\\n borrowState.block = uint32(blockNumberOrTimestamp);\\n }\\n }\\n\\n emit RewardTokenBorrowIndexUpdated(vToken, marketBorrowIndex);\\n }\\n\\n /**\\n * @notice Initializes the market state for a specific vToken called when contract is block-based\\n * @param vToken The address of the vToken to be initialized\\n * @param blockNumber current block number\\n */\\n function _initializeMarketBlockBased(address vToken, uint32 blockNumber) internal {\\n RewardToken storage supplyState = rewardTokenSupplyState[vToken];\\n RewardToken storage borrowState = rewardTokenBorrowState[vToken];\\n\\n /*\\n * Update market state indices\\n */\\n if (supplyState.index == 0) {\\n // Initialize supply state index with default value\\n supplyState.index = INITIAL_INDEX;\\n }\\n\\n if (borrowState.index == 0) {\\n // Initialize borrow state index with default value\\n borrowState.index = INITIAL_INDEX;\\n }\\n\\n /*\\n * Update market state block numbers\\n */\\n supplyState.block = borrowState.block = blockNumber;\\n }\\n\\n /**\\n * @notice Initializes the market state for a specific vToken called when contract is time-based\\n * @param vToken The address of the vToken to be initialized\\n * @param blockTimestamp current block timestamp\\n */\\n function _initializeMarketTimestampBased(address vToken, uint256 blockTimestamp) internal {\\n TimeBasedRewardToken storage supplyState = rewardTokenSupplyStateTimeBased[vToken];\\n TimeBasedRewardToken storage borrowState = rewardTokenBorrowStateTimeBased[vToken];\\n\\n /*\\n * Update market state indices\\n */\\n if (supplyState.index == 0) {\\n // Initialize supply state index with default value\\n supplyState.index = INITIAL_INDEX;\\n }\\n\\n if (borrowState.index == 0) {\\n // Initialize borrow state index with default value\\n borrowState.index = INITIAL_INDEX;\\n }\\n\\n /*\\n * Update market state block timestamp\\n */\\n supplyState.timestamp = borrowState.timestamp = blockTimestamp;\\n }\\n}\\n\",\"keccak256\":\"0x3cc824e59c923cfe25c4f1a875959b7e9410cde8e73cd405de13f301298c697f\",\"license\":\"BSD-3-Clause\"},\"contracts/Rewards/RewardsDistributorStorage.sol\":{\"content\":\"// SPDX-License-Identifier: BSD-3-Clause\\npragma solidity 0.8.25;\\n\\nimport { IERC20Upgradeable } from \\\"@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol\\\";\\n\\nimport { Comptroller } from \\\"../Comptroller.sol\\\";\\n\\n/**\\n * @title RewardsDistributorStorage\\n * @author Venus\\n * @dev Storage for RewardsDistributor\\n */\\ncontract RewardsDistributorStorage {\\n struct RewardToken {\\n // The market's last updated rewardTokenBorrowIndex or rewardTokenSupplyIndex\\n uint224 index;\\n // The block number the index was last updated at\\n uint32 block;\\n // The block number at which to stop rewards\\n uint32 lastRewardingBlock;\\n }\\n\\n struct TimeBasedRewardToken {\\n // The market's last updated rewardTokenBorrowIndex or rewardTokenSupplyIndex\\n uint224 index;\\n // The block timestamp the index was last updated at\\n uint256 timestamp;\\n // The block timestamp at which to stop rewards\\n uint256 lastRewardingTimestamp;\\n }\\n\\n /// @notice The REWARD TOKEN market supply state for each market\\n mapping(address => RewardToken) public rewardTokenSupplyState;\\n\\n /// @notice The REWARD TOKEN borrow index for each market for each supplier as of the last time they accrued REWARD TOKEN\\n mapping(address => mapping(address => uint256)) public rewardTokenSupplierIndex;\\n\\n /// @notice The REWARD TOKEN accrued but not yet transferred to each user\\n mapping(address => uint256) public rewardTokenAccrued;\\n\\n /// @notice The rate at which rewardToken is distributed to the corresponding borrow market per slot (block or second)\\n mapping(address => uint256) public rewardTokenBorrowSpeeds;\\n\\n /// @notice The rate at which rewardToken is distributed to the corresponding supply market per slot (block or second)\\n mapping(address => uint256) public rewardTokenSupplySpeeds;\\n\\n /// @notice The REWARD TOKEN market borrow state for each market\\n mapping(address => RewardToken) public rewardTokenBorrowState;\\n\\n /// @notice The portion of REWARD TOKEN that each contributor receives per slot (block or second)\\n mapping(address => uint256) public rewardTokenContributorSpeeds;\\n\\n /// @notice Last slot (block or second) at which a contributor's REWARD TOKEN rewards have been allocated\\n mapping(address => uint256) public lastContributorBlock;\\n\\n /// @notice The REWARD TOKEN borrow index for each market for each borrower as of the last time they accrued REWARD TOKEN\\n mapping(address => mapping(address => uint256)) public rewardTokenBorrowerIndex;\\n\\n Comptroller internal comptroller;\\n\\n IERC20Upgradeable public rewardToken;\\n\\n /// @notice The REWARD TOKEN market supply state for each market\\n mapping(address => TimeBasedRewardToken) public rewardTokenSupplyStateTimeBased;\\n\\n /// @notice The REWARD TOKEN market borrow state for each market\\n mapping(address => TimeBasedRewardToken) public rewardTokenBorrowStateTimeBased;\\n\\n /**\\n * @dev This empty reserved space is put in place to allow future versions to add new\\n * variables without shifting down storage in the inheritance chain\\n * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\\n */\\n uint256[37] private __gap;\\n}\\n\",\"keccak256\":\"0x70e5015a78a2acf95277949c8b4796e10b9ac194130be006d5e5217e158070c0\",\"license\":\"BSD-3-Clause\"},\"contracts/VToken.sol\":{\"content\":\"// SPDX-License-Identifier: BSD-3-Clause\\npragma solidity 0.8.25;\\n\\nimport { Ownable2StepUpgradeable } from \\\"@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol\\\";\\nimport { IERC20Upgradeable } from \\\"@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol\\\";\\nimport { SafeERC20Upgradeable } from \\\"@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol\\\";\\nimport { AccessControlledV8 } from \\\"@venusprotocol/governance-contracts/contracts/Governance/AccessControlledV8.sol\\\";\\nimport { IProtocolShareReserve } from \\\"@venusprotocol/protocol-reserve/contracts/Interfaces/IProtocolShareReserve.sol\\\";\\n\\nimport { VTokenInterface } from \\\"./VTokenInterfaces.sol\\\";\\nimport { ComptrollerInterface, ComptrollerViewInterface } from \\\"./ComptrollerInterface.sol\\\";\\nimport { TokenErrorReporter } from \\\"./ErrorReporter.sol\\\";\\nimport { InterestRateModel } from \\\"./InterestRateModel.sol\\\";\\nimport { ExponentialNoError } from \\\"./ExponentialNoError.sol\\\";\\nimport { TimeManagerV8 } from \\\"@venusprotocol/solidity-utilities/contracts/TimeManagerV8.sol\\\";\\nimport { ensureNonzeroAddress } from \\\"./lib/validators.sol\\\";\\n\\n/**\\n * @title VToken\\n * @author Venus\\n * @notice Each asset that is supported by a pool is integrated through an instance of the `VToken` contract. As outlined in the protocol overview,\\n * each isolated pool creates its own `vToken` corresponding to an asset. Within a given pool, each included `vToken` is referred to as a market of\\n * the pool. The main actions a user regularly interacts with in a market are:\\n\\n- mint/redeem of vTokens;\\n- transfer of vTokens;\\n- borrow/repay a loan on an underlying asset;\\n- liquidate a borrow or liquidate/heal an account.\\n\\n * A user supplies the underlying asset to a pool by minting `vTokens`, where the corresponding `vToken` amount is determined by the `exchangeRate`.\\n * The `exchangeRate` will change over time, dependent on a number of factors, some of which accrue interest. Additionally, once users have minted\\n * `vToken` in a pool, they can borrow any asset in the isolated pool by using their `vToken` as collateral. In order to borrow an asset or use a `vToken`\\n * as collateral, the user must be entered into each corresponding market (else, the `vToken` will not be considered collateral for a borrow). Note that\\n * a user may borrow up to a portion of their collateral determined by the market\\u2019s collateral factor. However, if their borrowed amount exceeds an amount\\n * calculated using the market\\u2019s corresponding liquidation threshold, the borrow is eligible for liquidation. When a user repays a borrow, they must also\\n * pay off interest accrued on the borrow.\\n * \\n * The Venus protocol includes unique mechanisms for healing an account and liquidating an account. These actions are performed in the `Comptroller`\\n * and consider all borrows and collateral for which a given account is entered within a market. These functions may only be called on an account with a\\n * total collateral amount that is no larger than a universal `minLiquidatableCollateral` value, which is used for all markets within a `Comptroller`.\\n * Both functions settle all of an account\\u2019s borrows, but `healAccount()` may add `badDebt` to a vToken. For more detail, see the description of\\n * `healAccount()` and `liquidateAccount()` in the `Comptroller` summary section below.\\n */\\ncontract VToken is\\n Ownable2StepUpgradeable,\\n AccessControlledV8,\\n VTokenInterface,\\n ExponentialNoError,\\n TokenErrorReporter,\\n TimeManagerV8\\n{\\n using SafeERC20Upgradeable for IERC20Upgradeable;\\n\\n uint256 internal constant DEFAULT_PROTOCOL_SEIZE_SHARE_MANTISSA = 5e16; // 5%\\n\\n // Maximum fraction of interest that can be set aside for reserves\\n uint256 internal constant MAX_RESERVE_FACTOR_MANTISSA = 1e18;\\n\\n // Maximum borrow rate that can ever be applied per slot(block or second)\\n /// @custom:oz-upgrades-unsafe-allow state-variable-immutable\\n uint256 internal immutable MAX_BORROW_RATE_MANTISSA;\\n\\n /**\\n * Reentrancy Guard **\\n */\\n\\n /**\\n * @dev Prevents a contract from calling itself, directly or indirectly.\\n */\\n modifier nonReentrant() {\\n require(_notEntered, \\\"re-entered\\\");\\n _notEntered = false;\\n _;\\n _notEntered = true; // get a gas-refund post-Istanbul\\n }\\n\\n /**\\n * @param timeBased_ A boolean indicating whether the contract is based on time or block.\\n * @param blocksPerYear_ The number of blocks per year\\n * @param maxBorrowRateMantissa_ The maximum value of borrowing rate mantissa\\n * @custom:oz-upgrades-unsafe-allow constructor\\n */\\n constructor(\\n bool timeBased_,\\n uint256 blocksPerYear_,\\n uint256 maxBorrowRateMantissa_\\n ) TimeManagerV8(timeBased_, blocksPerYear_) {\\n // Note that the contract is upgradeable. Use initialize() or reinitializers\\n // to set the state variables.\\n require(maxBorrowRateMantissa_ <= 1e18, \\\"Max borrow rate must be <= 1e18\\\");\\n\\n MAX_BORROW_RATE_MANTISSA = maxBorrowRateMantissa_;\\n _disableInitializers();\\n }\\n\\n /**\\n * @notice Construct a new money market\\n * @param underlying_ The address of the underlying asset\\n * @param comptroller_ The address of the Comptroller\\n * @param interestRateModel_ The address of the interest rate model\\n * @param initialExchangeRateMantissa_ The initial exchange rate, scaled by 1e18\\n * @param name_ ERC-20 name of this token\\n * @param symbol_ ERC-20 symbol of this token\\n * @param decimals_ ERC-20 decimal precision of this token\\n * @param admin_ Address of the administrator of this token\\n * @param accessControlManager_ AccessControlManager contract address\\n * @param riskManagement Addresses of risk & income related contracts\\n * @param reserveFactorMantissa_ Percentage of borrow interest that goes to reserves (from 0 to 1e18)\\n * @custom:error ZeroAddressNotAllowed is thrown when admin address is zero\\n * @custom:error ZeroAddressNotAllowed is thrown when shortfall contract address is zero\\n * @custom:error ZeroAddressNotAllowed is thrown when protocol share reserve address is zero\\n */\\n function initialize(\\n address underlying_,\\n ComptrollerInterface comptroller_,\\n InterestRateModel interestRateModel_,\\n uint256 initialExchangeRateMantissa_,\\n string memory name_,\\n string memory symbol_,\\n uint8 decimals_,\\n address admin_,\\n address accessControlManager_,\\n RiskManagementInit memory riskManagement,\\n uint256 reserveFactorMantissa_\\n ) external initializer {\\n ensureNonzeroAddress(admin_);\\n\\n // Initialize the market\\n _initialize(\\n underlying_,\\n comptroller_,\\n interestRateModel_,\\n initialExchangeRateMantissa_,\\n name_,\\n symbol_,\\n decimals_,\\n admin_,\\n accessControlManager_,\\n riskManagement,\\n reserveFactorMantissa_\\n );\\n }\\n\\n /**\\n * @notice Transfer `amount` tokens from `msg.sender` to `dst`\\n * @param dst The address of the destination account\\n * @param amount The number of tokens to transfer\\n * @return success True if the transfer succeeded, reverts otherwise\\n * @custom:event Emits Transfer event on success\\n * @custom:error TransferNotAllowed is thrown if trying to transfer to self\\n * @custom:access Not restricted\\n */\\n function transfer(address dst, uint256 amount) external override nonReentrant returns (bool) {\\n _transferTokens(msg.sender, msg.sender, dst, amount);\\n return true;\\n }\\n\\n /**\\n * @notice Transfer `amount` tokens from `src` to `dst`\\n * @param src The address of the source account\\n * @param dst The address of the destination account\\n * @param amount The number of tokens to transfer\\n * @return success True if the transfer succeeded, reverts otherwise\\n * @custom:event Emits Transfer event on success\\n * @custom:error TransferNotAllowed is thrown if trying to transfer to self\\n * @custom:access Not restricted\\n */\\n function transferFrom(address src, address dst, uint256 amount) external override nonReentrant returns (bool) {\\n _transferTokens(msg.sender, src, dst, amount);\\n return true;\\n }\\n\\n /**\\n * @notice Approve `spender` to transfer up to `amount` from `src`\\n * @dev This will overwrite the approval amount for `spender`\\n * and is subject to issues noted [here](https://eips.ethereum.org/EIPS/eip-20#approve)\\n * @param spender The address of the account which may transfer tokens\\n * @param amount The number of tokens that are approved (uint256.max means infinite)\\n * @return success Whether or not the approval succeeded\\n * @custom:event Emits Approval event\\n * @custom:access Not restricted\\n * @custom:error ZeroAddressNotAllowed is thrown when spender address is zero\\n */\\n function approve(address spender, uint256 amount) external override returns (bool) {\\n ensureNonzeroAddress(spender);\\n\\n address src = msg.sender;\\n transferAllowances[src][spender] = amount;\\n emit Approval(src, spender, amount);\\n return true;\\n }\\n\\n /**\\n * @notice Increase approval for `spender`\\n * @param spender The address of the account which may transfer tokens\\n * @param addedValue The number of additional tokens spender can transfer\\n * @return success Whether or not the approval succeeded\\n * @custom:event Emits Approval event\\n * @custom:access Not restricted\\n * @custom:error ZeroAddressNotAllowed is thrown when spender address is zero\\n */\\n function increaseAllowance(address spender, uint256 addedValue) external override returns (bool) {\\n ensureNonzeroAddress(spender);\\n\\n address src = msg.sender;\\n uint256 newAllowance = transferAllowances[src][spender];\\n newAllowance += addedValue;\\n transferAllowances[src][spender] = newAllowance;\\n\\n emit Approval(src, spender, newAllowance);\\n return true;\\n }\\n\\n /**\\n * @notice Decreases approval for `spender`\\n * @param spender The address of the account which may transfer tokens\\n * @param subtractedValue The number of tokens to remove from total approval\\n * @return success Whether or not the approval succeeded\\n * @custom:event Emits Approval event\\n * @custom:access Not restricted\\n * @custom:error ZeroAddressNotAllowed is thrown when spender address is zero\\n */\\n function decreaseAllowance(address spender, uint256 subtractedValue) external override returns (bool) {\\n ensureNonzeroAddress(spender);\\n\\n address src = msg.sender;\\n uint256 currentAllowance = transferAllowances[src][spender];\\n require(currentAllowance >= subtractedValue, \\\"decreased allowance below zero\\\");\\n unchecked {\\n currentAllowance -= subtractedValue;\\n }\\n\\n transferAllowances[src][spender] = currentAllowance;\\n\\n emit Approval(src, spender, currentAllowance);\\n return true;\\n }\\n\\n /**\\n * @notice Get the underlying balance of the `owner`\\n * @dev This also accrues interest in a transaction\\n * @param owner The address of the account to query\\n * @return amount The amount of underlying owned by `owner`\\n */\\n function balanceOfUnderlying(address owner) external override returns (uint256) {\\n Exp memory exchangeRate = Exp({ mantissa: exchangeRateCurrent() });\\n return mul_ScalarTruncate(exchangeRate, accountTokens[owner]);\\n }\\n\\n /**\\n * @notice Returns the current total borrows plus accrued interest\\n * @return totalBorrows The total borrows with interest\\n */\\n function totalBorrowsCurrent() external override nonReentrant returns (uint256) {\\n accrueInterest();\\n return totalBorrows;\\n }\\n\\n /**\\n * @notice Accrue interest to updated borrowIndex and then calculate account's borrow balance using the updated borrowIndex\\n * @param account The address whose balance should be calculated after updating borrowIndex\\n * @return borrowBalance The calculated balance\\n */\\n function borrowBalanceCurrent(address account) external override nonReentrant returns (uint256) {\\n accrueInterest();\\n return _borrowBalanceStored(account);\\n }\\n\\n /**\\n * @notice Sender supplies assets into the market and receives vTokens in exchange\\n * @dev Accrues interest whether or not the operation succeeds, unless reverted\\n * @param mintAmount The amount of the underlying asset to supply\\n * @return error Always NO_ERROR for compatibility with Venus core tooling\\n * @custom:event Emits Mint and Transfer events; may emit AccrueInterest\\n * @custom:access Not restricted\\n */\\n function mint(uint256 mintAmount) external override nonReentrant returns (uint256) {\\n accrueInterest();\\n\\n _mintFresh(msg.sender, msg.sender, mintAmount);\\n return NO_ERROR;\\n }\\n\\n /**\\n * @notice Sender calls on-behalf of minter. minter supplies assets into the market and receives vTokens in exchange\\n * @dev Accrues interest whether or not the operation succeeds, unless reverted\\n * @param minter User whom the supply will be attributed to\\n * @param mintAmount The amount of the underlying asset to supply\\n * @return error Always NO_ERROR for compatibility with Venus core tooling\\n * @custom:event Emits Mint and Transfer events; may emit AccrueInterest\\n * @custom:access Not restricted\\n * @custom:error ZeroAddressNotAllowed is thrown when minter address is zero\\n */\\n function mintBehalf(address minter, uint256 mintAmount) external override nonReentrant returns (uint256) {\\n ensureNonzeroAddress(minter);\\n\\n accrueInterest();\\n\\n _mintFresh(msg.sender, minter, mintAmount);\\n return NO_ERROR;\\n }\\n\\n /**\\n * @notice Sender redeems vTokens in exchange for the underlying asset\\n * @dev Accrues interest whether or not the operation succeeds, unless reverted\\n * @param redeemTokens The number of vTokens to redeem into underlying\\n * @return error Always NO_ERROR for compatibility with Venus core tooling\\n * @custom:event Emits Redeem and Transfer events; may emit AccrueInterest\\n * @custom:error RedeemTransferOutNotPossible is thrown when the protocol has insufficient cash\\n * @custom:access Not restricted\\n */\\n function redeem(uint256 redeemTokens) external override nonReentrant returns (uint256) {\\n accrueInterest();\\n\\n _redeemFresh(msg.sender, msg.sender, redeemTokens, 0);\\n return NO_ERROR;\\n }\\n\\n /**\\n * @notice Sender redeems assets on behalf of some other address. This function is only available\\n * for senders, explicitly marked as delegates of the supplier using `comptroller.updateDelegate`\\n * @dev Accrues interest whether or not the operation succeeds, unless reverted\\n * @param redeemer The user on behalf of whom to redeem\\n * @param redeemTokens The number of vTokens to redeem into underlying\\n * @return error Always NO_ERROR for compatibility with Venus core tooling\\n * @custom:error InsufficientRedeemApproval is thrown when sender is not approved by the redeemer for the given amount\\n * @custom:error RedeemTransferOutNotPossible is thrown when the protocol has insufficient cash\\n * @custom:event Emits Redeem and Transfer events; may emit AccrueInterest\\n * @custom:access Not restricted\\n */\\n function redeemBehalf(address redeemer, uint256 redeemTokens) external override nonReentrant returns (uint256) {\\n _ensureSenderIsDelegateOf(redeemer);\\n\\n accrueInterest();\\n\\n _redeemFresh(redeemer, msg.sender, redeemTokens, 0);\\n return NO_ERROR;\\n }\\n\\n /**\\n * @notice Sender redeems vTokens in exchange for a specified amount of underlying asset\\n * @dev Accrues interest whether or not the operation succeeds, unless reverted\\n * @param redeemAmount The amount of underlying to receive from redeeming vTokens\\n * @return error Always NO_ERROR for compatibility with Venus core tooling\\n */\\n function redeemUnderlying(uint256 redeemAmount) external override nonReentrant returns (uint256) {\\n accrueInterest();\\n\\n _redeemFresh(msg.sender, msg.sender, 0, redeemAmount);\\n return NO_ERROR;\\n }\\n\\n /**\\n * @notice Sender redeems underlying assets on behalf of some other address. This function is only available\\n * for senders, explicitly marked as delegates of the supplier using `comptroller.updateDelegate`\\n * @dev Accrues interest whether or not the operation succeeds, unless reverted\\n * @param redeemer, on behalf of whom to redeem\\n * @param redeemAmount The amount of underlying to receive from redeeming vTokens\\n * @return error Always NO_ERROR for compatibility with Venus core tooling\\n * @custom:error InsufficientRedeemApproval is thrown when sender is not approved by the redeemer for the given amount\\n * @custom:event Emits Redeem and Transfer events; may emit AccrueInterest\\n * @custom:access Not restricted\\n */\\n function redeemUnderlyingBehalf(\\n address redeemer,\\n uint256 redeemAmount\\n ) external override nonReentrant returns (uint256) {\\n _ensureSenderIsDelegateOf(redeemer);\\n\\n accrueInterest();\\n\\n _redeemFresh(redeemer, msg.sender, 0, redeemAmount);\\n return NO_ERROR;\\n }\\n\\n /**\\n * @notice Sender borrows assets from the protocol to their own address\\n * @param borrowAmount The amount of the underlying asset to borrow\\n * @return error Always NO_ERROR for compatibility with Venus core tooling\\n * @custom:event Emits Borrow event; may emit AccrueInterest\\n * @custom:error BorrowCashNotAvailable is thrown when the protocol has insufficient cash\\n * @custom:access Not restricted\\n */\\n function borrow(uint256 borrowAmount) external override nonReentrant returns (uint256) {\\n accrueInterest();\\n\\n _borrowFresh(msg.sender, msg.sender, borrowAmount);\\n return NO_ERROR;\\n }\\n\\n /**\\n * @notice Sender borrows assets on behalf of some other address. This function is only available\\n * for senders, explicitly marked as delegates of the borrower using `comptroller.updateDelegate`\\n * @param borrower The borrower, on behalf of whom to borrow\\n * @param borrowAmount The amount of the underlying asset to borrow\\n * @return error Always NO_ERROR for compatibility with Venus core tooling\\n * @custom:error DelegateNotApproved is thrown if caller is not approved delegate\\n * @custom:error BorrowCashNotAvailable is thrown when the protocol has insufficient cash\\n * @custom:event Emits Borrow event; may emit AccrueInterest\\n * @custom:access Not restricted\\n */\\n function borrowBehalf(address borrower, uint256 borrowAmount) external override returns (uint256) {\\n _ensureSenderIsDelegateOf(borrower);\\n accrueInterest();\\n\\n _borrowFresh(borrower, msg.sender, borrowAmount);\\n return NO_ERROR;\\n }\\n\\n /**\\n * @notice Sender repays their own borrow\\n * @param repayAmount The amount to repay, or type(uint256).max for the full outstanding amount\\n * @return error Always NO_ERROR for compatibility with Venus core tooling\\n * @custom:event Emits RepayBorrow event; may emit AccrueInterest\\n * @custom:access Not restricted\\n */\\n function repayBorrow(uint256 repayAmount) external override nonReentrant returns (uint256) {\\n accrueInterest();\\n\\n _repayBorrowFresh(msg.sender, msg.sender, repayAmount);\\n return NO_ERROR;\\n }\\n\\n /**\\n * @notice Sender repays a borrow belonging to borrower\\n * @param borrower the account with the debt being payed off\\n * @param repayAmount The amount to repay, or type(uint256).max for the full outstanding amount\\n * @return error Always NO_ERROR for compatibility with Venus core tooling\\n * @custom:event Emits RepayBorrow event; may emit AccrueInterest\\n * @custom:access Not restricted\\n */\\n function repayBorrowBehalf(address borrower, uint256 repayAmount) external override nonReentrant returns (uint256) {\\n accrueInterest();\\n\\n _repayBorrowFresh(msg.sender, borrower, repayAmount);\\n return NO_ERROR;\\n }\\n\\n /**\\n * @notice The sender liquidates the borrowers collateral.\\n * The collateral seized is transferred to the liquidator.\\n * @param borrower The borrower of this vToken to be liquidated\\n * @param repayAmount The amount of the underlying borrowed asset to repay\\n * @param vTokenCollateral The market in which to seize collateral from the borrower\\n * @return error Always NO_ERROR for compatibility with Venus core tooling\\n * @custom:event Emits LiquidateBorrow event; may emit AccrueInterest\\n * @custom:error LiquidateAccrueCollateralInterestFailed is thrown when it is not possible to accrue interest on the collateral vToken\\n * @custom:error LiquidateCollateralFreshnessCheck is thrown when interest has not been accrued on the collateral vToken\\n * @custom:error LiquidateLiquidatorIsBorrower is thrown when trying to liquidate self\\n * @custom:error LiquidateCloseAmountIsZero is thrown when repayment amount is zero\\n * @custom:error LiquidateCloseAmountIsUintMax is thrown when repayment amount is UINT_MAX\\n * @custom:access Not restricted\\n */\\n function liquidateBorrow(\\n address borrower,\\n uint256 repayAmount,\\n VTokenInterface vTokenCollateral\\n ) external override returns (uint256) {\\n _liquidateBorrow(msg.sender, borrower, repayAmount, vTokenCollateral, false);\\n return NO_ERROR;\\n }\\n\\n /**\\n * @notice sets protocol share accumulated from liquidations\\n * @dev must be equal or less than liquidation incentive - 1\\n * @param newProtocolSeizeShareMantissa_ new protocol share mantissa\\n * @custom:event Emits NewProtocolSeizeShare event on success\\n * @custom:error Unauthorized error is thrown when the call is not authorized by AccessControlManager\\n * @custom:error ProtocolSeizeShareTooBig is thrown when the new seize share is too high\\n * @custom:access Controlled by AccessControlManager\\n */\\n function setProtocolSeizeShare(uint256 newProtocolSeizeShareMantissa_) external {\\n _checkAccessAllowed(\\\"setProtocolSeizeShare(uint256)\\\");\\n uint256 liquidationIncentive = ComptrollerViewInterface(address(comptroller)).liquidationIncentiveMantissa();\\n if (newProtocolSeizeShareMantissa_ + MANTISSA_ONE > liquidationIncentive) {\\n revert ProtocolSeizeShareTooBig();\\n }\\n\\n uint256 oldProtocolSeizeShareMantissa = protocolSeizeShareMantissa;\\n protocolSeizeShareMantissa = newProtocolSeizeShareMantissa_;\\n emit NewProtocolSeizeShare(oldProtocolSeizeShareMantissa, newProtocolSeizeShareMantissa_);\\n }\\n\\n /**\\n * @notice accrues interest and sets a new reserve factor for the protocol using _setReserveFactorFresh\\n * @dev Admin function to accrue interest and set a new reserve factor\\n * @param newReserveFactorMantissa New reserve factor (from 0 to 1e18)\\n * @custom:event Emits NewReserveFactor event; may emit AccrueInterest\\n * @custom:error Unauthorized error is thrown when the call is not authorized by AccessControlManager\\n * @custom:error SetReserveFactorBoundsCheck is thrown when the new reserve factor is too high\\n * @custom:access Controlled by AccessControlManager\\n */\\n function setReserveFactor(uint256 newReserveFactorMantissa) external override nonReentrant {\\n _checkAccessAllowed(\\\"setReserveFactor(uint256)\\\");\\n\\n accrueInterest();\\n _setReserveFactorFresh(newReserveFactorMantissa);\\n }\\n\\n /**\\n * @notice Accrues interest and reduces reserves by transferring to the protocol reserve contract\\n * @dev Gracefully return if reserves already reduced in accrueInterest\\n * @param reduceAmount Amount of reduction to reserves\\n * @custom:event Emits ReservesReduced event; may emit AccrueInterest\\n * @custom:error ReduceReservesCashNotAvailable is thrown when the vToken does not have sufficient cash\\n * @custom:error ReduceReservesCashValidation is thrown when trying to withdraw more cash than the reserves have\\n * @custom:access Not restricted\\n */\\n function reduceReserves(uint256 reduceAmount) external override nonReentrant {\\n accrueInterest();\\n if (reduceReservesBlockNumber == getBlockNumberOrTimestamp()) return;\\n _reduceReservesFresh(reduceAmount);\\n }\\n\\n /**\\n * @notice The sender adds to reserves.\\n * @param addAmount The amount of underlying token to add as reserves\\n * @custom:event Emits ReservesAdded event; may emit AccrueInterest\\n * @custom:access Not restricted\\n */\\n function addReserves(uint256 addAmount) external override nonReentrant {\\n accrueInterest();\\n _addReservesFresh(addAmount);\\n }\\n\\n /**\\n * @notice accrues interest and updates the interest rate model using _setInterestRateModelFresh\\n * @dev Admin function to accrue interest and update the interest rate model\\n * @param newInterestRateModel the new interest rate model to use\\n * @custom:event Emits NewMarketInterestRateModel event; may emit AccrueInterest\\n * @custom:error Unauthorized error is thrown when the call is not authorized by AccessControlManager\\n * @custom:access Controlled by AccessControlManager\\n */\\n function setInterestRateModel(InterestRateModel newInterestRateModel) external override {\\n _checkAccessAllowed(\\\"setInterestRateModel(address)\\\");\\n\\n accrueInterest();\\n _setInterestRateModelFresh(newInterestRateModel);\\n }\\n\\n /**\\n * @notice Repays a certain amount of debt, treats the rest of the borrow as bad debt, essentially\\n * \\\"forgiving\\\" the borrower. Healing is a situation that should rarely happen. However, some pools\\n * may list risky assets or be configured improperly \\u2013 we want to still handle such cases gracefully.\\n * We assume that Comptroller does the seizing, so this function is only available to Comptroller.\\n * @dev This function does not call any Comptroller hooks (like \\\"healAllowed\\\"), because we assume\\n * the Comptroller does all the necessary checks before calling this function.\\n * @param payer account who repays the debt\\n * @param borrower account to heal\\n * @param repayAmount amount to repay\\n * @custom:event Emits RepayBorrow, BadDebtIncreased events; may emit AccrueInterest\\n * @custom:error HealBorrowUnauthorized is thrown when the request does not come from Comptroller\\n * @custom:access Only Comptroller\\n */\\n function healBorrow(address payer, address borrower, uint256 repayAmount) external override nonReentrant {\\n if (repayAmount != 0) {\\n comptroller.preRepayHook(address(this), borrower);\\n }\\n\\n if (msg.sender != address(comptroller)) {\\n revert HealBorrowUnauthorized();\\n }\\n\\n uint256 accountBorrowsPrev = _borrowBalanceStored(borrower);\\n uint256 totalBorrowsNew = totalBorrows;\\n\\n uint256 actualRepayAmount;\\n if (repayAmount != 0) {\\n // _doTransferIn reverts if anything goes wrong, since we can't be sure if side effects occurred.\\n // We violate checks-effects-interactions here to account for tokens that take transfer fees\\n actualRepayAmount = _doTransferIn(payer, repayAmount);\\n totalBorrowsNew = totalBorrowsNew - actualRepayAmount;\\n emit RepayBorrow(\\n payer,\\n borrower,\\n actualRepayAmount,\\n accountBorrowsPrev - actualRepayAmount,\\n totalBorrowsNew\\n );\\n }\\n\\n // The transaction will fail if trying to repay too much\\n uint256 badDebtDelta = accountBorrowsPrev - actualRepayAmount;\\n if (badDebtDelta != 0) {\\n uint256 badDebtOld = badDebt;\\n uint256 badDebtNew = badDebtOld + badDebtDelta;\\n totalBorrowsNew = totalBorrowsNew - badDebtDelta;\\n badDebt = badDebtNew;\\n\\n // We treat healing as \\\"repayment\\\", where vToken is the payer\\n emit RepayBorrow(address(this), borrower, badDebtDelta, 0, totalBorrowsNew);\\n emit BadDebtIncreased(borrower, badDebtDelta, badDebtOld, badDebtNew);\\n }\\n\\n accountBorrows[borrower].principal = 0;\\n accountBorrows[borrower].interestIndex = borrowIndex;\\n totalBorrows = totalBorrowsNew;\\n\\n emit HealBorrow(payer, borrower, repayAmount);\\n }\\n\\n /**\\n * @notice The extended version of liquidations, callable only by Comptroller. May skip\\n * the close factor check. The collateral seized is transferred to the liquidator.\\n * @param liquidator The address repaying the borrow and seizing collateral\\n * @param borrower The borrower of this vToken to be liquidated\\n * @param repayAmount The amount of the underlying borrowed asset to repay\\n * @param vTokenCollateral The market in which to seize collateral from the borrower\\n * @param skipLiquidityCheck If set to true, allows to liquidate up to 100% of the borrow\\n * regardless of the account liquidity\\n * @custom:event Emits LiquidateBorrow event; may emit AccrueInterest\\n * @custom:error ForceLiquidateBorrowUnauthorized is thrown when the request does not come from Comptroller\\n * @custom:error LiquidateAccrueCollateralInterestFailed is thrown when it is not possible to accrue interest on the collateral vToken\\n * @custom:error LiquidateCollateralFreshnessCheck is thrown when interest has not been accrued on the collateral vToken\\n * @custom:error LiquidateLiquidatorIsBorrower is thrown when trying to liquidate self\\n * @custom:error LiquidateCloseAmountIsZero is thrown when repayment amount is zero\\n * @custom:error LiquidateCloseAmountIsUintMax is thrown when repayment amount is UINT_MAX\\n * @custom:access Only Comptroller\\n */\\n function forceLiquidateBorrow(\\n address liquidator,\\n address borrower,\\n uint256 repayAmount,\\n VTokenInterface vTokenCollateral,\\n bool skipLiquidityCheck\\n ) external override {\\n if (msg.sender != address(comptroller)) {\\n revert ForceLiquidateBorrowUnauthorized();\\n }\\n _liquidateBorrow(liquidator, borrower, repayAmount, vTokenCollateral, skipLiquidityCheck);\\n }\\n\\n /**\\n * @notice Transfers collateral tokens (this market) to the liquidator.\\n * @dev Will fail unless called by another vToken during the process of liquidation.\\n * It's absolutely critical to use msg.sender as the borrowed vToken and not a parameter.\\n * @param liquidator The account receiving seized collateral\\n * @param borrower The account having collateral seized\\n * @param seizeTokens The number of vTokens to seize\\n * @custom:event Emits Transfer, ReservesAdded events\\n * @custom:error LiquidateSeizeLiquidatorIsBorrower is thrown when trying to liquidate self\\n * @custom:access Not restricted\\n */\\n function seize(address liquidator, address borrower, uint256 seizeTokens) external override nonReentrant {\\n _seize(msg.sender, liquidator, borrower, seizeTokens);\\n }\\n\\n /**\\n * @notice Updates bad debt\\n * @dev Called only when bad debt is recovered from auction. Updates internal cash balance.\\n * @param recoveredAmount_ The amount of bad debt recovered\\n * @custom:event Emits BadDebtRecovered event\\n * @custom:access Only Shortfall contract\\n */\\n function badDebtRecovered(uint256 recoveredAmount_) external {\\n require(msg.sender == shortfall, \\\"only shortfall contract can update bad debt\\\");\\n require(recoveredAmount_ <= badDebt, \\\"more than bad debt recovered from auction\\\");\\n\\n uint256 badDebtOld = badDebt;\\n uint256 badDebtNew = badDebtOld - recoveredAmount_;\\n badDebt = badDebtNew;\\n internalCash += recoveredAmount_;\\n\\n emit BadDebtRecovered(badDebtOld, badDebtNew);\\n }\\n\\n /**\\n * @notice Sets protocol share reserve contract address\\n * @param protocolShareReserve_ The address of the protocol share reserve contract\\n * @custom:error ZeroAddressNotAllowed is thrown when protocol share reserve address is zero\\n * @custom:access Only Governance\\n */\\n function setProtocolShareReserve(address payable protocolShareReserve_) external onlyOwner {\\n _setProtocolShareReserve(protocolShareReserve_);\\n }\\n\\n /**\\n * @notice Sets shortfall contract address\\n * @param shortfall_ The address of the shortfall contract\\n * @custom:error ZeroAddressNotAllowed is thrown when shortfall contract address is zero\\n * @custom:access Only Governance\\n */\\n function setShortfallContract(address shortfall_) external onlyOwner {\\n _setShortfallContract(shortfall_);\\n }\\n\\n /**\\n * @notice A public function to sweep accidental ERC-20 transfers to this contract. Tokens are sent to admin (timelock)\\n * @param token The address of the ERC-20 token to sweep\\n * @custom:access Only Governance\\n */\\n function sweepToken(IERC20Upgradeable token) external override {\\n require(msg.sender == owner(), \\\"VToken::sweepToken: only admin can sweep tokens\\\");\\n require(address(token) != underlying, \\\"VToken::sweepToken: can not sweep underlying token\\\");\\n uint256 balance = token.balanceOf(address(this));\\n token.safeTransfer(owner(), balance);\\n\\n emit SweepToken(address(token));\\n }\\n\\n /**\\n * @notice Sync internalCash with the actual underlying token balance\\n * @dev Used for one-time migration after upgrade to initialize internalCash\\n * @custom:event Emits CashSynced\\n * @custom:access Controlled by AccessControlManager\\n */\\n function syncCash() external override nonReentrant {\\n _checkAccessAllowed(\\\"syncCash()\\\");\\n uint256 oldInternalCash = internalCash;\\n uint256 actualCash = IERC20Upgradeable(underlying).balanceOf(address(this));\\n internalCash = actualCash;\\n emit CashSynced(oldInternalCash, actualCash);\\n }\\n\\n /**\\n * @notice A public function to set new threshold of slot(block or second) difference after which funds will be sent to the protocol share reserve\\n * @param _newReduceReservesBlockOrTimestampDelta slot(block or second) difference value\\n * @custom:access Only Governance\\n */\\n function setReduceReservesBlockDelta(uint256 _newReduceReservesBlockOrTimestampDelta) external {\\n _checkAccessAllowed(\\\"setReduceReservesBlockDelta(uint256)\\\");\\n require(_newReduceReservesBlockOrTimestampDelta > 0, \\\"Invalid Input\\\");\\n emit NewReduceReservesBlockDelta(reduceReservesBlockDelta, _newReduceReservesBlockOrTimestampDelta);\\n reduceReservesBlockDelta = _newReduceReservesBlockOrTimestampDelta;\\n }\\n\\n /**\\n * @notice Get the current allowance from `owner` for `spender`\\n * @param owner The address of the account which owns the tokens to be spent\\n * @param spender The address of the account which may transfer tokens\\n * @return amount The number of tokens allowed to be spent (type(uint256).max means infinite)\\n */\\n function allowance(address owner, address spender) external view override returns (uint256) {\\n return transferAllowances[owner][spender];\\n }\\n\\n /**\\n * @notice Get the token balance of the `owner`\\n * @param owner The address of the account to query\\n * @return amount The number of tokens owned by `owner`\\n */\\n function balanceOf(address owner) external view override returns (uint256) {\\n return accountTokens[owner];\\n }\\n\\n /**\\n * @notice Get a snapshot of the account's balances, and the cached exchange rate\\n * @dev This is used by comptroller to more efficiently perform liquidity checks.\\n * @param account Address of the account to snapshot\\n * @return error Always NO_ERROR for compatibility with Venus core tooling\\n * @return vTokenBalance User's balance of vTokens\\n * @return borrowBalance Amount owed in terms of underlying\\n * @return exchangeRate Stored exchange rate\\n */\\n function getAccountSnapshot(\\n address account\\n )\\n external\\n view\\n override\\n returns (uint256 error, uint256 vTokenBalance, uint256 borrowBalance, uint256 exchangeRate)\\n {\\n return (NO_ERROR, accountTokens[account], _borrowBalanceStored(account), _exchangeRateStored());\\n }\\n\\n /**\\n * @notice Get the internally tracked cash balance of this vToken in the underlying asset\\n * @return cash The quantity of underlying asset tracked internally by this contract\\n */\\n function getCash() external view override returns (uint256) {\\n return _getCashPrior();\\n }\\n\\n /**\\n * @notice Returns the current per slot(block or second) borrow interest rate for this vToken\\n * @return rate The borrow interest rate per slot(block or second), scaled by 1e18\\n */\\n function borrowRatePerBlock() external view override returns (uint256) {\\n return interestRateModel.getBorrowRate(_getCashPrior(), totalBorrows, totalReserves, badDebt);\\n }\\n\\n /**\\n * @notice Returns the current per-slot(block or second) supply interest rate for this v\\n * @return rate The supply interest rate per slot(block or second), scaled by 1e18\\n */\\n function supplyRatePerBlock() external view override returns (uint256) {\\n return\\n interestRateModel.getSupplyRate(\\n _getCashPrior(),\\n totalBorrows,\\n totalReserves,\\n reserveFactorMantissa,\\n badDebt\\n );\\n }\\n\\n /**\\n * @notice Return the borrow balance of account based on stored data\\n * @param account The address whose balance should be calculated\\n * @return borrowBalance The calculated balance\\n */\\n function borrowBalanceStored(address account) external view override returns (uint256) {\\n return _borrowBalanceStored(account);\\n }\\n\\n /**\\n * @notice Calculates the exchange rate from the underlying to the VToken\\n * @dev This function does not accrue interest before calculating the exchange rate\\n * @return exchangeRate Calculated exchange rate scaled by 1e18\\n */\\n function exchangeRateStored() external view override returns (uint256) {\\n return _exchangeRateStored();\\n }\\n\\n /**\\n * @notice Accrue interest then return the up-to-date exchange rate\\n * @return exchangeRate Calculated exchange rate scaled by 1e18\\n */\\n function exchangeRateCurrent() public override nonReentrant returns (uint256) {\\n accrueInterest();\\n return _exchangeRateStored();\\n }\\n\\n /**\\n * @notice Applies accrued interest to total borrows and reserves\\n * @dev This calculates interest accrued from the last checkpointed slot(block or second)\\n * up to the current slot(block or second) and writes new checkpoint to storage and\\n * reduce spread reserves to protocol share reserve\\n * if currentSlot - reduceReservesBlockNumber >= slotDelta\\n * @return Always NO_ERROR\\n * @custom:event Emits AccrueInterest event on success\\n * @custom:access Not restricted\\n */\\n function accrueInterest() public virtual override returns (uint256) {\\n /* Remember the initial block number or timestamp */\\n uint256 currentSlotNumber = getBlockNumberOrTimestamp();\\n uint256 accrualSlotNumberPrior = accrualBlockNumber;\\n\\n /* Short-circuit accumulating 0 interest */\\n if (accrualSlotNumberPrior == currentSlotNumber) {\\n return NO_ERROR;\\n }\\n\\n /* Read the previous values out of storage */\\n uint256 cashPrior = _getCashPrior();\\n uint256 borrowsPrior = totalBorrows;\\n uint256 reservesPrior = totalReserves;\\n uint256 borrowIndexPrior = borrowIndex;\\n\\n /* Calculate the current borrow interest rate */\\n uint256 borrowRateMantissa = interestRateModel.getBorrowRate(cashPrior, borrowsPrior, reservesPrior, badDebt);\\n require(borrowRateMantissa <= MAX_BORROW_RATE_MANTISSA, \\\"borrow rate is absurdly high\\\");\\n\\n /* Calculate the number of slots elapsed since the last accrual */\\n uint256 slotDelta = currentSlotNumber - accrualSlotNumberPrior;\\n\\n /*\\n * Calculate the interest accumulated into borrows and reserves and the new index:\\n * simpleInterestFactor = borrowRate * slotDelta\\n * interestAccumulated = simpleInterestFactor * totalBorrows\\n * totalBorrowsNew = interestAccumulated + totalBorrows\\n * totalReservesNew = interestAccumulated * reserveFactor + totalReserves\\n * borrowIndexNew = simpleInterestFactor * borrowIndex + borrowIndex\\n */\\n\\n Exp memory simpleInterestFactor = mul_(Exp({ mantissa: borrowRateMantissa }), slotDelta);\\n uint256 interestAccumulated = mul_ScalarTruncate(simpleInterestFactor, borrowsPrior);\\n uint256 totalBorrowsNew = interestAccumulated + borrowsPrior;\\n uint256 totalReservesNew = mul_ScalarTruncateAddUInt(\\n Exp({ mantissa: reserveFactorMantissa }),\\n interestAccumulated,\\n reservesPrior\\n );\\n uint256 borrowIndexNew = mul_ScalarTruncateAddUInt(simpleInterestFactor, borrowIndexPrior, borrowIndexPrior);\\n\\n /////////////////////////\\n // EFFECTS & INTERACTIONS\\n // (No safe failures beyond this point)\\n\\n /* We write the previously calculated values into storage */\\n accrualBlockNumber = currentSlotNumber;\\n borrowIndex = borrowIndexNew;\\n totalBorrows = totalBorrowsNew;\\n totalReserves = totalReservesNew;\\n\\n if (currentSlotNumber - reduceReservesBlockNumber >= reduceReservesBlockDelta) {\\n reduceReservesBlockNumber = currentSlotNumber;\\n if (cashPrior < totalReservesNew) {\\n _reduceReservesFresh(cashPrior);\\n } else {\\n _reduceReservesFresh(totalReservesNew);\\n }\\n }\\n\\n /* We emit an AccrueInterest event */\\n emit AccrueInterest(cashPrior, interestAccumulated, borrowIndexNew, totalBorrowsNew);\\n\\n return NO_ERROR;\\n }\\n\\n /**\\n * @notice User supplies assets into the market and receives vTokens in exchange\\n * @dev Assumes interest has already been accrued up to the current block or timestamp\\n * @param payer The address of the account which is sending the assets for supply\\n * @param minter The address of the account which is supplying the assets\\n * @param mintAmount The amount of the underlying asset to supply\\n */\\n function _mintFresh(address payer, address minter, uint256 mintAmount) internal {\\n /* Fail if mint not allowed */\\n comptroller.preMintHook(address(this), minter, mintAmount);\\n\\n /* Verify market's slot(block or second) number equals current slot(block or second) number */\\n if (accrualBlockNumber != getBlockNumberOrTimestamp()) {\\n revert MintFreshnessCheck();\\n }\\n\\n Exp memory exchangeRate = Exp({ mantissa: _exchangeRateStored() });\\n\\n /////////////////////////\\n // EFFECTS & INTERACTIONS\\n // (No safe failures beyond this point)\\n\\n /*\\n * We call `_doTransferIn` for the minter and the mintAmount.\\n * `_doTransferIn` reverts if anything goes wrong, since we can't be sure if\\n * side-effects occurred. The function returns the amount actually transferred,\\n * in case of a fee. On success, the vToken holds an additional `actualMintAmount`\\n * of cash.\\n */\\n uint256 actualMintAmount = _doTransferIn(payer, mintAmount);\\n\\n /*\\n * We get the current exchange rate and calculate the number of vTokens to be minted:\\n * mintTokens = actualMintAmount / exchangeRate\\n */\\n\\n uint256 mintTokens = div_(actualMintAmount, exchangeRate);\\n\\n /*\\n * We calculate the new total supply of vTokens and minter token balance, checking for overflow:\\n * totalSupplyNew = totalSupply + mintTokens\\n * accountTokensNew = accountTokens[minter] + mintTokens\\n * And write them into storage\\n */\\n totalSupply = totalSupply + mintTokens;\\n uint256 balanceAfter = accountTokens[minter] + mintTokens;\\n accountTokens[minter] = balanceAfter;\\n\\n /* We emit a Mint event, and a Transfer event */\\n emit Mint(minter, actualMintAmount, mintTokens, balanceAfter);\\n emit Transfer(address(0), minter, mintTokens);\\n\\n /* We call the defense and prime accrue interest hook */\\n comptroller.mintVerify(address(this), minter, actualMintAmount, mintTokens);\\n }\\n\\n /**\\n * @notice Redeemer redeems vTokens in exchange for the underlying assets, transferred to the receiver. Redeemer and receiver can be the same\\n * address, or different addresses if the receiver was previously approved by the redeemer as a valid delegate (see Comptroller.updateDelegate)\\n * @dev Assumes interest has already been accrued up to the current slot(block or second)\\n * @param redeemer The address of the account which is redeeming the tokens\\n * @param receiver The receiver of the underlying tokens\\n * @param redeemTokensIn The number of vTokens to redeem into underlying (only one of redeemTokensIn or redeemAmountIn may be non-zero)\\n * @param redeemAmountIn The number of underlying tokens to receive from redeeming vTokens (only one of redeemTokensIn or redeemAmountIn may be non-zero)\\n */\\n function _redeemFresh(address redeemer, address receiver, uint256 redeemTokensIn, uint256 redeemAmountIn) internal {\\n require(redeemTokensIn == 0 || redeemAmountIn == 0, \\\"one of redeemTokensIn or redeemAmountIn must be zero\\\");\\n\\n /* Verify market's slot(block or second) number equals current slot(block or second) number */\\n if (accrualBlockNumber != getBlockNumberOrTimestamp()) {\\n revert RedeemFreshnessCheck();\\n }\\n\\n /* exchangeRate = invoke Exchange Rate Stored() */\\n Exp memory exchangeRate = Exp({ mantissa: _exchangeRateStored() });\\n\\n uint256 redeemTokens;\\n uint256 redeemAmount;\\n\\n /* If redeemTokensIn > 0: */\\n if (redeemTokensIn > 0) {\\n /*\\n * We calculate the exchange rate and the amount of underlying to be redeemed:\\n * redeemTokens = redeemTokensIn\\n */\\n redeemTokens = redeemTokensIn;\\n } else {\\n /*\\n * We get the current exchange rate and calculate the amount to be redeemed:\\n * redeemTokens = redeemAmountIn / exchangeRate\\n */\\n redeemTokens = div_(redeemAmountIn, exchangeRate);\\n\\n uint256 _redeemAmount = mul_(redeemTokens, exchangeRate);\\n if (_redeemAmount != 0 && _redeemAmount != redeemAmountIn) redeemTokens++; // round up\\n }\\n\\n // redeemAmount = exchangeRate * redeemTokens\\n redeemAmount = mul_ScalarTruncate(exchangeRate, redeemTokens);\\n\\n // Revert if amount is zero\\n if (redeemAmount == 0) {\\n revert(\\\"redeemAmount is zero\\\");\\n }\\n\\n /* Fail if redeem not allowed */\\n comptroller.preRedeemHook(address(this), redeemer, redeemTokens);\\n\\n /* Fail gracefully if protocol has insufficient cash */\\n if (_getCashPrior() - totalReserves < redeemAmount) {\\n revert RedeemTransferOutNotPossible();\\n }\\n\\n /////////////////////////\\n // EFFECTS & INTERACTIONS\\n // (No safe failures beyond this point)\\n\\n /*\\n * We write the previously calculated values into storage.\\n * Note: Avoid token reentrancy attacks by writing reduced supply before external transfer.\\n */\\n totalSupply = totalSupply - redeemTokens;\\n uint256 balanceAfter = accountTokens[redeemer] - redeemTokens;\\n accountTokens[redeemer] = balanceAfter;\\n\\n /*\\n * We invoke _doTransferOut for the receiver and the redeemAmount.\\n * On success, the vToken has redeemAmount less of cash.\\n * _doTransferOut reverts if anything goes wrong, since we can't be sure if side effects occurred.\\n */\\n _doTransferOut(receiver, redeemAmount);\\n\\n /* We emit a Transfer event, and a Redeem event */\\n emit Transfer(redeemer, address(this), redeemTokens);\\n emit Redeem(redeemer, redeemAmount, redeemTokens, balanceAfter);\\n\\n /* We call the defense and prime accrue interest hook */\\n comptroller.redeemVerify(address(this), redeemer, redeemAmount, redeemTokens);\\n }\\n\\n /**\\n * @notice Users or their delegates borrow assets from the protocol\\n * @param borrower User who borrows the assets\\n * @param receiver The receiver of the tokens, if called by a delegate\\n * @param borrowAmount The amount of the underlying asset to borrow\\n */\\n function _borrowFresh(address borrower, address receiver, uint256 borrowAmount) internal {\\n /* Fail if borrow not allowed */\\n comptroller.preBorrowHook(address(this), borrower, borrowAmount);\\n\\n /* Verify market's slot(block or second) number equals current slot(block or second) number */\\n if (accrualBlockNumber != getBlockNumberOrTimestamp()) {\\n revert BorrowFreshnessCheck();\\n }\\n\\n /* Fail gracefully if protocol has insufficient underlying cash */\\n if (_getCashPrior() - totalReserves < borrowAmount) {\\n revert BorrowCashNotAvailable();\\n }\\n\\n /*\\n * We calculate the new borrower and total borrow balances, failing on overflow:\\n * accountBorrowNew = accountBorrow + borrowAmount\\n * totalBorrowsNew = totalBorrows + borrowAmount\\n */\\n uint256 accountBorrowsPrev = _borrowBalanceStored(borrower);\\n uint256 accountBorrowsNew = accountBorrowsPrev + borrowAmount;\\n uint256 totalBorrowsNew = totalBorrows + borrowAmount;\\n\\n /////////////////////////\\n // EFFECTS & INTERACTIONS\\n // (No safe failures beyond this point)\\n\\n /*\\n * We write the previously calculated values into storage.\\n * Note: Avoid token reentrancy attacks by writing increased borrow before external transfer.\\n `*/\\n accountBorrows[borrower].principal = accountBorrowsNew;\\n accountBorrows[borrower].interestIndex = borrowIndex;\\n totalBorrows = totalBorrowsNew;\\n\\n /*\\n * We invoke _doTransferOut for the receiver and the borrowAmount.\\n * On success, the vToken borrowAmount less of cash.\\n * _doTransferOut reverts if anything goes wrong, since we can't be sure if side effects occurred.\\n */\\n _doTransferOut(receiver, borrowAmount);\\n\\n /* We emit a Borrow event */\\n emit Borrow(borrower, borrowAmount, accountBorrowsNew, totalBorrowsNew);\\n\\n /* We call the defense and prime accrue interest hook */\\n comptroller.borrowVerify(address(this), borrower, borrowAmount);\\n }\\n\\n /**\\n * @notice Borrows are repaid by another user (possibly the borrower).\\n * @param payer the account paying off the borrow\\n * @param borrower the account with the debt being payed off\\n * @param repayAmount the amount of underlying tokens being returned, or type(uint256).max for the full outstanding amount\\n * @return (uint) the actual repayment amount.\\n */\\n function _repayBorrowFresh(address payer, address borrower, uint256 repayAmount) internal returns (uint256) {\\n /* Fail if repayBorrow not allowed */\\n comptroller.preRepayHook(address(this), borrower);\\n\\n /* Verify market's slot(block or second) number equals current slot(block or second) number */\\n if (accrualBlockNumber != getBlockNumberOrTimestamp()) {\\n revert RepayBorrowFreshnessCheck();\\n }\\n\\n /* We fetch the amount the borrower owes, with accumulated interest */\\n uint256 accountBorrowsPrev = _borrowBalanceStored(borrower);\\n\\n uint256 repayAmountFinal = repayAmount >= accountBorrowsPrev ? accountBorrowsPrev : repayAmount;\\n\\n /////////////////////////\\n // EFFECTS & INTERACTIONS\\n // (No safe failures beyond this point)\\n\\n /*\\n * We call _doTransferIn for the payer and the repayAmount\\n * On success, the vToken holds an additional repayAmount of cash.\\n * _doTransferIn reverts if anything goes wrong, since we can't be sure if side effects occurred.\\n * it returns the amount actually transferred, in case of a fee.\\n */\\n uint256 actualRepayAmount = _doTransferIn(payer, repayAmountFinal);\\n\\n /*\\n * We calculate the new borrower and total borrow balances, failing on underflow:\\n * accountBorrowsNew = accountBorrows - actualRepayAmount\\n * totalBorrowsNew = totalBorrows - actualRepayAmount\\n */\\n uint256 accountBorrowsNew = accountBorrowsPrev - actualRepayAmount;\\n uint256 totalBorrowsNew = totalBorrows - actualRepayAmount;\\n\\n /* We write the previously calculated values into storage */\\n accountBorrows[borrower].principal = accountBorrowsNew;\\n accountBorrows[borrower].interestIndex = borrowIndex;\\n totalBorrows = totalBorrowsNew;\\n\\n /* We emit a RepayBorrow event */\\n emit RepayBorrow(payer, borrower, actualRepayAmount, accountBorrowsNew, totalBorrowsNew);\\n\\n /* We call the defense and prime accrue interest hook */\\n comptroller.repayBorrowVerify(address(this), payer, borrower, actualRepayAmount, borrowIndex);\\n\\n return actualRepayAmount;\\n }\\n\\n /**\\n * @notice The sender liquidates the borrowers collateral.\\n * The collateral seized is transferred to the liquidator.\\n * @param liquidator The address repaying the borrow and seizing collateral\\n * @param borrower The borrower of this vToken to be liquidated\\n * @param repayAmount The amount of the underlying borrowed asset to repay\\n * @param vTokenCollateral The market in which to seize collateral from the borrower\\n * @param skipLiquidityCheck If set to true, allows to liquidate up to 100% of the borrow\\n * regardless of the account liquidity\\n */\\n function _liquidateBorrow(\\n address liquidator,\\n address borrower,\\n uint256 repayAmount,\\n VTokenInterface vTokenCollateral,\\n bool skipLiquidityCheck\\n ) internal nonReentrant {\\n accrueInterest();\\n\\n uint256 error = vTokenCollateral.accrueInterest();\\n if (error != NO_ERROR) {\\n // accrueInterest emits logs on errors, but we still want to log the fact that an attempted liquidation failed\\n revert LiquidateAccrueCollateralInterestFailed(error);\\n }\\n\\n _liquidateBorrowFresh(liquidator, borrower, repayAmount, vTokenCollateral, skipLiquidityCheck);\\n }\\n\\n /**\\n * @notice The liquidator liquidates the borrowers collateral.\\n * The collateral seized is transferred to the liquidator.\\n * @param liquidator The address repaying the borrow and seizing collateral\\n * @param borrower The borrower of this vToken to be liquidated\\n * @param repayAmount The amount of the underlying borrowed asset to repay\\n * @param vTokenCollateral The market in which to seize collateral from the borrower\\n * @param skipLiquidityCheck If set to true, allows to liquidate up to 100% of the borrow\\n * regardless of the account liquidity\\n */\\n function _liquidateBorrowFresh(\\n address liquidator,\\n address borrower,\\n uint256 repayAmount,\\n VTokenInterface vTokenCollateral,\\n bool skipLiquidityCheck\\n ) internal {\\n /* Fail if liquidate not allowed */\\n comptroller.preLiquidateHook(\\n address(this),\\n address(vTokenCollateral),\\n borrower,\\n repayAmount,\\n skipLiquidityCheck\\n );\\n\\n /* Verify market's slot(block or second) number equals current slot(block or second) number */\\n if (accrualBlockNumber != getBlockNumberOrTimestamp()) {\\n revert LiquidateFreshnessCheck();\\n }\\n\\n /* Verify vTokenCollateral market's slot(block or second) number equals current slot(block or second) number */\\n if (vTokenCollateral.accrualBlockNumber() != getBlockNumberOrTimestamp()) {\\n revert LiquidateCollateralFreshnessCheck();\\n }\\n\\n /* Fail if borrower = liquidator */\\n if (borrower == liquidator) {\\n revert LiquidateLiquidatorIsBorrower();\\n }\\n\\n /* Fail if repayAmount = 0 */\\n if (repayAmount == 0) {\\n revert LiquidateCloseAmountIsZero();\\n }\\n\\n /* Fail if repayAmount = type(uint256).max */\\n if (repayAmount == type(uint256).max) {\\n revert LiquidateCloseAmountIsUintMax();\\n }\\n\\n /* Fail if repayBorrow fails */\\n uint256 actualRepayAmount = _repayBorrowFresh(liquidator, borrower, repayAmount);\\n\\n /////////////////////////\\n // EFFECTS & INTERACTIONS\\n // (No safe failures beyond this point)\\n\\n /* We calculate the number of collateral tokens that will be seized */\\n (uint256 amountSeizeError, uint256 seizeTokens) = comptroller.liquidateCalculateSeizeTokens(\\n address(this),\\n address(vTokenCollateral),\\n actualRepayAmount\\n );\\n require(amountSeizeError == NO_ERROR, \\\"LIQUIDATE_COMPTROLLER_CALCULATE_AMOUNT_SEIZE_FAILED\\\");\\n\\n /* Revert if borrower collateral token balance < seizeTokens */\\n require(vTokenCollateral.balanceOf(borrower) >= seizeTokens, \\\"LIQUIDATE_SEIZE_TOO_MUCH\\\");\\n\\n // If this is also the collateral, call _seize internally to avoid re-entrancy, otherwise make an external call\\n if (address(vTokenCollateral) == address(this)) {\\n _seize(address(this), liquidator, borrower, seizeTokens);\\n } else {\\n vTokenCollateral.seize(liquidator, borrower, seizeTokens);\\n }\\n\\n /* We emit a LiquidateBorrow event */\\n emit LiquidateBorrow(liquidator, borrower, actualRepayAmount, address(vTokenCollateral), seizeTokens);\\n\\n /* We call the defense and prime accrue interest hook */\\n comptroller.liquidateBorrowVerify(\\n address(this),\\n address(vTokenCollateral),\\n liquidator,\\n borrower,\\n actualRepayAmount,\\n seizeTokens\\n );\\n }\\n\\n /**\\n * @notice Transfers collateral tokens (this market) to the liquidator.\\n * @dev Called only during an in-kind liquidation, or by liquidateBorrow during the liquidation of another VToken.\\n * It's absolutely critical to use msg.sender as the seizer vToken and not a parameter.\\n * @param seizerContract The contract seizing the collateral (either borrowed vToken or Comptroller)\\n * @param liquidator The account receiving seized collateral\\n * @param borrower The account having collateral seized\\n * @param seizeTokens The number of vTokens to seize\\n */\\n function _seize(address seizerContract, address liquidator, address borrower, uint256 seizeTokens) internal {\\n /* Fail if seize not allowed */\\n comptroller.preSeizeHook(address(this), seizerContract, liquidator, borrower);\\n\\n /* Fail if borrower = liquidator */\\n if (borrower == liquidator) {\\n revert LiquidateSeizeLiquidatorIsBorrower();\\n }\\n\\n /*\\n * We calculate the new borrower and liquidator token balances, failing on underflow/overflow:\\n * borrowerTokensNew = accountTokens[borrower] - seizeTokens\\n * liquidatorTokensNew = accountTokens[liquidator] + seizeTokens\\n */\\n uint256 liquidationIncentiveMantissa = ComptrollerViewInterface(address(comptroller))\\n .liquidationIncentiveMantissa();\\n uint256 numerator = mul_(seizeTokens, Exp({ mantissa: protocolSeizeShareMantissa }));\\n uint256 protocolSeizeTokens = div_(numerator, Exp({ mantissa: liquidationIncentiveMantissa }));\\n uint256 liquidatorSeizeTokens = seizeTokens - protocolSeizeTokens;\\n Exp memory exchangeRate = Exp({ mantissa: _exchangeRateStored() });\\n uint256 protocolSeizeAmount = mul_ScalarTruncate(exchangeRate, protocolSeizeTokens);\\n\\n /////////////////////////\\n // EFFECTS & INTERACTIONS\\n // (No safe failures beyond this point)\\n\\n /* We write the calculated values into storage */\\n totalSupply = totalSupply - protocolSeizeTokens;\\n accountTokens[borrower] = accountTokens[borrower] - seizeTokens;\\n accountTokens[liquidator] = accountTokens[liquidator] + liquidatorSeizeTokens;\\n\\n // _doTransferOut reverts if anything goes wrong, since we can't be sure if side effects occurred.\\n // Transferring an underlying asset to the protocolShareReserve contract to channel the funds for different use.\\n _doTransferOut(protocolShareReserve, protocolSeizeAmount);\\n\\n // Update the pool asset's state in the protocol share reserve for the above transfer.\\n IProtocolShareReserve(protocolShareReserve).updateAssetsState(\\n address(comptroller),\\n underlying,\\n IProtocolShareReserve.IncomeType.LIQUIDATION\\n );\\n\\n /* Emit a Transfer event */\\n emit Transfer(borrower, liquidator, liquidatorSeizeTokens);\\n emit ProtocolSeize(borrower, protocolShareReserve, protocolSeizeAmount);\\n\\n /* We call the defense and prime accrue interest hook */\\n comptroller.seizeVerify(address(this), seizerContract, liquidator, borrower, seizeTokens);\\n }\\n\\n function _setComptroller(ComptrollerInterface newComptroller) internal {\\n ComptrollerInterface oldComptroller = comptroller;\\n // Ensure invoke comptroller.isComptroller() returns true\\n require(newComptroller.isComptroller(), \\\"marker method returned false\\\");\\n\\n // Set market's comptroller to newComptroller\\n comptroller = newComptroller;\\n\\n // Emit NewComptroller(oldComptroller, newComptroller)\\n emit NewComptroller(oldComptroller, newComptroller);\\n }\\n\\n /**\\n * @notice Sets a new reserve factor for the protocol (*requires fresh interest accrual)\\n * @dev Admin function to set a new reserve factor\\n * @param newReserveFactorMantissa New reserve factor (from 0 to 1e18)\\n */\\n function _setReserveFactorFresh(uint256 newReserveFactorMantissa) internal {\\n // Verify market's slot(block or second) number equals current slot(block or second) number\\n if (accrualBlockNumber != getBlockNumberOrTimestamp()) {\\n revert SetReserveFactorFreshCheck();\\n }\\n\\n // Check newReserveFactor \\u2264 maxReserveFactor\\n if (newReserveFactorMantissa > MAX_RESERVE_FACTOR_MANTISSA) {\\n revert SetReserveFactorBoundsCheck();\\n }\\n\\n uint256 oldReserveFactorMantissa = reserveFactorMantissa;\\n reserveFactorMantissa = newReserveFactorMantissa;\\n\\n emit NewReserveFactor(oldReserveFactorMantissa, newReserveFactorMantissa);\\n }\\n\\n /**\\n * @notice Add reserves by transferring from caller\\n * @dev Requires fresh interest accrual\\n * @param addAmount Amount of addition to reserves\\n * @return actualAddAmount The actual amount added, excluding the potential token fees\\n */\\n function _addReservesFresh(uint256 addAmount) internal returns (uint256) {\\n // totalReserves + actualAddAmount\\n uint256 totalReservesNew;\\n uint256 actualAddAmount;\\n\\n // We fail gracefully unless market's slot(block or second) number equals current slot(block or second) number\\n if (accrualBlockNumber != getBlockNumberOrTimestamp()) {\\n revert AddReservesFactorFreshCheck(actualAddAmount);\\n }\\n\\n actualAddAmount = _doTransferIn(msg.sender, addAmount);\\n totalReservesNew = totalReserves + actualAddAmount;\\n totalReserves = totalReservesNew;\\n emit ReservesAdded(msg.sender, actualAddAmount, totalReservesNew);\\n\\n return actualAddAmount;\\n }\\n\\n /**\\n * @notice Reduces reserves by transferring to the protocol reserve contract\\n * @dev Requires fresh interest accrual\\n * @param reduceAmount Amount of reduction to reserves\\n */\\n function _reduceReservesFresh(uint256 reduceAmount) internal {\\n if (reduceAmount == 0) {\\n return;\\n }\\n // totalReserves - reduceAmount\\n uint256 totalReservesNew;\\n\\n // We fail gracefully unless market's slot(block or second) number equals current slot(block or second) number\\n if (accrualBlockNumber != getBlockNumberOrTimestamp()) {\\n revert ReduceReservesFreshCheck();\\n }\\n\\n // Fail gracefully if protocol has insufficient underlying cash\\n if (_getCashPrior() < reduceAmount) {\\n revert ReduceReservesCashNotAvailable();\\n }\\n\\n // Check reduceAmount \\u2264 reserves[n] (totalReserves)\\n if (reduceAmount > totalReserves) {\\n revert ReduceReservesCashValidation();\\n }\\n\\n /////////////////////////\\n // EFFECTS & INTERACTIONS\\n // (No safe failures beyond this point)\\n\\n totalReservesNew = totalReserves - reduceAmount;\\n\\n // Store reserves[n+1] = reserves[n] - reduceAmount\\n totalReserves = totalReservesNew;\\n\\n // _doTransferOut reverts if anything goes wrong, since we can't be sure if side effects occurred.\\n // Transferring an underlying asset to the protocolShareReserve contract to channel the funds for different use.\\n _doTransferOut(protocolShareReserve, reduceAmount);\\n\\n // Update the pool asset's state in the protocol share reserve for the above transfer.\\n IProtocolShareReserve(protocolShareReserve).updateAssetsState(\\n address(comptroller),\\n underlying,\\n IProtocolShareReserve.IncomeType.SPREAD\\n );\\n\\n emit SpreadReservesReduced(protocolShareReserve, reduceAmount, totalReservesNew);\\n }\\n\\n /**\\n * @notice updates the interest rate model (*requires fresh interest accrual)\\n * @dev Admin function to update the interest rate model\\n * @param newInterestRateModel the new interest rate model to use\\n */\\n function _setInterestRateModelFresh(InterestRateModel newInterestRateModel) internal {\\n // Used to store old model for use in the event that is emitted on success\\n InterestRateModel oldInterestRateModel;\\n\\n // We fail gracefully unless market's slot(block or second) number equals current slot(block or second) number\\n if (accrualBlockNumber != getBlockNumberOrTimestamp()) {\\n revert SetInterestRateModelFreshCheck();\\n }\\n\\n // Track the market's current interest rate model\\n oldInterestRateModel = interestRateModel;\\n\\n // Ensure invoke newInterestRateModel.isInterestRateModel() returns true\\n require(newInterestRateModel.isInterestRateModel(), \\\"marker method returned false\\\");\\n\\n // Set the interest rate model to newInterestRateModel\\n interestRateModel = newInterestRateModel;\\n\\n // Emit NewMarketInterestRateModel(oldInterestRateModel, newInterestRateModel)\\n emit NewMarketInterestRateModel(oldInterestRateModel, newInterestRateModel);\\n }\\n\\n /**\\n * Safe Token **\\n */\\n\\n /**\\n * @dev Similar to ERC-20 transfer, but handles tokens that have transfer fees.\\n * This function returns the actual amount received,\\n * which may be less than `amount` if there is a fee attached to the transfer.\\n * @param from Sender of the underlying tokens\\n * @param amount Amount of underlying to transfer\\n * @return Actual amount received\\n */\\n function _doTransferIn(address from, uint256 amount) internal virtual returns (uint256) {\\n IERC20Upgradeable token = IERC20Upgradeable(underlying);\\n uint256 balanceBefore = token.balanceOf(address(this));\\n token.safeTransferFrom(from, address(this), amount);\\n uint256 balanceAfter = token.balanceOf(address(this));\\n uint256 actualAmount = balanceAfter - balanceBefore;\\n internalCash += actualAmount;\\n // Return the amount that was *actually* transferred\\n return actualAmount;\\n }\\n\\n /**\\n * @dev Just a regular ERC-20 transfer, reverts on failure\\n * @param to Receiver of the underlying tokens\\n * @param amount Amount of underlying to transfer\\n */\\n function _doTransferOut(address to, uint256 amount) internal virtual {\\n IERC20Upgradeable token = IERC20Upgradeable(underlying);\\n internalCash -= amount;\\n token.safeTransfer(to, amount);\\n }\\n\\n /**\\n * @notice Transfer `tokens` tokens from `src` to `dst` by `spender`\\n * @dev Called by both `transfer` and `transferFrom` internally\\n * @param spender The address of the account performing the transfer\\n * @param src The address of the source account\\n * @param dst The address of the destination account\\n * @param tokens The number of tokens to transfer\\n */\\n function _transferTokens(address spender, address src, address dst, uint256 tokens) internal {\\n /* Fail if transfer not allowed */\\n comptroller.preTransferHook(address(this), src, dst, tokens);\\n\\n /* Do not allow self-transfers */\\n if (src == dst) {\\n revert TransferNotAllowed();\\n }\\n\\n /* Get the allowance, infinite for the account owner */\\n uint256 startingAllowance;\\n if (spender == src) {\\n startingAllowance = type(uint256).max;\\n } else {\\n startingAllowance = transferAllowances[src][spender];\\n }\\n\\n /* Do the calculations, checking for {under,over}flow */\\n uint256 allowanceNew = startingAllowance - tokens;\\n uint256 srcTokensNew = accountTokens[src] - tokens;\\n uint256 dstTokensNew = accountTokens[dst] + tokens;\\n\\n /////////////////////////\\n // EFFECTS & INTERACTIONS\\n\\n accountTokens[src] = srcTokensNew;\\n accountTokens[dst] = dstTokensNew;\\n\\n /* Eat some of the allowance (if necessary) */\\n if (startingAllowance != type(uint256).max) {\\n transferAllowances[src][spender] = allowanceNew;\\n }\\n\\n /* We emit a Transfer event */\\n emit Transfer(src, dst, tokens);\\n\\n comptroller.transferVerify(address(this), src, dst, tokens);\\n }\\n\\n /**\\n * @notice Initialize the money market\\n * @param underlying_ The address of the underlying asset\\n * @param comptroller_ The address of the Comptroller\\n * @param interestRateModel_ The address of the interest rate model\\n * @param initialExchangeRateMantissa_ The initial exchange rate, scaled by 1e18\\n * @param name_ ERC-20 name of this token\\n * @param symbol_ ERC-20 symbol of this token\\n * @param decimals_ ERC-20 decimal precision of this token\\n * @param admin_ Address of the administrator of this token\\n * @param accessControlManager_ AccessControlManager contract address\\n * @param riskManagement Addresses of risk & income related contracts\\n * @param reserveFactorMantissa_ Percentage of borrow interest that goes to reserves (from 0 to 1e18)\\n */\\n function _initialize(\\n address underlying_,\\n ComptrollerInterface comptroller_,\\n InterestRateModel interestRateModel_,\\n uint256 initialExchangeRateMantissa_,\\n string memory name_,\\n string memory symbol_,\\n uint8 decimals_,\\n address admin_,\\n address accessControlManager_,\\n RiskManagementInit memory riskManagement,\\n uint256 reserveFactorMantissa_\\n ) internal onlyInitializing {\\n __Ownable2Step_init();\\n __AccessControlled_init_unchained(accessControlManager_);\\n require(accrualBlockNumber == 0 && borrowIndex == 0, \\\"market may only be initialized once\\\");\\n\\n // Set initial exchange rate\\n initialExchangeRateMantissa = initialExchangeRateMantissa_;\\n require(initialExchangeRateMantissa > 0, \\\"initial exchange rate must be greater than zero.\\\");\\n\\n _setComptroller(comptroller_);\\n\\n // Initialize slot(block or second) number and borrow index (slot(block or second) number mocks depend on comptroller being set)\\n accrualBlockNumber = getBlockNumberOrTimestamp();\\n borrowIndex = MANTISSA_ONE;\\n\\n // Set the interest rate model (depends on slot(block or second) number / borrow index)\\n _setInterestRateModelFresh(interestRateModel_);\\n\\n _setReserveFactorFresh(reserveFactorMantissa_);\\n\\n name = name_;\\n symbol = symbol_;\\n decimals = decimals_;\\n _setShortfallContract(riskManagement.shortfall);\\n _setProtocolShareReserve(riskManagement.protocolShareReserve);\\n protocolSeizeShareMantissa = DEFAULT_PROTOCOL_SEIZE_SHARE_MANTISSA;\\n\\n // Set underlying and sanity check it\\n underlying = underlying_;\\n IERC20Upgradeable(underlying).totalSupply();\\n\\n // The counter starts true to prevent changing it from zero to non-zero (i.e. smaller cost/refund)\\n _notEntered = true;\\n _transferOwnership(admin_);\\n }\\n\\n function _setShortfallContract(address shortfall_) internal {\\n ensureNonzeroAddress(shortfall_);\\n address oldShortfall = shortfall;\\n shortfall = shortfall_;\\n emit NewShortfallContract(oldShortfall, shortfall_);\\n }\\n\\n function _setProtocolShareReserve(address payable protocolShareReserve_) internal {\\n ensureNonzeroAddress(protocolShareReserve_);\\n address oldProtocolShareReserve = address(protocolShareReserve);\\n protocolShareReserve = protocolShareReserve_;\\n emit NewProtocolShareReserve(oldProtocolShareReserve, address(protocolShareReserve_));\\n }\\n\\n function _ensureSenderIsDelegateOf(address user) internal view {\\n if (!ComptrollerViewInterface(address(comptroller)).approvedDelegates(user, msg.sender)) {\\n revert DelegateNotApproved();\\n }\\n }\\n\\n /**\\n * @notice Gets the internally tracked cash balance of this market\\n * @dev Returns the internalCash state variable, which is updated on transfers in/out\\n * @return The quantity of underlying tokens tracked internally by this contract\\n */\\n function _getCashPrior() internal view virtual returns (uint256) {\\n return internalCash;\\n }\\n\\n /**\\n * @notice Return the borrow balance of account based on stored data\\n * @param account The address whose balance should be calculated\\n * @return borrowBalance the calculated balance\\n */\\n function _borrowBalanceStored(address account) internal view returns (uint256) {\\n /* Get borrowBalance and borrowIndex */\\n BorrowSnapshot memory borrowSnapshot = accountBorrows[account];\\n\\n /* If borrowBalance = 0 then borrowIndex is likely also 0.\\n * Rather than failing the calculation with a division by 0, we immediately return 0 in this case.\\n */\\n if (borrowSnapshot.principal == 0) {\\n return 0;\\n }\\n\\n /* Calculate new borrow balance using the interest index:\\n * recentBorrowBalance = borrower.borrowBalance * market.borrowIndex / borrower.borrowIndex\\n */\\n uint256 principalTimesIndex = borrowSnapshot.principal * borrowIndex;\\n\\n return principalTimesIndex / borrowSnapshot.interestIndex;\\n }\\n\\n /**\\n * @notice Calculates the exchange rate from the underlying to the VToken\\n * @dev This function does not accrue interest before calculating the exchange rate\\n * @return exchangeRate Calculated exchange rate scaled by 1e18\\n */\\n function _exchangeRateStored() internal view virtual returns (uint256) {\\n uint256 _totalSupply = totalSupply;\\n if (_totalSupply == 0) {\\n /*\\n * If there are no tokens minted:\\n * exchangeRate = initialExchangeRate\\n */\\n return initialExchangeRateMantissa;\\n }\\n /*\\n * Otherwise:\\n * exchangeRate = (totalCash + totalBorrows + badDebt - totalReserves) / totalSupply\\n */\\n uint256 totalCash = _getCashPrior();\\n uint256 cashPlusBorrowsMinusReserves = totalCash + totalBorrows + badDebt - totalReserves;\\n uint256 exchangeRate = (cashPlusBorrowsMinusReserves * EXP_SCALE) / _totalSupply;\\n\\n return exchangeRate;\\n }\\n}\\n\",\"keccak256\":\"0x7267f5337062ad01a5011f7725a86cc2ad0351cca0aaceadc167341f168cdef2\",\"license\":\"BSD-3-Clause\"},\"contracts/VTokenInterfaces.sol\":{\"content\":\"// SPDX-License-Identifier: BSD-3-Clause\\npragma solidity 0.8.25;\\n\\nimport { IERC20Upgradeable } from \\\"@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol\\\";\\nimport { ResilientOracleInterface } from \\\"@venusprotocol/oracle/contracts/interfaces/OracleInterface.sol\\\";\\n\\nimport { ComptrollerInterface } from \\\"./ComptrollerInterface.sol\\\";\\nimport { InterestRateModel } from \\\"./InterestRateModel.sol\\\";\\n\\n/**\\n * @title VTokenStorage\\n * @author Venus\\n * @notice Storage layout used by the `VToken` contract\\n */\\n// solhint-disable-next-line max-states-count\\ncontract VTokenStorage {\\n /**\\n * @notice Container for borrow balance information\\n * @member principal Total balance (with accrued interest), after applying the most recent balance-changing action\\n * @member interestIndex Global borrowIndex as of the most recent balance-changing action\\n */\\n struct BorrowSnapshot {\\n uint256 principal;\\n uint256 interestIndex;\\n }\\n\\n /**\\n * @dev Guard variable for re-entrancy checks\\n */\\n bool internal _notEntered;\\n\\n /**\\n * @notice Underlying asset for this VToken\\n */\\n address public underlying;\\n\\n /**\\n * @notice EIP-20 token name for this token\\n */\\n string public name;\\n\\n /**\\n * @notice EIP-20 token symbol for this token\\n */\\n string public symbol;\\n\\n /**\\n * @notice EIP-20 token decimals for this token\\n */\\n uint8 public decimals;\\n\\n /**\\n * @notice Protocol share Reserve contract address\\n */\\n address payable public protocolShareReserve;\\n\\n /**\\n * @notice Contract which oversees inter-vToken operations\\n */\\n ComptrollerInterface public comptroller;\\n\\n /**\\n * @notice Model which tells what the current interest rate should be\\n */\\n InterestRateModel public interestRateModel;\\n\\n // Initial exchange rate used when minting the first VTokens (used when totalSupply = 0)\\n uint256 internal initialExchangeRateMantissa;\\n\\n /**\\n * @notice Fraction of interest currently set aside for reserves\\n */\\n uint256 public reserveFactorMantissa;\\n\\n /**\\n * @notice Slot(block or second) number that interest was last accrued at\\n */\\n uint256 public accrualBlockNumber;\\n\\n /**\\n * @notice Accumulator of the total earned interest rate since the opening of the market\\n */\\n uint256 public borrowIndex;\\n\\n /**\\n * @notice Total amount of outstanding borrows of the underlying in this market\\n */\\n uint256 public totalBorrows;\\n\\n /**\\n * @notice Total amount of reserves of the underlying held in this market\\n */\\n uint256 public totalReserves;\\n\\n /**\\n * @notice Total number of tokens in circulation\\n */\\n uint256 public totalSupply;\\n\\n /**\\n * @notice Total bad debt of the market\\n */\\n uint256 public badDebt;\\n\\n // Official record of token balances for each account\\n mapping(address => uint256) internal accountTokens;\\n\\n // Approved token transfer amounts on behalf of others\\n mapping(address => mapping(address => uint256)) internal transferAllowances;\\n\\n // Mapping of account addresses to outstanding borrow balances\\n mapping(address => BorrowSnapshot) internal accountBorrows;\\n\\n /**\\n * @notice Share of seized collateral that is added to reserves\\n */\\n uint256 public protocolSeizeShareMantissa;\\n\\n /**\\n * @notice Storage of Shortfall contract address\\n */\\n address public shortfall;\\n\\n /**\\n * @notice delta slot (block or second) after which reserves will be reduced\\n */\\n uint256 public reduceReservesBlockDelta;\\n\\n /**\\n * @notice last slot (block or second) number at which reserves were reduced\\n */\\n uint256 public reduceReservesBlockNumber;\\n\\n /**\\n * @notice Tracked internal cash balance, immune to direct token transfers (donation attacks)\\n * @dev Updated via _doTransferIn(), _doTransferOut(), syncCash(), and badDebtRecovered(). Must be initialized via syncCash() after upgrade.\\n */\\n uint256 public internalCash;\\n\\n /**\\n * @dev This empty reserved space is put in place to allow future versions to add new\\n * variables without shifting down storage in the inheritance chain.\\n * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\\n */\\n uint256[47] private __gap;\\n}\\n\\n/**\\n * @title VTokenInterface\\n * @author Venus\\n * @notice Interface implemented by the `VToken` contract\\n */\\nabstract contract VTokenInterface is VTokenStorage {\\n struct RiskManagementInit {\\n address shortfall;\\n address payable protocolShareReserve;\\n }\\n\\n /*** Market Events ***/\\n\\n /**\\n * @notice Event emitted when interest is accrued\\n */\\n event AccrueInterest(uint256 cashPrior, uint256 interestAccumulated, uint256 borrowIndex, uint256 totalBorrows);\\n\\n /**\\n * @notice Event emitted when tokens are minted\\n */\\n event Mint(address indexed minter, uint256 mintAmount, uint256 mintTokens, uint256 accountBalance);\\n\\n /**\\n * @notice Event emitted when tokens are redeemed\\n */\\n event Redeem(address indexed redeemer, uint256 redeemAmount, uint256 redeemTokens, uint256 accountBalance);\\n\\n /**\\n * @notice Event emitted when underlying is borrowed\\n */\\n event Borrow(address indexed borrower, uint256 borrowAmount, uint256 accountBorrows, uint256 totalBorrows);\\n\\n /**\\n * @notice Event emitted when a borrow is repaid\\n */\\n event RepayBorrow(\\n address indexed payer,\\n address indexed borrower,\\n uint256 repayAmount,\\n uint256 accountBorrows,\\n uint256 totalBorrows\\n );\\n\\n /**\\n * @notice Event emitted when bad debt is accumulated on a market\\n * @param borrower borrower to \\\"forgive\\\"\\n * @param badDebtDelta amount of new bad debt recorded\\n * @param badDebtOld previous bad debt value\\n * @param badDebtNew new bad debt value\\n */\\n event BadDebtIncreased(address indexed borrower, uint256 badDebtDelta, uint256 badDebtOld, uint256 badDebtNew);\\n\\n /**\\n * @notice Event emitted when bad debt is recovered via an auction\\n * @param badDebtOld previous bad debt value\\n * @param badDebtNew new bad debt value\\n */\\n event BadDebtRecovered(uint256 badDebtOld, uint256 badDebtNew);\\n\\n /**\\n * @notice Event emitted when a borrow is liquidated\\n */\\n event LiquidateBorrow(\\n address indexed liquidator,\\n address indexed borrower,\\n uint256 repayAmount,\\n address indexed vTokenCollateral,\\n uint256 seizeTokens\\n );\\n\\n /*** Admin Events ***/\\n\\n /**\\n * @notice Event emitted when comptroller is changed\\n */\\n event NewComptroller(ComptrollerInterface indexed oldComptroller, ComptrollerInterface indexed newComptroller);\\n\\n /**\\n * @notice Event emitted when shortfall contract address is changed\\n */\\n event NewShortfallContract(address indexed oldShortfall, address indexed newShortfall);\\n\\n /**\\n * @notice Event emitted when protocol share reserve contract address is changed\\n */\\n event NewProtocolShareReserve(address indexed oldProtocolShareReserve, address indexed newProtocolShareReserve);\\n\\n /**\\n * @notice Event emitted when interestRateModel is changed\\n */\\n event NewMarketInterestRateModel(\\n InterestRateModel indexed oldInterestRateModel,\\n InterestRateModel indexed newInterestRateModel\\n );\\n\\n /**\\n * @notice Event emitted when protocol seize share is changed\\n */\\n event NewProtocolSeizeShare(uint256 oldProtocolSeizeShareMantissa, uint256 newProtocolSeizeShareMantissa);\\n\\n /**\\n * @notice Event emitted when the reserve factor is changed\\n */\\n event NewReserveFactor(uint256 oldReserveFactorMantissa, uint256 newReserveFactorMantissa);\\n\\n /**\\n * @notice Event emitted when the reserves are added\\n */\\n event ReservesAdded(address indexed benefactor, uint256 addAmount, uint256 newTotalReserves);\\n\\n /**\\n * @notice Event emitted when the spread reserves are reduced\\n */\\n event SpreadReservesReduced(address indexed protocolShareReserve, uint256 reduceAmount, uint256 newTotalReserves);\\n\\n /**\\n * @notice EIP20 Transfer event\\n */\\n event Transfer(address indexed from, address indexed to, uint256 amount);\\n\\n /**\\n * @notice EIP20 Approval event\\n */\\n event Approval(address indexed owner, address indexed spender, uint256 amount);\\n\\n /**\\n * @notice Event emitted when healing the borrow\\n */\\n event HealBorrow(address indexed payer, address indexed borrower, uint256 repayAmount);\\n\\n /**\\n * @notice Event emitted when tokens are swept\\n */\\n event SweepToken(address indexed token);\\n\\n /**\\n * @notice Event emitted when reduce reserves slot (block or second) delta is changed\\n */\\n event NewReduceReservesBlockDelta(\\n uint256 oldReduceReservesBlockOrTimestampDelta,\\n uint256 newReduceReservesBlockOrTimestampDelta\\n );\\n\\n /**\\n * @notice Event emitted when liquidation reserves are reduced\\n */\\n event ProtocolSeize(address indexed from, address indexed to, uint256 amount);\\n\\n /**\\n * @notice Event emitted when internalCash is synced with actual token balance\\n */\\n event CashSynced(uint256 oldInternalCash, uint256 newInternalCash);\\n\\n /*** User Interface ***/\\n\\n function mint(uint256 mintAmount) external virtual returns (uint256);\\n\\n function mintBehalf(address minter, uint256 mintAllowed) external virtual returns (uint256);\\n\\n function redeem(uint256 redeemTokens) external virtual returns (uint256);\\n\\n function redeemBehalf(address redeemer, uint256 redeemTokens) external virtual returns (uint256);\\n\\n function redeemUnderlying(uint256 redeemAmount) external virtual returns (uint256);\\n\\n function redeemUnderlyingBehalf(address redeemer, uint256 redeemAmount) external virtual returns (uint256);\\n\\n function borrow(uint256 borrowAmount) external virtual returns (uint256);\\n\\n function borrowBehalf(address borrwwer, uint256 borrowAmount) external virtual returns (uint256);\\n\\n function repayBorrow(uint256 repayAmount) external virtual returns (uint256);\\n\\n function repayBorrowBehalf(address borrower, uint256 repayAmount) external virtual returns (uint256);\\n\\n function liquidateBorrow(\\n address borrower,\\n uint256 repayAmount,\\n VTokenInterface vTokenCollateral\\n ) external virtual returns (uint256);\\n\\n function healBorrow(address payer, address borrower, uint256 repayAmount) external virtual;\\n\\n function forceLiquidateBorrow(\\n address liquidator,\\n address borrower,\\n uint256 repayAmount,\\n VTokenInterface vTokenCollateral,\\n bool skipCloseFactorCheck\\n ) external virtual;\\n\\n function seize(address liquidator, address borrower, uint256 seizeTokens) external virtual;\\n\\n function transfer(address dst, uint256 amount) external virtual returns (bool);\\n\\n function transferFrom(address src, address dst, uint256 amount) external virtual returns (bool);\\n\\n function accrueInterest() external virtual returns (uint256);\\n\\n function sweepToken(IERC20Upgradeable token) external virtual;\\n\\n /*** Admin Functions ***/\\n\\n function setReserveFactor(uint256 newReserveFactorMantissa) external virtual;\\n\\n function reduceReserves(uint256 reduceAmount) external virtual;\\n\\n function exchangeRateCurrent() external virtual returns (uint256);\\n\\n function borrowBalanceCurrent(address account) external virtual returns (uint256);\\n\\n function setInterestRateModel(InterestRateModel newInterestRateModel) external virtual;\\n\\n function addReserves(uint256 addAmount) external virtual;\\n\\n function syncCash() external virtual;\\n\\n function totalBorrowsCurrent() external virtual returns (uint256);\\n\\n function balanceOfUnderlying(address owner) external virtual returns (uint256);\\n\\n function approve(address spender, uint256 amount) external virtual returns (bool);\\n\\n function increaseAllowance(address spender, uint256 addedValue) external virtual returns (bool);\\n\\n function decreaseAllowance(address spender, uint256 subtractedValue) external virtual returns (bool);\\n\\n function allowance(address owner, address spender) external view virtual returns (uint256);\\n\\n function balanceOf(address owner) external view virtual returns (uint256);\\n\\n function getAccountSnapshot(address account) external view virtual returns (uint256, uint256, uint256, uint256);\\n\\n function borrowRatePerBlock() external view virtual returns (uint256);\\n\\n function supplyRatePerBlock() external view virtual returns (uint256);\\n\\n function borrowBalanceStored(address account) external view virtual returns (uint256);\\n\\n function exchangeRateStored() external view virtual returns (uint256);\\n\\n function getCash() external view virtual returns (uint256);\\n\\n /**\\n * @notice Indicator that this is a VToken contract (for inspection)\\n * @return Always true\\n */\\n function isVToken() external pure virtual returns (bool) {\\n return true;\\n }\\n}\\n\",\"keccak256\":\"0x155684e9cb12cdd8be63d2314c9452b68ee44ff98a00e0d65cd1fd7d0bdd4391\",\"license\":\"BSD-3-Clause\"},\"contracts/lib/constants.sol\":{\"content\":\"// SPDX-License-Identifier: BSD-3-Clause\\npragma solidity ^0.8.25;\\n\\n/// @dev The approximate number of seconds per year\\nuint256 constant SECONDS_PER_YEAR = 31_536_000;\\n\\n/// @dev Base unit for computations, usually used in scaling (multiplications, divisions)\\nuint256 constant EXP_SCALE = 1e18;\\n\\n/// @dev A unit (literal one) in EXP_SCALE, usually used in additions/subtractions\\nuint256 constant MANTISSA_ONE = EXP_SCALE;\\n\",\"keccak256\":\"0x54ab3a6f3bc87569ed12370f3470a1ec84cea9796d4d0ccf3d07dd4280c044aa\",\"license\":\"BSD-3-Clause\"},\"contracts/lib/validators.sol\":{\"content\":\"// SPDX-License-Identifier: BSD-3-Clause\\npragma solidity 0.8.25;\\n\\n/// @notice Thrown if the supplied address is a zero address where it is not allowed\\nerror ZeroAddressNotAllowed();\\n\\n/// @notice Checks if the provided address is nonzero, reverts otherwise\\n/// @param address_ Address to check\\n/// @custom:error ZeroAddressNotAllowed is thrown if the provided address is a zero address\\nfunction ensureNonzeroAddress(address address_) pure {\\n if (address_ == address(0)) {\\n revert ZeroAddressNotAllowed();\\n }\\n}\\n\",\"keccak256\":\"0x93bdd5cfb100f0f9a1d446857e23c3633df6ab12c266333c978428edd96b1367\",\"license\":\"BSD-3-Clause\"}},\"version\":1}", + "bytecode": "0x61010060405234801561001157600080fd5b5060405161523f38038061523f83398101604081905261003091610202565b82828115801561003e575080155b1561005c576040516302723dfb60e21b815260040160405180910390fd5b81801561006857508015155b156100865760405163ae0fcab360e01b815260040160405180910390fd5b81151560a05281610097578061009d565b6301e133805b608052816100b45761013f60201b612205176100bf565b61014360201b612209175b6001600160401b031660c0525050670de0b6b3a764000081111561012a5760405162461bcd60e51b815260206004820152601f60248201527f4d617820626f72726f772072617465206d757374206265203c3d20316531380060448201526064015b60405180910390fd5b60e0819052610137610147565b50505061023e565b4390565b4290565b600054610100900460ff16156101af5760405162461bcd60e51b815260206004820152602760248201527f496e697469616c697a61626c653a20636f6e747261637420697320696e697469604482015266616c697a696e6760c81b6064820152608401610121565b60005460ff90811614610200576000805460ff191660ff9081179091556040519081527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b565b60008060006060848603121561021757600080fd5b8351801515811461022757600080fd5b602085015160409095015190969495509392505050565b60805160a05160c05160e051614fc86102776000396000611a6701526000611fc001526000610896015260006106750152614fc86000f3fe608060405234801561001057600080fd5b50600436106104495760003560e01c80637821a51411610241578063b4a0bdf31161013b578063dd62ed3e116100c3578063ef60450c11610087578063ef60450c14610959578063f2fde38b1461096c578063f3fdb15a1461097f578063f5e3c46214610992578063f8f9da28146109a557600080fd5b8063dd62ed3e146108dc578063df3a516e14610915578063e1d146fb14610928578063e30c397814610930578063e9a44fd91461094157600080fd5b8063c5ebeaec1161010a578063c5ebeaec1461087e578063c7ad089514610891578063cf4b3b53146108b8578063d1109c2f146108c0578063db006a75146108c957600080fd5b8063b4a0bdf314610829578063bbcac5571461083a578063bd6d894d14610843578063c37f68e21461084b57600080fd5b806395d89b41116101c9578063a9059cbb1161018d578063a9059cbb146107e9578063aa5af0fd146107fc578063ae96f14114610805578063ae9d70b01461080e578063b2a02ff11461081657600080fd5b806395d89b41146107a057806395dd9193146107a8578063a0712d68146107bb578063a457c2d7146107ce578063a6afed95146107e157600080fd5b80638a42c319116102105780638a42c3191461074d5780638bbdb6db146107605780638bcd4016146107735780638da5cb5b146107865780638f840ddd1461079757600080fd5b80637821a5141461070c57806379ba50971461071f578063852a12e314610727578063856e5bb31461073a57600080fd5b80632608f818116103525780635fe3b567116102da5780636f307dc31161029e5780636f307dc3146106a857806370a08231146106c0578063715018a6146106e957806373acee98146106f1578063757212f0146106f957600080fd5b80635fe3b567146106545780636752e702146106675780636857249c1461067057806369ab3250146106975780636c540baf1461069f57600080fd5b80633b1d21a2116103215780633b1d21a2146106165780633d9ea3a11461061e57806341f641ee1461062557806344fe6ffe1461063857806347bd37181461064b57600080fd5b80632608f818146105be578063313ce567146105d157806339509351146105f05780633af9e6691461060357600080fd5b8063182df0f5116103d5578063210bc052116103a4578063210bc0521461056957806322abdbf51461057c57806323323e031461058557806323b872dd146105985780632464176b146105ab57600080fd5b8063182df0f51461051057806319b1faef146105185780631be19560146105435780631c4469831461055657600080fd5b80630e7527021161041c5780630e752702146104b7578063107568df146104d8578063173b9904146104eb57806317bfdfbc146104f457806318160ddd1461050757600080fd5b806306fdde031461044e57806307e279591461046c578063095ea7b3146104815780630e32cb86146104a4575b600080fd5b6104566109ad565b6040516104639190614803565b60405180910390f35b61047f61047a366004614816565b610a3b565b005b61049461048f366004614854565b610aa2565b6040519015158152602001610463565b61047f6104b2366004614880565b610b13565b6104ca6104c5366004614816565b610b27565b604051908152602001610463565b61047f6104e6366004614880565b610b81565b6104ca60d05481565b6104ca610502366004614880565b610b92565b6104ca60d55481565b6104ca610be7565b60db5461052b906001600160a01b031681565b6040516001600160a01b039091168152602001610463565b61047f610551366004614880565b610bf6565b61047f610564366004614816565b610db1565b6104ca610577366004614854565b610e2c565b6104ca60de5481565b6104ca610593366004614854565b610e90565b6104946105a636600461489d565b610edc565b61047f6105b9366004614816565b610f2e565b6104ca6105cc366004614854565b610fd0565b60cc546105de9060ff1681565b60405160ff9091168152602001610463565b6104946105fe366004614854565b61102b565b6104ca610611366004614880565b6110d3565b6104ca611119565b6001610494565b61047f610633366004614880565b611124565b61047f61064636600461489d565b611135565b6104ca60d35481565b60cd5461052b906001600160a01b031681565b6104ca60da5481565b6104ca7f000000000000000000000000000000000000000000000000000000000000000081565b6104ca600081565b6104ca60d15481565b60c95461052b9061010090046001600160a01b031681565b6104ca6106ce366004614880565b6001600160a01b0316600090815260d7602052604090205490565b61047f6113d8565b6104ca6113ec565b61047f610707366004614816565b611438565b61047f61071a366004614816565b61155f565b61047f6115ae565b6104ca610735366004614816565b611625565b6104ca610748366004614854565b61167f565b61047f61075b3660046149f5565b6116a7565b61047f61076e366004614af6565b6117d7565b61047f610781366004614880565b611816565b6033546001600160a01b031661052b565b6104ca60d45481565b610456611866565b6104ca6107b6366004614880565b611873565b6104ca6107c9366004614816565b61187e565b6104946107dc366004614854565b6118c1565b6104ca61199e565b6104946107f7366004614854565b611bf4565b6104ca60d25481565b6104ca60dd5481565b6104ca611c45565b61047f61082436600461489d565b611ce9565b6097546001600160a01b031661052b565b6104ca60d65481565b6104ca611d33565b61085e610859366004614880565b611d85565b604080519485526020850193909352918301526060820152608001610463565b6104ca61088c366004614816565b611dc6565b6104947f000000000000000000000000000000000000000000000000000000000000000081565b61047f611e09565b6104ca60dc5481565b6104ca6108d7366004614816565b611f26565b6104ca6108ea366004614b5e565b6001600160a01b03918216600090815260d86020908152604080832093909416825291909152205490565b6104ca610923366004614854565b611f6b565b6104ca611fb9565b6065546001600160a01b031661052b565b60cc5461052b9061010090046001600160a01b031681565b61047f610967366004614816565b611fe7565b61047f61097a366004614880565b61211e565b60ce5461052b906001600160a01b031681565b6104ca6109a0366004614b97565b61218f565b6104ca6121a9565b60ca80546109ba90614bd9565b80601f01602080910402602001604051908101604052809291908181526020018280546109e690614bd9565b8015610a335780601f10610a0857610100808354040283529160200191610a33565b820191906000526020600020905b815481529060010190602001808311610a1657829003601f168201915b505050505081565b60c95460ff16610a665760405162461bcd60e51b8152600401610a5d90614c13565b60405180910390fd5b60c9805460ff19169055610a7861199e565b50610a81611fb9565b60dd5414610a9257610a928161220d565b5060c9805460ff19166001179055565b6000610aad8361238a565b33600081815260d8602090815260408083206001600160a01b038816808552908352928190208690555185815283917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a360019150505b92915050565b610b1b6123b1565b610b248161240b565b50565b60c95460009060ff16610b4c5760405162461bcd60e51b8152600401610a5d90614c13565b60c9805460ff19169055610b5e61199e565b50610b6a3333846124d1565b506000905060c9805460ff19166001179055919050565b610b896123b1565b610b24816126ac565b60c95460009060ff16610bb75760405162461bcd60e51b8152600401610a5d90614c13565b60c9805460ff19169055610bc961199e565b50610bd38261270f565b905060c9805460ff19166001179055919050565b6000610bf161277f565b905090565b6033546001600160a01b03163314610c685760405162461bcd60e51b815260206004820152602f60248201527f56546f6b656e3a3a7377656570546f6b656e3a206f6e6c792061646d696e206360448201526e616e20737765657020746f6b656e7360881b6064820152608401610a5d565b60c9546001600160a01b03610100909104811690821603610ce65760405162461bcd60e51b815260206004820152603260248201527f56546f6b656e3a3a7377656570546f6b656e3a2063616e206e6f74207377656560448201527138103ab73232b9363cb4b733903a37b5b2b760711b6064820152608401610a5d565b6040516370a0823160e01b81523060048201526000906001600160a01b038316906370a0823190602401602060405180830381865afa158015610d2d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d519190614c37565b9050610d79610d686033546001600160a01b031690565b6001600160a01b03841690836127f5565b6040516001600160a01b038316907f35ce4c546a473796a8e70ec2d4af4f2031afe357afa7057b6ea7fa340730e1b290600090a25050565b60c95460ff16610dd35760405162461bcd60e51b8152600401610a5d90614c13565b60c9805460ff1916905560408051808201909152601981527f73657452657365727665466163746f722875696e7432353629000000000000006020820152610e1a9061285d565b610e2261199e565b50610a92816128fb565b60c95460009060ff16610e515760405162461bcd60e51b8152600401610a5d90614c13565b60c9805460ff19169055610e648361298b565b610e6c61199e565b50610e7a8333846000612a1c565b50600060c9805460ff1916600117905592915050565b60c95460009060ff16610eb55760405162461bcd60e51b8152600401610a5d90614c13565b60c9805460ff19169055610ec88361238a565b610ed061199e565b50610e7a338484612d56565b60c95460009060ff16610f015760405162461bcd60e51b8152600401610a5d90614c13565b60c9805460ff19169055610f1733858585612f5b565b50600160c9805460ff191660011790559392505050565b610f4f604051806060016040528060248152602001614f4f6024913961285d565b60008111610f8f5760405162461bcd60e51b815260206004820152600d60248201526c125b9d985b1a5908125b9c1d5d609a1b6044820152606401610a5d565b60dc5460408051918252602082018390527fc2ac513cdb57f91eb2bef4db918c285829524f549682b99717c6cb06cc011183910160405180910390a160dc55565b60c95460009060ff16610ff55760405162461bcd60e51b8152600401610a5d90614c13565b60c9805460ff1916905561100761199e565b506110133384846124d1565b506000905060c9805460ff1916600117905592915050565b60006110368361238a565b33600081815260d8602090815260408083206001600160a01b03881684529091529020546110648482614c66565b6001600160a01b03838116600081815260d860209081526040808320948b16808452948252918290208590559051848152939450919290917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591015b60405180910390a3506001949350505050565b60008060405180602001604052806110e9611d33565b90526001600160a01b038416600090815260d76020526040902054909150611112908290613185565b9392505050565b6000610bf160de5490565b61112c6123b1565b610b248161319d565b60c95460ff166111575760405162461bcd60e51b8152600401610a5d90614c13565b60c9805460ff1916905580156111cd5760cd5460405163eade3eed60e01b81523060048201526001600160a01b0384811660248301529091169063eade3eed90604401600060405180830381600087803b1580156111b457600080fd5b505af11580156111c8573d6000803e3d6000fd5b505050505b60cd546001600160a01b031633146111f857604051632c40292560e01b815260040160405180910390fd5b60006112038361270f565b60d354909150600083156112835761121b86856131f8565b90506112278183614c79565b91506001600160a01b038086169087167f1a2a22cb034d26d1854bdc6666a5b91fe25efbbb5dcad3b0355478d6f5c362a1836112638188614c79565b604080519283526020830191909152810186905260600160405180910390a35b600061128f8285614c79565b905080156113575760d65460006112a68383614c66565b90506112b28386614c79565b60d682905560408051858152600060208201529081018290529095506001600160a01b0389169030907f1a2a22cb034d26d1854bdc6666a5b91fe25efbbb5dcad3b0355478d6f5c362a19060600160405180910390a360408051848152602081018490529081018290526001600160a01b038916907f90125ffdb441e57c4f6bf69789206424859f206bea5727f2d81ad2470826ef6a9060600160405180910390a250505b6001600160a01b03808716600081815260d9602052604080822091825560d25460019092019190915560d38690555190918916907f9fe0294717a8efbc6ace1c151b73a4c89982339b2228a27d1ca21394e348986f906113ba9089815260200190565b60405180910390a3505060c9805460ff191660011790555050505050565b6113e06123b1565b6113ea6000613322565b565b60c95460009060ff166114115760405162461bcd60e51b8152600401610a5d90614c13565b60c9805460ff1916905561142361199e565b505060d35460c9805460ff1916600117905590565b6114766040518060400160405280601e81526020017f73657450726f746f636f6c5365697a6553686172652875696e7432353629000081525061285d565b60cd5460408051634ada90af60e01b815290516000926001600160a01b031691634ada90af9160048083019260209291908290030181865afa1580156114c0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114e49190614c37565b9050806114f9670de0b6b3a764000084614c66565b11156115185760405163034dd2c160e11b815260040160405180910390fd5b60da80549083905560408051828152602081018590527ff5815f353a60e815cce7553e4f60c533a59d26b1b5504ea4b6db8d60da3e4da291015b60405180910390a1505050565b60c95460ff166115815760405162461bcd60e51b8152600401610a5d90614c13565b60c9805460ff1916905561159361199e565b5061159d8161333b565b505060c9805460ff19166001179055565b60655433906001600160a01b0316811461161c5760405162461bcd60e51b815260206004820152602960248201527f4f776e61626c6532537465703a2063616c6c6572206973206e6f7420746865206044820152683732bb9037bbb732b960b91b6064820152608401610a5d565b610b2481613322565b60c95460009060ff1661164a5760405162461bcd60e51b8152600401610a5d90614c13565b60c9805460ff1916905561165c61199e565b5061166a3333600085612a1c565b50600060c9805460ff19166001179055919050565b600061168a8361298b565b61169261199e565b5061169e8333846133d0565b50600092915050565b600054610100900460ff16158080156116c75750600054600160ff909116105b806116e15750303b1580156116e1575060005460ff166001145b6117445760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b6064820152608401610a5d565b6000805460ff191660011790558015611767576000805461ff0019166101001790555b6117708561238a565b6117838c8c8c8c8c8c8c8c8c8c8c6135af565b80156117c9576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b505050505050505050505050565b60cd546001600160a01b0316331461180257604051635c85a5e760e01b815260040160405180910390fd5b61180f85858585856137dc565b5050505050565b6118546040518060400160405280601d81526020017f736574496e746572657374526174654d6f64656c28616464726573732900000081525061285d565b61185c61199e565b50610b24816138bd565b60cb80546109ba90614bd9565b6000610b0d8261270f565b60c95460009060ff166118a35760405162461bcd60e51b8152600401610a5d90614c13565b60c9805460ff191690556118b561199e565b5061166a333384612d56565b60006118cc8361238a565b33600081815260d8602090815260408083206001600160a01b0388168452909152902054838110156119405760405162461bcd60e51b815260206004820152601e60248201527f64656372656173656420616c6c6f77616e63652062656c6f77207a65726f00006044820152606401610a5d565b6001600160a01b03828116600081815260d860209081526040808320948a1680845294825291829020948890039485905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591016110c0565b6000806119a9611fb9565b60d1549091508181036119bf5760009250505090565b60006119ca60de5490565b60d35460d45460d25460ce5460d6546040516301cee29d60e21b815260048101879052602481018690526044810185905260648101919091529495509293919290916000916001600160a01b03169063073b8a7490608401602060405180830381865afa158015611a3f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a639190614c37565b90507f0000000000000000000000000000000000000000000000000000000000000000811115611ad55760405162461bcd60e51b815260206004820152601c60248201527f626f72726f772072617465206973206162737572646c792068696768000000006044820152606401610a5d565b6000611ae18789614c79565b90506000611afd604051806020016040528085815250836139ff565b90506000611b0b8288613185565b90506000611b198883614c66565b90506000611b38604051806020016040528060d054815250848a613a30565b90506000611b4785898a613a30565b60d18e905560d281905560d384905560d483905560dc5460dd5491925090611b6f908f614c79565b10611b985760dd8d9055818b1015611b8f57611b8a8b61220d565b611b98565b611b988261220d565b604080518c815260208101869052908101829052606081018490527f4dec04e750ca11537cabcd8a9eab06494de08da3735bc8871cd41250e190bc049060800160405180910390a160009d505050505050505050505050505090565b60c95460009060ff16611c195760405162461bcd60e51b8152600401610a5d90614c13565b60c9805460ff19169055611c2f33808585612f5b565b50600160c9805460ff1916600117905592915050565b60ce546000906001600160a01b0316630cde8d1c611c6260de5490565b60d35460d45460d05460d6546040516001600160e01b031960e088901b1681526004810195909552602485019390935260448401919091526064830152608482015260a4015b602060405180830381865afa158015611cc5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bf19190614c37565b60c95460ff16611d0b5760405162461bcd60e51b8152600401610a5d90614c13565b60c9805460ff19169055611d2133848484613a51565b505060c9805460ff1916600117905550565b60c95460009060ff16611d585760405162461bcd60e51b8152600401610a5d90614c13565b60c9805460ff19169055611d6a61199e565b50611d7361277f565b905060c9805460ff1916600117905590565b6001600160a01b038116600090815260d760205260408120548190819081908190611daf8761270f565b611db761277f565b93509350935093509193509193565b60c95460009060ff16611deb5760405162461bcd60e51b8152600401610a5d90614c13565b60c9805460ff19169055611dfd61199e565b5061166a3333846133d0565b60c95460ff16611e2b5760405162461bcd60e51b8152600401610a5d90614c13565b60c9805460ff1916905560408051808201909152600a81526973796e6343617368282960b01b6020820152611e5f9061285d565b60de5460c9546040516370a0823160e01b815230600482015260009161010090046001600160a01b0316906370a0823190602401602060405180830381865afa158015611eb0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ed49190614c37565b60de81905560408051848152602081018390529192507f56a36ec0a133fa42db4951702e0f2f268ded6733b1ace7e7c90ec2dc45c58d30910160405180910390a1505060c9805460ff19166001179055565b60c95460009060ff16611f4b5760405162461bcd60e51b8152600401610a5d90614c13565b60c9805460ff19169055611f5d61199e565b5061166a3333846000612a1c565b60c95460009060ff16611f905760405162461bcd60e51b8152600401610a5d90614c13565b60c9805460ff19169055611fa38361298b565b611fab61199e565b50610e7a8333600085612a1c565b6000610bf17f000000000000000000000000000000000000000000000000000000000000000063ffffffff16565b60db546001600160a01b031633146120555760405162461bcd60e51b815260206004820152602b60248201527f6f6e6c792073686f727466616c6c20636f6e74726163742063616e207570646160448201526a1d1948189859081919589d60aa1b6064820152608401610a5d565b60d6548111156120b95760405162461bcd60e51b815260206004820152602960248201527f6d6f7265207468616e206261642064656274207265636f76657265642066726f604482015268369030bab1ba34b7b760b91b6064820152608401610a5d565b60d65460006120c88383614c79565b90508060d6819055508260de60008282546120e39190614c66565b909155505060408051838152602081018390527f9e19ec7d2b8f8a94df8cc0072453ace318d221e3cbb2731d0eaa0baac856520f9101611552565b6121266123b1565b606580546001600160a01b0383166001600160a01b031990911681179091556121576033546001600160a01b031690565b6001600160a01b03167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e2270060405160405180910390a350565b600061219f3385858560006137dc565b5060009392505050565b60ce546000906001600160a01b031663073b8a746121c660de5490565b60d35460d45460d6546040516001600160e01b031960e087901b1681526004810194909452602484019290925260448301526064820152608401611ca8565b4390565b4290565b806000036122185750565b6000612222611fb9565b60d1541461224357604051630dff50cb60e41b815260040160405180910390fd5b8161224d60de5490565b101561226c57604051633345e99960e01b815260040160405180910390fd5b60d45482111561228f576040516378d2980560e11b815260040160405180910390fd5b8160d45461229d9190614c79565b60d481905560cc549091506122c09061010090046001600160a01b031683613dfb565b60cc5460cd5460c9546040516305bebb3b60e21b81526001600160a01b03610100948590048116946316faecec94612308949083169391900490911690600090600401614c8c565b600060405180830381600087803b15801561232257600080fd5b505af1158015612336573d6000803e3d6000fd5b505060cc5460408051868152602081018690526101009092046001600160a01b031693507f9cc63bb4ef37ad6a5f5f657dfaf94865531d4234acbc431cc8ac035468f6272092500160405180910390a25050565b6001600160a01b038116610b24576040516342bcdf7f60e11b815260040160405180910390fd5b6033546001600160a01b031633146113ea5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610a5d565b6001600160a01b03811661246f5760405162461bcd60e51b815260206004820152602560248201527f696e76616c696420616365737320636f6e74726f6c206d616e61676572206164604482015264647265737360d81b6064820152608401610a5d565b609780546001600160a01b038381166001600160a01b031983168117909355604080519190921680825260208201939093527f66fd58e82f7b31a2a5c30e0888f3093efe4e111b00cd2b0c31fe014601293aa091015b60405180910390a15050565b60cd5460405163eade3eed60e01b81523060048201526001600160a01b038481166024830152600092169063eade3eed90604401600060405180830381600087803b15801561251f57600080fd5b505af1158015612533573d6000803e3d6000fd5b5050505061253f611fb9565b60d154146125605760405163c9021e2f60e01b815260040160405180910390fd5b600061256b8461270f565b905060008184101561257d578361257f565b815b9050600061258d87836131f8565b9050600061259b8285614c79565b905060008260d3546125ad9190614c79565b6001600160a01b03898116600081815260d9602090815260409182902087815560d25460019091015560d3859055815188815290810187905290810184905292935091908b16907f1a2a22cb034d26d1854bdc6666a5b91fe25efbbb5dcad3b0355478d6f5c362a19060600160405180910390a360cd5460d254604051631ededc9160e01b81523060048201526001600160a01b038c811660248301528b81166044830152606482018790526084820192909252911690631ededc919060a401600060405180830381600087803b15801561268757600080fd5b505af115801561269b573d6000803e3d6000fd5b50949b9a5050505050505050505050565b6126b58161238a565b60cc80546001600160a01b03838116610100818102610100600160a81b031985161790945560405193909204169182907fafec95c8612496c3ecf5dddc71e393528fe29bd145fbaf9c6b496d78d7e2d79b90600090a35050565b6001600160a01b038116600090815260d96020908152604080832081518083019092528054808352600190910154928201929092529082036127545750600092915050565b60d254815160009161276591614cd0565b90508160200151816127779190614ce7565b949350505050565b60d55460009080820361279457505060cf5490565b600061279f60de5490565b9050600060d45460d65460d354846127b79190614c66565b6127c19190614c66565b6127cb9190614c79565b90506000836127e2670de0b6b3a764000084614cd0565b6127ec9190614ce7565b95945050505050565b6040516001600160a01b03831660248201526044810182905261285890849063a9059cbb60e01b906064015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152613e41565b505050565b6097546040516318c5e8ab60e01b81526000916001600160a01b0316906318c5e8ab906128909033908690600401614d09565b602060405180830381865afa1580156128ad573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128d19190614d2d565b9050806128f757333083604051634a3fa29360e01b8152600401610a5d93929190614d4a565b5050565b612903611fb9565b60d1541461292457604051637dfca6b760e11b815260040160405180910390fd5b670de0b6b3a764000081111561294d5760405163717220f360e11b815260040160405180910390fd5b60d080549082905560408051828152602081018490527faaa68312e2ea9d50e16af5068410ab56e1a1fd06037b1a35664812c30f82146091016124c5565b60cd54604051630217306760e31b81526001600160a01b038381166004830152336024830152909116906310b9833890604401602060405180830381865afa1580156129db573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129ff9190614d2d565b610b2457604051630cf0b6f560e01b815260040160405180910390fd5b811580612a27575080155b612a905760405162461bcd60e51b815260206004820152603460248201527f6f6e65206f662072656465656d546f6b656e73496e206f722072656465656d416044820152736d6f756e74496e206d757374206265207a65726f60601b6064820152608401610a5d565b612a98611fb9565b60d15414612ab9576040516397b5cfcd60e01b815260040160405180910390fd5b60006040518060200160405280612ace61277f565b905290506000808415612ae357849150612b21565b612aed8484613f16565b91506000612afb8385613f34565b90508015801590612b0c5750848114155b15612b1f5782612b1b81614d76565b9350505b505b612b2b8383613185565b905080600003612b745760405162461bcd60e51b815260206004820152601460248201527372656465656d416d6f756e74206973207a65726f60601b6044820152606401610a5d565b60cd54604051634732387560e11b81526001600160a01b0390911690638e6470ea90612ba89030908b908790600401614d8f565b600060405180830381600087803b158015612bc257600080fd5b505af1158015612bd6573d6000803e3d6000fd5b505050508060d454612be760de5490565b612bf19190614c79565b1015612c10576040516391240a1b60e01b815260040160405180910390fd5b8160d554612c1e9190614c79565b60d5556001600160a01b038716600090815260d76020526040812054612c45908490614c79565b6001600160a01b038916600090815260d7602052604090208190559050612c6c8783613dfb565b60405183815230906001600160a01b038a1690600080516020614f738339815191529060200160405180910390a360408051838152602081018590529081018290526001600160a01b038916907fbd5034ffbd47e4e72a94baa2cdb74c6fad73cb3bcdc13036b72ec8306f5a76469060600160405180910390a260cd546040516351dff98960e01b81523060048201526001600160a01b038a811660248301526044820185905260648201869052909116906351dff989906084015b600060405180830381600087803b158015612d4257600080fd5b505af11580156117c9573d6000803e3d6000fd5b60cd5460405163c0891ba960e01b81526001600160a01b039091169063c0891ba990612d8a90309086908690600401614d8f565b600060405180830381600087803b158015612da457600080fd5b505af1158015612db8573d6000803e3d6000fd5b50505050612dc4611fb9565b60d15414612de5576040516338d8859760e01b815260040160405180910390fd5b60006040518060200160405280612dfa61277f565b905290506000612e0a85846131f8565b90506000612e188284613f16565b90508060d554612e289190614c66565b60d5556001600160a01b038516600090815260d76020526040812054612e4f908390614c66565b6001600160a01b038716600081815260d760209081526040918290208490558151878152908101869052908101839052919250907fb4c03061fb5b7fed76389d5af8f2e0ddb09f8c70d1333abbb62582835e10accb9060600160405180910390a26040518281526001600160a01b03871690600090600080516020614f738339815191529060200160405180910390a360cd546040516341c728b960e01b81523060048201526001600160a01b0388811660248301526044820186905260648201859052909116906341c728b990608401600060405180830381600087803b158015612f3a57600080fd5b505af1158015612f4e573d6000803e3d6000fd5b5050505050505050505050565b60cd54604051636d0be88d60e01b81523060048201526001600160a01b03858116602483015284811660448301526064820184905290911690636d0be88d90608401600060405180830381600087803b158015612fb757600080fd5b505af1158015612fcb573d6000803e3d6000fd5b50505050816001600160a01b0316836001600160a01b03160361300157604051638cd22d1960e01b815260040160405180910390fd5b6000836001600160a01b0316856001600160a01b031603613025575060001961304d565b506001600160a01b03808416600090815260d860209081526040808320938816835292905220545b60006130598383614c79565b6001600160a01b038616600090815260d7602052604081205491925090613081908590614c79565b6001600160a01b038616600090815260d76020526040812054919250906130a9908690614c66565b6001600160a01b03808916600090815260d7602052604080822086905591891681522081905590506000198414613103576001600160a01b03808816600090815260d860209081526040808320938c168352929052208390555b856001600160a01b0316876001600160a01b0316600080516020614f738339815191528760405161313691815260200190565b60405180910390a360cd5460405163352b4a3f60e11b81523060048201526001600160a01b03898116602483015288811660448301526064820188905290911690636a56947e90608401612d28565b60008061319284846139ff565b905061277781613f57565b6131a68161238a565b60db80546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f6dbf1ff28f860de5edafa4c6505e37c0aba213288cc4166c5352b6d3776c79ef90600090a35050565b60c9546040516370a0823160e01b815230600482015260009161010090046001600160a01b031690829082906370a0823190602401602060405180830381865afa15801561324a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061326e9190614c37565b90506132856001600160a01b038316863087613f6f565b6040516370a0823160e01b81523060048201526000906001600160a01b038416906370a0823190602401602060405180830381865afa1580156132cc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132f09190614c37565b905060006132fe8383614c79565b90508060de60008282546133129190614c66565b9091555090979650505050505050565b606580546001600160a01b0319169055610b2481613f96565b6000806000613348611fb9565b60d1541461336c576040516338acf79960e01b815260048101829052602401610a5d565b61337633856131f8565b90508060d4546133869190614c66565b60d4819055604080518381526020810183905291935033917fa91e67c5ea634cd43a12c5a482724b03de01e85ca68702a53d0c2f45cb7c1dc5910160405180910390a29392505050565b60cd5460405163df71403b60e01b81526001600160a01b039091169063df71403b9061340490309087908690600401614d8f565b600060405180830381600087803b15801561341e57600080fd5b505af1158015613432573d6000803e3d6000fd5b5050505061343e611fb9565b60d1541461345f57604051630e8d8c6160e21b815260040160405180910390fd5b8060d45461346c60de5490565b6134769190614c79565b1015613495576040516348c2588160e01b815260040160405180910390fd5b60006134a08461270f565b905060006134ae8383614c66565b905060008360d3546134c09190614c66565b6001600160a01b038716600090815260d96020526040902083815560d25460019091015560d381905590506134f58585613dfb565b60408051858152602081018490529081018290526001600160a01b038716907f13ed6866d4e1ee6da46f845c46d7e54120883d75c5ea9a2dacc1c4ca8984ab809060600160405180910390a260cd54604051635c77860560e01b81526001600160a01b0390911690635c778605906135759030908a908990600401614d8f565b600060405180830381600087803b15801561358f57600080fd5b505af11580156135a3573d6000803e3d6000fd5b50505050505050505050565b600054610100900460ff166135d65760405162461bcd60e51b8152600401610a5d90614db3565b6135de613fe8565b6135e783614017565b60d1541580156135f7575060d254155b61364f5760405162461bcd60e51b815260206004820152602360248201527f6d61726b6574206d6179206f6e6c7920626520696e697469616c697a6564206f6044820152626e636560e81b6064820152608401610a5d565b60cf889055876136ba5760405162461bcd60e51b815260206004820152603060248201527f696e697469616c2065786368616e67652072617465206d75737420626520677260448201526f32b0ba32b9103a3430b7103d32b9379760811b6064820152608401610a5d565b6136c38a61403e565b6136cb611fb9565b60d155670de0b6b3a764000060d2556136e3896138bd565b6136ec816128fb565b60ca6136f88882614e4e565b5060cb6137058782614e4e565b5060cc805460ff191660ff871617905581516137209061319d565b61372d82602001516126ac565b66b1a2bc2ec5000060da5560c98054610100600160a81b0319166101006001600160a01b038e811682029290921792839055604080516318160ddd60e01b8152905191909304909116916318160ddd9160048083019260209291908290030181865afa1580156137a1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137c59190614c37565b5060c9805460ff19166001179055612f4e84613322565b60c95460ff166137fe5760405162461bcd60e51b8152600401610a5d90614c13565b60c9805460ff1916905561381061199e565b506000826001600160a01b031663a6afed956040518163ffffffff1660e01b81526004016020604051808303816000875af1158015613853573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906138779190614c37565b9050801561389b57604051633eea49b760e11b815260048101829052602401610a5d565b6138a88686868686614149565b505060c9805460ff1916600117905550505050565b60006138c7611fb9565b60d154146138e857604051630be2a5cb60e11b815260040160405180910390fd5b60ce60009054906101000a90046001600160a01b03169050816001600160a01b0316632191f92a6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561393e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906139629190614d2d565b6139ae5760405162461bcd60e51b815260206004820152601c60248201527f6d61726b6572206d6574686f642072657475726e65642066616c7365000000006044820152606401610a5d565b60ce80546001600160a01b0319166001600160a01b0384811691821790925560405190918316907fedffc32e068c7c95dfd4bdfd5c4d939a084d6b11c4199eac8436ed234d72f92690600090a35050565b6040805160208101909152600081526040518060200160405280613a278560000151856145d7565b90529392505050565b600080613a3d85856139ff565b90506127ec613a4b82613f57565b846145e3565b60cd5460405163037883e560e31b81523060048201526001600160a01b0386811660248301528581166044830152848116606483015290911690631bc41f2890608401600060405180830381600087803b158015613aae57600080fd5b505af1158015613ac2573d6000803e3d6000fd5b50505050826001600160a01b0316826001600160a01b031603613af857604051633a94626760e11b815260040160405180910390fd5b60cd5460408051634ada90af60e01b815290516000926001600160a01b031691634ada90af9160048083019260209291908290030181865afa158015613b42573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613b669190614c37565b90506000613b8483604051806020016040528060da54815250613f34565b90506000613ba082604051806020016040528086815250613f16565b90506000613bae8286614c79565b905060006040518060200160405280613bc561277f565b905290506000613bd58285613185565b90508360d554613be59190614c79565b60d5556001600160a01b038816600090815260d76020526040902054613c0c908890614c79565b6001600160a01b03808a16600090815260d7602052604080822093909355908b1681522054613c3c908490614c66565b6001600160a01b03808b16600090815260d7602052604090209190915560cc54613c6d916101009091041682613dfb565b60cc5460cd5460c9546040516305bebb3b60e21b81526001600160a01b03610100948590048116946316faecec94613cb5949083169391900490911690600190600401614c8c565b600060405180830381600087803b158015613ccf57600080fd5b505af1158015613ce3573d6000803e3d6000fd5b50505050886001600160a01b0316886001600160a01b0316600080516020614f7383398151915285604051613d1a91815260200190565b60405180910390a360cc546040516001600160a01b036101009092048216918a16907f3ac0548d62d3fa3c9a817cd33899b9acacd57e8958ebe51bc7d9a79f26a8a5db90613d6b9085815260200190565b60405180910390a360cd54604051636d35bf9160e01b81523060048201526001600160a01b038c811660248301528b811660448301528a81166064830152608482018a905290911690636d35bf919060a401600060405180830381600087803b158015613dd757600080fd5b505af1158015613deb573d6000803e3d6000fd5b5050505050505050505050505050565b600060c960019054906101000a90046001600160a01b031690508160de6000828254613e279190614c79565b9091555061285890506001600160a01b03821684846127f5565b6000613e96826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166145ef9092919063ffffffff16565b9050805160001480613eb7575080806020019051810190613eb79190614d2d565b6128585760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610a5d565b6000611112613f2d84670de0b6b3a76400006145d7565b83516145fe565b6000670de0b6b3a7640000613f4d8484600001516145d7565b6111129190614ce7565b8051600090610b0d90670de0b6b3a764000090614ce7565b613f90846323b872dd60e01b85858560405160240161282193929190614d8f565b50505050565b603380546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b600054610100900460ff1661400f5760405162461bcd60e51b8152600401610a5d90614db3565b6113ea61460a565b600054610100900460ff16610b1b5760405162461bcd60e51b8152600401610a5d90614db3565b60cd5460408051623f1ee960e11b815290516001600160a01b0392831692841691627e3dd29160048083019260209291908290030181865afa158015614088573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906140ac9190614d2d565b6140f85760405162461bcd60e51b815260206004820152601c60248201527f6d61726b6572206d6574686f642072657475726e65642066616c7365000000006044820152606401610a5d565b60cd80546001600160a01b0319166001600160a01b0384811691821790925560405190918316907f7ac369dbd14fa5ea3f473ed67cc9d598964a77501540ba6751eb0b3decf5870d90600090a35050565b60cd5460405163e89d51ad60e01b81523060048201526001600160a01b03848116602483015286811660448301526064820186905283151560848301529091169063e89d51ad9060a401600060405180830381600087803b1580156141ad57600080fd5b505af11580156141c1573d6000803e3d6000fd5b505050506141cd611fb9565b60d154146141ee576040516380965b1b60e01b815260040160405180910390fd5b6141f6611fb9565b826001600160a01b0316636c540baf6040518163ffffffff1660e01b8152600401602060405180830381865afa158015614234573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906142589190614c37565b1461427657604051631046f38d60e31b815260040160405180910390fd5b846001600160a01b0316846001600160a01b0316036142a857604051631bd1a62160e21b815260040160405180910390fd5b826000036142c95760405163d29da7ef60e01b815260040160405180910390fd5b60001983036142eb57604051635982c5bb60e11b815260040160405180910390fd5b60006142f88686866124d1565b60cd5460405163c488847b60e01b815291925060009182916001600160a01b03169063c488847b9061433290309089908890600401614d8f565b6040805180830381865afa15801561434e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906143729190614f0e565b91509150600082146143e25760405162461bcd60e51b815260206004820152603360248201527f4c49515549444154455f434f4d5054524f4c4c45525f43414c43554c4154455f604482015272105353d5539517d4d152569157d19052531151606a1b6064820152608401610a5d565b6040516370a0823160e01b81526001600160a01b0388811660048301528291908716906370a0823190602401602060405180830381865afa15801561442b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061444f9190614c37565b101561449d5760405162461bcd60e51b815260206004820152601860248201527f4c49515549444154455f5345495a455f544f4f5f4d55434800000000000000006044820152606401610a5d565b306001600160a01b038616036144be576144b930898984613a51565b614521565b60405163b2a02ff160e01b81526001600160a01b0386169063b2a02ff1906144ee908b908b908690600401614d8f565b600060405180830381600087803b15801561450857600080fd5b505af115801561451c573d6000803e3d6000fd5b505050505b846001600160a01b0316876001600160a01b0316896001600160a01b03167f298637f684da70674f26509b10f07ec2fbc77a335ab1e7d6215a4b2484d8bb528685604051614579929190918252602082015260400190565b60405180910390a460cd546040516347ef3b3b60e01b81523060048201526001600160a01b0387811660248301528a8116604483015289811660648301526084820186905260a48201849052909116906347ef3b3b9060c401612d28565b60006111128284614cd0565b60006111128284614c66565b6060612777848460008561463a565b60006111128284614ce7565b600054610100900460ff166146315760405162461bcd60e51b8152600401610a5d90614db3565b6113ea33613322565b60608247101561469b5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b6064820152608401610a5d565b600080866001600160a01b031685876040516146b79190614f32565b60006040518083038185875af1925050503d80600081146146f4576040519150601f19603f3d011682016040523d82523d6000602084013e6146f9565b606091505b509150915061470a87838387614715565b979650505050505050565b6060831561478457825160000361477d576001600160a01b0385163b61477d5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610a5d565b5081612777565b61277783838151156147995781518083602001fd5b8060405162461bcd60e51b8152600401610a5d9190614803565b60005b838110156147ce5781810151838201526020016147b6565b50506000910152565b600081518084526147ef8160208601602086016147b3565b601f01601f19169290920160200192915050565b60208152600061111260208301846147d7565b60006020828403121561482857600080fd5b5035919050565b6001600160a01b0381168114610b2457600080fd5b803561484f8161482f565b919050565b6000806040838503121561486757600080fd5b82356148728161482f565b946020939093013593505050565b60006020828403121561489257600080fd5b81356111128161482f565b6000806000606084860312156148b257600080fd5b83356148bd8161482f565b925060208401356148cd8161482f565b929592945050506040919091013590565b634e487b7160e01b600052604160045260246000fd5b600082601f83011261490557600080fd5b813567ffffffffffffffff80821115614920576149206148de565b604051601f8301601f19908116603f01168101908282118183101715614948576149486148de565b8160405283815286602085880101111561496157600080fd5b836020870160208301376000602085830101528094505050505092915050565b803560ff8116811461484f57600080fd5b6000604082840312156149a457600080fd5b6040516040810181811067ffffffffffffffff821117156149c7576149c76148de565b60405290508082356149d88161482f565b815260208301356149e88161482f565b6020919091015292915050565b60008060008060008060008060008060006101808c8e031215614a1757600080fd5b614a208c614844565b9a50614a2e60208d01614844565b9950614a3c60408d01614844565b985060608c0135975067ffffffffffffffff8060808e01351115614a5f57600080fd5b614a6f8e60808f01358f016148f4565b97508060a08e01351115614a8257600080fd5b50614a938d60a08e01358e016148f4565b9550614aa160c08d01614981565b9450614aaf60e08d01614844565b9350614abe6101008d01614844565b9250614ace8d6101208e01614992565b91506101608c013590509295989b509295989b9093969950565b8015158114610b2457600080fd5b600080600080600060a08688031215614b0e57600080fd5b8535614b198161482f565b94506020860135614b298161482f565b9350604086013592506060860135614b408161482f565b91506080860135614b5081614ae8565b809150509295509295909350565b60008060408385031215614b7157600080fd5b8235614b7c8161482f565b91506020830135614b8c8161482f565b809150509250929050565b600080600060608486031215614bac57600080fd5b8335614bb78161482f565b9250602084013591506040840135614bce8161482f565b809150509250925092565b600181811c90821680614bed57607f821691505b602082108103614c0d57634e487b7160e01b600052602260045260246000fd5b50919050565b6020808252600a90820152691c994b595b9d195c995960b21b604082015260600190565b600060208284031215614c4957600080fd5b5051919050565b634e487b7160e01b600052601160045260246000fd5b80820180821115610b0d57610b0d614c50565b81810381811115610b0d57610b0d614c50565b6001600160a01b038481168252831660208201526060810160048310614cc257634e487b7160e01b600052602160045260246000fd5b826040830152949350505050565b8082028115828204841417610b0d57610b0d614c50565b600082614d0457634e487b7160e01b600052601260045260246000fd5b500490565b6001600160a01b0383168152604060208201819052600090612777908301846147d7565b600060208284031215614d3f57600080fd5b815161111281614ae8565b6001600160a01b038481168252831660208201526060604082018190526000906127ec908301846147d7565b600060018201614d8857614d88614c50565b5060010190565b6001600160a01b039384168152919092166020820152604081019190915260600190565b6020808252602b908201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960408201526a6e697469616c697a696e6760a81b606082015260800190565b601f821115612858576000816000526020600020601f850160051c81016020861015614e275750805b601f850160051c820191505b81811015614e4657828155600101614e33565b505050505050565b815167ffffffffffffffff811115614e6857614e686148de565b614e7c81614e768454614bd9565b84614dfe565b602080601f831160018114614eb15760008415614e995750858301515b600019600386901b1c1916600185901b178555614e46565b600085815260208120601f198616915b82811015614ee057888601518255948401946001909101908401614ec1565b5085821015614efe5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b60008060408385031215614f2157600080fd5b505080516020909101519092909150565b60008251614f448184602087016147b3565b919091019291505056fe7365745265647563655265736572766573426c6f636b44656c74612875696e7432353629ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa26469706673582212206e3b4df073eb91e6ad504b20b56c7d98f41c77c0f5da0d116d232e472c92581b64736f6c63430008190033", + "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106104495760003560e01c80637821a51411610241578063b4a0bdf31161013b578063dd62ed3e116100c3578063ef60450c11610087578063ef60450c14610959578063f2fde38b1461096c578063f3fdb15a1461097f578063f5e3c46214610992578063f8f9da28146109a557600080fd5b8063dd62ed3e146108dc578063df3a516e14610915578063e1d146fb14610928578063e30c397814610930578063e9a44fd91461094157600080fd5b8063c5ebeaec1161010a578063c5ebeaec1461087e578063c7ad089514610891578063cf4b3b53146108b8578063d1109c2f146108c0578063db006a75146108c957600080fd5b8063b4a0bdf314610829578063bbcac5571461083a578063bd6d894d14610843578063c37f68e21461084b57600080fd5b806395d89b41116101c9578063a9059cbb1161018d578063a9059cbb146107e9578063aa5af0fd146107fc578063ae96f14114610805578063ae9d70b01461080e578063b2a02ff11461081657600080fd5b806395d89b41146107a057806395dd9193146107a8578063a0712d68146107bb578063a457c2d7146107ce578063a6afed95146107e157600080fd5b80638a42c319116102105780638a42c3191461074d5780638bbdb6db146107605780638bcd4016146107735780638da5cb5b146107865780638f840ddd1461079757600080fd5b80637821a5141461070c57806379ba50971461071f578063852a12e314610727578063856e5bb31461073a57600080fd5b80632608f818116103525780635fe3b567116102da5780636f307dc31161029e5780636f307dc3146106a857806370a08231146106c0578063715018a6146106e957806373acee98146106f1578063757212f0146106f957600080fd5b80635fe3b567146106545780636752e702146106675780636857249c1461067057806369ab3250146106975780636c540baf1461069f57600080fd5b80633b1d21a2116103215780633b1d21a2146106165780633d9ea3a11461061e57806341f641ee1461062557806344fe6ffe1461063857806347bd37181461064b57600080fd5b80632608f818146105be578063313ce567146105d157806339509351146105f05780633af9e6691461060357600080fd5b8063182df0f5116103d5578063210bc052116103a4578063210bc0521461056957806322abdbf51461057c57806323323e031461058557806323b872dd146105985780632464176b146105ab57600080fd5b8063182df0f51461051057806319b1faef146105185780631be19560146105435780631c4469831461055657600080fd5b80630e7527021161041c5780630e752702146104b7578063107568df146104d8578063173b9904146104eb57806317bfdfbc146104f457806318160ddd1461050757600080fd5b806306fdde031461044e57806307e279591461046c578063095ea7b3146104815780630e32cb86146104a4575b600080fd5b6104566109ad565b6040516104639190614803565b60405180910390f35b61047f61047a366004614816565b610a3b565b005b61049461048f366004614854565b610aa2565b6040519015158152602001610463565b61047f6104b2366004614880565b610b13565b6104ca6104c5366004614816565b610b27565b604051908152602001610463565b61047f6104e6366004614880565b610b81565b6104ca60d05481565b6104ca610502366004614880565b610b92565b6104ca60d55481565b6104ca610be7565b60db5461052b906001600160a01b031681565b6040516001600160a01b039091168152602001610463565b61047f610551366004614880565b610bf6565b61047f610564366004614816565b610db1565b6104ca610577366004614854565b610e2c565b6104ca60de5481565b6104ca610593366004614854565b610e90565b6104946105a636600461489d565b610edc565b61047f6105b9366004614816565b610f2e565b6104ca6105cc366004614854565b610fd0565b60cc546105de9060ff1681565b60405160ff9091168152602001610463565b6104946105fe366004614854565b61102b565b6104ca610611366004614880565b6110d3565b6104ca611119565b6001610494565b61047f610633366004614880565b611124565b61047f61064636600461489d565b611135565b6104ca60d35481565b60cd5461052b906001600160a01b031681565b6104ca60da5481565b6104ca7f000000000000000000000000000000000000000000000000000000000000000081565b6104ca600081565b6104ca60d15481565b60c95461052b9061010090046001600160a01b031681565b6104ca6106ce366004614880565b6001600160a01b0316600090815260d7602052604090205490565b61047f6113d8565b6104ca6113ec565b61047f610707366004614816565b611438565b61047f61071a366004614816565b61155f565b61047f6115ae565b6104ca610735366004614816565b611625565b6104ca610748366004614854565b61167f565b61047f61075b3660046149f5565b6116a7565b61047f61076e366004614af6565b6117d7565b61047f610781366004614880565b611816565b6033546001600160a01b031661052b565b6104ca60d45481565b610456611866565b6104ca6107b6366004614880565b611873565b6104ca6107c9366004614816565b61187e565b6104946107dc366004614854565b6118c1565b6104ca61199e565b6104946107f7366004614854565b611bf4565b6104ca60d25481565b6104ca60dd5481565b6104ca611c45565b61047f61082436600461489d565b611ce9565b6097546001600160a01b031661052b565b6104ca60d65481565b6104ca611d33565b61085e610859366004614880565b611d85565b604080519485526020850193909352918301526060820152608001610463565b6104ca61088c366004614816565b611dc6565b6104947f000000000000000000000000000000000000000000000000000000000000000081565b61047f611e09565b6104ca60dc5481565b6104ca6108d7366004614816565b611f26565b6104ca6108ea366004614b5e565b6001600160a01b03918216600090815260d86020908152604080832093909416825291909152205490565b6104ca610923366004614854565b611f6b565b6104ca611fb9565b6065546001600160a01b031661052b565b60cc5461052b9061010090046001600160a01b031681565b61047f610967366004614816565b611fe7565b61047f61097a366004614880565b61211e565b60ce5461052b906001600160a01b031681565b6104ca6109a0366004614b97565b61218f565b6104ca6121a9565b60ca80546109ba90614bd9565b80601f01602080910402602001604051908101604052809291908181526020018280546109e690614bd9565b8015610a335780601f10610a0857610100808354040283529160200191610a33565b820191906000526020600020905b815481529060010190602001808311610a1657829003601f168201915b505050505081565b60c95460ff16610a665760405162461bcd60e51b8152600401610a5d90614c13565b60405180910390fd5b60c9805460ff19169055610a7861199e565b50610a81611fb9565b60dd5414610a9257610a928161220d565b5060c9805460ff19166001179055565b6000610aad8361238a565b33600081815260d8602090815260408083206001600160a01b038816808552908352928190208690555185815283917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a360019150505b92915050565b610b1b6123b1565b610b248161240b565b50565b60c95460009060ff16610b4c5760405162461bcd60e51b8152600401610a5d90614c13565b60c9805460ff19169055610b5e61199e565b50610b6a3333846124d1565b506000905060c9805460ff19166001179055919050565b610b896123b1565b610b24816126ac565b60c95460009060ff16610bb75760405162461bcd60e51b8152600401610a5d90614c13565b60c9805460ff19169055610bc961199e565b50610bd38261270f565b905060c9805460ff19166001179055919050565b6000610bf161277f565b905090565b6033546001600160a01b03163314610c685760405162461bcd60e51b815260206004820152602f60248201527f56546f6b656e3a3a7377656570546f6b656e3a206f6e6c792061646d696e206360448201526e616e20737765657020746f6b656e7360881b6064820152608401610a5d565b60c9546001600160a01b03610100909104811690821603610ce65760405162461bcd60e51b815260206004820152603260248201527f56546f6b656e3a3a7377656570546f6b656e3a2063616e206e6f74207377656560448201527138103ab73232b9363cb4b733903a37b5b2b760711b6064820152608401610a5d565b6040516370a0823160e01b81523060048201526000906001600160a01b038316906370a0823190602401602060405180830381865afa158015610d2d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d519190614c37565b9050610d79610d686033546001600160a01b031690565b6001600160a01b03841690836127f5565b6040516001600160a01b038316907f35ce4c546a473796a8e70ec2d4af4f2031afe357afa7057b6ea7fa340730e1b290600090a25050565b60c95460ff16610dd35760405162461bcd60e51b8152600401610a5d90614c13565b60c9805460ff1916905560408051808201909152601981527f73657452657365727665466163746f722875696e7432353629000000000000006020820152610e1a9061285d565b610e2261199e565b50610a92816128fb565b60c95460009060ff16610e515760405162461bcd60e51b8152600401610a5d90614c13565b60c9805460ff19169055610e648361298b565b610e6c61199e565b50610e7a8333846000612a1c565b50600060c9805460ff1916600117905592915050565b60c95460009060ff16610eb55760405162461bcd60e51b8152600401610a5d90614c13565b60c9805460ff19169055610ec88361238a565b610ed061199e565b50610e7a338484612d56565b60c95460009060ff16610f015760405162461bcd60e51b8152600401610a5d90614c13565b60c9805460ff19169055610f1733858585612f5b565b50600160c9805460ff191660011790559392505050565b610f4f604051806060016040528060248152602001614f4f6024913961285d565b60008111610f8f5760405162461bcd60e51b815260206004820152600d60248201526c125b9d985b1a5908125b9c1d5d609a1b6044820152606401610a5d565b60dc5460408051918252602082018390527fc2ac513cdb57f91eb2bef4db918c285829524f549682b99717c6cb06cc011183910160405180910390a160dc55565b60c95460009060ff16610ff55760405162461bcd60e51b8152600401610a5d90614c13565b60c9805460ff1916905561100761199e565b506110133384846124d1565b506000905060c9805460ff1916600117905592915050565b60006110368361238a565b33600081815260d8602090815260408083206001600160a01b03881684529091529020546110648482614c66565b6001600160a01b03838116600081815260d860209081526040808320948b16808452948252918290208590559051848152939450919290917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591015b60405180910390a3506001949350505050565b60008060405180602001604052806110e9611d33565b90526001600160a01b038416600090815260d76020526040902054909150611112908290613185565b9392505050565b6000610bf160de5490565b61112c6123b1565b610b248161319d565b60c95460ff166111575760405162461bcd60e51b8152600401610a5d90614c13565b60c9805460ff1916905580156111cd5760cd5460405163eade3eed60e01b81523060048201526001600160a01b0384811660248301529091169063eade3eed90604401600060405180830381600087803b1580156111b457600080fd5b505af11580156111c8573d6000803e3d6000fd5b505050505b60cd546001600160a01b031633146111f857604051632c40292560e01b815260040160405180910390fd5b60006112038361270f565b60d354909150600083156112835761121b86856131f8565b90506112278183614c79565b91506001600160a01b038086169087167f1a2a22cb034d26d1854bdc6666a5b91fe25efbbb5dcad3b0355478d6f5c362a1836112638188614c79565b604080519283526020830191909152810186905260600160405180910390a35b600061128f8285614c79565b905080156113575760d65460006112a68383614c66565b90506112b28386614c79565b60d682905560408051858152600060208201529081018290529095506001600160a01b0389169030907f1a2a22cb034d26d1854bdc6666a5b91fe25efbbb5dcad3b0355478d6f5c362a19060600160405180910390a360408051848152602081018490529081018290526001600160a01b038916907f90125ffdb441e57c4f6bf69789206424859f206bea5727f2d81ad2470826ef6a9060600160405180910390a250505b6001600160a01b03808716600081815260d9602052604080822091825560d25460019092019190915560d38690555190918916907f9fe0294717a8efbc6ace1c151b73a4c89982339b2228a27d1ca21394e348986f906113ba9089815260200190565b60405180910390a3505060c9805460ff191660011790555050505050565b6113e06123b1565b6113ea6000613322565b565b60c95460009060ff166114115760405162461bcd60e51b8152600401610a5d90614c13565b60c9805460ff1916905561142361199e565b505060d35460c9805460ff1916600117905590565b6114766040518060400160405280601e81526020017f73657450726f746f636f6c5365697a6553686172652875696e7432353629000081525061285d565b60cd5460408051634ada90af60e01b815290516000926001600160a01b031691634ada90af9160048083019260209291908290030181865afa1580156114c0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114e49190614c37565b9050806114f9670de0b6b3a764000084614c66565b11156115185760405163034dd2c160e11b815260040160405180910390fd5b60da80549083905560408051828152602081018590527ff5815f353a60e815cce7553e4f60c533a59d26b1b5504ea4b6db8d60da3e4da291015b60405180910390a1505050565b60c95460ff166115815760405162461bcd60e51b8152600401610a5d90614c13565b60c9805460ff1916905561159361199e565b5061159d8161333b565b505060c9805460ff19166001179055565b60655433906001600160a01b0316811461161c5760405162461bcd60e51b815260206004820152602960248201527f4f776e61626c6532537465703a2063616c6c6572206973206e6f7420746865206044820152683732bb9037bbb732b960b91b6064820152608401610a5d565b610b2481613322565b60c95460009060ff1661164a5760405162461bcd60e51b8152600401610a5d90614c13565b60c9805460ff1916905561165c61199e565b5061166a3333600085612a1c565b50600060c9805460ff19166001179055919050565b600061168a8361298b565b61169261199e565b5061169e8333846133d0565b50600092915050565b600054610100900460ff16158080156116c75750600054600160ff909116105b806116e15750303b1580156116e1575060005460ff166001145b6117445760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b6064820152608401610a5d565b6000805460ff191660011790558015611767576000805461ff0019166101001790555b6117708561238a565b6117838c8c8c8c8c8c8c8c8c8c8c6135af565b80156117c9576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b505050505050505050505050565b60cd546001600160a01b0316331461180257604051635c85a5e760e01b815260040160405180910390fd5b61180f85858585856137dc565b5050505050565b6118546040518060400160405280601d81526020017f736574496e746572657374526174654d6f64656c28616464726573732900000081525061285d565b61185c61199e565b50610b24816138bd565b60cb80546109ba90614bd9565b6000610b0d8261270f565b60c95460009060ff166118a35760405162461bcd60e51b8152600401610a5d90614c13565b60c9805460ff191690556118b561199e565b5061166a333384612d56565b60006118cc8361238a565b33600081815260d8602090815260408083206001600160a01b0388168452909152902054838110156119405760405162461bcd60e51b815260206004820152601e60248201527f64656372656173656420616c6c6f77616e63652062656c6f77207a65726f00006044820152606401610a5d565b6001600160a01b03828116600081815260d860209081526040808320948a1680845294825291829020948890039485905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591016110c0565b6000806119a9611fb9565b60d1549091508181036119bf5760009250505090565b60006119ca60de5490565b60d35460d45460d25460ce5460d6546040516301cee29d60e21b815260048101879052602481018690526044810185905260648101919091529495509293919290916000916001600160a01b03169063073b8a7490608401602060405180830381865afa158015611a3f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a639190614c37565b90507f0000000000000000000000000000000000000000000000000000000000000000811115611ad55760405162461bcd60e51b815260206004820152601c60248201527f626f72726f772072617465206973206162737572646c792068696768000000006044820152606401610a5d565b6000611ae18789614c79565b90506000611afd604051806020016040528085815250836139ff565b90506000611b0b8288613185565b90506000611b198883614c66565b90506000611b38604051806020016040528060d054815250848a613a30565b90506000611b4785898a613a30565b60d18e905560d281905560d384905560d483905560dc5460dd5491925090611b6f908f614c79565b10611b985760dd8d9055818b1015611b8f57611b8a8b61220d565b611b98565b611b988261220d565b604080518c815260208101869052908101829052606081018490527f4dec04e750ca11537cabcd8a9eab06494de08da3735bc8871cd41250e190bc049060800160405180910390a160009d505050505050505050505050505090565b60c95460009060ff16611c195760405162461bcd60e51b8152600401610a5d90614c13565b60c9805460ff19169055611c2f33808585612f5b565b50600160c9805460ff1916600117905592915050565b60ce546000906001600160a01b0316630cde8d1c611c6260de5490565b60d35460d45460d05460d6546040516001600160e01b031960e088901b1681526004810195909552602485019390935260448401919091526064830152608482015260a4015b602060405180830381865afa158015611cc5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bf19190614c37565b60c95460ff16611d0b5760405162461bcd60e51b8152600401610a5d90614c13565b60c9805460ff19169055611d2133848484613a51565b505060c9805460ff1916600117905550565b60c95460009060ff16611d585760405162461bcd60e51b8152600401610a5d90614c13565b60c9805460ff19169055611d6a61199e565b50611d7361277f565b905060c9805460ff1916600117905590565b6001600160a01b038116600090815260d760205260408120548190819081908190611daf8761270f565b611db761277f565b93509350935093509193509193565b60c95460009060ff16611deb5760405162461bcd60e51b8152600401610a5d90614c13565b60c9805460ff19169055611dfd61199e565b5061166a3333846133d0565b60c95460ff16611e2b5760405162461bcd60e51b8152600401610a5d90614c13565b60c9805460ff1916905560408051808201909152600a81526973796e6343617368282960b01b6020820152611e5f9061285d565b60de5460c9546040516370a0823160e01b815230600482015260009161010090046001600160a01b0316906370a0823190602401602060405180830381865afa158015611eb0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ed49190614c37565b60de81905560408051848152602081018390529192507f56a36ec0a133fa42db4951702e0f2f268ded6733b1ace7e7c90ec2dc45c58d30910160405180910390a1505060c9805460ff19166001179055565b60c95460009060ff16611f4b5760405162461bcd60e51b8152600401610a5d90614c13565b60c9805460ff19169055611f5d61199e565b5061166a3333846000612a1c565b60c95460009060ff16611f905760405162461bcd60e51b8152600401610a5d90614c13565b60c9805460ff19169055611fa38361298b565b611fab61199e565b50610e7a8333600085612a1c565b6000610bf17f000000000000000000000000000000000000000000000000000000000000000063ffffffff16565b60db546001600160a01b031633146120555760405162461bcd60e51b815260206004820152602b60248201527f6f6e6c792073686f727466616c6c20636f6e74726163742063616e207570646160448201526a1d1948189859081919589d60aa1b6064820152608401610a5d565b60d6548111156120b95760405162461bcd60e51b815260206004820152602960248201527f6d6f7265207468616e206261642064656274207265636f76657265642066726f604482015268369030bab1ba34b7b760b91b6064820152608401610a5d565b60d65460006120c88383614c79565b90508060d6819055508260de60008282546120e39190614c66565b909155505060408051838152602081018390527f9e19ec7d2b8f8a94df8cc0072453ace318d221e3cbb2731d0eaa0baac856520f9101611552565b6121266123b1565b606580546001600160a01b0383166001600160a01b031990911681179091556121576033546001600160a01b031690565b6001600160a01b03167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e2270060405160405180910390a350565b600061219f3385858560006137dc565b5060009392505050565b60ce546000906001600160a01b031663073b8a746121c660de5490565b60d35460d45460d6546040516001600160e01b031960e087901b1681526004810194909452602484019290925260448301526064820152608401611ca8565b4390565b4290565b806000036122185750565b6000612222611fb9565b60d1541461224357604051630dff50cb60e41b815260040160405180910390fd5b8161224d60de5490565b101561226c57604051633345e99960e01b815260040160405180910390fd5b60d45482111561228f576040516378d2980560e11b815260040160405180910390fd5b8160d45461229d9190614c79565b60d481905560cc549091506122c09061010090046001600160a01b031683613dfb565b60cc5460cd5460c9546040516305bebb3b60e21b81526001600160a01b03610100948590048116946316faecec94612308949083169391900490911690600090600401614c8c565b600060405180830381600087803b15801561232257600080fd5b505af1158015612336573d6000803e3d6000fd5b505060cc5460408051868152602081018690526101009092046001600160a01b031693507f9cc63bb4ef37ad6a5f5f657dfaf94865531d4234acbc431cc8ac035468f6272092500160405180910390a25050565b6001600160a01b038116610b24576040516342bcdf7f60e11b815260040160405180910390fd5b6033546001600160a01b031633146113ea5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610a5d565b6001600160a01b03811661246f5760405162461bcd60e51b815260206004820152602560248201527f696e76616c696420616365737320636f6e74726f6c206d616e61676572206164604482015264647265737360d81b6064820152608401610a5d565b609780546001600160a01b038381166001600160a01b031983168117909355604080519190921680825260208201939093527f66fd58e82f7b31a2a5c30e0888f3093efe4e111b00cd2b0c31fe014601293aa091015b60405180910390a15050565b60cd5460405163eade3eed60e01b81523060048201526001600160a01b038481166024830152600092169063eade3eed90604401600060405180830381600087803b15801561251f57600080fd5b505af1158015612533573d6000803e3d6000fd5b5050505061253f611fb9565b60d154146125605760405163c9021e2f60e01b815260040160405180910390fd5b600061256b8461270f565b905060008184101561257d578361257f565b815b9050600061258d87836131f8565b9050600061259b8285614c79565b905060008260d3546125ad9190614c79565b6001600160a01b03898116600081815260d9602090815260409182902087815560d25460019091015560d3859055815188815290810187905290810184905292935091908b16907f1a2a22cb034d26d1854bdc6666a5b91fe25efbbb5dcad3b0355478d6f5c362a19060600160405180910390a360cd5460d254604051631ededc9160e01b81523060048201526001600160a01b038c811660248301528b81166044830152606482018790526084820192909252911690631ededc919060a401600060405180830381600087803b15801561268757600080fd5b505af115801561269b573d6000803e3d6000fd5b50949b9a5050505050505050505050565b6126b58161238a565b60cc80546001600160a01b03838116610100818102610100600160a81b031985161790945560405193909204169182907fafec95c8612496c3ecf5dddc71e393528fe29bd145fbaf9c6b496d78d7e2d79b90600090a35050565b6001600160a01b038116600090815260d96020908152604080832081518083019092528054808352600190910154928201929092529082036127545750600092915050565b60d254815160009161276591614cd0565b90508160200151816127779190614ce7565b949350505050565b60d55460009080820361279457505060cf5490565b600061279f60de5490565b9050600060d45460d65460d354846127b79190614c66565b6127c19190614c66565b6127cb9190614c79565b90506000836127e2670de0b6b3a764000084614cd0565b6127ec9190614ce7565b95945050505050565b6040516001600160a01b03831660248201526044810182905261285890849063a9059cbb60e01b906064015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152613e41565b505050565b6097546040516318c5e8ab60e01b81526000916001600160a01b0316906318c5e8ab906128909033908690600401614d09565b602060405180830381865afa1580156128ad573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128d19190614d2d565b9050806128f757333083604051634a3fa29360e01b8152600401610a5d93929190614d4a565b5050565b612903611fb9565b60d1541461292457604051637dfca6b760e11b815260040160405180910390fd5b670de0b6b3a764000081111561294d5760405163717220f360e11b815260040160405180910390fd5b60d080549082905560408051828152602081018490527faaa68312e2ea9d50e16af5068410ab56e1a1fd06037b1a35664812c30f82146091016124c5565b60cd54604051630217306760e31b81526001600160a01b038381166004830152336024830152909116906310b9833890604401602060405180830381865afa1580156129db573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129ff9190614d2d565b610b2457604051630cf0b6f560e01b815260040160405180910390fd5b811580612a27575080155b612a905760405162461bcd60e51b815260206004820152603460248201527f6f6e65206f662072656465656d546f6b656e73496e206f722072656465656d416044820152736d6f756e74496e206d757374206265207a65726f60601b6064820152608401610a5d565b612a98611fb9565b60d15414612ab9576040516397b5cfcd60e01b815260040160405180910390fd5b60006040518060200160405280612ace61277f565b905290506000808415612ae357849150612b21565b612aed8484613f16565b91506000612afb8385613f34565b90508015801590612b0c5750848114155b15612b1f5782612b1b81614d76565b9350505b505b612b2b8383613185565b905080600003612b745760405162461bcd60e51b815260206004820152601460248201527372656465656d416d6f756e74206973207a65726f60601b6044820152606401610a5d565b60cd54604051634732387560e11b81526001600160a01b0390911690638e6470ea90612ba89030908b908790600401614d8f565b600060405180830381600087803b158015612bc257600080fd5b505af1158015612bd6573d6000803e3d6000fd5b505050508060d454612be760de5490565b612bf19190614c79565b1015612c10576040516391240a1b60e01b815260040160405180910390fd5b8160d554612c1e9190614c79565b60d5556001600160a01b038716600090815260d76020526040812054612c45908490614c79565b6001600160a01b038916600090815260d7602052604090208190559050612c6c8783613dfb565b60405183815230906001600160a01b038a1690600080516020614f738339815191529060200160405180910390a360408051838152602081018590529081018290526001600160a01b038916907fbd5034ffbd47e4e72a94baa2cdb74c6fad73cb3bcdc13036b72ec8306f5a76469060600160405180910390a260cd546040516351dff98960e01b81523060048201526001600160a01b038a811660248301526044820185905260648201869052909116906351dff989906084015b600060405180830381600087803b158015612d4257600080fd5b505af11580156117c9573d6000803e3d6000fd5b60cd5460405163c0891ba960e01b81526001600160a01b039091169063c0891ba990612d8a90309086908690600401614d8f565b600060405180830381600087803b158015612da457600080fd5b505af1158015612db8573d6000803e3d6000fd5b50505050612dc4611fb9565b60d15414612de5576040516338d8859760e01b815260040160405180910390fd5b60006040518060200160405280612dfa61277f565b905290506000612e0a85846131f8565b90506000612e188284613f16565b90508060d554612e289190614c66565b60d5556001600160a01b038516600090815260d76020526040812054612e4f908390614c66565b6001600160a01b038716600081815260d760209081526040918290208490558151878152908101869052908101839052919250907fb4c03061fb5b7fed76389d5af8f2e0ddb09f8c70d1333abbb62582835e10accb9060600160405180910390a26040518281526001600160a01b03871690600090600080516020614f738339815191529060200160405180910390a360cd546040516341c728b960e01b81523060048201526001600160a01b0388811660248301526044820186905260648201859052909116906341c728b990608401600060405180830381600087803b158015612f3a57600080fd5b505af1158015612f4e573d6000803e3d6000fd5b5050505050505050505050565b60cd54604051636d0be88d60e01b81523060048201526001600160a01b03858116602483015284811660448301526064820184905290911690636d0be88d90608401600060405180830381600087803b158015612fb757600080fd5b505af1158015612fcb573d6000803e3d6000fd5b50505050816001600160a01b0316836001600160a01b03160361300157604051638cd22d1960e01b815260040160405180910390fd5b6000836001600160a01b0316856001600160a01b031603613025575060001961304d565b506001600160a01b03808416600090815260d860209081526040808320938816835292905220545b60006130598383614c79565b6001600160a01b038616600090815260d7602052604081205491925090613081908590614c79565b6001600160a01b038616600090815260d76020526040812054919250906130a9908690614c66565b6001600160a01b03808916600090815260d7602052604080822086905591891681522081905590506000198414613103576001600160a01b03808816600090815260d860209081526040808320938c168352929052208390555b856001600160a01b0316876001600160a01b0316600080516020614f738339815191528760405161313691815260200190565b60405180910390a360cd5460405163352b4a3f60e11b81523060048201526001600160a01b03898116602483015288811660448301526064820188905290911690636a56947e90608401612d28565b60008061319284846139ff565b905061277781613f57565b6131a68161238a565b60db80546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f6dbf1ff28f860de5edafa4c6505e37c0aba213288cc4166c5352b6d3776c79ef90600090a35050565b60c9546040516370a0823160e01b815230600482015260009161010090046001600160a01b031690829082906370a0823190602401602060405180830381865afa15801561324a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061326e9190614c37565b90506132856001600160a01b038316863087613f6f565b6040516370a0823160e01b81523060048201526000906001600160a01b038416906370a0823190602401602060405180830381865afa1580156132cc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132f09190614c37565b905060006132fe8383614c79565b90508060de60008282546133129190614c66565b9091555090979650505050505050565b606580546001600160a01b0319169055610b2481613f96565b6000806000613348611fb9565b60d1541461336c576040516338acf79960e01b815260048101829052602401610a5d565b61337633856131f8565b90508060d4546133869190614c66565b60d4819055604080518381526020810183905291935033917fa91e67c5ea634cd43a12c5a482724b03de01e85ca68702a53d0c2f45cb7c1dc5910160405180910390a29392505050565b60cd5460405163df71403b60e01b81526001600160a01b039091169063df71403b9061340490309087908690600401614d8f565b600060405180830381600087803b15801561341e57600080fd5b505af1158015613432573d6000803e3d6000fd5b5050505061343e611fb9565b60d1541461345f57604051630e8d8c6160e21b815260040160405180910390fd5b8060d45461346c60de5490565b6134769190614c79565b1015613495576040516348c2588160e01b815260040160405180910390fd5b60006134a08461270f565b905060006134ae8383614c66565b905060008360d3546134c09190614c66565b6001600160a01b038716600090815260d96020526040902083815560d25460019091015560d381905590506134f58585613dfb565b60408051858152602081018490529081018290526001600160a01b038716907f13ed6866d4e1ee6da46f845c46d7e54120883d75c5ea9a2dacc1c4ca8984ab809060600160405180910390a260cd54604051635c77860560e01b81526001600160a01b0390911690635c778605906135759030908a908990600401614d8f565b600060405180830381600087803b15801561358f57600080fd5b505af11580156135a3573d6000803e3d6000fd5b50505050505050505050565b600054610100900460ff166135d65760405162461bcd60e51b8152600401610a5d90614db3565b6135de613fe8565b6135e783614017565b60d1541580156135f7575060d254155b61364f5760405162461bcd60e51b815260206004820152602360248201527f6d61726b6574206d6179206f6e6c7920626520696e697469616c697a6564206f6044820152626e636560e81b6064820152608401610a5d565b60cf889055876136ba5760405162461bcd60e51b815260206004820152603060248201527f696e697469616c2065786368616e67652072617465206d75737420626520677260448201526f32b0ba32b9103a3430b7103d32b9379760811b6064820152608401610a5d565b6136c38a61403e565b6136cb611fb9565b60d155670de0b6b3a764000060d2556136e3896138bd565b6136ec816128fb565b60ca6136f88882614e4e565b5060cb6137058782614e4e565b5060cc805460ff191660ff871617905581516137209061319d565b61372d82602001516126ac565b66b1a2bc2ec5000060da5560c98054610100600160a81b0319166101006001600160a01b038e811682029290921792839055604080516318160ddd60e01b8152905191909304909116916318160ddd9160048083019260209291908290030181865afa1580156137a1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137c59190614c37565b5060c9805460ff19166001179055612f4e84613322565b60c95460ff166137fe5760405162461bcd60e51b8152600401610a5d90614c13565b60c9805460ff1916905561381061199e565b506000826001600160a01b031663a6afed956040518163ffffffff1660e01b81526004016020604051808303816000875af1158015613853573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906138779190614c37565b9050801561389b57604051633eea49b760e11b815260048101829052602401610a5d565b6138a88686868686614149565b505060c9805460ff1916600117905550505050565b60006138c7611fb9565b60d154146138e857604051630be2a5cb60e11b815260040160405180910390fd5b60ce60009054906101000a90046001600160a01b03169050816001600160a01b0316632191f92a6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561393e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906139629190614d2d565b6139ae5760405162461bcd60e51b815260206004820152601c60248201527f6d61726b6572206d6574686f642072657475726e65642066616c7365000000006044820152606401610a5d565b60ce80546001600160a01b0319166001600160a01b0384811691821790925560405190918316907fedffc32e068c7c95dfd4bdfd5c4d939a084d6b11c4199eac8436ed234d72f92690600090a35050565b6040805160208101909152600081526040518060200160405280613a278560000151856145d7565b90529392505050565b600080613a3d85856139ff565b90506127ec613a4b82613f57565b846145e3565b60cd5460405163037883e560e31b81523060048201526001600160a01b0386811660248301528581166044830152848116606483015290911690631bc41f2890608401600060405180830381600087803b158015613aae57600080fd5b505af1158015613ac2573d6000803e3d6000fd5b50505050826001600160a01b0316826001600160a01b031603613af857604051633a94626760e11b815260040160405180910390fd5b60cd5460408051634ada90af60e01b815290516000926001600160a01b031691634ada90af9160048083019260209291908290030181865afa158015613b42573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613b669190614c37565b90506000613b8483604051806020016040528060da54815250613f34565b90506000613ba082604051806020016040528086815250613f16565b90506000613bae8286614c79565b905060006040518060200160405280613bc561277f565b905290506000613bd58285613185565b90508360d554613be59190614c79565b60d5556001600160a01b038816600090815260d76020526040902054613c0c908890614c79565b6001600160a01b03808a16600090815260d7602052604080822093909355908b1681522054613c3c908490614c66565b6001600160a01b03808b16600090815260d7602052604090209190915560cc54613c6d916101009091041682613dfb565b60cc5460cd5460c9546040516305bebb3b60e21b81526001600160a01b03610100948590048116946316faecec94613cb5949083169391900490911690600190600401614c8c565b600060405180830381600087803b158015613ccf57600080fd5b505af1158015613ce3573d6000803e3d6000fd5b50505050886001600160a01b0316886001600160a01b0316600080516020614f7383398151915285604051613d1a91815260200190565b60405180910390a360cc546040516001600160a01b036101009092048216918a16907f3ac0548d62d3fa3c9a817cd33899b9acacd57e8958ebe51bc7d9a79f26a8a5db90613d6b9085815260200190565b60405180910390a360cd54604051636d35bf9160e01b81523060048201526001600160a01b038c811660248301528b811660448301528a81166064830152608482018a905290911690636d35bf919060a401600060405180830381600087803b158015613dd757600080fd5b505af1158015613deb573d6000803e3d6000fd5b5050505050505050505050505050565b600060c960019054906101000a90046001600160a01b031690508160de6000828254613e279190614c79565b9091555061285890506001600160a01b03821684846127f5565b6000613e96826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166145ef9092919063ffffffff16565b9050805160001480613eb7575080806020019051810190613eb79190614d2d565b6128585760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610a5d565b6000611112613f2d84670de0b6b3a76400006145d7565b83516145fe565b6000670de0b6b3a7640000613f4d8484600001516145d7565b6111129190614ce7565b8051600090610b0d90670de0b6b3a764000090614ce7565b613f90846323b872dd60e01b85858560405160240161282193929190614d8f565b50505050565b603380546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b600054610100900460ff1661400f5760405162461bcd60e51b8152600401610a5d90614db3565b6113ea61460a565b600054610100900460ff16610b1b5760405162461bcd60e51b8152600401610a5d90614db3565b60cd5460408051623f1ee960e11b815290516001600160a01b0392831692841691627e3dd29160048083019260209291908290030181865afa158015614088573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906140ac9190614d2d565b6140f85760405162461bcd60e51b815260206004820152601c60248201527f6d61726b6572206d6574686f642072657475726e65642066616c7365000000006044820152606401610a5d565b60cd80546001600160a01b0319166001600160a01b0384811691821790925560405190918316907f7ac369dbd14fa5ea3f473ed67cc9d598964a77501540ba6751eb0b3decf5870d90600090a35050565b60cd5460405163e89d51ad60e01b81523060048201526001600160a01b03848116602483015286811660448301526064820186905283151560848301529091169063e89d51ad9060a401600060405180830381600087803b1580156141ad57600080fd5b505af11580156141c1573d6000803e3d6000fd5b505050506141cd611fb9565b60d154146141ee576040516380965b1b60e01b815260040160405180910390fd5b6141f6611fb9565b826001600160a01b0316636c540baf6040518163ffffffff1660e01b8152600401602060405180830381865afa158015614234573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906142589190614c37565b1461427657604051631046f38d60e31b815260040160405180910390fd5b846001600160a01b0316846001600160a01b0316036142a857604051631bd1a62160e21b815260040160405180910390fd5b826000036142c95760405163d29da7ef60e01b815260040160405180910390fd5b60001983036142eb57604051635982c5bb60e11b815260040160405180910390fd5b60006142f88686866124d1565b60cd5460405163c488847b60e01b815291925060009182916001600160a01b03169063c488847b9061433290309089908890600401614d8f565b6040805180830381865afa15801561434e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906143729190614f0e565b91509150600082146143e25760405162461bcd60e51b815260206004820152603360248201527f4c49515549444154455f434f4d5054524f4c4c45525f43414c43554c4154455f604482015272105353d5539517d4d152569157d19052531151606a1b6064820152608401610a5d565b6040516370a0823160e01b81526001600160a01b0388811660048301528291908716906370a0823190602401602060405180830381865afa15801561442b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061444f9190614c37565b101561449d5760405162461bcd60e51b815260206004820152601860248201527f4c49515549444154455f5345495a455f544f4f5f4d55434800000000000000006044820152606401610a5d565b306001600160a01b038616036144be576144b930898984613a51565b614521565b60405163b2a02ff160e01b81526001600160a01b0386169063b2a02ff1906144ee908b908b908690600401614d8f565b600060405180830381600087803b15801561450857600080fd5b505af115801561451c573d6000803e3d6000fd5b505050505b846001600160a01b0316876001600160a01b0316896001600160a01b03167f298637f684da70674f26509b10f07ec2fbc77a335ab1e7d6215a4b2484d8bb528685604051614579929190918252602082015260400190565b60405180910390a460cd546040516347ef3b3b60e01b81523060048201526001600160a01b0387811660248301528a8116604483015289811660648301526084820186905260a48201849052909116906347ef3b3b9060c401612d28565b60006111128284614cd0565b60006111128284614c66565b6060612777848460008561463a565b60006111128284614ce7565b600054610100900460ff166146315760405162461bcd60e51b8152600401610a5d90614db3565b6113ea33613322565b60608247101561469b5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b6064820152608401610a5d565b600080866001600160a01b031685876040516146b79190614f32565b60006040518083038185875af1925050503d80600081146146f4576040519150601f19603f3d011682016040523d82523d6000602084013e6146f9565b606091505b509150915061470a87838387614715565b979650505050505050565b6060831561478457825160000361477d576001600160a01b0385163b61477d5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610a5d565b5081612777565b61277783838151156147995781518083602001fd5b8060405162461bcd60e51b8152600401610a5d9190614803565b60005b838110156147ce5781810151838201526020016147b6565b50506000910152565b600081518084526147ef8160208601602086016147b3565b601f01601f19169290920160200192915050565b60208152600061111260208301846147d7565b60006020828403121561482857600080fd5b5035919050565b6001600160a01b0381168114610b2457600080fd5b803561484f8161482f565b919050565b6000806040838503121561486757600080fd5b82356148728161482f565b946020939093013593505050565b60006020828403121561489257600080fd5b81356111128161482f565b6000806000606084860312156148b257600080fd5b83356148bd8161482f565b925060208401356148cd8161482f565b929592945050506040919091013590565b634e487b7160e01b600052604160045260246000fd5b600082601f83011261490557600080fd5b813567ffffffffffffffff80821115614920576149206148de565b604051601f8301601f19908116603f01168101908282118183101715614948576149486148de565b8160405283815286602085880101111561496157600080fd5b836020870160208301376000602085830101528094505050505092915050565b803560ff8116811461484f57600080fd5b6000604082840312156149a457600080fd5b6040516040810181811067ffffffffffffffff821117156149c7576149c76148de565b60405290508082356149d88161482f565b815260208301356149e88161482f565b6020919091015292915050565b60008060008060008060008060008060006101808c8e031215614a1757600080fd5b614a208c614844565b9a50614a2e60208d01614844565b9950614a3c60408d01614844565b985060608c0135975067ffffffffffffffff8060808e01351115614a5f57600080fd5b614a6f8e60808f01358f016148f4565b97508060a08e01351115614a8257600080fd5b50614a938d60a08e01358e016148f4565b9550614aa160c08d01614981565b9450614aaf60e08d01614844565b9350614abe6101008d01614844565b9250614ace8d6101208e01614992565b91506101608c013590509295989b509295989b9093969950565b8015158114610b2457600080fd5b600080600080600060a08688031215614b0e57600080fd5b8535614b198161482f565b94506020860135614b298161482f565b9350604086013592506060860135614b408161482f565b91506080860135614b5081614ae8565b809150509295509295909350565b60008060408385031215614b7157600080fd5b8235614b7c8161482f565b91506020830135614b8c8161482f565b809150509250929050565b600080600060608486031215614bac57600080fd5b8335614bb78161482f565b9250602084013591506040840135614bce8161482f565b809150509250925092565b600181811c90821680614bed57607f821691505b602082108103614c0d57634e487b7160e01b600052602260045260246000fd5b50919050565b6020808252600a90820152691c994b595b9d195c995960b21b604082015260600190565b600060208284031215614c4957600080fd5b5051919050565b634e487b7160e01b600052601160045260246000fd5b80820180821115610b0d57610b0d614c50565b81810381811115610b0d57610b0d614c50565b6001600160a01b038481168252831660208201526060810160048310614cc257634e487b7160e01b600052602160045260246000fd5b826040830152949350505050565b8082028115828204841417610b0d57610b0d614c50565b600082614d0457634e487b7160e01b600052601260045260246000fd5b500490565b6001600160a01b0383168152604060208201819052600090612777908301846147d7565b600060208284031215614d3f57600080fd5b815161111281614ae8565b6001600160a01b038481168252831660208201526060604082018190526000906127ec908301846147d7565b600060018201614d8857614d88614c50565b5060010190565b6001600160a01b039384168152919092166020820152604081019190915260600190565b6020808252602b908201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960408201526a6e697469616c697a696e6760a81b606082015260800190565b601f821115612858576000816000526020600020601f850160051c81016020861015614e275750805b601f850160051c820191505b81811015614e4657828155600101614e33565b505050505050565b815167ffffffffffffffff811115614e6857614e686148de565b614e7c81614e768454614bd9565b84614dfe565b602080601f831160018114614eb15760008415614e995750858301515b600019600386901b1c1916600185901b178555614e46565b600085815260208120601f198616915b82811015614ee057888601518255948401946001909101908401614ec1565b5085821015614efe5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b60008060408385031215614f2157600080fd5b505080516020909101519092909150565b60008251614f448184602087016147b3565b919091019291505056fe7365745265647563655265736572766573426c6f636b44656c74612875696e7432353629ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa26469706673582212206e3b4df073eb91e6ad504b20b56c7d98f41c77c0f5da0d116d232e472c92581b64736f6c63430008190033", + "devdoc": { + "author": "Venus", + "events": { + "BadDebtIncreased(address,uint256,uint256,uint256)": { + "params": { + "badDebtDelta": "amount of new bad debt recorded", + "badDebtNew": "new bad debt value", + "badDebtOld": "previous bad debt value", + "borrower": "borrower to \"forgive\"" + } + }, + "BadDebtRecovered(uint256,uint256)": { + "params": { + "badDebtNew": "new bad debt value", + "badDebtOld": "previous bad debt value" + } + }, + "Initialized(uint8)": { + "details": "Triggered when the contract has been initialized or reinitialized." + } + }, + "kind": "dev", + "methods": { + "acceptOwnership()": { + "details": "The new owner accepts the ownership transfer." + }, + "accrueInterest()": { + "custom:access": "Not restricted", + "custom:event": "Emits AccrueInterest event on success", + "details": "This calculates interest accrued from the last checkpointed slot(block or second) up to the current slot(block or second) and writes new checkpoint to storage and reduce spread reserves to protocol share reserve if currentSlot - reduceReservesBlockNumber >= slotDelta", + "returns": { + "_0": "Always NO_ERROR" + } + }, + "addReserves(uint256)": { + "custom:access": "Not restricted", + "custom:event": "Emits ReservesAdded event; may emit AccrueInterest", + "params": { + "addAmount": "The amount of underlying token to add as reserves" + } + }, + "allowance(address,address)": { + "params": { + "owner": "The address of the account which owns the tokens to be spent", + "spender": "The address of the account which may transfer tokens" + }, + "returns": { + "_0": "amount The number of tokens allowed to be spent (type(uint256).max means infinite)" + } + }, + "approve(address,uint256)": { + "custom:access": "Not restricted", + "custom:error": "ZeroAddressNotAllowed is thrown when spender address is zero", + "custom:event": "Emits Approval event", + "details": "This will overwrite the approval amount for `spender` and is subject to issues noted [here](https://eips.ethereum.org/EIPS/eip-20#approve)", + "params": { + "amount": "The number of tokens that are approved (uint256.max means infinite)", + "spender": "The address of the account which may transfer tokens" + }, + "returns": { + "_0": "success Whether or not the approval succeeded" + } + }, + "badDebtRecovered(uint256)": { + "custom:access": "Only Shortfall contract", + "custom:event": "Emits BadDebtRecovered event", + "details": "Called only when bad debt is recovered from auction. Updates internal cash balance.", + "params": { + "recoveredAmount_": "The amount of bad debt recovered" + } + }, + "balanceOf(address)": { + "params": { + "owner": "The address of the account to query" + }, + "returns": { + "_0": "amount The number of tokens owned by `owner`" + } + }, + "balanceOfUnderlying(address)": { + "details": "This also accrues interest in a transaction", + "params": { + "owner": "The address of the account to query" + }, + "returns": { + "_0": "amount The amount of underlying owned by `owner`" + } + }, + "borrow(uint256)": { + "custom:access": "Not restricted", + "custom:error": "BorrowCashNotAvailable is thrown when the protocol has insufficient cash", + "custom:event": "Emits Borrow event; may emit AccrueInterest", + "params": { + "borrowAmount": "The amount of the underlying asset to borrow" + }, + "returns": { + "_0": "error Always NO_ERROR for compatibility with Venus core tooling" + } + }, + "borrowBalanceCurrent(address)": { + "params": { + "account": "The address whose balance should be calculated after updating borrowIndex" + }, + "returns": { + "_0": "borrowBalance The calculated balance" + } + }, + "borrowBalanceStored(address)": { + "params": { + "account": "The address whose balance should be calculated" + }, + "returns": { + "_0": "borrowBalance The calculated balance" + } + }, + "borrowBehalf(address,uint256)": { + "custom:access": "Not restricted", + "custom:error": "DelegateNotApproved is thrown if caller is not approved delegateBorrowCashNotAvailable is thrown when the protocol has insufficient cash", + "custom:event": "Emits Borrow event; may emit AccrueInterest", + "params": { + "borrowAmount": "The amount of the underlying asset to borrow", + "borrower": "The borrower, on behalf of whom to borrow" + }, + "returns": { + "_0": "error Always NO_ERROR for compatibility with Venus core tooling" + } + }, + "borrowRatePerBlock()": { + "returns": { + "_0": "rate The borrow interest rate per slot(block or second), scaled by 1e18" + } + }, + "constructor": { + "custom:oz-upgrades-unsafe-allow": "constructor", + "params": { + "blocksPerYear_": "The number of blocks per year", + "maxBorrowRateMantissa_": "The maximum value of borrowing rate mantissa", + "timeBased_": "A boolean indicating whether the contract is based on time or block." + } + }, + "decreaseAllowance(address,uint256)": { + "custom:access": "Not restricted", + "custom:error": "ZeroAddressNotAllowed is thrown when spender address is zero", + "custom:event": "Emits Approval event", + "params": { + "spender": "The address of the account which may transfer tokens", + "subtractedValue": "The number of tokens to remove from total approval" + }, + "returns": { + "_0": "success Whether or not the approval succeeded" + } + }, + "exchangeRateCurrent()": { + "returns": { + "_0": "exchangeRate Calculated exchange rate scaled by 1e18" + } + }, + "exchangeRateStored()": { + "details": "This function does not accrue interest before calculating the exchange rate", + "returns": { + "_0": "exchangeRate Calculated exchange rate scaled by 1e18" + } + }, + "forceLiquidateBorrow(address,address,uint256,address,bool)": { + "custom:access": "Only Comptroller", + "custom:error": "ForceLiquidateBorrowUnauthorized is thrown when the request does not come from ComptrollerLiquidateAccrueCollateralInterestFailed is thrown when it is not possible to accrue interest on the collateral vTokenLiquidateCollateralFreshnessCheck is thrown when interest has not been accrued on the collateral vTokenLiquidateLiquidatorIsBorrower is thrown when trying to liquidate selfLiquidateCloseAmountIsZero is thrown when repayment amount is zeroLiquidateCloseAmountIsUintMax is thrown when repayment amount is UINT_MAX", + "custom:event": "Emits LiquidateBorrow event; may emit AccrueInterest", + "params": { + "borrower": "The borrower of this vToken to be liquidated", + "liquidator": "The address repaying the borrow and seizing collateral", + "repayAmount": "The amount of the underlying borrowed asset to repay", + "skipLiquidityCheck": "If set to true, allows to liquidate up to 100% of the borrow regardless of the account liquidity", + "vTokenCollateral": "The market in which to seize collateral from the borrower" + } + }, + "getAccountSnapshot(address)": { + "details": "This is used by comptroller to more efficiently perform liquidity checks.", + "params": { + "account": "Address of the account to snapshot" + }, + "returns": { + "borrowBalance": "Amount owed in terms of underlying", + "error": "Always NO_ERROR for compatibility with Venus core tooling", + "exchangeRate": "Stored exchange rate", + "vTokenBalance": "User's balance of vTokens" + } + }, + "getBlockNumberOrTimestamp()": { + "details": "Function to simply retrieve block number or block timestamp", + "returns": { + "_0": "Current block number or block timestamp" + } + }, + "getCash()": { + "returns": { + "_0": "cash The quantity of underlying asset tracked internally by this contract" + } + }, + "healBorrow(address,address,uint256)": { + "custom:access": "Only Comptroller", + "custom:error": "HealBorrowUnauthorized is thrown when the request does not come from Comptroller", + "custom:event": "Emits RepayBorrow, BadDebtIncreased events; may emit AccrueInterest", + "details": "This function does not call any Comptroller hooks (like \"healAllowed\"), because we assume the Comptroller does all the necessary checks before calling this function.", + "params": { + "borrower": "account to heal", + "payer": "account who repays the debt", + "repayAmount": "amount to repay" + } + }, + "increaseAllowance(address,uint256)": { + "custom:access": "Not restricted", + "custom:error": "ZeroAddressNotAllowed is thrown when spender address is zero", + "custom:event": "Emits Approval event", + "params": { + "addedValue": "The number of additional tokens spender can transfer", + "spender": "The address of the account which may transfer tokens" + }, + "returns": { + "_0": "success Whether or not the approval succeeded" + } + }, + "initialize(address,address,address,uint256,string,string,uint8,address,address,(address,address),uint256)": { + "custom:error": "ZeroAddressNotAllowed is thrown when admin address is zeroZeroAddressNotAllowed is thrown when shortfall contract address is zeroZeroAddressNotAllowed is thrown when protocol share reserve address is zero", + "params": { + "accessControlManager_": "AccessControlManager contract address", + "admin_": "Address of the administrator of this token", + "comptroller_": "The address of the Comptroller", + "decimals_": "ERC-20 decimal precision of this token", + "initialExchangeRateMantissa_": "The initial exchange rate, scaled by 1e18", + "interestRateModel_": "The address of the interest rate model", + "name_": "ERC-20 name of this token", + "reserveFactorMantissa_": "Percentage of borrow interest that goes to reserves (from 0 to 1e18)", + "riskManagement": "Addresses of risk & income related contracts", + "symbol_": "ERC-20 symbol of this token", + "underlying_": "The address of the underlying asset" + } + }, + "isVToken()": { + "returns": { + "_0": "Always true" + } + }, + "liquidateBorrow(address,uint256,address)": { + "custom:access": "Not restricted", + "custom:error": "LiquidateAccrueCollateralInterestFailed is thrown when it is not possible to accrue interest on the collateral vTokenLiquidateCollateralFreshnessCheck is thrown when interest has not been accrued on the collateral vTokenLiquidateLiquidatorIsBorrower is thrown when trying to liquidate selfLiquidateCloseAmountIsZero is thrown when repayment amount is zeroLiquidateCloseAmountIsUintMax is thrown when repayment amount is UINT_MAX", + "custom:event": "Emits LiquidateBorrow event; may emit AccrueInterest", + "params": { + "borrower": "The borrower of this vToken to be liquidated", + "repayAmount": "The amount of the underlying borrowed asset to repay", + "vTokenCollateral": "The market in which to seize collateral from the borrower" + }, + "returns": { + "_0": "error Always NO_ERROR for compatibility with Venus core tooling" + } + }, + "mint(uint256)": { + "custom:access": "Not restricted", + "custom:event": "Emits Mint and Transfer events; may emit AccrueInterest", + "details": "Accrues interest whether or not the operation succeeds, unless reverted", + "params": { + "mintAmount": "The amount of the underlying asset to supply" + }, + "returns": { + "_0": "error Always NO_ERROR for compatibility with Venus core tooling" + } + }, + "mintBehalf(address,uint256)": { + "custom:access": "Not restricted", + "custom:error": "ZeroAddressNotAllowed is thrown when minter address is zero", + "custom:event": "Emits Mint and Transfer events; may emit AccrueInterest", + "details": "Accrues interest whether or not the operation succeeds, unless reverted", + "params": { + "mintAmount": "The amount of the underlying asset to supply", + "minter": "User whom the supply will be attributed to" + }, + "returns": { + "_0": "error Always NO_ERROR for compatibility with Venus core tooling" + } + }, + "owner()": { + "details": "Returns the address of the current owner." + }, + "pendingOwner()": { + "details": "Returns the address of the pending owner." + }, + "redeem(uint256)": { + "custom:access": "Not restricted", + "custom:error": "RedeemTransferOutNotPossible is thrown when the protocol has insufficient cash", + "custom:event": "Emits Redeem and Transfer events; may emit AccrueInterest", + "details": "Accrues interest whether or not the operation succeeds, unless reverted", + "params": { + "redeemTokens": "The number of vTokens to redeem into underlying" + }, + "returns": { + "_0": "error Always NO_ERROR for compatibility with Venus core tooling" + } + }, + "redeemBehalf(address,uint256)": { + "custom:access": "Not restricted", + "custom:error": "InsufficientRedeemApproval is thrown when sender is not approved by the redeemer for the given amountRedeemTransferOutNotPossible is thrown when the protocol has insufficient cash", + "custom:event": "Emits Redeem and Transfer events; may emit AccrueInterest", + "details": "Accrues interest whether or not the operation succeeds, unless reverted", + "params": { + "redeemTokens": "The number of vTokens to redeem into underlying", + "redeemer": "The user on behalf of whom to redeem" + }, + "returns": { + "_0": "error Always NO_ERROR for compatibility with Venus core tooling" + } + }, + "redeemUnderlying(uint256)": { + "details": "Accrues interest whether or not the operation succeeds, unless reverted", + "params": { + "redeemAmount": "The amount of underlying to receive from redeeming vTokens" + }, + "returns": { + "_0": "error Always NO_ERROR for compatibility with Venus core tooling" + } + }, + "redeemUnderlyingBehalf(address,uint256)": { + "custom:access": "Not restricted", + "custom:error": "InsufficientRedeemApproval is thrown when sender is not approved by the redeemer for the given amount", + "custom:event": "Emits Redeem and Transfer events; may emit AccrueInterest", + "details": "Accrues interest whether or not the operation succeeds, unless reverted", + "params": { + "redeemAmount": "The amount of underlying to receive from redeeming vTokens", + "redeemer": ", on behalf of whom to redeem" + }, + "returns": { + "_0": "error Always NO_ERROR for compatibility with Venus core tooling" + } + }, + "reduceReserves(uint256)": { + "custom:access": "Not restricted", + "custom:error": "ReduceReservesCashNotAvailable is thrown when the vToken does not have sufficient cashReduceReservesCashValidation is thrown when trying to withdraw more cash than the reserves have", + "custom:event": "Emits ReservesReduced event; may emit AccrueInterest", + "details": "Gracefully return if reserves already reduced in accrueInterest", + "params": { + "reduceAmount": "Amount of reduction to reserves" + } + }, + "renounceOwnership()": { + "details": "Leaves the contract without owner. It will not be possible to call `onlyOwner` functions. Can only be called by the current owner. NOTE: Renouncing ownership will leave the contract without an owner, thereby disabling any functionality that is only available to the owner." + }, + "repayBorrow(uint256)": { + "custom:access": "Not restricted", + "custom:event": "Emits RepayBorrow event; may emit AccrueInterest", + "params": { + "repayAmount": "The amount to repay, or type(uint256).max for the full outstanding amount" + }, + "returns": { + "_0": "error Always NO_ERROR for compatibility with Venus core tooling" + } + }, + "repayBorrowBehalf(address,uint256)": { + "custom:access": "Not restricted", + "custom:event": "Emits RepayBorrow event; may emit AccrueInterest", + "params": { + "borrower": "the account with the debt being payed off", + "repayAmount": "The amount to repay, or type(uint256).max for the full outstanding amount" + }, + "returns": { + "_0": "error Always NO_ERROR for compatibility with Venus core tooling" + } + }, + "seize(address,address,uint256)": { + "custom:access": "Not restricted", + "custom:error": "LiquidateSeizeLiquidatorIsBorrower is thrown when trying to liquidate self", + "custom:event": "Emits Transfer, ReservesAdded events", + "details": "Will fail unless called by another vToken during the process of liquidation. It's absolutely critical to use msg.sender as the borrowed vToken and not a parameter.", + "params": { + "borrower": "The account having collateral seized", + "liquidator": "The account receiving seized collateral", + "seizeTokens": "The number of vTokens to seize" + } + }, + "setAccessControlManager(address)": { + "custom:access": "Only Governance", + "custom:event": "Emits NewAccessControlManager event", + "details": "Admin function to set address of AccessControlManager", + "params": { + "accessControlManager_": "The new address of the AccessControlManager" + } + }, + "setInterestRateModel(address)": { + "custom:access": "Controlled by AccessControlManager", + "custom:error": "Unauthorized error is thrown when the call is not authorized by AccessControlManager", + "custom:event": "Emits NewMarketInterestRateModel event; may emit AccrueInterest", + "details": "Admin function to accrue interest and update the interest rate model", + "params": { + "newInterestRateModel": "the new interest rate model to use" + } + }, + "setProtocolSeizeShare(uint256)": { + "custom:access": "Controlled by AccessControlManager", + "custom:error": "Unauthorized error is thrown when the call is not authorized by AccessControlManagerProtocolSeizeShareTooBig is thrown when the new seize share is too high", + "custom:event": "Emits NewProtocolSeizeShare event on success", + "details": "must be equal or less than liquidation incentive - 1", + "params": { + "newProtocolSeizeShareMantissa_": "new protocol share mantissa" + } + }, + "setProtocolShareReserve(address)": { + "custom:access": "Only Governance", + "custom:error": "ZeroAddressNotAllowed is thrown when protocol share reserve address is zero", + "params": { + "protocolShareReserve_": "The address of the protocol share reserve contract" + } + }, + "setReduceReservesBlockDelta(uint256)": { + "custom:access": "Only Governance", + "params": { + "_newReduceReservesBlockOrTimestampDelta": "slot(block or second) difference value" + } + }, + "setReserveFactor(uint256)": { + "custom:access": "Controlled by AccessControlManager", + "custom:error": "Unauthorized error is thrown when the call is not authorized by AccessControlManagerSetReserveFactorBoundsCheck is thrown when the new reserve factor is too high", + "custom:event": "Emits NewReserveFactor event; may emit AccrueInterest", + "details": "Admin function to accrue interest and set a new reserve factor", + "params": { + "newReserveFactorMantissa": "New reserve factor (from 0 to 1e18)" + } + }, + "setShortfallContract(address)": { + "custom:access": "Only Governance", + "custom:error": "ZeroAddressNotAllowed is thrown when shortfall contract address is zero", + "params": { + "shortfall_": "The address of the shortfall contract" + } + }, + "supplyRatePerBlock()": { + "returns": { + "_0": "rate The supply interest rate per slot(block or second), scaled by 1e18" + } + }, + "sweepToken(address)": { + "custom:access": "Only Governance", + "params": { + "token": "The address of the ERC-20 token to sweep" + } + }, + "syncCash()": { + "custom:access": "Controlled by AccessControlManager", + "custom:event": "Emits CashSynced", + "details": "Used for one-time migration after upgrade to initialize internalCash" + }, + "totalBorrowsCurrent()": { + "returns": { + "_0": "totalBorrows The total borrows with interest" + } + }, + "transfer(address,uint256)": { + "custom:access": "Not restricted", + "custom:error": "TransferNotAllowed is thrown if trying to transfer to self", + "custom:event": "Emits Transfer event on success", + "params": { + "amount": "The number of tokens to transfer", + "dst": "The address of the destination account" + }, + "returns": { + "_0": "success True if the transfer succeeded, reverts otherwise" + } + }, + "transferFrom(address,address,uint256)": { + "custom:access": "Not restricted", + "custom:error": "TransferNotAllowed is thrown if trying to transfer to self", + "custom:event": "Emits Transfer event on success", + "params": { + "amount": "The number of tokens to transfer", + "dst": "The address of the destination account", + "src": "The address of the source account" + }, + "returns": { + "_0": "success True if the transfer succeeded, reverts otherwise" + } + }, + "transferOwnership(address)": { + "details": "Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one. Can only be called by the current owner." + } + }, + "stateVariables": { + "MAX_BORROW_RATE_MANTISSA": { + "custom:oz-upgrades-unsafe-allow": "state-variable-immutable" + } + }, + "title": "VToken", + "version": 1 + }, + "userdoc": { + "errors": { + "InvalidBlocksPerYear()": [ + { + "notice": "Thrown when blocks per year is invalid" + } + ], + "InvalidTimeBasedConfiguration()": [ + { + "notice": "Thrown when time based but blocks per year is provided" + } + ], + "Unauthorized(address,address,string)": [ + { + "notice": "Thrown when the action is prohibited by AccessControlManager" + } + ], + "ZeroAddressNotAllowed()": [ + { + "notice": "Thrown if the supplied address is a zero address where it is not allowed" + } + ] + }, + "events": { + "AccrueInterest(uint256,uint256,uint256,uint256)": { + "notice": "Event emitted when interest is accrued" + }, + "Approval(address,address,uint256)": { + "notice": "EIP20 Approval event" + }, + "BadDebtIncreased(address,uint256,uint256,uint256)": { + "notice": "Event emitted when bad debt is accumulated on a market" + }, + "BadDebtRecovered(uint256,uint256)": { + "notice": "Event emitted when bad debt is recovered via an auction" + }, + "Borrow(address,uint256,uint256,uint256)": { + "notice": "Event emitted when underlying is borrowed" + }, + "CashSynced(uint256,uint256)": { + "notice": "Event emitted when internalCash is synced with actual token balance" + }, + "HealBorrow(address,address,uint256)": { + "notice": "Event emitted when healing the borrow" + }, + "LiquidateBorrow(address,address,uint256,address,uint256)": { + "notice": "Event emitted when a borrow is liquidated" + }, + "Mint(address,uint256,uint256,uint256)": { + "notice": "Event emitted when tokens are minted" + }, + "NewAccessControlManager(address,address)": { + "notice": "Emitted when access control manager contract address is changed" + }, + "NewComptroller(address,address)": { + "notice": "Event emitted when comptroller is changed" + }, + "NewMarketInterestRateModel(address,address)": { + "notice": "Event emitted when interestRateModel is changed" + }, + "NewProtocolSeizeShare(uint256,uint256)": { + "notice": "Event emitted when protocol seize share is changed" + }, + "NewProtocolShareReserve(address,address)": { + "notice": "Event emitted when protocol share reserve contract address is changed" + }, + "NewReduceReservesBlockDelta(uint256,uint256)": { + "notice": "Event emitted when reduce reserves slot (block or second) delta is changed" + }, + "NewReserveFactor(uint256,uint256)": { + "notice": "Event emitted when the reserve factor is changed" + }, + "NewShortfallContract(address,address)": { + "notice": "Event emitted when shortfall contract address is changed" + }, + "ProtocolSeize(address,address,uint256)": { + "notice": "Event emitted when liquidation reserves are reduced" + }, + "Redeem(address,uint256,uint256,uint256)": { + "notice": "Event emitted when tokens are redeemed" + }, + "RepayBorrow(address,address,uint256,uint256,uint256)": { + "notice": "Event emitted when a borrow is repaid" + }, + "ReservesAdded(address,uint256,uint256)": { + "notice": "Event emitted when the reserves are added" + }, + "SpreadReservesReduced(address,uint256,uint256)": { + "notice": "Event emitted when the spread reserves are reduced" + }, + "SweepToken(address)": { + "notice": "Event emitted when tokens are swept" + }, + "Transfer(address,address,uint256)": { + "notice": "EIP20 Transfer event" + } + }, + "kind": "user", + "methods": { + "accessControlManager()": { + "notice": "Returns the address of the access control manager contract" + }, + "accrualBlockNumber()": { + "notice": "Slot(block or second) number that interest was last accrued at" + }, + "accrueInterest()": { + "notice": "Applies accrued interest to total borrows and reserves" + }, + "addReserves(uint256)": { + "notice": "The sender adds to reserves." + }, + "allowance(address,address)": { + "notice": "Get the current allowance from `owner` for `spender`" + }, + "approve(address,uint256)": { + "notice": "Approve `spender` to transfer up to `amount` from `src`" + }, + "badDebt()": { + "notice": "Total bad debt of the market" + }, + "badDebtRecovered(uint256)": { + "notice": "Updates bad debt" + }, + "balanceOf(address)": { + "notice": "Get the token balance of the `owner`" + }, + "balanceOfUnderlying(address)": { + "notice": "Get the underlying balance of the `owner`" + }, + "blocksOrSecondsPerYear()": { + "notice": "Stores blocksPerYear if isTimeBased is true else secondsPerYear is stored" + }, + "borrow(uint256)": { + "notice": "Sender borrows assets from the protocol to their own address" + }, + "borrowBalanceCurrent(address)": { + "notice": "Accrue interest to updated borrowIndex and then calculate account's borrow balance using the updated borrowIndex" + }, + "borrowBalanceStored(address)": { + "notice": "Return the borrow balance of account based on stored data" + }, + "borrowBehalf(address,uint256)": { + "notice": "Sender borrows assets on behalf of some other address. This function is only available for senders, explicitly marked as delegates of the borrower using `comptroller.updateDelegate`" + }, + "borrowIndex()": { + "notice": "Accumulator of the total earned interest rate since the opening of the market" + }, + "borrowRatePerBlock()": { + "notice": "Returns the current per slot(block or second) borrow interest rate for this vToken" + }, + "comptroller()": { + "notice": "Contract which oversees inter-vToken operations" + }, + "decimals()": { + "notice": "EIP-20 token decimals for this token" + }, + "decreaseAllowance(address,uint256)": { + "notice": "Decreases approval for `spender`" + }, + "exchangeRateCurrent()": { + "notice": "Accrue interest then return the up-to-date exchange rate" + }, + "exchangeRateStored()": { + "notice": "Calculates the exchange rate from the underlying to the VToken" + }, + "forceLiquidateBorrow(address,address,uint256,address,bool)": { + "notice": "The extended version of liquidations, callable only by Comptroller. May skip the close factor check. The collateral seized is transferred to the liquidator." + }, + "getAccountSnapshot(address)": { + "notice": "Get a snapshot of the account's balances, and the cached exchange rate" + }, + "getCash()": { + "notice": "Get the internally tracked cash balance of this vToken in the underlying asset" + }, + "healBorrow(address,address,uint256)": { + "notice": "Repays a certain amount of debt, treats the rest of the borrow as bad debt, essentially \"forgiving\" the borrower. Healing is a situation that should rarely happen. However, some pools may list risky assets or be configured improperly – we want to still handle such cases gracefully. We assume that Comptroller does the seizing, so this function is only available to Comptroller." + }, + "increaseAllowance(address,uint256)": { + "notice": "Increase approval for `spender`" + }, + "initialize(address,address,address,uint256,string,string,uint8,address,address,(address,address),uint256)": { + "notice": "Construct a new money market" + }, + "interestRateModel()": { + "notice": "Model which tells what the current interest rate should be" + }, + "internalCash()": { + "notice": "Tracked internal cash balance, immune to direct token transfers (donation attacks)" + }, + "isTimeBased()": { + "notice": "Acknowledges if a contract is time based or not" + }, + "isVToken()": { + "notice": "Indicator that this is a VToken contract (for inspection)" + }, + "liquidateBorrow(address,uint256,address)": { + "notice": "The sender liquidates the borrowers collateral. The collateral seized is transferred to the liquidator." + }, + "mint(uint256)": { + "notice": "Sender supplies assets into the market and receives vTokens in exchange" + }, + "mintBehalf(address,uint256)": { + "notice": "Sender calls on-behalf of minter. minter supplies assets into the market and receives vTokens in exchange" + }, + "name()": { + "notice": "EIP-20 token name for this token" + }, + "protocolSeizeShareMantissa()": { + "notice": "Share of seized collateral that is added to reserves" + }, + "protocolShareReserve()": { + "notice": "Protocol share Reserve contract address" + }, + "redeem(uint256)": { + "notice": "Sender redeems vTokens in exchange for the underlying asset" + }, + "redeemBehalf(address,uint256)": { + "notice": "Sender redeems assets on behalf of some other address. This function is only available for senders, explicitly marked as delegates of the supplier using `comptroller.updateDelegate`" + }, + "redeemUnderlying(uint256)": { + "notice": "Sender redeems vTokens in exchange for a specified amount of underlying asset" + }, + "redeemUnderlyingBehalf(address,uint256)": { + "notice": "Sender redeems underlying assets on behalf of some other address. This function is only available for senders, explicitly marked as delegates of the supplier using `comptroller.updateDelegate`" + }, + "reduceReserves(uint256)": { + "notice": "Accrues interest and reduces reserves by transferring to the protocol reserve contract" + }, + "reduceReservesBlockDelta()": { + "notice": "delta slot (block or second) after which reserves will be reduced" + }, + "reduceReservesBlockNumber()": { + "notice": "last slot (block or second) number at which reserves were reduced" + }, + "repayBorrow(uint256)": { + "notice": "Sender repays their own borrow" + }, + "repayBorrowBehalf(address,uint256)": { + "notice": "Sender repays a borrow belonging to borrower" + }, + "reserveFactorMantissa()": { + "notice": "Fraction of interest currently set aside for reserves" + }, + "seize(address,address,uint256)": { + "notice": "Transfers collateral tokens (this market) to the liquidator." + }, + "setAccessControlManager(address)": { + "notice": "Sets the address of AccessControlManager" + }, + "setInterestRateModel(address)": { + "notice": "accrues interest and updates the interest rate model using _setInterestRateModelFresh" + }, + "setProtocolSeizeShare(uint256)": { + "notice": "sets protocol share accumulated from liquidations" + }, + "setProtocolShareReserve(address)": { + "notice": "Sets protocol share reserve contract address" + }, + "setReduceReservesBlockDelta(uint256)": { + "notice": "A public function to set new threshold of slot(block or second) difference after which funds will be sent to the protocol share reserve" + }, + "setReserveFactor(uint256)": { + "notice": "accrues interest and sets a new reserve factor for the protocol using _setReserveFactorFresh" + }, + "setShortfallContract(address)": { + "notice": "Sets shortfall contract address" + }, + "shortfall()": { + "notice": "Storage of Shortfall contract address" + }, + "supplyRatePerBlock()": { + "notice": "Returns the current per-slot(block or second) supply interest rate for this v" + }, + "sweepToken(address)": { + "notice": "A public function to sweep accidental ERC-20 transfers to this contract. Tokens are sent to admin (timelock)" + }, + "symbol()": { + "notice": "EIP-20 token symbol for this token" + }, + "syncCash()": { + "notice": "Sync internalCash with the actual underlying token balance" + }, + "totalBorrows()": { + "notice": "Total amount of outstanding borrows of the underlying in this market" + }, + "totalBorrowsCurrent()": { + "notice": "Returns the current total borrows plus accrued interest" + }, + "totalReserves()": { + "notice": "Total amount of reserves of the underlying held in this market" + }, + "totalSupply()": { + "notice": "Total number of tokens in circulation" + }, + "transfer(address,uint256)": { + "notice": "Transfer `amount` tokens from `msg.sender` to `dst`" + }, + "transferFrom(address,address,uint256)": { + "notice": "Transfer `amount` tokens from `src` to `dst`" + }, + "underlying()": { + "notice": "Underlying asset for this VToken" + } + }, + "notice": "Each asset that is supported by a pool is integrated through an instance of the `VToken` contract. As outlined in the protocol overview, each isolated pool creates its own `vToken` corresponding to an asset. Within a given pool, each included `vToken` is referred to as a market of the pool. The main actions a user regularly interacts with in a market are: - mint/redeem of vTokens; - transfer of vTokens; - borrow/repay a loan on an underlying asset; - liquidate a borrow or liquidate/heal an account. A user supplies the underlying asset to a pool by minting `vTokens`, where the corresponding `vToken` amount is determined by the `exchangeRate`. The `exchangeRate` will change over time, dependent on a number of factors, some of which accrue interest. Additionally, once users have minted `vToken` in a pool, they can borrow any asset in the isolated pool by using their `vToken` as collateral. In order to borrow an asset or use a `vToken` as collateral, the user must be entered into each corresponding market (else, the `vToken` will not be considered collateral for a borrow). Note that a user may borrow up to a portion of their collateral determined by the market’s collateral factor. However, if their borrowed amount exceeds an amount calculated using the market’s corresponding liquidation threshold, the borrow is eligible for liquidation. When a user repays a borrow, they must also pay off interest accrued on the borrow. The Venus protocol includes unique mechanisms for healing an account and liquidating an account. These actions are performed in the `Comptroller` and consider all borrows and collateral for which a given account is entered within a market. These functions may only be called on an account with a total collateral amount that is no larger than a universal `minLiquidatableCollateral` value, which is used for all markets within a `Comptroller`. Both functions settle all of an account’s borrows, but `healAccount()` may add `badDebt` to a vToken. For more detail, see the description of `healAccount()` and `liquidateAccount()` in the `Comptroller` summary section below.", + "version": 1 + }, + "storageLayout": { + "storage": [ + { + "astId": 292, + "contract": "contracts/VToken.sol:VToken", + "label": "_initialized", + "offset": 0, + "slot": "0", + "type": "t_uint8" + }, + { + "astId": 295, + "contract": "contracts/VToken.sol:VToken", + "label": "_initializing", + "offset": 1, + "slot": "0", + "type": "t_bool" + }, + { + "astId": 1562, + "contract": "contracts/VToken.sol:VToken", + "label": "__gap", + "offset": 0, + "slot": "1", + "type": "t_array(t_uint256)50_storage" + }, + { + "astId": 164, + "contract": "contracts/VToken.sol:VToken", + "label": "_owner", + "offset": 0, + "slot": "51", + "type": "t_address" + }, + { + "astId": 284, + "contract": "contracts/VToken.sol:VToken", + "label": "__gap", + "offset": 0, + "slot": "52", + "type": "t_array(t_uint256)49_storage" + }, + { + "astId": 57, + "contract": "contracts/VToken.sol:VToken", + "label": "_pendingOwner", + "offset": 0, + "slot": "101", + "type": "t_address" + }, + { + "astId": 151, + "contract": "contracts/VToken.sol:VToken", + "label": "__gap", + "offset": 0, + "slot": "102", + "type": "t_array(t_uint256)49_storage" + }, + { + "astId": 7293, + "contract": "contracts/VToken.sol:VToken", + "label": "_accessControlManager", + "offset": 0, + "slot": "151", + "type": "t_contract(IAccessControlManagerV8)7478" + }, + { + "astId": 7298, + "contract": "contracts/VToken.sol:VToken", + "label": "__gap", + "offset": 0, + "slot": "152", + "type": "t_array(t_uint256)49_storage" + }, + { + "astId": 42944, + "contract": "contracts/VToken.sol:VToken", + "label": "_notEntered", + "offset": 0, + "slot": "201", + "type": "t_bool" + }, + { + "astId": 42947, + "contract": "contracts/VToken.sol:VToken", + "label": "underlying", + "offset": 1, + "slot": "201", + "type": "t_address" + }, + { + "astId": 42950, + "contract": "contracts/VToken.sol:VToken", + "label": "name", + "offset": 0, + "slot": "202", + "type": "t_string_storage" + }, + { + "astId": 42953, + "contract": "contracts/VToken.sol:VToken", + "label": "symbol", + "offset": 0, + "slot": "203", + "type": "t_string_storage" + }, + { + "astId": 42956, + "contract": "contracts/VToken.sol:VToken", + "label": "decimals", + "offset": 0, + "slot": "204", + "type": "t_uint8" + }, + { + "astId": 42959, + "contract": "contracts/VToken.sol:VToken", + "label": "protocolShareReserve", + "offset": 1, + "slot": "204", + "type": "t_address_payable" + }, + { + "astId": 42963, + "contract": "contracts/VToken.sol:VToken", + "label": "comptroller", + "offset": 0, + "slot": "205", + "type": "t_contract(ComptrollerInterface)20611" + }, + { + "astId": 42967, + "contract": "contracts/VToken.sol:VToken", + "label": "interestRateModel", + "offset": 0, + "slot": "206", + "type": "t_contract(InterestRateModel)22338" + }, + { + "astId": 42969, + "contract": "contracts/VToken.sol:VToken", + "label": "initialExchangeRateMantissa", + "offset": 0, + "slot": "207", + "type": "t_uint256" + }, + { + "astId": 42972, + "contract": "contracts/VToken.sol:VToken", + "label": "reserveFactorMantissa", + "offset": 0, + "slot": "208", + "type": "t_uint256" + }, + { + "astId": 42975, + "contract": "contracts/VToken.sol:VToken", + "label": "accrualBlockNumber", + "offset": 0, + "slot": "209", + "type": "t_uint256" + }, + { + "astId": 42978, + "contract": "contracts/VToken.sol:VToken", + "label": "borrowIndex", + "offset": 0, + "slot": "210", + "type": "t_uint256" + }, + { + "astId": 42981, + "contract": "contracts/VToken.sol:VToken", + "label": "totalBorrows", + "offset": 0, + "slot": "211", + "type": "t_uint256" + }, + { + "astId": 42984, + "contract": "contracts/VToken.sol:VToken", + "label": "totalReserves", + "offset": 0, + "slot": "212", + "type": "t_uint256" + }, + { + "astId": 42987, + "contract": "contracts/VToken.sol:VToken", + "label": "totalSupply", + "offset": 0, + "slot": "213", + "type": "t_uint256" + }, + { + "astId": 42990, + "contract": "contracts/VToken.sol:VToken", + "label": "badDebt", + "offset": 0, + "slot": "214", + "type": "t_uint256" + }, + { + "astId": 42994, + "contract": "contracts/VToken.sol:VToken", + "label": "accountTokens", + "offset": 0, + "slot": "215", + "type": "t_mapping(t_address,t_uint256)" + }, + { + "astId": 43000, + "contract": "contracts/VToken.sol:VToken", + "label": "transferAllowances", + "offset": 0, + "slot": "216", + "type": "t_mapping(t_address,t_mapping(t_address,t_uint256))" + }, + { + "astId": 43005, + "contract": "contracts/VToken.sol:VToken", + "label": "accountBorrows", + "offset": 0, + "slot": "217", + "type": "t_mapping(t_address,t_struct(BorrowSnapshot)42941_storage)" + }, + { + "astId": 43008, + "contract": "contracts/VToken.sol:VToken", + "label": "protocolSeizeShareMantissa", + "offset": 0, + "slot": "218", + "type": "t_uint256" + }, + { + "astId": 43011, + "contract": "contracts/VToken.sol:VToken", + "label": "shortfall", + "offset": 0, + "slot": "219", + "type": "t_address" + }, + { + "astId": 43014, + "contract": "contracts/VToken.sol:VToken", + "label": "reduceReservesBlockDelta", + "offset": 0, + "slot": "220", + "type": "t_uint256" + }, + { + "astId": 43017, + "contract": "contracts/VToken.sol:VToken", + "label": "reduceReservesBlockNumber", + "offset": 0, + "slot": "221", + "type": "t_uint256" + }, + { + "astId": 43020, + "contract": "contracts/VToken.sol:VToken", + "label": "internalCash", + "offset": 0, + "slot": "222", + "type": "t_uint256" + }, + { + "astId": 43025, + "contract": "contracts/VToken.sol:VToken", + "label": "__gap", + "offset": 0, + "slot": "223", + "type": "t_array(t_uint256)47_storage" + }, + { + "astId": 10223, + "contract": "contracts/VToken.sol:VToken", + "label": "__gap", + "offset": 0, + "slot": "270", + "type": "t_array(t_uint256)48_storage" + } + ], + "types": { + "t_address": { + "encoding": "inplace", + "label": "address", + "numberOfBytes": "20" + }, + "t_address_payable": { + "encoding": "inplace", + "label": "address payable", + "numberOfBytes": "20" + }, + "t_array(t_uint256)47_storage": { + "base": "t_uint256", + "encoding": "inplace", + "label": "uint256[47]", + "numberOfBytes": "1504" + }, + "t_array(t_uint256)48_storage": { + "base": "t_uint256", + "encoding": "inplace", + "label": "uint256[48]", + "numberOfBytes": "1536" + }, + "t_array(t_uint256)49_storage": { + "base": "t_uint256", + "encoding": "inplace", + "label": "uint256[49]", + "numberOfBytes": "1568" + }, + "t_array(t_uint256)50_storage": { + "base": "t_uint256", + "encoding": "inplace", + "label": "uint256[50]", + "numberOfBytes": "1600" + }, + "t_bool": { + "encoding": "inplace", + "label": "bool", + "numberOfBytes": "1" + }, + "t_contract(ComptrollerInterface)20611": { + "encoding": "inplace", + "label": "contract ComptrollerInterface", + "numberOfBytes": "20" + }, + "t_contract(IAccessControlManagerV8)7478": { + "encoding": "inplace", + "label": "contract IAccessControlManagerV8", + "numberOfBytes": "20" + }, + "t_contract(InterestRateModel)22338": { + "encoding": "inplace", + "label": "contract InterestRateModel", + "numberOfBytes": "20" + }, + "t_mapping(t_address,t_mapping(t_address,t_uint256))": { + "encoding": "mapping", + "key": "t_address", + "label": "mapping(address => mapping(address => uint256))", + "numberOfBytes": "32", + "value": "t_mapping(t_address,t_uint256)" + }, + "t_mapping(t_address,t_struct(BorrowSnapshot)42941_storage)": { + "encoding": "mapping", + "key": "t_address", + "label": "mapping(address => struct VTokenStorage.BorrowSnapshot)", + "numberOfBytes": "32", + "value": "t_struct(BorrowSnapshot)42941_storage" + }, + "t_mapping(t_address,t_uint256)": { + "encoding": "mapping", + "key": "t_address", + "label": "mapping(address => uint256)", + "numberOfBytes": "32", + "value": "t_uint256" + }, + "t_string_storage": { + "encoding": "bytes", + "label": "string", + "numberOfBytes": "32" + }, + "t_struct(BorrowSnapshot)42941_storage": { + "encoding": "inplace", + "label": "struct VTokenStorage.BorrowSnapshot", + "members": [ + { + "astId": 42938, + "contract": "contracts/VToken.sol:VToken", + "label": "principal", + "offset": 0, + "slot": "0", + "type": "t_uint256" + }, + { + "astId": 42940, + "contract": "contracts/VToken.sol:VToken", + "label": "interestIndex", + "offset": 0, + "slot": "1", + "type": "t_uint256" + } + ], + "numberOfBytes": "64" + }, + "t_uint256": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + }, + "t_uint8": { + "encoding": "inplace", + "label": "uint8", + "numberOfBytes": "1" + } + } + } +} diff --git a/deployments/bsctestnet/VToken_vUSDC_HubSpoke.json b/deployments/bsctestnet/VToken_vUSDC_HubSpoke.json new file mode 100644 index 000000000..b443cc96e --- /dev/null +++ b/deployments/bsctestnet/VToken_vUSDC_HubSpoke.json @@ -0,0 +1,257 @@ +{ + "address": "0xD05514217FD359659aE7da7740e79C11947eBB32", + "abi": [ + { + "inputs": [ + { + "internalType": "address", + "name": "beacon", + "type": "address" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "stateMutability": "payable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "previousAdmin", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "newAdmin", + "type": "address" + } + ], + "name": "AdminChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "beacon", + "type": "address" + } + ], + "name": "BeaconUpgraded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "implementation", + "type": "address" + } + ], + "name": "Upgraded", + "type": "event" + }, + { + "stateMutability": "payable", + "type": "fallback" + }, + { + "stateMutability": "payable", + "type": "receive" + } + ], + "transactionHash": "0x8660fbd3ffb068085956906e791f1126a1671244a29860708288a78bb8e6829b", + "receipt": { + "to": null, + "from": "0x33C6476F88eeA28D7E7900F759B4597704Ef95B7", + "contractAddress": "0xD05514217FD359659aE7da7740e79C11947eBB32", + "transactionIndex": 0, + "gasUsed": "563635", + "logsBloom": "0x01000000000400000000004000000000000000000000000000800000000000000000000000040040000000020000004000004000000000000000000800008000001000000000000000000000001000000001000022000000000000000000002000000000020000000080000000000800000004000000000001000000000000400000000000000000800000000000080c10000000000080100008040008000000000000000000000100000400000500000000000000800000000020000000004000000004000000000002010000040000010000000000100000800000000020000000000000000000000000800000040400000800000000000000004000010000", + "blockHash": "0xebc95ca51da8d2abcf52f6779b2f1b61f6dc6bf9a25d6576ae49313838d2e674", + "transactionHash": "0x8660fbd3ffb068085956906e791f1126a1671244a29860708288a78bb8e6829b", + "logs": [ + { + "transactionIndex": 0, + "blockNumber": 129845780, + "transactionHash": "0x8660fbd3ffb068085956906e791f1126a1671244a29860708288a78bb8e6829b", + "address": "0xD05514217FD359659aE7da7740e79C11947eBB32", + "topics": [ + "0x1cf3b03a6cf19fa2baba4df148e9dcabedea7f8a5c07840e207e5c089be95d3e", + "0x000000000000000000000000b9c3b5a6f13fd5bef51ee8b62ed3ea384a9d1b45" + ], + "data": "0x", + "logIndex": 0, + "blockHash": "0xebc95ca51da8d2abcf52f6779b2f1b61f6dc6bf9a25d6576ae49313838d2e674" + }, + { + "transactionIndex": 0, + "blockNumber": 129845780, + "transactionHash": "0x8660fbd3ffb068085956906e791f1126a1671244a29860708288a78bb8e6829b", + "address": "0xD05514217FD359659aE7da7740e79C11947eBB32", + "topics": [ + "0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x00000000000000000000000033c6476f88eea28d7e7900f759b4597704ef95b7" + ], + "data": "0x", + "logIndex": 1, + "blockHash": "0xebc95ca51da8d2abcf52f6779b2f1b61f6dc6bf9a25d6576ae49313838d2e674" + }, + { + "transactionIndex": 0, + "blockNumber": 129845780, + "transactionHash": "0x8660fbd3ffb068085956906e791f1126a1671244a29860708288a78bb8e6829b", + "address": "0xD05514217FD359659aE7da7740e79C11947eBB32", + "topics": ["0x66fd58e82f7b31a2a5c30e0888f3093efe4e111b00cd2b0c31fe014601293aa0"], + "data": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000045f8a08f534f34a97187626e05d4b6648eeaa9aa", + "logIndex": 2, + "blockHash": "0xebc95ca51da8d2abcf52f6779b2f1b61f6dc6bf9a25d6576ae49313838d2e674" + }, + { + "transactionIndex": 0, + "blockNumber": 129845780, + "transactionHash": "0x8660fbd3ffb068085956906e791f1126a1671244a29860708288a78bb8e6829b", + "address": "0xD05514217FD359659aE7da7740e79C11947eBB32", + "topics": [ + "0x7ac369dbd14fa5ea3f473ed67cc9d598964a77501540ba6751eb0b3decf5870d", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x00000000000000000000000011960c84d6c4f2a978a12372721c3a6a88c78f4c" + ], + "data": "0x", + "logIndex": 3, + "blockHash": "0xebc95ca51da8d2abcf52f6779b2f1b61f6dc6bf9a25d6576ae49313838d2e674" + }, + { + "transactionIndex": 0, + "blockNumber": 129845780, + "transactionHash": "0x8660fbd3ffb068085956906e791f1126a1671244a29860708288a78bb8e6829b", + "address": "0xD05514217FD359659aE7da7740e79C11947eBB32", + "topics": [ + "0xedffc32e068c7c95dfd4bdfd5c4d939a084d6b11c4199eac8436ed234d72f926", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000006700020659b6100a92ac1817d5489d67ee8d8f32" + ], + "data": "0x", + "logIndex": 4, + "blockHash": "0xebc95ca51da8d2abcf52f6779b2f1b61f6dc6bf9a25d6576ae49313838d2e674" + }, + { + "transactionIndex": 0, + "blockNumber": 129845780, + "transactionHash": "0x8660fbd3ffb068085956906e791f1126a1671244a29860708288a78bb8e6829b", + "address": "0xD05514217FD359659aE7da7740e79C11947eBB32", + "topics": ["0xaaa68312e2ea9d50e16af5068410ab56e1a1fd06037b1a35664812c30f821460"], + "data": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016345785d8a0000", + "logIndex": 5, + "blockHash": "0xebc95ca51da8d2abcf52f6779b2f1b61f6dc6bf9a25d6576ae49313838d2e674" + }, + { + "transactionIndex": 0, + "blockNumber": 129845780, + "transactionHash": "0x8660fbd3ffb068085956906e791f1126a1671244a29860708288a78bb8e6829b", + "address": "0xD05514217FD359659aE7da7740e79C11947eBB32", + "topics": [ + "0x6dbf1ff28f860de5edafa4c6505e37c0aba213288cc4166c5352b6d3776c79ef", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x000000000000000000000000503574a82fe2a9f968d355c8aac1ba0481859369" + ], + "data": "0x", + "logIndex": 6, + "blockHash": "0xebc95ca51da8d2abcf52f6779b2f1b61f6dc6bf9a25d6576ae49313838d2e674" + }, + { + "transactionIndex": 0, + "blockNumber": 129845780, + "transactionHash": "0x8660fbd3ffb068085956906e791f1126a1671244a29860708288a78bb8e6829b", + "address": "0xD05514217FD359659aE7da7740e79C11947eBB32", + "topics": [ + "0xafec95c8612496c3ecf5dddc71e393528fe29bd145fbaf9c6b496d78d7e2d79b", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x00000000000000000000000025c7c7d6bf710949fd7f03364e9ba19a1b3c10e3" + ], + "data": "0x", + "logIndex": 7, + "blockHash": "0xebc95ca51da8d2abcf52f6779b2f1b61f6dc6bf9a25d6576ae49313838d2e674" + }, + { + "transactionIndex": 0, + "blockNumber": 129845780, + "transactionHash": "0x8660fbd3ffb068085956906e791f1126a1671244a29860708288a78bb8e6829b", + "address": "0xD05514217FD359659aE7da7740e79C11947eBB32", + "topics": [ + "0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0", + "0x00000000000000000000000033c6476f88eea28d7e7900f759b4597704ef95b7", + "0x000000000000000000000000ce10739590001705f7ff231611ba4a48b2820327" + ], + "data": "0x", + "logIndex": 8, + "blockHash": "0xebc95ca51da8d2abcf52f6779b2f1b61f6dc6bf9a25d6576ae49313838d2e674" + }, + { + "transactionIndex": 0, + "blockNumber": 129845780, + "transactionHash": "0x8660fbd3ffb068085956906e791f1126a1671244a29860708288a78bb8e6829b", + "address": "0xD05514217FD359659aE7da7740e79C11947eBB32", + "topics": ["0x7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498"], + "data": "0x0000000000000000000000000000000000000000000000000000000000000001", + "logIndex": 9, + "blockHash": "0xebc95ca51da8d2abcf52f6779b2f1b61f6dc6bf9a25d6576ae49313838d2e674" + } + ], + "blockNumber": 129845780, + "cumulativeGasUsed": "563635", + "status": 1, + "byzantium": true + }, + "args": [ + "0xB9c3b5A6f13FD5BEF51eE8B62ED3EA384a9d1B45", + "0x8a42c31900000000000000000000000016227d60f7a0e586c66b005219dfc887d13c953100000000000000000000000011960c84d6c4f2a978a12372721c3a6a88c78f4c0000000000000000000000006700020659b6100a92ac1817d5489d67ee8d8f32000000000000000000000000000000000000000000000000002386f26fc10000000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001c00000000000000000000000000000000000000000000000000000000000000008000000000000000000000000ce10739590001705f7ff231611ba4a48b282032700000000000000000000000045f8a08f534f34a97187626e05d4b6648eeaa9aa000000000000000000000000503574a82fe2a9f968d355c8aac1ba048185936900000000000000000000000025c7c7d6bf710949fd7f03364e9ba19a1b3c10e3000000000000000000000000000000000000000000000000016345785d8a0000000000000000000000000000000000000000000000000000000000000000001d56656e7573205553444320284875622d66756e6465642073706f6b6529000000000000000000000000000000000000000000000000000000000000000000000e76555344435f48756253706f6b65000000000000000000000000000000000000" + ], + "numDeployments": 1, + "solcInputHash": "02cfe3974aa10474c22406a3f2870e89", + "metadata": "{\"compiler\":{\"version\":\"0.8.25+commit.b61c2a91\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"beacon\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"stateMutability\":\"payable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"previousAdmin\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"newAdmin\",\"type\":\"address\"}],\"name\":\"AdminChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"beacon\",\"type\":\"address\"}],\"name\":\"BeaconUpgraded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"implementation\",\"type\":\"address\"}],\"name\":\"Upgraded\",\"type\":\"event\"},{\"stateMutability\":\"payable\",\"type\":\"fallback\"},{\"stateMutability\":\"payable\",\"type\":\"receive\"}],\"devdoc\":{\"details\":\"This contract implements a proxy that gets the implementation address for each call from an {UpgradeableBeacon}. The beacon address is stored in storage slot `uint256(keccak256('eip1967.proxy.beacon')) - 1`, so that it doesn't conflict with the storage layout of the implementation behind the proxy. _Available since v3.4._\",\"events\":{\"AdminChanged(address,address)\":{\"details\":\"Emitted when the admin account has changed.\"},\"BeaconUpgraded(address)\":{\"details\":\"Emitted when the beacon is changed.\"},\"Upgraded(address)\":{\"details\":\"Emitted when the implementation is upgraded.\"}},\"kind\":\"dev\",\"methods\":{\"constructor\":{\"details\":\"Initializes the proxy with `beacon`. If `data` is nonempty, it's used as data in a delegate call to the implementation returned by the beacon. This will typically be an encoded function call, and allows initializing the storage of the proxy like a Solidity constructor. Requirements: - `beacon` must be a contract with the interface {IBeacon}.\"}},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"@openzeppelin/contracts/proxy/beacon/BeaconProxy.sol\":\"BeaconProxy\"},\"evmVersion\":\"paris\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\",\"useLiteralContent\":true},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[]},\"sources\":{\"@openzeppelin/contracts/interfaces/IERC1967.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts (last updated v4.9.0) (interfaces/IERC1967.sol)\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev ERC-1967: Proxy Storage Slots. This interface contains the events defined in the ERC.\\n *\\n * _Available since v4.8.3._\\n */\\ninterface IERC1967 {\\n /**\\n * @dev Emitted when the implementation is upgraded.\\n */\\n event Upgraded(address indexed implementation);\\n\\n /**\\n * @dev Emitted when the admin account has changed.\\n */\\n event AdminChanged(address previousAdmin, address newAdmin);\\n\\n /**\\n * @dev Emitted when the beacon is changed.\\n */\\n event BeaconUpgraded(address indexed beacon);\\n}\\n\",\"keccak256\":\"0x3cbef5ebc24b415252e2f8c0c9254555d30d9f085603b4b80d9b5ed20ab87e90\",\"license\":\"MIT\"},\"@openzeppelin/contracts/interfaces/draft-IERC1822.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts (last updated v4.5.0) (interfaces/draft-IERC1822.sol)\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev ERC1822: Universal Upgradeable Proxy Standard (UUPS) documents a method for upgradeability through a simplified\\n * proxy whose upgrades are fully controlled by the current implementation.\\n */\\ninterface IERC1822Proxiable {\\n /**\\n * @dev Returns the storage slot that the proxiable contract assumes is being used to store the implementation\\n * address.\\n *\\n * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks\\n * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this\\n * function revert if invoked through a proxy.\\n */\\n function proxiableUUID() external view returns (bytes32);\\n}\\n\",\"keccak256\":\"0x1d4afe6cb24200cc4545eed814ecf5847277dfe5d613a1707aad5fceecebcfff\",\"license\":\"MIT\"},\"@openzeppelin/contracts/proxy/ERC1967/ERC1967Upgrade.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts (last updated v4.9.0) (proxy/ERC1967/ERC1967Upgrade.sol)\\n\\npragma solidity ^0.8.2;\\n\\nimport \\\"../beacon/IBeacon.sol\\\";\\nimport \\\"../../interfaces/IERC1967.sol\\\";\\nimport \\\"../../interfaces/draft-IERC1822.sol\\\";\\nimport \\\"../../utils/Address.sol\\\";\\nimport \\\"../../utils/StorageSlot.sol\\\";\\n\\n/**\\n * @dev This abstract contract provides getters and event emitting update functions for\\n * https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots.\\n *\\n * _Available since v4.1._\\n */\\nabstract contract ERC1967Upgrade is IERC1967 {\\n // This is the keccak-256 hash of \\\"eip1967.proxy.rollback\\\" subtracted by 1\\n bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143;\\n\\n /**\\n * @dev Storage slot with the address of the current implementation.\\n * This is the keccak-256 hash of \\\"eip1967.proxy.implementation\\\" subtracted by 1, and is\\n * validated in the constructor.\\n */\\n bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;\\n\\n /**\\n * @dev Returns the current implementation address.\\n */\\n function _getImplementation() internal view returns (address) {\\n return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;\\n }\\n\\n /**\\n * @dev Stores a new address in the EIP1967 implementation slot.\\n */\\n function _setImplementation(address newImplementation) private {\\n require(Address.isContract(newImplementation), \\\"ERC1967: new implementation is not a contract\\\");\\n StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;\\n }\\n\\n /**\\n * @dev Perform implementation upgrade\\n *\\n * Emits an {Upgraded} event.\\n */\\n function _upgradeTo(address newImplementation) internal {\\n _setImplementation(newImplementation);\\n emit Upgraded(newImplementation);\\n }\\n\\n /**\\n * @dev Perform implementation upgrade with additional setup call.\\n *\\n * Emits an {Upgraded} event.\\n */\\n function _upgradeToAndCall(address newImplementation, bytes memory data, bool forceCall) internal {\\n _upgradeTo(newImplementation);\\n if (data.length > 0 || forceCall) {\\n Address.functionDelegateCall(newImplementation, data);\\n }\\n }\\n\\n /**\\n * @dev Perform implementation upgrade with security checks for UUPS proxies, and additional setup call.\\n *\\n * Emits an {Upgraded} event.\\n */\\n function _upgradeToAndCallUUPS(address newImplementation, bytes memory data, bool forceCall) internal {\\n // Upgrades from old implementations will perform a rollback test. This test requires the new\\n // implementation to upgrade back to the old, non-ERC1822 compliant, implementation. Removing\\n // this special case will break upgrade paths from old UUPS implementation to new ones.\\n if (StorageSlot.getBooleanSlot(_ROLLBACK_SLOT).value) {\\n _setImplementation(newImplementation);\\n } else {\\n try IERC1822Proxiable(newImplementation).proxiableUUID() returns (bytes32 slot) {\\n require(slot == _IMPLEMENTATION_SLOT, \\\"ERC1967Upgrade: unsupported proxiableUUID\\\");\\n } catch {\\n revert(\\\"ERC1967Upgrade: new implementation is not UUPS\\\");\\n }\\n _upgradeToAndCall(newImplementation, data, forceCall);\\n }\\n }\\n\\n /**\\n * @dev Storage slot with the admin of the contract.\\n * This is the keccak-256 hash of \\\"eip1967.proxy.admin\\\" subtracted by 1, and is\\n * validated in the constructor.\\n */\\n bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;\\n\\n /**\\n * @dev Returns the current admin.\\n */\\n function _getAdmin() internal view returns (address) {\\n return StorageSlot.getAddressSlot(_ADMIN_SLOT).value;\\n }\\n\\n /**\\n * @dev Stores a new address in the EIP1967 admin slot.\\n */\\n function _setAdmin(address newAdmin) private {\\n require(newAdmin != address(0), \\\"ERC1967: new admin is the zero address\\\");\\n StorageSlot.getAddressSlot(_ADMIN_SLOT).value = newAdmin;\\n }\\n\\n /**\\n * @dev Changes the admin of the proxy.\\n *\\n * Emits an {AdminChanged} event.\\n */\\n function _changeAdmin(address newAdmin) internal {\\n emit AdminChanged(_getAdmin(), newAdmin);\\n _setAdmin(newAdmin);\\n }\\n\\n /**\\n * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.\\n * This is bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)) and is validated in the constructor.\\n */\\n bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;\\n\\n /**\\n * @dev Returns the current beacon.\\n */\\n function _getBeacon() internal view returns (address) {\\n return StorageSlot.getAddressSlot(_BEACON_SLOT).value;\\n }\\n\\n /**\\n * @dev Stores a new beacon in the EIP1967 beacon slot.\\n */\\n function _setBeacon(address newBeacon) private {\\n require(Address.isContract(newBeacon), \\\"ERC1967: new beacon is not a contract\\\");\\n require(\\n Address.isContract(IBeacon(newBeacon).implementation()),\\n \\\"ERC1967: beacon implementation is not a contract\\\"\\n );\\n StorageSlot.getAddressSlot(_BEACON_SLOT).value = newBeacon;\\n }\\n\\n /**\\n * @dev Perform beacon upgrade with additional setup call. Note: This upgrades the address of the beacon, it does\\n * not upgrade the implementation contained in the beacon (see {UpgradeableBeacon-_setImplementation} for that).\\n *\\n * Emits a {BeaconUpgraded} event.\\n */\\n function _upgradeBeaconToAndCall(address newBeacon, bytes memory data, bool forceCall) internal {\\n _setBeacon(newBeacon);\\n emit BeaconUpgraded(newBeacon);\\n if (data.length > 0 || forceCall) {\\n Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data);\\n }\\n }\\n}\\n\",\"keccak256\":\"0x3b21ae06bf5957f73fa16754b0669c77b7abd8ba6c072d35c3281d446fdb86c2\",\"license\":\"MIT\"},\"@openzeppelin/contracts/proxy/Proxy.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts (last updated v4.6.0) (proxy/Proxy.sol)\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM\\n * instruction `delegatecall`. We refer to the second contract as the _implementation_ behind the proxy, and it has to\\n * be specified by overriding the virtual {_implementation} function.\\n *\\n * Additionally, delegation to the implementation can be triggered manually through the {_fallback} function, or to a\\n * different contract through the {_delegate} function.\\n *\\n * The success and return data of the delegated call will be returned back to the caller of the proxy.\\n */\\nabstract contract Proxy {\\n /**\\n * @dev Delegates the current call to `implementation`.\\n *\\n * This function does not return to its internal call site, it will return directly to the external caller.\\n */\\n function _delegate(address implementation) internal virtual {\\n assembly {\\n // Copy msg.data. We take full control of memory in this inline assembly\\n // block because it will not return to Solidity code. We overwrite the\\n // Solidity scratch pad at memory position 0.\\n calldatacopy(0, 0, calldatasize())\\n\\n // Call the implementation.\\n // out and outsize are 0 because we don't know the size yet.\\n let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)\\n\\n // Copy the returned data.\\n returndatacopy(0, 0, returndatasize())\\n\\n switch result\\n // delegatecall returns 0 on error.\\n case 0 {\\n revert(0, returndatasize())\\n }\\n default {\\n return(0, returndatasize())\\n }\\n }\\n }\\n\\n /**\\n * @dev This is a virtual function that should be overridden so it returns the address to which the fallback function\\n * and {_fallback} should delegate.\\n */\\n function _implementation() internal view virtual returns (address);\\n\\n /**\\n * @dev Delegates the current call to the address returned by `_implementation()`.\\n *\\n * This function does not return to its internal call site, it will return directly to the external caller.\\n */\\n function _fallback() internal virtual {\\n _beforeFallback();\\n _delegate(_implementation());\\n }\\n\\n /**\\n * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other\\n * function in the contract matches the call data.\\n */\\n fallback() external payable virtual {\\n _fallback();\\n }\\n\\n /**\\n * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if call data\\n * is empty.\\n */\\n receive() external payable virtual {\\n _fallback();\\n }\\n\\n /**\\n * @dev Hook that is called before falling back to the implementation. Can happen as part of a manual `_fallback`\\n * call, or as part of the Solidity `fallback` or `receive` functions.\\n *\\n * If overridden should call `super._beforeFallback()`.\\n */\\n function _beforeFallback() internal virtual {}\\n}\\n\",\"keccak256\":\"0xc130fe33f1b2132158531a87734153293f6d07bc263ff4ac90e85da9c82c0e27\",\"license\":\"MIT\"},\"@openzeppelin/contracts/proxy/beacon/BeaconProxy.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts (last updated v4.7.0) (proxy/beacon/BeaconProxy.sol)\\n\\npragma solidity ^0.8.0;\\n\\nimport \\\"./IBeacon.sol\\\";\\nimport \\\"../Proxy.sol\\\";\\nimport \\\"../ERC1967/ERC1967Upgrade.sol\\\";\\n\\n/**\\n * @dev This contract implements a proxy that gets the implementation address for each call from an {UpgradeableBeacon}.\\n *\\n * The beacon address is stored in storage slot `uint256(keccak256('eip1967.proxy.beacon')) - 1`, so that it doesn't\\n * conflict with the storage layout of the implementation behind the proxy.\\n *\\n * _Available since v3.4._\\n */\\ncontract BeaconProxy is Proxy, ERC1967Upgrade {\\n /**\\n * @dev Initializes the proxy with `beacon`.\\n *\\n * If `data` is nonempty, it's used as data in a delegate call to the implementation returned by the beacon. This\\n * will typically be an encoded function call, and allows initializing the storage of the proxy like a Solidity\\n * constructor.\\n *\\n * Requirements:\\n *\\n * - `beacon` must be a contract with the interface {IBeacon}.\\n */\\n constructor(address beacon, bytes memory data) payable {\\n _upgradeBeaconToAndCall(beacon, data, false);\\n }\\n\\n /**\\n * @dev Returns the current beacon address.\\n */\\n function _beacon() internal view virtual returns (address) {\\n return _getBeacon();\\n }\\n\\n /**\\n * @dev Returns the current implementation address of the associated beacon.\\n */\\n function _implementation() internal view virtual override returns (address) {\\n return IBeacon(_getBeacon()).implementation();\\n }\\n\\n /**\\n * @dev Changes the proxy to use a new beacon. Deprecated: see {_upgradeBeaconToAndCall}.\\n *\\n * If `data` is nonempty, it's used as data in a delegate call to the implementation returned by the beacon.\\n *\\n * Requirements:\\n *\\n * - `beacon` must be a contract.\\n * - The implementation returned by `beacon` must be a contract.\\n */\\n function _setBeacon(address beacon, bytes memory data) internal virtual {\\n _upgradeBeaconToAndCall(beacon, data, false);\\n }\\n}\\n\",\"keccak256\":\"0x85439e74ab467b6a23d45d32bdc9506cbc3760320289afd605f11638c4138e95\",\"license\":\"MIT\"},\"@openzeppelin/contracts/proxy/beacon/IBeacon.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts v4.4.1 (proxy/beacon/IBeacon.sol)\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev This is the interface that {BeaconProxy} expects of its beacon.\\n */\\ninterface IBeacon {\\n /**\\n * @dev Must return an address that can be used as a delegate call target.\\n *\\n * {BeaconProxy} will check that this address is a contract.\\n */\\n function implementation() external view returns (address);\\n}\\n\",\"keccak256\":\"0xd50a3421ac379ccb1be435fa646d66a65c986b4924f0849839f08692f39dde61\",\"license\":\"MIT\"},\"@openzeppelin/contracts/utils/Address.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)\\n\\npragma solidity ^0.8.1;\\n\\n/**\\n * @dev Collection of functions related to the address type\\n */\\nlibrary Address {\\n /**\\n * @dev Returns true if `account` is a contract.\\n *\\n * [IMPORTANT]\\n * ====\\n * It is unsafe to assume that an address for which this function returns\\n * false is an externally-owned account (EOA) and not a contract.\\n *\\n * Among others, `isContract` will return false for the following\\n * types of addresses:\\n *\\n * - an externally-owned account\\n * - a contract in construction\\n * - an address where a contract will be created\\n * - an address where a contract lived, but was destroyed\\n *\\n * Furthermore, `isContract` will also return true if the target contract within\\n * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,\\n * which only has an effect at the end of a transaction.\\n * ====\\n *\\n * [IMPORTANT]\\n * ====\\n * You shouldn't rely on `isContract` to protect against flash loan attacks!\\n *\\n * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets\\n * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract\\n * constructor.\\n * ====\\n */\\n function isContract(address account) internal view returns (bool) {\\n // This method relies on extcodesize/address.code.length, which returns 0\\n // for contracts in construction, since the code is only stored at the end\\n // of the constructor execution.\\n\\n return account.code.length > 0;\\n }\\n\\n /**\\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\\n * `recipient`, forwarding all available gas and reverting on errors.\\n *\\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\\n * imposed by `transfer`, making them unable to receive funds via\\n * `transfer`. {sendValue} removes this limitation.\\n *\\n * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].\\n *\\n * IMPORTANT: because control is transferred to `recipient`, care must be\\n * taken to not create reentrancy vulnerabilities. Consider using\\n * {ReentrancyGuard} or the\\n * https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\\n */\\n function sendValue(address payable recipient, uint256 amount) internal {\\n require(address(this).balance >= amount, \\\"Address: insufficient balance\\\");\\n\\n (bool success, ) = recipient.call{value: amount}(\\\"\\\");\\n require(success, \\\"Address: unable to send value, recipient may have reverted\\\");\\n }\\n\\n /**\\n * @dev Performs a Solidity function call using a low level `call`. A\\n * plain `call` is an unsafe replacement for a function call: use this\\n * function instead.\\n *\\n * If `target` reverts with a revert reason, it is bubbled up by this\\n * function (like regular Solidity function calls).\\n *\\n * Returns the raw returned data. To convert to the expected return value,\\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\\n *\\n * Requirements:\\n *\\n * - `target` must be a contract.\\n * - calling `target` with `data` must not revert.\\n *\\n * _Available since v3.1._\\n */\\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\\n return functionCallWithValue(target, data, 0, \\\"Address: low-level call failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\\n * `errorMessage` as a fallback revert reason when `target` reverts.\\n *\\n * _Available since v3.1._\\n */\\n function functionCall(\\n address target,\\n bytes memory data,\\n string memory errorMessage\\n ) internal returns (bytes memory) {\\n return functionCallWithValue(target, data, 0, errorMessage);\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\\n * but also transferring `value` wei to `target`.\\n *\\n * Requirements:\\n *\\n * - the calling contract must have an ETH balance of at least `value`.\\n * - the called Solidity function must be `payable`.\\n *\\n * _Available since v3.1._\\n */\\n function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {\\n return functionCallWithValue(target, data, value, \\\"Address: low-level call with value failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\\n * with `errorMessage` as a fallback revert reason when `target` reverts.\\n *\\n * _Available since v3.1._\\n */\\n function functionCallWithValue(\\n address target,\\n bytes memory data,\\n uint256 value,\\n string memory errorMessage\\n ) internal returns (bytes memory) {\\n require(address(this).balance >= value, \\\"Address: insufficient balance for call\\\");\\n (bool success, bytes memory returndata) = target.call{value: value}(data);\\n return verifyCallResultFromTarget(target, success, returndata, errorMessage);\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\\n * but performing a static call.\\n *\\n * _Available since v3.3._\\n */\\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\\n return functionStaticCall(target, data, \\\"Address: low-level static call failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\\n * but performing a static call.\\n *\\n * _Available since v3.3._\\n */\\n function functionStaticCall(\\n address target,\\n bytes memory data,\\n string memory errorMessage\\n ) internal view returns (bytes memory) {\\n (bool success, bytes memory returndata) = target.staticcall(data);\\n return verifyCallResultFromTarget(target, success, returndata, errorMessage);\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\\n * but performing a delegate call.\\n *\\n * _Available since v3.4._\\n */\\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\\n return functionDelegateCall(target, data, \\\"Address: low-level delegate call failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\\n * but performing a delegate call.\\n *\\n * _Available since v3.4._\\n */\\n function functionDelegateCall(\\n address target,\\n bytes memory data,\\n string memory errorMessage\\n ) internal returns (bytes memory) {\\n (bool success, bytes memory returndata) = target.delegatecall(data);\\n return verifyCallResultFromTarget(target, success, returndata, errorMessage);\\n }\\n\\n /**\\n * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling\\n * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.\\n *\\n * _Available since v4.8._\\n */\\n function verifyCallResultFromTarget(\\n address target,\\n bool success,\\n bytes memory returndata,\\n string memory errorMessage\\n ) internal view returns (bytes memory) {\\n if (success) {\\n if (returndata.length == 0) {\\n // only check isContract if the call was successful and the return data is empty\\n // otherwise we already know that it was a contract\\n require(isContract(target), \\\"Address: call to non-contract\\\");\\n }\\n return returndata;\\n } else {\\n _revert(returndata, errorMessage);\\n }\\n }\\n\\n /**\\n * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the\\n * revert reason or using the provided one.\\n *\\n * _Available since v4.3._\\n */\\n function verifyCallResult(\\n bool success,\\n bytes memory returndata,\\n string memory errorMessage\\n ) internal pure returns (bytes memory) {\\n if (success) {\\n return returndata;\\n } else {\\n _revert(returndata, errorMessage);\\n }\\n }\\n\\n function _revert(bytes memory returndata, string memory errorMessage) private pure {\\n // Look for revert reason and bubble it up if present\\n if (returndata.length > 0) {\\n // The easiest way to bubble the revert reason is using memory via assembly\\n /// @solidity memory-safe-assembly\\n assembly {\\n let returndata_size := mload(returndata)\\n revert(add(32, returndata), returndata_size)\\n }\\n } else {\\n revert(errorMessage);\\n }\\n }\\n}\\n\",\"keccak256\":\"0x006dd67219697fe68d7fbfdea512e7c4cb64a43565ed86171d67e844982da6fa\",\"license\":\"MIT\"},\"@openzeppelin/contracts/utils/StorageSlot.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts (last updated v4.9.0) (utils/StorageSlot.sol)\\n// This file was procedurally generated from scripts/generate/templates/StorageSlot.js.\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev Library for reading and writing primitive types to specific storage slots.\\n *\\n * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.\\n * This library helps with reading and writing to such slots without the need for inline assembly.\\n *\\n * The functions in this library return Slot structs that contain a `value` member that can be used to read or write.\\n *\\n * Example usage to set ERC1967 implementation slot:\\n * ```solidity\\n * contract ERC1967 {\\n * bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;\\n *\\n * function _getImplementation() internal view returns (address) {\\n * return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;\\n * }\\n *\\n * function _setImplementation(address newImplementation) internal {\\n * require(Address.isContract(newImplementation), \\\"ERC1967: new implementation is not a contract\\\");\\n * StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;\\n * }\\n * }\\n * ```\\n *\\n * _Available since v4.1 for `address`, `bool`, `bytes32`, `uint256`._\\n * _Available since v4.9 for `string`, `bytes`._\\n */\\nlibrary StorageSlot {\\n struct AddressSlot {\\n address value;\\n }\\n\\n struct BooleanSlot {\\n bool value;\\n }\\n\\n struct Bytes32Slot {\\n bytes32 value;\\n }\\n\\n struct Uint256Slot {\\n uint256 value;\\n }\\n\\n struct StringSlot {\\n string value;\\n }\\n\\n struct BytesSlot {\\n bytes value;\\n }\\n\\n /**\\n * @dev Returns an `AddressSlot` with member `value` located at `slot`.\\n */\\n function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {\\n /// @solidity memory-safe-assembly\\n assembly {\\n r.slot := slot\\n }\\n }\\n\\n /**\\n * @dev Returns an `BooleanSlot` with member `value` located at `slot`.\\n */\\n function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {\\n /// @solidity memory-safe-assembly\\n assembly {\\n r.slot := slot\\n }\\n }\\n\\n /**\\n * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.\\n */\\n function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {\\n /// @solidity memory-safe-assembly\\n assembly {\\n r.slot := slot\\n }\\n }\\n\\n /**\\n * @dev Returns an `Uint256Slot` with member `value` located at `slot`.\\n */\\n function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {\\n /// @solidity memory-safe-assembly\\n assembly {\\n r.slot := slot\\n }\\n }\\n\\n /**\\n * @dev Returns an `StringSlot` with member `value` located at `slot`.\\n */\\n function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {\\n /// @solidity memory-safe-assembly\\n assembly {\\n r.slot := slot\\n }\\n }\\n\\n /**\\n * @dev Returns an `StringSlot` representation of the string storage pointer `store`.\\n */\\n function getStringSlot(string storage store) internal pure returns (StringSlot storage r) {\\n /// @solidity memory-safe-assembly\\n assembly {\\n r.slot := store.slot\\n }\\n }\\n\\n /**\\n * @dev Returns an `BytesSlot` with member `value` located at `slot`.\\n */\\n function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {\\n /// @solidity memory-safe-assembly\\n assembly {\\n r.slot := slot\\n }\\n }\\n\\n /**\\n * @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.\\n */\\n function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) {\\n /// @solidity memory-safe-assembly\\n assembly {\\n r.slot := store.slot\\n }\\n }\\n}\\n\",\"keccak256\":\"0xf09e68aa0dc6722a25bc46490e8d48ed864466d17313b8a0b254c36b54e49899\",\"license\":\"MIT\"}},\"version\":1}", + "bytecode": "0x60806040526040516106cc3803806106cc83398101604081905261002291610420565b61002e82826000610035565b505061054a565b61003e836100f6565b6040516001600160a01b038416907f1cf3b03a6cf19fa2baba4df148e9dcabedea7f8a5c07840e207e5c089be95d3e90600090a260008251118061007f5750805b156100f1576100ef836001600160a01b0316635c60da1b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156100c5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906100e991906104e0565b8361027a565b505b505050565b6001600160a01b0381163b6101605760405162461bcd60e51b815260206004820152602560248201527f455243313936373a206e657720626561636f6e206973206e6f74206120636f6e6044820152641d1c9858dd60da1b60648201526084015b60405180910390fd5b6101d4816001600160a01b0316635c60da1b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156101a1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906101c591906104e0565b6001600160a01b03163b151590565b6102395760405162461bcd60e51b815260206004820152603060248201527f455243313936373a20626561636f6e20696d706c656d656e746174696f6e206960448201526f1cc81b9bdd08184818dbdb9d1c9858dd60821b6064820152608401610157565b7fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d5080546001600160a01b0319166001600160a01b0392909216919091179055565b606061029f83836040518060600160405280602781526020016106a5602791396102a6565b9392505050565b6060600080856001600160a01b0316856040516102c391906104fb565b600060405180830381855af49150503d80600081146102fe576040519150601f19603f3d011682016040523d82523d6000602084013e610303565b606091505b5090925090506103158683838761031f565b9695505050505050565b6060831561038e578251600003610387576001600160a01b0385163b6103875760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610157565b5081610398565b61039883836103a0565b949350505050565b8151156103b05781518083602001fd5b8060405162461bcd60e51b81526004016101579190610517565b80516001600160a01b03811681146103e157600080fd5b919050565b634e487b7160e01b600052604160045260246000fd5b60005b838110156104175781810151838201526020016103ff565b50506000910152565b6000806040838503121561043357600080fd5b61043c836103ca565b60208401519092506001600160401b038082111561045957600080fd5b818501915085601f83011261046d57600080fd5b81518181111561047f5761047f6103e6565b604051601f8201601f19908116603f011681019083821181831017156104a7576104a76103e6565b816040528281528860208487010111156104c057600080fd5b6104d18360208301602088016103fc565b80955050505050509250929050565b6000602082840312156104f257600080fd5b61029f826103ca565b6000825161050d8184602087016103fc565b9190910192915050565b60208152600082518060208401526105368160408501602087016103fc565b601f01601f19169190910160400192915050565b61014c806105596000396000f3fe60806040523661001357610011610017565b005b6100115b610027610022610029565b6100c2565b565b600061005c7fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50546001600160a01b031690565b6001600160a01b0316635c60da1b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610099573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906100bd91906100e6565b905090565b3660008037600080366000845af43d6000803e8080156100e1573d6000f35b3d6000fd5b6000602082840312156100f857600080fd5b81516001600160a01b038116811461010f57600080fd5b939250505056fea264697066735822122046b4f5e91a9c4ce5899089d64fe12e498987ddec7b0ba224fa5caa490cf7c92464736f6c63430008190033416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564", + "deployedBytecode": "0x60806040523661001357610011610017565b005b6100115b610027610022610029565b6100c2565b565b600061005c7fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50546001600160a01b031690565b6001600160a01b0316635c60da1b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610099573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906100bd91906100e6565b905090565b3660008037600080366000845af43d6000803e8080156100e1573d6000f35b3d6000fd5b6000602082840312156100f857600080fd5b81516001600160a01b038116811461010f57600080fd5b939250505056fea264697066735822122046b4f5e91a9c4ce5899089d64fe12e498987ddec7b0ba224fa5caa490cf7c92464736f6c63430008190033", + "devdoc": { + "details": "This contract implements a proxy that gets the implementation address for each call from an {UpgradeableBeacon}. The beacon address is stored in storage slot `uint256(keccak256('eip1967.proxy.beacon')) - 1`, so that it doesn't conflict with the storage layout of the implementation behind the proxy. _Available since v3.4._", + "events": { + "AdminChanged(address,address)": { + "details": "Emitted when the admin account has changed." + }, + "BeaconUpgraded(address)": { + "details": "Emitted when the beacon is changed." + }, + "Upgraded(address)": { + "details": "Emitted when the implementation is upgraded." + } + }, + "kind": "dev", + "methods": { + "constructor": { + "details": "Initializes the proxy with `beacon`. If `data` is nonempty, it's used as data in a delegate call to the implementation returned by the beacon. This will typically be an encoded function call, and allows initializing the storage of the proxy like a Solidity constructor. Requirements: - `beacon` must be a contract with the interface {IBeacon}." + } + }, + "version": 1 + }, + "userdoc": { + "kind": "user", + "methods": {}, + "version": 1 + }, + "storageLayout": { + "storage": [], + "types": null + } +} diff --git a/deployments/bsctestnet/VToken_vUSDT_HubSpoke.json b/deployments/bsctestnet/VToken_vUSDT_HubSpoke.json new file mode 100644 index 000000000..f03157343 --- /dev/null +++ b/deployments/bsctestnet/VToken_vUSDT_HubSpoke.json @@ -0,0 +1,257 @@ +{ + "address": "0xC88bAF0bA49a98F15A00182752f6d10bd3932F6a", + "abi": [ + { + "inputs": [ + { + "internalType": "address", + "name": "beacon", + "type": "address" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "stateMutability": "payable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "previousAdmin", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "newAdmin", + "type": "address" + } + ], + "name": "AdminChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "beacon", + "type": "address" + } + ], + "name": "BeaconUpgraded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "implementation", + "type": "address" + } + ], + "name": "Upgraded", + "type": "event" + }, + { + "stateMutability": "payable", + "type": "fallback" + }, + { + "stateMutability": "payable", + "type": "receive" + } + ], + "transactionHash": "0x9e94a92c0201d17b7c7e5b559908e28ce59274aedca3a5f3f9ba2d355ffc23d9", + "receipt": { + "to": null, + "from": "0x33C6476F88eeA28D7E7900F759B4597704Ef95B7", + "contractAddress": "0xC88bAF0bA49a98F15A00182752f6d10bd3932F6a", + "transactionIndex": 0, + "gasUsed": "563647", + "logsBloom": "0x01000000000400000000004000000000000000000004000000800000000000000000000000040040000000020000004000000000000000000000000800008000001000000000000000000000001000000001000022000000000000000000002000000000020000000080000000000800000004000000000000000000000000400000000000000000800000000000080c10000000000080100008000008000000000000000000000100000400000500000000000000800000000020000000004000000004000000000002010000040000010000000000100000800000000020000000000000000000000000800000040400000800000000000000004100010000", + "blockHash": "0x885377dcd338a2beaa0e2dc4fac14055cd18fb8c08dd7f274ed1741ba4da5ff2", + "transactionHash": "0x9e94a92c0201d17b7c7e5b559908e28ce59274aedca3a5f3f9ba2d355ffc23d9", + "logs": [ + { + "transactionIndex": 0, + "blockNumber": 129845665, + "transactionHash": "0x9e94a92c0201d17b7c7e5b559908e28ce59274aedca3a5f3f9ba2d355ffc23d9", + "address": "0xC88bAF0bA49a98F15A00182752f6d10bd3932F6a", + "topics": [ + "0x1cf3b03a6cf19fa2baba4df148e9dcabedea7f8a5c07840e207e5c089be95d3e", + "0x000000000000000000000000b9c3b5a6f13fd5bef51ee8b62ed3ea384a9d1b45" + ], + "data": "0x", + "logIndex": 0, + "blockHash": "0x885377dcd338a2beaa0e2dc4fac14055cd18fb8c08dd7f274ed1741ba4da5ff2" + }, + { + "transactionIndex": 0, + "blockNumber": 129845665, + "transactionHash": "0x9e94a92c0201d17b7c7e5b559908e28ce59274aedca3a5f3f9ba2d355ffc23d9", + "address": "0xC88bAF0bA49a98F15A00182752f6d10bd3932F6a", + "topics": [ + "0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x00000000000000000000000033c6476f88eea28d7e7900f759b4597704ef95b7" + ], + "data": "0x", + "logIndex": 1, + "blockHash": "0x885377dcd338a2beaa0e2dc4fac14055cd18fb8c08dd7f274ed1741ba4da5ff2" + }, + { + "transactionIndex": 0, + "blockNumber": 129845665, + "transactionHash": "0x9e94a92c0201d17b7c7e5b559908e28ce59274aedca3a5f3f9ba2d355ffc23d9", + "address": "0xC88bAF0bA49a98F15A00182752f6d10bd3932F6a", + "topics": ["0x66fd58e82f7b31a2a5c30e0888f3093efe4e111b00cd2b0c31fe014601293aa0"], + "data": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000045f8a08f534f34a97187626e05d4b6648eeaa9aa", + "logIndex": 2, + "blockHash": "0x885377dcd338a2beaa0e2dc4fac14055cd18fb8c08dd7f274ed1741ba4da5ff2" + }, + { + "transactionIndex": 0, + "blockNumber": 129845665, + "transactionHash": "0x9e94a92c0201d17b7c7e5b559908e28ce59274aedca3a5f3f9ba2d355ffc23d9", + "address": "0xC88bAF0bA49a98F15A00182752f6d10bd3932F6a", + "topics": [ + "0x7ac369dbd14fa5ea3f473ed67cc9d598964a77501540ba6751eb0b3decf5870d", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x00000000000000000000000011960c84d6c4f2a978a12372721c3a6a88c78f4c" + ], + "data": "0x", + "logIndex": 3, + "blockHash": "0x885377dcd338a2beaa0e2dc4fac14055cd18fb8c08dd7f274ed1741ba4da5ff2" + }, + { + "transactionIndex": 0, + "blockNumber": 129845665, + "transactionHash": "0x9e94a92c0201d17b7c7e5b559908e28ce59274aedca3a5f3f9ba2d355ffc23d9", + "address": "0xC88bAF0bA49a98F15A00182752f6d10bd3932F6a", + "topics": [ + "0xedffc32e068c7c95dfd4bdfd5c4d939a084d6b11c4199eac8436ed234d72f926", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000006700020659b6100a92ac1817d5489d67ee8d8f32" + ], + "data": "0x", + "logIndex": 4, + "blockHash": "0x885377dcd338a2beaa0e2dc4fac14055cd18fb8c08dd7f274ed1741ba4da5ff2" + }, + { + "transactionIndex": 0, + "blockNumber": 129845665, + "transactionHash": "0x9e94a92c0201d17b7c7e5b559908e28ce59274aedca3a5f3f9ba2d355ffc23d9", + "address": "0xC88bAF0bA49a98F15A00182752f6d10bd3932F6a", + "topics": ["0xaaa68312e2ea9d50e16af5068410ab56e1a1fd06037b1a35664812c30f821460"], + "data": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016345785d8a0000", + "logIndex": 5, + "blockHash": "0x885377dcd338a2beaa0e2dc4fac14055cd18fb8c08dd7f274ed1741ba4da5ff2" + }, + { + "transactionIndex": 0, + "blockNumber": 129845665, + "transactionHash": "0x9e94a92c0201d17b7c7e5b559908e28ce59274aedca3a5f3f9ba2d355ffc23d9", + "address": "0xC88bAF0bA49a98F15A00182752f6d10bd3932F6a", + "topics": [ + "0x6dbf1ff28f860de5edafa4c6505e37c0aba213288cc4166c5352b6d3776c79ef", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x000000000000000000000000503574a82fe2a9f968d355c8aac1ba0481859369" + ], + "data": "0x", + "logIndex": 6, + "blockHash": "0x885377dcd338a2beaa0e2dc4fac14055cd18fb8c08dd7f274ed1741ba4da5ff2" + }, + { + "transactionIndex": 0, + "blockNumber": 129845665, + "transactionHash": "0x9e94a92c0201d17b7c7e5b559908e28ce59274aedca3a5f3f9ba2d355ffc23d9", + "address": "0xC88bAF0bA49a98F15A00182752f6d10bd3932F6a", + "topics": [ + "0xafec95c8612496c3ecf5dddc71e393528fe29bd145fbaf9c6b496d78d7e2d79b", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x00000000000000000000000025c7c7d6bf710949fd7f03364e9ba19a1b3c10e3" + ], + "data": "0x", + "logIndex": 7, + "blockHash": "0x885377dcd338a2beaa0e2dc4fac14055cd18fb8c08dd7f274ed1741ba4da5ff2" + }, + { + "transactionIndex": 0, + "blockNumber": 129845665, + "transactionHash": "0x9e94a92c0201d17b7c7e5b559908e28ce59274aedca3a5f3f9ba2d355ffc23d9", + "address": "0xC88bAF0bA49a98F15A00182752f6d10bd3932F6a", + "topics": [ + "0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0", + "0x00000000000000000000000033c6476f88eea28d7e7900f759b4597704ef95b7", + "0x000000000000000000000000ce10739590001705f7ff231611ba4a48b2820327" + ], + "data": "0x", + "logIndex": 8, + "blockHash": "0x885377dcd338a2beaa0e2dc4fac14055cd18fb8c08dd7f274ed1741ba4da5ff2" + }, + { + "transactionIndex": 0, + "blockNumber": 129845665, + "transactionHash": "0x9e94a92c0201d17b7c7e5b559908e28ce59274aedca3a5f3f9ba2d355ffc23d9", + "address": "0xC88bAF0bA49a98F15A00182752f6d10bd3932F6a", + "topics": ["0x7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498"], + "data": "0x0000000000000000000000000000000000000000000000000000000000000001", + "logIndex": 9, + "blockHash": "0x885377dcd338a2beaa0e2dc4fac14055cd18fb8c08dd7f274ed1741ba4da5ff2" + } + ], + "blockNumber": 129845665, + "cumulativeGasUsed": "563647", + "status": 1, + "byzantium": true + }, + "args": [ + "0xB9c3b5A6f13FD5BEF51eE8B62ED3EA384a9d1B45", + "0x8a42c319000000000000000000000000a11c8d9dc9b66e209ef60f0c8d969d3cd988782c00000000000000000000000011960c84d6c4f2a978a12372721c3a6a88c78f4c0000000000000000000000006700020659b6100a92ac1817d5489d67ee8d8f32000000000000000000000000000000000000000000000000002386f26fc10000000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001c00000000000000000000000000000000000000000000000000000000000000008000000000000000000000000ce10739590001705f7ff231611ba4a48b282032700000000000000000000000045f8a08f534f34a97187626e05d4b6648eeaa9aa000000000000000000000000503574a82fe2a9f968d355c8aac1ba048185936900000000000000000000000025c7c7d6bf710949fd7f03364e9ba19a1b3c10e3000000000000000000000000000000000000000000000000016345785d8a0000000000000000000000000000000000000000000000000000000000000000001d56656e7573205553445420284875622d66756e6465642073706f6b6529000000000000000000000000000000000000000000000000000000000000000000000e76555344545f48756253706f6b65000000000000000000000000000000000000" + ], + "numDeployments": 1, + "solcInputHash": "02cfe3974aa10474c22406a3f2870e89", + "metadata": "{\"compiler\":{\"version\":\"0.8.25+commit.b61c2a91\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"beacon\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"stateMutability\":\"payable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"previousAdmin\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"newAdmin\",\"type\":\"address\"}],\"name\":\"AdminChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"beacon\",\"type\":\"address\"}],\"name\":\"BeaconUpgraded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"implementation\",\"type\":\"address\"}],\"name\":\"Upgraded\",\"type\":\"event\"},{\"stateMutability\":\"payable\",\"type\":\"fallback\"},{\"stateMutability\":\"payable\",\"type\":\"receive\"}],\"devdoc\":{\"details\":\"This contract implements a proxy that gets the implementation address for each call from an {UpgradeableBeacon}. The beacon address is stored in storage slot `uint256(keccak256('eip1967.proxy.beacon')) - 1`, so that it doesn't conflict with the storage layout of the implementation behind the proxy. _Available since v3.4._\",\"events\":{\"AdminChanged(address,address)\":{\"details\":\"Emitted when the admin account has changed.\"},\"BeaconUpgraded(address)\":{\"details\":\"Emitted when the beacon is changed.\"},\"Upgraded(address)\":{\"details\":\"Emitted when the implementation is upgraded.\"}},\"kind\":\"dev\",\"methods\":{\"constructor\":{\"details\":\"Initializes the proxy with `beacon`. If `data` is nonempty, it's used as data in a delegate call to the implementation returned by the beacon. This will typically be an encoded function call, and allows initializing the storage of the proxy like a Solidity constructor. Requirements: - `beacon` must be a contract with the interface {IBeacon}.\"}},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"@openzeppelin/contracts/proxy/beacon/BeaconProxy.sol\":\"BeaconProxy\"},\"evmVersion\":\"paris\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\",\"useLiteralContent\":true},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[]},\"sources\":{\"@openzeppelin/contracts/interfaces/IERC1967.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts (last updated v4.9.0) (interfaces/IERC1967.sol)\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev ERC-1967: Proxy Storage Slots. This interface contains the events defined in the ERC.\\n *\\n * _Available since v4.8.3._\\n */\\ninterface IERC1967 {\\n /**\\n * @dev Emitted when the implementation is upgraded.\\n */\\n event Upgraded(address indexed implementation);\\n\\n /**\\n * @dev Emitted when the admin account has changed.\\n */\\n event AdminChanged(address previousAdmin, address newAdmin);\\n\\n /**\\n * @dev Emitted when the beacon is changed.\\n */\\n event BeaconUpgraded(address indexed beacon);\\n}\\n\",\"keccak256\":\"0x3cbef5ebc24b415252e2f8c0c9254555d30d9f085603b4b80d9b5ed20ab87e90\",\"license\":\"MIT\"},\"@openzeppelin/contracts/interfaces/draft-IERC1822.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts (last updated v4.5.0) (interfaces/draft-IERC1822.sol)\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev ERC1822: Universal Upgradeable Proxy Standard (UUPS) documents a method for upgradeability through a simplified\\n * proxy whose upgrades are fully controlled by the current implementation.\\n */\\ninterface IERC1822Proxiable {\\n /**\\n * @dev Returns the storage slot that the proxiable contract assumes is being used to store the implementation\\n * address.\\n *\\n * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks\\n * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this\\n * function revert if invoked through a proxy.\\n */\\n function proxiableUUID() external view returns (bytes32);\\n}\\n\",\"keccak256\":\"0x1d4afe6cb24200cc4545eed814ecf5847277dfe5d613a1707aad5fceecebcfff\",\"license\":\"MIT\"},\"@openzeppelin/contracts/proxy/ERC1967/ERC1967Upgrade.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts (last updated v4.9.0) (proxy/ERC1967/ERC1967Upgrade.sol)\\n\\npragma solidity ^0.8.2;\\n\\nimport \\\"../beacon/IBeacon.sol\\\";\\nimport \\\"../../interfaces/IERC1967.sol\\\";\\nimport \\\"../../interfaces/draft-IERC1822.sol\\\";\\nimport \\\"../../utils/Address.sol\\\";\\nimport \\\"../../utils/StorageSlot.sol\\\";\\n\\n/**\\n * @dev This abstract contract provides getters and event emitting update functions for\\n * https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots.\\n *\\n * _Available since v4.1._\\n */\\nabstract contract ERC1967Upgrade is IERC1967 {\\n // This is the keccak-256 hash of \\\"eip1967.proxy.rollback\\\" subtracted by 1\\n bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143;\\n\\n /**\\n * @dev Storage slot with the address of the current implementation.\\n * This is the keccak-256 hash of \\\"eip1967.proxy.implementation\\\" subtracted by 1, and is\\n * validated in the constructor.\\n */\\n bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;\\n\\n /**\\n * @dev Returns the current implementation address.\\n */\\n function _getImplementation() internal view returns (address) {\\n return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;\\n }\\n\\n /**\\n * @dev Stores a new address in the EIP1967 implementation slot.\\n */\\n function _setImplementation(address newImplementation) private {\\n require(Address.isContract(newImplementation), \\\"ERC1967: new implementation is not a contract\\\");\\n StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;\\n }\\n\\n /**\\n * @dev Perform implementation upgrade\\n *\\n * Emits an {Upgraded} event.\\n */\\n function _upgradeTo(address newImplementation) internal {\\n _setImplementation(newImplementation);\\n emit Upgraded(newImplementation);\\n }\\n\\n /**\\n * @dev Perform implementation upgrade with additional setup call.\\n *\\n * Emits an {Upgraded} event.\\n */\\n function _upgradeToAndCall(address newImplementation, bytes memory data, bool forceCall) internal {\\n _upgradeTo(newImplementation);\\n if (data.length > 0 || forceCall) {\\n Address.functionDelegateCall(newImplementation, data);\\n }\\n }\\n\\n /**\\n * @dev Perform implementation upgrade with security checks for UUPS proxies, and additional setup call.\\n *\\n * Emits an {Upgraded} event.\\n */\\n function _upgradeToAndCallUUPS(address newImplementation, bytes memory data, bool forceCall) internal {\\n // Upgrades from old implementations will perform a rollback test. This test requires the new\\n // implementation to upgrade back to the old, non-ERC1822 compliant, implementation. Removing\\n // this special case will break upgrade paths from old UUPS implementation to new ones.\\n if (StorageSlot.getBooleanSlot(_ROLLBACK_SLOT).value) {\\n _setImplementation(newImplementation);\\n } else {\\n try IERC1822Proxiable(newImplementation).proxiableUUID() returns (bytes32 slot) {\\n require(slot == _IMPLEMENTATION_SLOT, \\\"ERC1967Upgrade: unsupported proxiableUUID\\\");\\n } catch {\\n revert(\\\"ERC1967Upgrade: new implementation is not UUPS\\\");\\n }\\n _upgradeToAndCall(newImplementation, data, forceCall);\\n }\\n }\\n\\n /**\\n * @dev Storage slot with the admin of the contract.\\n * This is the keccak-256 hash of \\\"eip1967.proxy.admin\\\" subtracted by 1, and is\\n * validated in the constructor.\\n */\\n bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;\\n\\n /**\\n * @dev Returns the current admin.\\n */\\n function _getAdmin() internal view returns (address) {\\n return StorageSlot.getAddressSlot(_ADMIN_SLOT).value;\\n }\\n\\n /**\\n * @dev Stores a new address in the EIP1967 admin slot.\\n */\\n function _setAdmin(address newAdmin) private {\\n require(newAdmin != address(0), \\\"ERC1967: new admin is the zero address\\\");\\n StorageSlot.getAddressSlot(_ADMIN_SLOT).value = newAdmin;\\n }\\n\\n /**\\n * @dev Changes the admin of the proxy.\\n *\\n * Emits an {AdminChanged} event.\\n */\\n function _changeAdmin(address newAdmin) internal {\\n emit AdminChanged(_getAdmin(), newAdmin);\\n _setAdmin(newAdmin);\\n }\\n\\n /**\\n * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.\\n * This is bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)) and is validated in the constructor.\\n */\\n bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;\\n\\n /**\\n * @dev Returns the current beacon.\\n */\\n function _getBeacon() internal view returns (address) {\\n return StorageSlot.getAddressSlot(_BEACON_SLOT).value;\\n }\\n\\n /**\\n * @dev Stores a new beacon in the EIP1967 beacon slot.\\n */\\n function _setBeacon(address newBeacon) private {\\n require(Address.isContract(newBeacon), \\\"ERC1967: new beacon is not a contract\\\");\\n require(\\n Address.isContract(IBeacon(newBeacon).implementation()),\\n \\\"ERC1967: beacon implementation is not a contract\\\"\\n );\\n StorageSlot.getAddressSlot(_BEACON_SLOT).value = newBeacon;\\n }\\n\\n /**\\n * @dev Perform beacon upgrade with additional setup call. Note: This upgrades the address of the beacon, it does\\n * not upgrade the implementation contained in the beacon (see {UpgradeableBeacon-_setImplementation} for that).\\n *\\n * Emits a {BeaconUpgraded} event.\\n */\\n function _upgradeBeaconToAndCall(address newBeacon, bytes memory data, bool forceCall) internal {\\n _setBeacon(newBeacon);\\n emit BeaconUpgraded(newBeacon);\\n if (data.length > 0 || forceCall) {\\n Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data);\\n }\\n }\\n}\\n\",\"keccak256\":\"0x3b21ae06bf5957f73fa16754b0669c77b7abd8ba6c072d35c3281d446fdb86c2\",\"license\":\"MIT\"},\"@openzeppelin/contracts/proxy/Proxy.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts (last updated v4.6.0) (proxy/Proxy.sol)\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM\\n * instruction `delegatecall`. We refer to the second contract as the _implementation_ behind the proxy, and it has to\\n * be specified by overriding the virtual {_implementation} function.\\n *\\n * Additionally, delegation to the implementation can be triggered manually through the {_fallback} function, or to a\\n * different contract through the {_delegate} function.\\n *\\n * The success and return data of the delegated call will be returned back to the caller of the proxy.\\n */\\nabstract contract Proxy {\\n /**\\n * @dev Delegates the current call to `implementation`.\\n *\\n * This function does not return to its internal call site, it will return directly to the external caller.\\n */\\n function _delegate(address implementation) internal virtual {\\n assembly {\\n // Copy msg.data. We take full control of memory in this inline assembly\\n // block because it will not return to Solidity code. We overwrite the\\n // Solidity scratch pad at memory position 0.\\n calldatacopy(0, 0, calldatasize())\\n\\n // Call the implementation.\\n // out and outsize are 0 because we don't know the size yet.\\n let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)\\n\\n // Copy the returned data.\\n returndatacopy(0, 0, returndatasize())\\n\\n switch result\\n // delegatecall returns 0 on error.\\n case 0 {\\n revert(0, returndatasize())\\n }\\n default {\\n return(0, returndatasize())\\n }\\n }\\n }\\n\\n /**\\n * @dev This is a virtual function that should be overridden so it returns the address to which the fallback function\\n * and {_fallback} should delegate.\\n */\\n function _implementation() internal view virtual returns (address);\\n\\n /**\\n * @dev Delegates the current call to the address returned by `_implementation()`.\\n *\\n * This function does not return to its internal call site, it will return directly to the external caller.\\n */\\n function _fallback() internal virtual {\\n _beforeFallback();\\n _delegate(_implementation());\\n }\\n\\n /**\\n * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other\\n * function in the contract matches the call data.\\n */\\n fallback() external payable virtual {\\n _fallback();\\n }\\n\\n /**\\n * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if call data\\n * is empty.\\n */\\n receive() external payable virtual {\\n _fallback();\\n }\\n\\n /**\\n * @dev Hook that is called before falling back to the implementation. Can happen as part of a manual `_fallback`\\n * call, or as part of the Solidity `fallback` or `receive` functions.\\n *\\n * If overridden should call `super._beforeFallback()`.\\n */\\n function _beforeFallback() internal virtual {}\\n}\\n\",\"keccak256\":\"0xc130fe33f1b2132158531a87734153293f6d07bc263ff4ac90e85da9c82c0e27\",\"license\":\"MIT\"},\"@openzeppelin/contracts/proxy/beacon/BeaconProxy.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts (last updated v4.7.0) (proxy/beacon/BeaconProxy.sol)\\n\\npragma solidity ^0.8.0;\\n\\nimport \\\"./IBeacon.sol\\\";\\nimport \\\"../Proxy.sol\\\";\\nimport \\\"../ERC1967/ERC1967Upgrade.sol\\\";\\n\\n/**\\n * @dev This contract implements a proxy that gets the implementation address for each call from an {UpgradeableBeacon}.\\n *\\n * The beacon address is stored in storage slot `uint256(keccak256('eip1967.proxy.beacon')) - 1`, so that it doesn't\\n * conflict with the storage layout of the implementation behind the proxy.\\n *\\n * _Available since v3.4._\\n */\\ncontract BeaconProxy is Proxy, ERC1967Upgrade {\\n /**\\n * @dev Initializes the proxy with `beacon`.\\n *\\n * If `data` is nonempty, it's used as data in a delegate call to the implementation returned by the beacon. This\\n * will typically be an encoded function call, and allows initializing the storage of the proxy like a Solidity\\n * constructor.\\n *\\n * Requirements:\\n *\\n * - `beacon` must be a contract with the interface {IBeacon}.\\n */\\n constructor(address beacon, bytes memory data) payable {\\n _upgradeBeaconToAndCall(beacon, data, false);\\n }\\n\\n /**\\n * @dev Returns the current beacon address.\\n */\\n function _beacon() internal view virtual returns (address) {\\n return _getBeacon();\\n }\\n\\n /**\\n * @dev Returns the current implementation address of the associated beacon.\\n */\\n function _implementation() internal view virtual override returns (address) {\\n return IBeacon(_getBeacon()).implementation();\\n }\\n\\n /**\\n * @dev Changes the proxy to use a new beacon. Deprecated: see {_upgradeBeaconToAndCall}.\\n *\\n * If `data` is nonempty, it's used as data in a delegate call to the implementation returned by the beacon.\\n *\\n * Requirements:\\n *\\n * - `beacon` must be a contract.\\n * - The implementation returned by `beacon` must be a contract.\\n */\\n function _setBeacon(address beacon, bytes memory data) internal virtual {\\n _upgradeBeaconToAndCall(beacon, data, false);\\n }\\n}\\n\",\"keccak256\":\"0x85439e74ab467b6a23d45d32bdc9506cbc3760320289afd605f11638c4138e95\",\"license\":\"MIT\"},\"@openzeppelin/contracts/proxy/beacon/IBeacon.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts v4.4.1 (proxy/beacon/IBeacon.sol)\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev This is the interface that {BeaconProxy} expects of its beacon.\\n */\\ninterface IBeacon {\\n /**\\n * @dev Must return an address that can be used as a delegate call target.\\n *\\n * {BeaconProxy} will check that this address is a contract.\\n */\\n function implementation() external view returns (address);\\n}\\n\",\"keccak256\":\"0xd50a3421ac379ccb1be435fa646d66a65c986b4924f0849839f08692f39dde61\",\"license\":\"MIT\"},\"@openzeppelin/contracts/utils/Address.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)\\n\\npragma solidity ^0.8.1;\\n\\n/**\\n * @dev Collection of functions related to the address type\\n */\\nlibrary Address {\\n /**\\n * @dev Returns true if `account` is a contract.\\n *\\n * [IMPORTANT]\\n * ====\\n * It is unsafe to assume that an address for which this function returns\\n * false is an externally-owned account (EOA) and not a contract.\\n *\\n * Among others, `isContract` will return false for the following\\n * types of addresses:\\n *\\n * - an externally-owned account\\n * - a contract in construction\\n * - an address where a contract will be created\\n * - an address where a contract lived, but was destroyed\\n *\\n * Furthermore, `isContract` will also return true if the target contract within\\n * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,\\n * which only has an effect at the end of a transaction.\\n * ====\\n *\\n * [IMPORTANT]\\n * ====\\n * You shouldn't rely on `isContract` to protect against flash loan attacks!\\n *\\n * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets\\n * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract\\n * constructor.\\n * ====\\n */\\n function isContract(address account) internal view returns (bool) {\\n // This method relies on extcodesize/address.code.length, which returns 0\\n // for contracts in construction, since the code is only stored at the end\\n // of the constructor execution.\\n\\n return account.code.length > 0;\\n }\\n\\n /**\\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\\n * `recipient`, forwarding all available gas and reverting on errors.\\n *\\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\\n * imposed by `transfer`, making them unable to receive funds via\\n * `transfer`. {sendValue} removes this limitation.\\n *\\n * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].\\n *\\n * IMPORTANT: because control is transferred to `recipient`, care must be\\n * taken to not create reentrancy vulnerabilities. Consider using\\n * {ReentrancyGuard} or the\\n * https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\\n */\\n function sendValue(address payable recipient, uint256 amount) internal {\\n require(address(this).balance >= amount, \\\"Address: insufficient balance\\\");\\n\\n (bool success, ) = recipient.call{value: amount}(\\\"\\\");\\n require(success, \\\"Address: unable to send value, recipient may have reverted\\\");\\n }\\n\\n /**\\n * @dev Performs a Solidity function call using a low level `call`. A\\n * plain `call` is an unsafe replacement for a function call: use this\\n * function instead.\\n *\\n * If `target` reverts with a revert reason, it is bubbled up by this\\n * function (like regular Solidity function calls).\\n *\\n * Returns the raw returned data. To convert to the expected return value,\\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\\n *\\n * Requirements:\\n *\\n * - `target` must be a contract.\\n * - calling `target` with `data` must not revert.\\n *\\n * _Available since v3.1._\\n */\\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\\n return functionCallWithValue(target, data, 0, \\\"Address: low-level call failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\\n * `errorMessage` as a fallback revert reason when `target` reverts.\\n *\\n * _Available since v3.1._\\n */\\n function functionCall(\\n address target,\\n bytes memory data,\\n string memory errorMessage\\n ) internal returns (bytes memory) {\\n return functionCallWithValue(target, data, 0, errorMessage);\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\\n * but also transferring `value` wei to `target`.\\n *\\n * Requirements:\\n *\\n * - the calling contract must have an ETH balance of at least `value`.\\n * - the called Solidity function must be `payable`.\\n *\\n * _Available since v3.1._\\n */\\n function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {\\n return functionCallWithValue(target, data, value, \\\"Address: low-level call with value failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\\n * with `errorMessage` as a fallback revert reason when `target` reverts.\\n *\\n * _Available since v3.1._\\n */\\n function functionCallWithValue(\\n address target,\\n bytes memory data,\\n uint256 value,\\n string memory errorMessage\\n ) internal returns (bytes memory) {\\n require(address(this).balance >= value, \\\"Address: insufficient balance for call\\\");\\n (bool success, bytes memory returndata) = target.call{value: value}(data);\\n return verifyCallResultFromTarget(target, success, returndata, errorMessage);\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\\n * but performing a static call.\\n *\\n * _Available since v3.3._\\n */\\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\\n return functionStaticCall(target, data, \\\"Address: low-level static call failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\\n * but performing a static call.\\n *\\n * _Available since v3.3._\\n */\\n function functionStaticCall(\\n address target,\\n bytes memory data,\\n string memory errorMessage\\n ) internal view returns (bytes memory) {\\n (bool success, bytes memory returndata) = target.staticcall(data);\\n return verifyCallResultFromTarget(target, success, returndata, errorMessage);\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\\n * but performing a delegate call.\\n *\\n * _Available since v3.4._\\n */\\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\\n return functionDelegateCall(target, data, \\\"Address: low-level delegate call failed\\\");\\n }\\n\\n /**\\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\\n * but performing a delegate call.\\n *\\n * _Available since v3.4._\\n */\\n function functionDelegateCall(\\n address target,\\n bytes memory data,\\n string memory errorMessage\\n ) internal returns (bytes memory) {\\n (bool success, bytes memory returndata) = target.delegatecall(data);\\n return verifyCallResultFromTarget(target, success, returndata, errorMessage);\\n }\\n\\n /**\\n * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling\\n * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.\\n *\\n * _Available since v4.8._\\n */\\n function verifyCallResultFromTarget(\\n address target,\\n bool success,\\n bytes memory returndata,\\n string memory errorMessage\\n ) internal view returns (bytes memory) {\\n if (success) {\\n if (returndata.length == 0) {\\n // only check isContract if the call was successful and the return data is empty\\n // otherwise we already know that it was a contract\\n require(isContract(target), \\\"Address: call to non-contract\\\");\\n }\\n return returndata;\\n } else {\\n _revert(returndata, errorMessage);\\n }\\n }\\n\\n /**\\n * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the\\n * revert reason or using the provided one.\\n *\\n * _Available since v4.3._\\n */\\n function verifyCallResult(\\n bool success,\\n bytes memory returndata,\\n string memory errorMessage\\n ) internal pure returns (bytes memory) {\\n if (success) {\\n return returndata;\\n } else {\\n _revert(returndata, errorMessage);\\n }\\n }\\n\\n function _revert(bytes memory returndata, string memory errorMessage) private pure {\\n // Look for revert reason and bubble it up if present\\n if (returndata.length > 0) {\\n // The easiest way to bubble the revert reason is using memory via assembly\\n /// @solidity memory-safe-assembly\\n assembly {\\n let returndata_size := mload(returndata)\\n revert(add(32, returndata), returndata_size)\\n }\\n } else {\\n revert(errorMessage);\\n }\\n }\\n}\\n\",\"keccak256\":\"0x006dd67219697fe68d7fbfdea512e7c4cb64a43565ed86171d67e844982da6fa\",\"license\":\"MIT\"},\"@openzeppelin/contracts/utils/StorageSlot.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n// OpenZeppelin Contracts (last updated v4.9.0) (utils/StorageSlot.sol)\\n// This file was procedurally generated from scripts/generate/templates/StorageSlot.js.\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev Library for reading and writing primitive types to specific storage slots.\\n *\\n * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.\\n * This library helps with reading and writing to such slots without the need for inline assembly.\\n *\\n * The functions in this library return Slot structs that contain a `value` member that can be used to read or write.\\n *\\n * Example usage to set ERC1967 implementation slot:\\n * ```solidity\\n * contract ERC1967 {\\n * bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;\\n *\\n * function _getImplementation() internal view returns (address) {\\n * return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;\\n * }\\n *\\n * function _setImplementation(address newImplementation) internal {\\n * require(Address.isContract(newImplementation), \\\"ERC1967: new implementation is not a contract\\\");\\n * StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;\\n * }\\n * }\\n * ```\\n *\\n * _Available since v4.1 for `address`, `bool`, `bytes32`, `uint256`._\\n * _Available since v4.9 for `string`, `bytes`._\\n */\\nlibrary StorageSlot {\\n struct AddressSlot {\\n address value;\\n }\\n\\n struct BooleanSlot {\\n bool value;\\n }\\n\\n struct Bytes32Slot {\\n bytes32 value;\\n }\\n\\n struct Uint256Slot {\\n uint256 value;\\n }\\n\\n struct StringSlot {\\n string value;\\n }\\n\\n struct BytesSlot {\\n bytes value;\\n }\\n\\n /**\\n * @dev Returns an `AddressSlot` with member `value` located at `slot`.\\n */\\n function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {\\n /// @solidity memory-safe-assembly\\n assembly {\\n r.slot := slot\\n }\\n }\\n\\n /**\\n * @dev Returns an `BooleanSlot` with member `value` located at `slot`.\\n */\\n function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {\\n /// @solidity memory-safe-assembly\\n assembly {\\n r.slot := slot\\n }\\n }\\n\\n /**\\n * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.\\n */\\n function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {\\n /// @solidity memory-safe-assembly\\n assembly {\\n r.slot := slot\\n }\\n }\\n\\n /**\\n * @dev Returns an `Uint256Slot` with member `value` located at `slot`.\\n */\\n function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {\\n /// @solidity memory-safe-assembly\\n assembly {\\n r.slot := slot\\n }\\n }\\n\\n /**\\n * @dev Returns an `StringSlot` with member `value` located at `slot`.\\n */\\n function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {\\n /// @solidity memory-safe-assembly\\n assembly {\\n r.slot := slot\\n }\\n }\\n\\n /**\\n * @dev Returns an `StringSlot` representation of the string storage pointer `store`.\\n */\\n function getStringSlot(string storage store) internal pure returns (StringSlot storage r) {\\n /// @solidity memory-safe-assembly\\n assembly {\\n r.slot := store.slot\\n }\\n }\\n\\n /**\\n * @dev Returns an `BytesSlot` with member `value` located at `slot`.\\n */\\n function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {\\n /// @solidity memory-safe-assembly\\n assembly {\\n r.slot := slot\\n }\\n }\\n\\n /**\\n * @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.\\n */\\n function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) {\\n /// @solidity memory-safe-assembly\\n assembly {\\n r.slot := store.slot\\n }\\n }\\n}\\n\",\"keccak256\":\"0xf09e68aa0dc6722a25bc46490e8d48ed864466d17313b8a0b254c36b54e49899\",\"license\":\"MIT\"}},\"version\":1}", + "bytecode": "0x60806040526040516106cc3803806106cc83398101604081905261002291610420565b61002e82826000610035565b505061054a565b61003e836100f6565b6040516001600160a01b038416907f1cf3b03a6cf19fa2baba4df148e9dcabedea7f8a5c07840e207e5c089be95d3e90600090a260008251118061007f5750805b156100f1576100ef836001600160a01b0316635c60da1b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156100c5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906100e991906104e0565b8361027a565b505b505050565b6001600160a01b0381163b6101605760405162461bcd60e51b815260206004820152602560248201527f455243313936373a206e657720626561636f6e206973206e6f74206120636f6e6044820152641d1c9858dd60da1b60648201526084015b60405180910390fd5b6101d4816001600160a01b0316635c60da1b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156101a1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906101c591906104e0565b6001600160a01b03163b151590565b6102395760405162461bcd60e51b815260206004820152603060248201527f455243313936373a20626561636f6e20696d706c656d656e746174696f6e206960448201526f1cc81b9bdd08184818dbdb9d1c9858dd60821b6064820152608401610157565b7fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d5080546001600160a01b0319166001600160a01b0392909216919091179055565b606061029f83836040518060600160405280602781526020016106a5602791396102a6565b9392505050565b6060600080856001600160a01b0316856040516102c391906104fb565b600060405180830381855af49150503d80600081146102fe576040519150601f19603f3d011682016040523d82523d6000602084013e610303565b606091505b5090925090506103158683838761031f565b9695505050505050565b6060831561038e578251600003610387576001600160a01b0385163b6103875760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610157565b5081610398565b61039883836103a0565b949350505050565b8151156103b05781518083602001fd5b8060405162461bcd60e51b81526004016101579190610517565b80516001600160a01b03811681146103e157600080fd5b919050565b634e487b7160e01b600052604160045260246000fd5b60005b838110156104175781810151838201526020016103ff565b50506000910152565b6000806040838503121561043357600080fd5b61043c836103ca565b60208401519092506001600160401b038082111561045957600080fd5b818501915085601f83011261046d57600080fd5b81518181111561047f5761047f6103e6565b604051601f8201601f19908116603f011681019083821181831017156104a7576104a76103e6565b816040528281528860208487010111156104c057600080fd5b6104d18360208301602088016103fc565b80955050505050509250929050565b6000602082840312156104f257600080fd5b61029f826103ca565b6000825161050d8184602087016103fc565b9190910192915050565b60208152600082518060208401526105368160408501602087016103fc565b601f01601f19169190910160400192915050565b61014c806105596000396000f3fe60806040523661001357610011610017565b005b6100115b610027610022610029565b6100c2565b565b600061005c7fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50546001600160a01b031690565b6001600160a01b0316635c60da1b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610099573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906100bd91906100e6565b905090565b3660008037600080366000845af43d6000803e8080156100e1573d6000f35b3d6000fd5b6000602082840312156100f857600080fd5b81516001600160a01b038116811461010f57600080fd5b939250505056fea264697066735822122046b4f5e91a9c4ce5899089d64fe12e498987ddec7b0ba224fa5caa490cf7c92464736f6c63430008190033416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564", + "deployedBytecode": "0x60806040523661001357610011610017565b005b6100115b610027610022610029565b6100c2565b565b600061005c7fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50546001600160a01b031690565b6001600160a01b0316635c60da1b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610099573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906100bd91906100e6565b905090565b3660008037600080366000845af43d6000803e8080156100e1573d6000f35b3d6000fd5b6000602082840312156100f857600080fd5b81516001600160a01b038116811461010f57600080fd5b939250505056fea264697066735822122046b4f5e91a9c4ce5899089d64fe12e498987ddec7b0ba224fa5caa490cf7c92464736f6c63430008190033", + "devdoc": { + "details": "This contract implements a proxy that gets the implementation address for each call from an {UpgradeableBeacon}. The beacon address is stored in storage slot `uint256(keccak256('eip1967.proxy.beacon')) - 1`, so that it doesn't conflict with the storage layout of the implementation behind the proxy. _Available since v3.4._", + "events": { + "AdminChanged(address,address)": { + "details": "Emitted when the admin account has changed." + }, + "BeaconUpgraded(address)": { + "details": "Emitted when the beacon is changed." + }, + "Upgraded(address)": { + "details": "Emitted when the implementation is upgraded." + } + }, + "kind": "dev", + "methods": { + "constructor": { + "details": "Initializes the proxy with `beacon`. If `data` is nonempty, it's used as data in a delegate call to the implementation returned by the beacon. This will typically be an encoded function call, and allows initializing the storage of the proxy like a Solidity constructor. Requirements: - `beacon` must be a contract with the interface {IBeacon}." + } + }, + "version": 1 + }, + "userdoc": { + "kind": "user", + "methods": {}, + "version": 1 + }, + "storageLayout": { + "storage": [], + "types": null + } +} diff --git a/deployments/bsctestnet/solcInputs/02cfe3974aa10474c22406a3f2870e89.json b/deployments/bsctestnet/solcInputs/02cfe3974aa10474c22406a3f2870e89.json new file mode 100644 index 000000000..dbcdbf935 --- /dev/null +++ b/deployments/bsctestnet/solcInputs/02cfe3974aa10474c22406a3f2870e89.json @@ -0,0 +1,493 @@ +{ + "language": "Solidity", + "sources": { + "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface AggregatorV3Interface {\n function decimals() external view returns (uint8);\n\n function description() external view returns (string memory);\n\n function version() external view returns (uint256);\n\n function getRoundData(uint80 _roundId)\n external\n view\n returns (\n uint80 roundId,\n int256 answer,\n uint256 startedAt,\n uint256 updatedAt,\n uint80 answeredInRound\n );\n\n function latestRoundData()\n external\n view\n returns (\n uint80 roundId,\n int256 answer,\n uint256 startedAt,\n uint256 updatedAt,\n uint80 answeredInRound\n );\n}\n" + }, + "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable2Step.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./OwnableUpgradeable.sol\";\nimport {Initializable} from \"../proxy/utils/Initializable.sol\";\n\n/**\n * @dev Contract module which provides access control mechanism, where\n * there is an account (an owner) that can be granted exclusive access to\n * specific functions.\n *\n * By default, the owner account will be the one that deploys the contract. This\n * can later be changed with {transferOwnership} and {acceptOwnership}.\n *\n * This module is used through inheritance. It will make available all functions\n * from parent (Ownable).\n */\nabstract contract Ownable2StepUpgradeable is Initializable, OwnableUpgradeable {\n address private _pendingOwner;\n\n event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner);\n\n function __Ownable2Step_init() internal onlyInitializing {\n __Ownable_init_unchained();\n }\n\n function __Ownable2Step_init_unchained() internal onlyInitializing {\n }\n /**\n * @dev Returns the address of the pending owner.\n */\n function pendingOwner() public view virtual returns (address) {\n return _pendingOwner;\n }\n\n /**\n * @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one.\n * Can only be called by the current owner.\n */\n function transferOwnership(address newOwner) public virtual override onlyOwner {\n _pendingOwner = newOwner;\n emit OwnershipTransferStarted(owner(), newOwner);\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner.\n * Internal function without access restriction.\n */\n function _transferOwnership(address newOwner) internal virtual override {\n delete _pendingOwner;\n super._transferOwnership(newOwner);\n }\n\n /**\n * @dev The new owner accepts the ownership transfer.\n */\n function acceptOwnership() public virtual {\n address sender = _msgSender();\n require(pendingOwner() == sender, \"Ownable2Step: caller is not the new owner\");\n _transferOwnership(sender);\n }\n\n /**\n * @dev This empty reserved space is put in place to allow future versions to add new\n * variables without shifting down storage in the inheritance chain.\n * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\n */\n uint256[49] private __gap;\n}\n" + }, + "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../utils/ContextUpgradeable.sol\";\nimport {Initializable} from \"../proxy/utils/Initializable.sol\";\n\n/**\n * @dev Contract module which provides a basic access control mechanism, where\n * there is an account (an owner) that can be granted exclusive access to\n * specific functions.\n *\n * By default, the owner account will be the one that deploys the contract. This\n * can later be changed with {transferOwnership}.\n *\n * This module is used through inheritance. It will make available the modifier\n * `onlyOwner`, which can be applied to your functions to restrict their use to\n * the owner.\n */\nabstract contract OwnableUpgradeable is Initializable, ContextUpgradeable {\n address private _owner;\n\n event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n\n /**\n * @dev Initializes the contract setting the deployer as the initial owner.\n */\n function __Ownable_init() internal onlyInitializing {\n __Ownable_init_unchained();\n }\n\n function __Ownable_init_unchained() internal onlyInitializing {\n _transferOwnership(_msgSender());\n }\n\n /**\n * @dev Throws if called by any account other than the owner.\n */\n modifier onlyOwner() {\n _checkOwner();\n _;\n }\n\n /**\n * @dev Returns the address of the current owner.\n */\n function owner() public view virtual returns (address) {\n return _owner;\n }\n\n /**\n * @dev Throws if the sender is not the owner.\n */\n function _checkOwner() internal view virtual {\n require(owner() == _msgSender(), \"Ownable: caller is not the owner\");\n }\n\n /**\n * @dev Leaves the contract without owner. It will not be possible to call\n * `onlyOwner` functions. Can only be called by the current owner.\n *\n * NOTE: Renouncing ownership will leave the contract without an owner,\n * thereby disabling any functionality that is only available to the owner.\n */\n function renounceOwnership() public virtual onlyOwner {\n _transferOwnership(address(0));\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Can only be called by the current owner.\n */\n function transferOwnership(address newOwner) public virtual onlyOwner {\n require(newOwner != address(0), \"Ownable: new owner is the zero address\");\n _transferOwnership(newOwner);\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Internal function without access restriction.\n */\n function _transferOwnership(address newOwner) internal virtual {\n address oldOwner = _owner;\n _owner = newOwner;\n emit OwnershipTransferred(oldOwner, newOwner);\n }\n\n /**\n * @dev This empty reserved space is put in place to allow future versions to add new\n * variables without shifting down storage in the inheritance chain.\n * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\n */\n uint256[49] private __gap;\n}\n" + }, + "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.0) (proxy/utils/Initializable.sol)\n\npragma solidity ^0.8.2;\n\nimport \"../../utils/AddressUpgradeable.sol\";\n\n/**\n * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed\n * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an\n * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer\n * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.\n *\n * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be\n * reused. This mechanism prevents re-execution of each \"step\" but allows the creation of new initialization steps in\n * case an upgrade adds a module that needs to be initialized.\n *\n * For example:\n *\n * [.hljs-theme-light.nopadding]\n * ```solidity\n * contract MyToken is ERC20Upgradeable {\n * function initialize() initializer public {\n * __ERC20_init(\"MyToken\", \"MTK\");\n * }\n * }\n *\n * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {\n * function initializeV2() reinitializer(2) public {\n * __ERC20Permit_init(\"MyToken\");\n * }\n * }\n * ```\n *\n * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as\n * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.\n *\n * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure\n * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.\n *\n * [CAUTION]\n * ====\n * Avoid leaving a contract uninitialized.\n *\n * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation\n * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke\n * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:\n *\n * [.hljs-theme-light.nopadding]\n * ```\n * /// @custom:oz-upgrades-unsafe-allow constructor\n * constructor() {\n * _disableInitializers();\n * }\n * ```\n * ====\n */\nabstract contract Initializable {\n /**\n * @dev Indicates that the contract has been initialized.\n * @custom:oz-retyped-from bool\n */\n uint8 private _initialized;\n\n /**\n * @dev Indicates that the contract is in the process of being initialized.\n */\n bool private _initializing;\n\n /**\n * @dev Triggered when the contract has been initialized or reinitialized.\n */\n event Initialized(uint8 version);\n\n /**\n * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,\n * `onlyInitializing` functions can be used to initialize parent contracts.\n *\n * Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a\n * constructor.\n *\n * Emits an {Initialized} event.\n */\n modifier initializer() {\n bool isTopLevelCall = !_initializing;\n require(\n (isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1),\n \"Initializable: contract is already initialized\"\n );\n _initialized = 1;\n if (isTopLevelCall) {\n _initializing = true;\n }\n _;\n if (isTopLevelCall) {\n _initializing = false;\n emit Initialized(1);\n }\n }\n\n /**\n * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the\n * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be\n * used to initialize parent contracts.\n *\n * A reinitializer may be used after the original initialization step. This is essential to configure modules that\n * are added through upgrades and that require initialization.\n *\n * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`\n * cannot be nested. If one is invoked in the context of another, execution will revert.\n *\n * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in\n * a contract, executing them in the right order is up to the developer or operator.\n *\n * WARNING: setting the version to 255 will prevent any future reinitialization.\n *\n * Emits an {Initialized} event.\n */\n modifier reinitializer(uint8 version) {\n require(!_initializing && _initialized < version, \"Initializable: contract is already initialized\");\n _initialized = version;\n _initializing = true;\n _;\n _initializing = false;\n emit Initialized(version);\n }\n\n /**\n * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the\n * {initializer} and {reinitializer} modifiers, directly or indirectly.\n */\n modifier onlyInitializing() {\n require(_initializing, \"Initializable: contract is not initializing\");\n _;\n }\n\n /**\n * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.\n * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized\n * to any version. It is recommended to use this to lock implementation contracts that are designed to be called\n * through proxies.\n *\n * Emits an {Initialized} event the first time it is successfully executed.\n */\n function _disableInitializers() internal virtual {\n require(!_initializing, \"Initializable: contract is initializing\");\n if (_initialized != type(uint8).max) {\n _initialized = type(uint8).max;\n emit Initialized(type(uint8).max);\n }\n }\n\n /**\n * @dev Returns the highest version that has been initialized. See {reinitializer}.\n */\n function _getInitializedVersion() internal view returns (uint8) {\n return _initialized;\n }\n\n /**\n * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.\n */\n function _isInitializing() internal view returns (bool) {\n return _initializing;\n }\n}\n" + }, + "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../utils/ContextUpgradeable.sol\";\nimport {Initializable} from \"../proxy/utils/Initializable.sol\";\n\n/**\n * @dev Contract module which allows children to implement an emergency stop\n * mechanism that can be triggered by an authorized account.\n *\n * This module is used through inheritance. It will make available the\n * modifiers `whenNotPaused` and `whenPaused`, which can be applied to\n * the functions of your contract. Note that they will not be pausable by\n * simply including this module, only once the modifiers are put in place.\n */\nabstract contract PausableUpgradeable is Initializable, ContextUpgradeable {\n /**\n * @dev Emitted when the pause is triggered by `account`.\n */\n event Paused(address account);\n\n /**\n * @dev Emitted when the pause is lifted by `account`.\n */\n event Unpaused(address account);\n\n bool private _paused;\n\n /**\n * @dev Initializes the contract in unpaused state.\n */\n function __Pausable_init() internal onlyInitializing {\n __Pausable_init_unchained();\n }\n\n function __Pausable_init_unchained() internal onlyInitializing {\n _paused = false;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is not paused.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n modifier whenNotPaused() {\n _requireNotPaused();\n _;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is paused.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n modifier whenPaused() {\n _requirePaused();\n _;\n }\n\n /**\n * @dev Returns true if the contract is paused, and false otherwise.\n */\n function paused() public view virtual returns (bool) {\n return _paused;\n }\n\n /**\n * @dev Throws if the contract is paused.\n */\n function _requireNotPaused() internal view virtual {\n require(!paused(), \"Pausable: paused\");\n }\n\n /**\n * @dev Throws if the contract is not paused.\n */\n function _requirePaused() internal view virtual {\n require(paused(), \"Pausable: not paused\");\n }\n\n /**\n * @dev Triggers stopped state.\n *\n * Requirements:\n *\n * - The contract must not be paused.\n */\n function _pause() internal virtual whenNotPaused {\n _paused = true;\n emit Paused(_msgSender());\n }\n\n /**\n * @dev Returns to normal state.\n *\n * Requirements:\n *\n * - The contract must be paused.\n */\n function _unpause() internal virtual whenPaused {\n _paused = false;\n emit Unpaused(_msgSender());\n }\n\n /**\n * @dev This empty reserved space is put in place to allow future versions to add new\n * variables without shifting down storage in the inheritance chain.\n * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\n */\n uint256[49] private __gap;\n}\n" + }, + "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.0) (security/ReentrancyGuard.sol)\n\npragma solidity ^0.8.0;\nimport {Initializable} from \"../proxy/utils/Initializable.sol\";\n\n/**\n * @dev Contract module that helps prevent reentrant calls to a function.\n *\n * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier\n * available, which can be applied to functions to make sure there are no nested\n * (reentrant) calls to them.\n *\n * Note that because there is a single `nonReentrant` guard, functions marked as\n * `nonReentrant` may not call one another. This can be worked around by making\n * those functions `private`, and then adding `external` `nonReentrant` entry\n * points to them.\n *\n * TIP: If you would like to learn more about reentrancy and alternative ways\n * to protect against it, check out our blog post\n * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].\n */\nabstract contract ReentrancyGuardUpgradeable is Initializable {\n // Booleans are more expensive than uint256 or any type that takes up a full\n // word because each write operation emits an extra SLOAD to first read the\n // slot's contents, replace the bits taken up by the boolean, and then write\n // back. This is the compiler's defense against contract upgrades and\n // pointer aliasing, and it cannot be disabled.\n\n // The values being non-zero value makes deployment a bit more expensive,\n // but in exchange the refund on every call to nonReentrant will be lower in\n // amount. Since refunds are capped to a percentage of the total\n // transaction's gas, it is best to keep them low in cases like this one, to\n // increase the likelihood of the full refund coming into effect.\n uint256 private constant _NOT_ENTERED = 1;\n uint256 private constant _ENTERED = 2;\n\n uint256 private _status;\n\n function __ReentrancyGuard_init() internal onlyInitializing {\n __ReentrancyGuard_init_unchained();\n }\n\n function __ReentrancyGuard_init_unchained() internal onlyInitializing {\n _status = _NOT_ENTERED;\n }\n\n /**\n * @dev Prevents a contract from calling itself, directly or indirectly.\n * Calling a `nonReentrant` function from another `nonReentrant`\n * function is not supported. It is possible to prevent this from happening\n * by making the `nonReentrant` function external, and making it call a\n * `private` function that does the actual work.\n */\n modifier nonReentrant() {\n _nonReentrantBefore();\n _;\n _nonReentrantAfter();\n }\n\n function _nonReentrantBefore() private {\n // On the first call to nonReentrant, _status will be _NOT_ENTERED\n require(_status != _ENTERED, \"ReentrancyGuard: reentrant call\");\n\n // Any calls to nonReentrant after this point will fail\n _status = _ENTERED;\n }\n\n function _nonReentrantAfter() private {\n // By storing the original value once again, a refund is triggered (see\n // https://eips.ethereum.org/EIPS/eip-2200)\n _status = _NOT_ENTERED;\n }\n\n /**\n * @dev Returns true if the reentrancy guard is currently set to \"entered\", which indicates there is a\n * `nonReentrant` function in the call stack.\n */\n function _reentrancyGuardEntered() internal view returns (bool) {\n return _status == _ENTERED;\n }\n\n /**\n * @dev This empty reserved space is put in place to allow future versions to add new\n * variables without shifting down storage in the inheritance chain.\n * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\n */\n uint256[49] private __gap;\n}\n" + }, + "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/IERC20MetadataUpgradeable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC20Upgradeable.sol\";\n\n/**\n * @dev Interface for the optional metadata functions from the ERC20 standard.\n *\n * _Available since v4.1._\n */\ninterface IERC20MetadataUpgradeable is IERC20Upgradeable {\n /**\n * @dev Returns the name of the token.\n */\n function name() external view returns (string memory);\n\n /**\n * @dev Returns the symbol of the token.\n */\n function symbol() external view returns (string memory);\n\n /**\n * @dev Returns the decimals places of the token.\n */\n function decimals() external view returns (uint8);\n}\n" + }, + "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/IERC20PermitUpgradeable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.4) (token/ERC20/extensions/IERC20Permit.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in\n * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].\n *\n * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by\n * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't\n * need to send a transaction, and thus is not required to hold Ether at all.\n *\n * ==== Security Considerations\n *\n * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature\n * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be\n * considered as an intention to spend the allowance in any specific way. The second is that because permits have\n * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should\n * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be\n * generally recommended is:\n *\n * ```solidity\n * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {\n * try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}\n * doThing(..., value);\n * }\n *\n * function doThing(..., uint256 value) public {\n * token.safeTransferFrom(msg.sender, address(this), value);\n * ...\n * }\n * ```\n *\n * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of\n * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also\n * {SafeERC20-safeTransferFrom}).\n *\n * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so\n * contracts should have entry points that don't rely on permit.\n */\ninterface IERC20PermitUpgradeable {\n /**\n * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,\n * given ``owner``'s signed approval.\n *\n * IMPORTANT: The same issues {IERC20-approve} has related to transaction\n * ordering also apply here.\n *\n * Emits an {Approval} event.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n * - `deadline` must be a timestamp in the future.\n * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`\n * over the EIP712-formatted function arguments.\n * - the signature must use ``owner``'s current nonce (see {nonces}).\n *\n * For more information on the signature format, see the\n * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP\n * section].\n *\n * CAUTION: See Security Considerations above.\n */\n function permit(\n address owner,\n address spender,\n uint256 value,\n uint256 deadline,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) external;\n\n /**\n * @dev Returns the current nonce for `owner`. This value must be\n * included whenever a signature is generated for {permit}.\n *\n * Every successful call to {permit} increases ``owner``'s nonce by one. This\n * prevents a signature from being used multiple times.\n */\n function nonces(address owner) external view returns (uint256);\n\n /**\n * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.\n */\n // solhint-disable-next-line func-name-mixedcase\n function DOMAIN_SEPARATOR() external view returns (bytes32);\n}\n" + }, + "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20Upgradeable {\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n\n /**\n * @dev Returns the amount of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves `amount` tokens from the caller's account to `to`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address to, uint256 amount) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender's allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @dev Moves `amount` tokens from `from` to `to` using the\n * allowance mechanism. `amount` is then deducted from the caller's\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(address from, address to, uint256 amount) external returns (bool);\n}\n" + }, + "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.3) (token/ERC20/utils/SafeERC20.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC20Upgradeable.sol\";\nimport \"../extensions/IERC20PermitUpgradeable.sol\";\nimport \"../../../utils/AddressUpgradeable.sol\";\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20Upgradeable {\n using AddressUpgradeable for address;\n\n /**\n * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,\n * non-reverting calls are assumed to be successful.\n */\n function safeTransfer(IERC20Upgradeable token, address to, uint256 value) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\n }\n\n /**\n * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the\n * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.\n */\n function safeTransferFrom(IERC20Upgradeable token, address from, address to, uint256 value) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\n }\n\n /**\n * @dev Deprecated. This function has issues similar to the ones found in\n * {IERC20-approve}, and its usage is discouraged.\n *\n * Whenever possible, use {safeIncreaseAllowance} and\n * {safeDecreaseAllowance} instead.\n */\n function safeApprove(IERC20Upgradeable token, address spender, uint256 value) internal {\n // safeApprove should only be called when setting an initial allowance,\n // or when resetting it to zero. To increase and decrease it, use\n // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\n require(\n (value == 0) || (token.allowance(address(this), spender) == 0),\n \"SafeERC20: approve from non-zero to non-zero allowance\"\n );\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\n }\n\n /**\n * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,\n * non-reverting calls are assumed to be successful.\n */\n function safeIncreaseAllowance(IERC20Upgradeable token, address spender, uint256 value) internal {\n uint256 oldAllowance = token.allowance(address(this), spender);\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));\n }\n\n /**\n * @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,\n * non-reverting calls are assumed to be successful.\n */\n function safeDecreaseAllowance(IERC20Upgradeable token, address spender, uint256 value) internal {\n unchecked {\n uint256 oldAllowance = token.allowance(address(this), spender);\n require(oldAllowance >= value, \"SafeERC20: decreased allowance below zero\");\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));\n }\n }\n\n /**\n * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,\n * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval\n * to be set to zero before setting it to a non-zero value, such as USDT.\n */\n function forceApprove(IERC20Upgradeable token, address spender, uint256 value) internal {\n bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);\n\n if (!_callOptionalReturnBool(token, approvalCall)) {\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));\n _callOptionalReturn(token, approvalCall);\n }\n }\n\n /**\n * @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.\n * Revert on invalid signature.\n */\n function safePermit(\n IERC20PermitUpgradeable token,\n address owner,\n address spender,\n uint256 value,\n uint256 deadline,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) internal {\n uint256 nonceBefore = token.nonces(owner);\n token.permit(owner, spender, value, deadline, v, r, s);\n uint256 nonceAfter = token.nonces(owner);\n require(nonceAfter == nonceBefore + 1, \"SafeERC20: permit did not succeed\");\n }\n\n /**\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n * on the return value: the return value is optional (but if data is returned, it must not be false).\n * @param token The token targeted by the call.\n * @param data The call data (encoded using abi.encode or one of its variants).\n */\n function _callOptionalReturn(IERC20Upgradeable token, bytes memory data) private {\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that\n // the target address contains contract code and also asserts for success in the low-level call.\n\n bytes memory returndata = address(token).functionCall(data, \"SafeERC20: low-level call failed\");\n require(returndata.length == 0 || abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n }\n\n /**\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n * on the return value: the return value is optional (but if data is returned, it must not be false).\n * @param token The token targeted by the call.\n * @param data The call data (encoded using abi.encode or one of its variants).\n *\n * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.\n */\n function _callOptionalReturnBool(IERC20Upgradeable token, bytes memory data) private returns (bool) {\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false\n // and not revert is the subcall reverts.\n\n (bool success, bytes memory returndata) = address(token).call(data);\n return\n success && (returndata.length == 0 || abi.decode(returndata, (bool))) && AddressUpgradeable.isContract(address(token));\n }\n}\n" + }, + "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)\n\npragma solidity ^0.8.1;\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary AddressUpgradeable {\n /**\n * @dev Returns true if `account` is a contract.\n *\n * [IMPORTANT]\n * ====\n * It is unsafe to assume that an address for which this function returns\n * false is an externally-owned account (EOA) and not a contract.\n *\n * Among others, `isContract` will return false for the following\n * types of addresses:\n *\n * - an externally-owned account\n * - a contract in construction\n * - an address where a contract will be created\n * - an address where a contract lived, but was destroyed\n *\n * Furthermore, `isContract` will also return true if the target contract within\n * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,\n * which only has an effect at the end of a transaction.\n * ====\n *\n * [IMPORTANT]\n * ====\n * You shouldn't rely on `isContract` to protect against flash loan attacks!\n *\n * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets\n * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract\n * constructor.\n * ====\n */\n function isContract(address account) internal view returns (bool) {\n // This method relies on extcodesize/address.code.length, which returns 0\n // for contracts in construction, since the code is only stored at the end\n // of the constructor execution.\n\n return account.code.length > 0;\n }\n\n /**\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n * `recipient`, forwarding all available gas and reverting on errors.\n *\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\n * imposed by `transfer`, making them unable to receive funds via\n * `transfer`. {sendValue} removes this limitation.\n *\n * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n *\n * IMPORTANT: because control is transferred to `recipient`, care must be\n * taken to not create reentrancy vulnerabilities. Consider using\n * {ReentrancyGuard} or the\n * https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n */\n function sendValue(address payable recipient, uint256 amount) internal {\n require(address(this).balance >= amount, \"Address: insufficient balance\");\n\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Address: unable to send value, recipient may have reverted\");\n }\n\n /**\n * @dev Performs a Solidity function call using a low level `call`. A\n * plain `call` is an unsafe replacement for a function call: use this\n * function instead.\n *\n * If `target` reverts with a revert reason, it is bubbled up by this\n * function (like regular Solidity function calls).\n *\n * Returns the raw returned data. To convert to the expected return value,\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n *\n * Requirements:\n *\n * - `target` must be a contract.\n * - calling `target` with `data` must not revert.\n *\n * _Available since v3.1._\n */\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, \"Address: low-level call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n * `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but also transferring `value` wei to `target`.\n *\n * Requirements:\n *\n * - the calling contract must have an ETH balance of at least `value`.\n * - the called Solidity function must be `payable`.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {\n return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n * with `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(address(this).balance >= value, \"Address: insufficient balance for call\");\n (bool success, bytes memory returndata) = target.call{value: value}(data);\n return verifyCallResultFromTarget(target, success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n return functionStaticCall(target, data, \"Address: low-level static call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n (bool success, bytes memory returndata) = target.staticcall(data);\n return verifyCallResultFromTarget(target, success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n (bool success, bytes memory returndata) = target.delegatecall(data);\n return verifyCallResultFromTarget(target, success, returndata, errorMessage);\n }\n\n /**\n * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling\n * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.\n *\n * _Available since v4.8._\n */\n function verifyCallResultFromTarget(\n address target,\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n if (success) {\n if (returndata.length == 0) {\n // only check isContract if the call was successful and the return data is empty\n // otherwise we already know that it was a contract\n require(isContract(target), \"Address: call to non-contract\");\n }\n return returndata;\n } else {\n _revert(returndata, errorMessage);\n }\n }\n\n /**\n * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the\n * revert reason or using the provided one.\n *\n * _Available since v4.3._\n */\n function verifyCallResult(\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal pure returns (bytes memory) {\n if (success) {\n return returndata;\n } else {\n _revert(returndata, errorMessage);\n }\n }\n\n function _revert(bytes memory returndata, string memory errorMessage) private pure {\n // Look for revert reason and bubble it up if present\n if (returndata.length > 0) {\n // The easiest way to bubble the revert reason is using memory via assembly\n /// @solidity memory-safe-assembly\n assembly {\n let returndata_size := mload(returndata)\n revert(add(32, returndata), returndata_size)\n }\n } else {\n revert(errorMessage);\n }\n }\n}\n" + }, + "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.4) (utils/Context.sol)\n\npragma solidity ^0.8.0;\nimport {Initializable} from \"../proxy/utils/Initializable.sol\";\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract ContextUpgradeable is Initializable {\n function __Context_init() internal onlyInitializing {\n }\n\n function __Context_init_unchained() internal onlyInitializing {\n }\n function _msgSender() internal view virtual returns (address) {\n return msg.sender;\n }\n\n function _msgData() internal view virtual returns (bytes calldata) {\n return msg.data;\n }\n\n function _contextSuffixLength() internal view virtual returns (uint256) {\n return 0;\n }\n\n /**\n * @dev This empty reserved space is put in place to allow future versions to add new\n * variables without shifting down storage in the inheritance chain.\n * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\n */\n uint256[50] private __gap;\n}\n" + }, + "@openzeppelin/contracts-upgradeable/utils/math/SafeCastUpgradeable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SafeCast.sol)\n// This file was procedurally generated from scripts/generate/templates/SafeCast.js.\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow\n * checks.\n *\n * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can\n * easily result in undesired exploitation or bugs, since developers usually\n * assume that overflows raise errors. `SafeCast` restores this intuition by\n * reverting the transaction when such an operation overflows.\n *\n * Using this library instead of the unchecked operations eliminates an entire\n * class of bugs, so it's recommended to use it always.\n *\n * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing\n * all math on `uint256` and `int256` and then downcasting.\n */\nlibrary SafeCastUpgradeable {\n /**\n * @dev Returns the downcasted uint248 from uint256, reverting on\n * overflow (when the input is greater than largest uint248).\n *\n * Counterpart to Solidity's `uint248` operator.\n *\n * Requirements:\n *\n * - input must fit into 248 bits\n *\n * _Available since v4.7._\n */\n function toUint248(uint256 value) internal pure returns (uint248) {\n require(value <= type(uint248).max, \"SafeCast: value doesn't fit in 248 bits\");\n return uint248(value);\n }\n\n /**\n * @dev Returns the downcasted uint240 from uint256, reverting on\n * overflow (when the input is greater than largest uint240).\n *\n * Counterpart to Solidity's `uint240` operator.\n *\n * Requirements:\n *\n * - input must fit into 240 bits\n *\n * _Available since v4.7._\n */\n function toUint240(uint256 value) internal pure returns (uint240) {\n require(value <= type(uint240).max, \"SafeCast: value doesn't fit in 240 bits\");\n return uint240(value);\n }\n\n /**\n * @dev Returns the downcasted uint232 from uint256, reverting on\n * overflow (when the input is greater than largest uint232).\n *\n * Counterpart to Solidity's `uint232` operator.\n *\n * Requirements:\n *\n * - input must fit into 232 bits\n *\n * _Available since v4.7._\n */\n function toUint232(uint256 value) internal pure returns (uint232) {\n require(value <= type(uint232).max, \"SafeCast: value doesn't fit in 232 bits\");\n return uint232(value);\n }\n\n /**\n * @dev Returns the downcasted uint224 from uint256, reverting on\n * overflow (when the input is greater than largest uint224).\n *\n * Counterpart to Solidity's `uint224` operator.\n *\n * Requirements:\n *\n * - input must fit into 224 bits\n *\n * _Available since v4.2._\n */\n function toUint224(uint256 value) internal pure returns (uint224) {\n require(value <= type(uint224).max, \"SafeCast: value doesn't fit in 224 bits\");\n return uint224(value);\n }\n\n /**\n * @dev Returns the downcasted uint216 from uint256, reverting on\n * overflow (when the input is greater than largest uint216).\n *\n * Counterpart to Solidity's `uint216` operator.\n *\n * Requirements:\n *\n * - input must fit into 216 bits\n *\n * _Available since v4.7._\n */\n function toUint216(uint256 value) internal pure returns (uint216) {\n require(value <= type(uint216).max, \"SafeCast: value doesn't fit in 216 bits\");\n return uint216(value);\n }\n\n /**\n * @dev Returns the downcasted uint208 from uint256, reverting on\n * overflow (when the input is greater than largest uint208).\n *\n * Counterpart to Solidity's `uint208` operator.\n *\n * Requirements:\n *\n * - input must fit into 208 bits\n *\n * _Available since v4.7._\n */\n function toUint208(uint256 value) internal pure returns (uint208) {\n require(value <= type(uint208).max, \"SafeCast: value doesn't fit in 208 bits\");\n return uint208(value);\n }\n\n /**\n * @dev Returns the downcasted uint200 from uint256, reverting on\n * overflow (when the input is greater than largest uint200).\n *\n * Counterpart to Solidity's `uint200` operator.\n *\n * Requirements:\n *\n * - input must fit into 200 bits\n *\n * _Available since v4.7._\n */\n function toUint200(uint256 value) internal pure returns (uint200) {\n require(value <= type(uint200).max, \"SafeCast: value doesn't fit in 200 bits\");\n return uint200(value);\n }\n\n /**\n * @dev Returns the downcasted uint192 from uint256, reverting on\n * overflow (when the input is greater than largest uint192).\n *\n * Counterpart to Solidity's `uint192` operator.\n *\n * Requirements:\n *\n * - input must fit into 192 bits\n *\n * _Available since v4.7._\n */\n function toUint192(uint256 value) internal pure returns (uint192) {\n require(value <= type(uint192).max, \"SafeCast: value doesn't fit in 192 bits\");\n return uint192(value);\n }\n\n /**\n * @dev Returns the downcasted uint184 from uint256, reverting on\n * overflow (when the input is greater than largest uint184).\n *\n * Counterpart to Solidity's `uint184` operator.\n *\n * Requirements:\n *\n * - input must fit into 184 bits\n *\n * _Available since v4.7._\n */\n function toUint184(uint256 value) internal pure returns (uint184) {\n require(value <= type(uint184).max, \"SafeCast: value doesn't fit in 184 bits\");\n return uint184(value);\n }\n\n /**\n * @dev Returns the downcasted uint176 from uint256, reverting on\n * overflow (when the input is greater than largest uint176).\n *\n * Counterpart to Solidity's `uint176` operator.\n *\n * Requirements:\n *\n * - input must fit into 176 bits\n *\n * _Available since v4.7._\n */\n function toUint176(uint256 value) internal pure returns (uint176) {\n require(value <= type(uint176).max, \"SafeCast: value doesn't fit in 176 bits\");\n return uint176(value);\n }\n\n /**\n * @dev Returns the downcasted uint168 from uint256, reverting on\n * overflow (when the input is greater than largest uint168).\n *\n * Counterpart to Solidity's `uint168` operator.\n *\n * Requirements:\n *\n * - input must fit into 168 bits\n *\n * _Available since v4.7._\n */\n function toUint168(uint256 value) internal pure returns (uint168) {\n require(value <= type(uint168).max, \"SafeCast: value doesn't fit in 168 bits\");\n return uint168(value);\n }\n\n /**\n * @dev Returns the downcasted uint160 from uint256, reverting on\n * overflow (when the input is greater than largest uint160).\n *\n * Counterpart to Solidity's `uint160` operator.\n *\n * Requirements:\n *\n * - input must fit into 160 bits\n *\n * _Available since v4.7._\n */\n function toUint160(uint256 value) internal pure returns (uint160) {\n require(value <= type(uint160).max, \"SafeCast: value doesn't fit in 160 bits\");\n return uint160(value);\n }\n\n /**\n * @dev Returns the downcasted uint152 from uint256, reverting on\n * overflow (when the input is greater than largest uint152).\n *\n * Counterpart to Solidity's `uint152` operator.\n *\n * Requirements:\n *\n * - input must fit into 152 bits\n *\n * _Available since v4.7._\n */\n function toUint152(uint256 value) internal pure returns (uint152) {\n require(value <= type(uint152).max, \"SafeCast: value doesn't fit in 152 bits\");\n return uint152(value);\n }\n\n /**\n * @dev Returns the downcasted uint144 from uint256, reverting on\n * overflow (when the input is greater than largest uint144).\n *\n * Counterpart to Solidity's `uint144` operator.\n *\n * Requirements:\n *\n * - input must fit into 144 bits\n *\n * _Available since v4.7._\n */\n function toUint144(uint256 value) internal pure returns (uint144) {\n require(value <= type(uint144).max, \"SafeCast: value doesn't fit in 144 bits\");\n return uint144(value);\n }\n\n /**\n * @dev Returns the downcasted uint136 from uint256, reverting on\n * overflow (when the input is greater than largest uint136).\n *\n * Counterpart to Solidity's `uint136` operator.\n *\n * Requirements:\n *\n * - input must fit into 136 bits\n *\n * _Available since v4.7._\n */\n function toUint136(uint256 value) internal pure returns (uint136) {\n require(value <= type(uint136).max, \"SafeCast: value doesn't fit in 136 bits\");\n return uint136(value);\n }\n\n /**\n * @dev Returns the downcasted uint128 from uint256, reverting on\n * overflow (when the input is greater than largest uint128).\n *\n * Counterpart to Solidity's `uint128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n *\n * _Available since v2.5._\n */\n function toUint128(uint256 value) internal pure returns (uint128) {\n require(value <= type(uint128).max, \"SafeCast: value doesn't fit in 128 bits\");\n return uint128(value);\n }\n\n /**\n * @dev Returns the downcasted uint120 from uint256, reverting on\n * overflow (when the input is greater than largest uint120).\n *\n * Counterpart to Solidity's `uint120` operator.\n *\n * Requirements:\n *\n * - input must fit into 120 bits\n *\n * _Available since v4.7._\n */\n function toUint120(uint256 value) internal pure returns (uint120) {\n require(value <= type(uint120).max, \"SafeCast: value doesn't fit in 120 bits\");\n return uint120(value);\n }\n\n /**\n * @dev Returns the downcasted uint112 from uint256, reverting on\n * overflow (when the input is greater than largest uint112).\n *\n * Counterpart to Solidity's `uint112` operator.\n *\n * Requirements:\n *\n * - input must fit into 112 bits\n *\n * _Available since v4.7._\n */\n function toUint112(uint256 value) internal pure returns (uint112) {\n require(value <= type(uint112).max, \"SafeCast: value doesn't fit in 112 bits\");\n return uint112(value);\n }\n\n /**\n * @dev Returns the downcasted uint104 from uint256, reverting on\n * overflow (when the input is greater than largest uint104).\n *\n * Counterpart to Solidity's `uint104` operator.\n *\n * Requirements:\n *\n * - input must fit into 104 bits\n *\n * _Available since v4.7._\n */\n function toUint104(uint256 value) internal pure returns (uint104) {\n require(value <= type(uint104).max, \"SafeCast: value doesn't fit in 104 bits\");\n return uint104(value);\n }\n\n /**\n * @dev Returns the downcasted uint96 from uint256, reverting on\n * overflow (when the input is greater than largest uint96).\n *\n * Counterpart to Solidity's `uint96` operator.\n *\n * Requirements:\n *\n * - input must fit into 96 bits\n *\n * _Available since v4.2._\n */\n function toUint96(uint256 value) internal pure returns (uint96) {\n require(value <= type(uint96).max, \"SafeCast: value doesn't fit in 96 bits\");\n return uint96(value);\n }\n\n /**\n * @dev Returns the downcasted uint88 from uint256, reverting on\n * overflow (when the input is greater than largest uint88).\n *\n * Counterpart to Solidity's `uint88` operator.\n *\n * Requirements:\n *\n * - input must fit into 88 bits\n *\n * _Available since v4.7._\n */\n function toUint88(uint256 value) internal pure returns (uint88) {\n require(value <= type(uint88).max, \"SafeCast: value doesn't fit in 88 bits\");\n return uint88(value);\n }\n\n /**\n * @dev Returns the downcasted uint80 from uint256, reverting on\n * overflow (when the input is greater than largest uint80).\n *\n * Counterpart to Solidity's `uint80` operator.\n *\n * Requirements:\n *\n * - input must fit into 80 bits\n *\n * _Available since v4.7._\n */\n function toUint80(uint256 value) internal pure returns (uint80) {\n require(value <= type(uint80).max, \"SafeCast: value doesn't fit in 80 bits\");\n return uint80(value);\n }\n\n /**\n * @dev Returns the downcasted uint72 from uint256, reverting on\n * overflow (when the input is greater than largest uint72).\n *\n * Counterpart to Solidity's `uint72` operator.\n *\n * Requirements:\n *\n * - input must fit into 72 bits\n *\n * _Available since v4.7._\n */\n function toUint72(uint256 value) internal pure returns (uint72) {\n require(value <= type(uint72).max, \"SafeCast: value doesn't fit in 72 bits\");\n return uint72(value);\n }\n\n /**\n * @dev Returns the downcasted uint64 from uint256, reverting on\n * overflow (when the input is greater than largest uint64).\n *\n * Counterpart to Solidity's `uint64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n *\n * _Available since v2.5._\n */\n function toUint64(uint256 value) internal pure returns (uint64) {\n require(value <= type(uint64).max, \"SafeCast: value doesn't fit in 64 bits\");\n return uint64(value);\n }\n\n /**\n * @dev Returns the downcasted uint56 from uint256, reverting on\n * overflow (when the input is greater than largest uint56).\n *\n * Counterpart to Solidity's `uint56` operator.\n *\n * Requirements:\n *\n * - input must fit into 56 bits\n *\n * _Available since v4.7._\n */\n function toUint56(uint256 value) internal pure returns (uint56) {\n require(value <= type(uint56).max, \"SafeCast: value doesn't fit in 56 bits\");\n return uint56(value);\n }\n\n /**\n * @dev Returns the downcasted uint48 from uint256, reverting on\n * overflow (when the input is greater than largest uint48).\n *\n * Counterpart to Solidity's `uint48` operator.\n *\n * Requirements:\n *\n * - input must fit into 48 bits\n *\n * _Available since v4.7._\n */\n function toUint48(uint256 value) internal pure returns (uint48) {\n require(value <= type(uint48).max, \"SafeCast: value doesn't fit in 48 bits\");\n return uint48(value);\n }\n\n /**\n * @dev Returns the downcasted uint40 from uint256, reverting on\n * overflow (when the input is greater than largest uint40).\n *\n * Counterpart to Solidity's `uint40` operator.\n *\n * Requirements:\n *\n * - input must fit into 40 bits\n *\n * _Available since v4.7._\n */\n function toUint40(uint256 value) internal pure returns (uint40) {\n require(value <= type(uint40).max, \"SafeCast: value doesn't fit in 40 bits\");\n return uint40(value);\n }\n\n /**\n * @dev Returns the downcasted uint32 from uint256, reverting on\n * overflow (when the input is greater than largest uint32).\n *\n * Counterpart to Solidity's `uint32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n *\n * _Available since v2.5._\n */\n function toUint32(uint256 value) internal pure returns (uint32) {\n require(value <= type(uint32).max, \"SafeCast: value doesn't fit in 32 bits\");\n return uint32(value);\n }\n\n /**\n * @dev Returns the downcasted uint24 from uint256, reverting on\n * overflow (when the input is greater than largest uint24).\n *\n * Counterpart to Solidity's `uint24` operator.\n *\n * Requirements:\n *\n * - input must fit into 24 bits\n *\n * _Available since v4.7._\n */\n function toUint24(uint256 value) internal pure returns (uint24) {\n require(value <= type(uint24).max, \"SafeCast: value doesn't fit in 24 bits\");\n return uint24(value);\n }\n\n /**\n * @dev Returns the downcasted uint16 from uint256, reverting on\n * overflow (when the input is greater than largest uint16).\n *\n * Counterpart to Solidity's `uint16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n *\n * _Available since v2.5._\n */\n function toUint16(uint256 value) internal pure returns (uint16) {\n require(value <= type(uint16).max, \"SafeCast: value doesn't fit in 16 bits\");\n return uint16(value);\n }\n\n /**\n * @dev Returns the downcasted uint8 from uint256, reverting on\n * overflow (when the input is greater than largest uint8).\n *\n * Counterpart to Solidity's `uint8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits\n *\n * _Available since v2.5._\n */\n function toUint8(uint256 value) internal pure returns (uint8) {\n require(value <= type(uint8).max, \"SafeCast: value doesn't fit in 8 bits\");\n return uint8(value);\n }\n\n /**\n * @dev Converts a signed int256 into an unsigned uint256.\n *\n * Requirements:\n *\n * - input must be greater than or equal to 0.\n *\n * _Available since v3.0._\n */\n function toUint256(int256 value) internal pure returns (uint256) {\n require(value >= 0, \"SafeCast: value must be positive\");\n return uint256(value);\n }\n\n /**\n * @dev Returns the downcasted int248 from int256, reverting on\n * overflow (when the input is less than smallest int248 or\n * greater than largest int248).\n *\n * Counterpart to Solidity's `int248` operator.\n *\n * Requirements:\n *\n * - input must fit into 248 bits\n *\n * _Available since v4.7._\n */\n function toInt248(int256 value) internal pure returns (int248 downcasted) {\n downcasted = int248(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 248 bits\");\n }\n\n /**\n * @dev Returns the downcasted int240 from int256, reverting on\n * overflow (when the input is less than smallest int240 or\n * greater than largest int240).\n *\n * Counterpart to Solidity's `int240` operator.\n *\n * Requirements:\n *\n * - input must fit into 240 bits\n *\n * _Available since v4.7._\n */\n function toInt240(int256 value) internal pure returns (int240 downcasted) {\n downcasted = int240(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 240 bits\");\n }\n\n /**\n * @dev Returns the downcasted int232 from int256, reverting on\n * overflow (when the input is less than smallest int232 or\n * greater than largest int232).\n *\n * Counterpart to Solidity's `int232` operator.\n *\n * Requirements:\n *\n * - input must fit into 232 bits\n *\n * _Available since v4.7._\n */\n function toInt232(int256 value) internal pure returns (int232 downcasted) {\n downcasted = int232(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 232 bits\");\n }\n\n /**\n * @dev Returns the downcasted int224 from int256, reverting on\n * overflow (when the input is less than smallest int224 or\n * greater than largest int224).\n *\n * Counterpart to Solidity's `int224` operator.\n *\n * Requirements:\n *\n * - input must fit into 224 bits\n *\n * _Available since v4.7._\n */\n function toInt224(int256 value) internal pure returns (int224 downcasted) {\n downcasted = int224(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 224 bits\");\n }\n\n /**\n * @dev Returns the downcasted int216 from int256, reverting on\n * overflow (when the input is less than smallest int216 or\n * greater than largest int216).\n *\n * Counterpart to Solidity's `int216` operator.\n *\n * Requirements:\n *\n * - input must fit into 216 bits\n *\n * _Available since v4.7._\n */\n function toInt216(int256 value) internal pure returns (int216 downcasted) {\n downcasted = int216(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 216 bits\");\n }\n\n /**\n * @dev Returns the downcasted int208 from int256, reverting on\n * overflow (when the input is less than smallest int208 or\n * greater than largest int208).\n *\n * Counterpart to Solidity's `int208` operator.\n *\n * Requirements:\n *\n * - input must fit into 208 bits\n *\n * _Available since v4.7._\n */\n function toInt208(int256 value) internal pure returns (int208 downcasted) {\n downcasted = int208(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 208 bits\");\n }\n\n /**\n * @dev Returns the downcasted int200 from int256, reverting on\n * overflow (when the input is less than smallest int200 or\n * greater than largest int200).\n *\n * Counterpart to Solidity's `int200` operator.\n *\n * Requirements:\n *\n * - input must fit into 200 bits\n *\n * _Available since v4.7._\n */\n function toInt200(int256 value) internal pure returns (int200 downcasted) {\n downcasted = int200(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 200 bits\");\n }\n\n /**\n * @dev Returns the downcasted int192 from int256, reverting on\n * overflow (when the input is less than smallest int192 or\n * greater than largest int192).\n *\n * Counterpart to Solidity's `int192` operator.\n *\n * Requirements:\n *\n * - input must fit into 192 bits\n *\n * _Available since v4.7._\n */\n function toInt192(int256 value) internal pure returns (int192 downcasted) {\n downcasted = int192(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 192 bits\");\n }\n\n /**\n * @dev Returns the downcasted int184 from int256, reverting on\n * overflow (when the input is less than smallest int184 or\n * greater than largest int184).\n *\n * Counterpart to Solidity's `int184` operator.\n *\n * Requirements:\n *\n * - input must fit into 184 bits\n *\n * _Available since v4.7._\n */\n function toInt184(int256 value) internal pure returns (int184 downcasted) {\n downcasted = int184(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 184 bits\");\n }\n\n /**\n * @dev Returns the downcasted int176 from int256, reverting on\n * overflow (when the input is less than smallest int176 or\n * greater than largest int176).\n *\n * Counterpart to Solidity's `int176` operator.\n *\n * Requirements:\n *\n * - input must fit into 176 bits\n *\n * _Available since v4.7._\n */\n function toInt176(int256 value) internal pure returns (int176 downcasted) {\n downcasted = int176(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 176 bits\");\n }\n\n /**\n * @dev Returns the downcasted int168 from int256, reverting on\n * overflow (when the input is less than smallest int168 or\n * greater than largest int168).\n *\n * Counterpart to Solidity's `int168` operator.\n *\n * Requirements:\n *\n * - input must fit into 168 bits\n *\n * _Available since v4.7._\n */\n function toInt168(int256 value) internal pure returns (int168 downcasted) {\n downcasted = int168(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 168 bits\");\n }\n\n /**\n * @dev Returns the downcasted int160 from int256, reverting on\n * overflow (when the input is less than smallest int160 or\n * greater than largest int160).\n *\n * Counterpart to Solidity's `int160` operator.\n *\n * Requirements:\n *\n * - input must fit into 160 bits\n *\n * _Available since v4.7._\n */\n function toInt160(int256 value) internal pure returns (int160 downcasted) {\n downcasted = int160(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 160 bits\");\n }\n\n /**\n * @dev Returns the downcasted int152 from int256, reverting on\n * overflow (when the input is less than smallest int152 or\n * greater than largest int152).\n *\n * Counterpart to Solidity's `int152` operator.\n *\n * Requirements:\n *\n * - input must fit into 152 bits\n *\n * _Available since v4.7._\n */\n function toInt152(int256 value) internal pure returns (int152 downcasted) {\n downcasted = int152(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 152 bits\");\n }\n\n /**\n * @dev Returns the downcasted int144 from int256, reverting on\n * overflow (when the input is less than smallest int144 or\n * greater than largest int144).\n *\n * Counterpart to Solidity's `int144` operator.\n *\n * Requirements:\n *\n * - input must fit into 144 bits\n *\n * _Available since v4.7._\n */\n function toInt144(int256 value) internal pure returns (int144 downcasted) {\n downcasted = int144(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 144 bits\");\n }\n\n /**\n * @dev Returns the downcasted int136 from int256, reverting on\n * overflow (when the input is less than smallest int136 or\n * greater than largest int136).\n *\n * Counterpart to Solidity's `int136` operator.\n *\n * Requirements:\n *\n * - input must fit into 136 bits\n *\n * _Available since v4.7._\n */\n function toInt136(int256 value) internal pure returns (int136 downcasted) {\n downcasted = int136(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 136 bits\");\n }\n\n /**\n * @dev Returns the downcasted int128 from int256, reverting on\n * overflow (when the input is less than smallest int128 or\n * greater than largest int128).\n *\n * Counterpart to Solidity's `int128` operator.\n *\n * Requirements:\n *\n * - input must fit into 128 bits\n *\n * _Available since v3.1._\n */\n function toInt128(int256 value) internal pure returns (int128 downcasted) {\n downcasted = int128(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 128 bits\");\n }\n\n /**\n * @dev Returns the downcasted int120 from int256, reverting on\n * overflow (when the input is less than smallest int120 or\n * greater than largest int120).\n *\n * Counterpart to Solidity's `int120` operator.\n *\n * Requirements:\n *\n * - input must fit into 120 bits\n *\n * _Available since v4.7._\n */\n function toInt120(int256 value) internal pure returns (int120 downcasted) {\n downcasted = int120(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 120 bits\");\n }\n\n /**\n * @dev Returns the downcasted int112 from int256, reverting on\n * overflow (when the input is less than smallest int112 or\n * greater than largest int112).\n *\n * Counterpart to Solidity's `int112` operator.\n *\n * Requirements:\n *\n * - input must fit into 112 bits\n *\n * _Available since v4.7._\n */\n function toInt112(int256 value) internal pure returns (int112 downcasted) {\n downcasted = int112(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 112 bits\");\n }\n\n /**\n * @dev Returns the downcasted int104 from int256, reverting on\n * overflow (when the input is less than smallest int104 or\n * greater than largest int104).\n *\n * Counterpart to Solidity's `int104` operator.\n *\n * Requirements:\n *\n * - input must fit into 104 bits\n *\n * _Available since v4.7._\n */\n function toInt104(int256 value) internal pure returns (int104 downcasted) {\n downcasted = int104(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 104 bits\");\n }\n\n /**\n * @dev Returns the downcasted int96 from int256, reverting on\n * overflow (when the input is less than smallest int96 or\n * greater than largest int96).\n *\n * Counterpart to Solidity's `int96` operator.\n *\n * Requirements:\n *\n * - input must fit into 96 bits\n *\n * _Available since v4.7._\n */\n function toInt96(int256 value) internal pure returns (int96 downcasted) {\n downcasted = int96(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 96 bits\");\n }\n\n /**\n * @dev Returns the downcasted int88 from int256, reverting on\n * overflow (when the input is less than smallest int88 or\n * greater than largest int88).\n *\n * Counterpart to Solidity's `int88` operator.\n *\n * Requirements:\n *\n * - input must fit into 88 bits\n *\n * _Available since v4.7._\n */\n function toInt88(int256 value) internal pure returns (int88 downcasted) {\n downcasted = int88(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 88 bits\");\n }\n\n /**\n * @dev Returns the downcasted int80 from int256, reverting on\n * overflow (when the input is less than smallest int80 or\n * greater than largest int80).\n *\n * Counterpart to Solidity's `int80` operator.\n *\n * Requirements:\n *\n * - input must fit into 80 bits\n *\n * _Available since v4.7._\n */\n function toInt80(int256 value) internal pure returns (int80 downcasted) {\n downcasted = int80(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 80 bits\");\n }\n\n /**\n * @dev Returns the downcasted int72 from int256, reverting on\n * overflow (when the input is less than smallest int72 or\n * greater than largest int72).\n *\n * Counterpart to Solidity's `int72` operator.\n *\n * Requirements:\n *\n * - input must fit into 72 bits\n *\n * _Available since v4.7._\n */\n function toInt72(int256 value) internal pure returns (int72 downcasted) {\n downcasted = int72(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 72 bits\");\n }\n\n /**\n * @dev Returns the downcasted int64 from int256, reverting on\n * overflow (when the input is less than smallest int64 or\n * greater than largest int64).\n *\n * Counterpart to Solidity's `int64` operator.\n *\n * Requirements:\n *\n * - input must fit into 64 bits\n *\n * _Available since v3.1._\n */\n function toInt64(int256 value) internal pure returns (int64 downcasted) {\n downcasted = int64(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 64 bits\");\n }\n\n /**\n * @dev Returns the downcasted int56 from int256, reverting on\n * overflow (when the input is less than smallest int56 or\n * greater than largest int56).\n *\n * Counterpart to Solidity's `int56` operator.\n *\n * Requirements:\n *\n * - input must fit into 56 bits\n *\n * _Available since v4.7._\n */\n function toInt56(int256 value) internal pure returns (int56 downcasted) {\n downcasted = int56(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 56 bits\");\n }\n\n /**\n * @dev Returns the downcasted int48 from int256, reverting on\n * overflow (when the input is less than smallest int48 or\n * greater than largest int48).\n *\n * Counterpart to Solidity's `int48` operator.\n *\n * Requirements:\n *\n * - input must fit into 48 bits\n *\n * _Available since v4.7._\n */\n function toInt48(int256 value) internal pure returns (int48 downcasted) {\n downcasted = int48(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 48 bits\");\n }\n\n /**\n * @dev Returns the downcasted int40 from int256, reverting on\n * overflow (when the input is less than smallest int40 or\n * greater than largest int40).\n *\n * Counterpart to Solidity's `int40` operator.\n *\n * Requirements:\n *\n * - input must fit into 40 bits\n *\n * _Available since v4.7._\n */\n function toInt40(int256 value) internal pure returns (int40 downcasted) {\n downcasted = int40(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 40 bits\");\n }\n\n /**\n * @dev Returns the downcasted int32 from int256, reverting on\n * overflow (when the input is less than smallest int32 or\n * greater than largest int32).\n *\n * Counterpart to Solidity's `int32` operator.\n *\n * Requirements:\n *\n * - input must fit into 32 bits\n *\n * _Available since v3.1._\n */\n function toInt32(int256 value) internal pure returns (int32 downcasted) {\n downcasted = int32(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 32 bits\");\n }\n\n /**\n * @dev Returns the downcasted int24 from int256, reverting on\n * overflow (when the input is less than smallest int24 or\n * greater than largest int24).\n *\n * Counterpart to Solidity's `int24` operator.\n *\n * Requirements:\n *\n * - input must fit into 24 bits\n *\n * _Available since v4.7._\n */\n function toInt24(int256 value) internal pure returns (int24 downcasted) {\n downcasted = int24(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 24 bits\");\n }\n\n /**\n * @dev Returns the downcasted int16 from int256, reverting on\n * overflow (when the input is less than smallest int16 or\n * greater than largest int16).\n *\n * Counterpart to Solidity's `int16` operator.\n *\n * Requirements:\n *\n * - input must fit into 16 bits\n *\n * _Available since v3.1._\n */\n function toInt16(int256 value) internal pure returns (int16 downcasted) {\n downcasted = int16(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 16 bits\");\n }\n\n /**\n * @dev Returns the downcasted int8 from int256, reverting on\n * overflow (when the input is less than smallest int8 or\n * greater than largest int8).\n *\n * Counterpart to Solidity's `int8` operator.\n *\n * Requirements:\n *\n * - input must fit into 8 bits\n *\n * _Available since v3.1._\n */\n function toInt8(int256 value) internal pure returns (int8 downcasted) {\n downcasted = int8(value);\n require(downcasted == value, \"SafeCast: value doesn't fit in 8 bits\");\n }\n\n /**\n * @dev Converts an unsigned uint256 into a signed int256.\n *\n * Requirements:\n *\n * - input must be less than or equal to maxInt256.\n *\n * _Available since v3.0._\n */\n function toInt256(uint256 value) internal pure returns (int256) {\n // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive\n require(value <= uint256(type(int256).max), \"SafeCast: value doesn't fit in an int256\");\n return int256(value);\n }\n}\n" + }, + "@openzeppelin/contracts/access/AccessControl.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.0) (access/AccessControl.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IAccessControl.sol\";\nimport \"../utils/Context.sol\";\nimport \"../utils/Strings.sol\";\nimport \"../utils/introspection/ERC165.sol\";\n\n/**\n * @dev Contract module that allows children to implement role-based access\n * control mechanisms. This is a lightweight version that doesn't allow enumerating role\n * members except through off-chain means by accessing the contract event logs. Some\n * applications may benefit from on-chain enumerability, for those cases see\n * {AccessControlEnumerable}.\n *\n * Roles are referred to by their `bytes32` identifier. These should be exposed\n * in the external API and be unique. The best way to achieve this is by\n * using `public constant` hash digests:\n *\n * ```solidity\n * bytes32 public constant MY_ROLE = keccak256(\"MY_ROLE\");\n * ```\n *\n * Roles can be used to represent a set of permissions. To restrict access to a\n * function call, use {hasRole}:\n *\n * ```solidity\n * function foo() public {\n * require(hasRole(MY_ROLE, msg.sender));\n * ...\n * }\n * ```\n *\n * Roles can be granted and revoked dynamically via the {grantRole} and\n * {revokeRole} functions. Each role has an associated admin role, and only\n * accounts that have a role's admin role can call {grantRole} and {revokeRole}.\n *\n * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means\n * that only accounts with this role will be able to grant or revoke other\n * roles. More complex role relationships can be created by using\n * {_setRoleAdmin}.\n *\n * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to\n * grant and revoke this role. Extra precautions should be taken to secure\n * accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules}\n * to enforce additional security measures for this role.\n */\nabstract contract AccessControl is Context, IAccessControl, ERC165 {\n struct RoleData {\n mapping(address => bool) members;\n bytes32 adminRole;\n }\n\n mapping(bytes32 => RoleData) private _roles;\n\n bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;\n\n /**\n * @dev Modifier that checks that an account has a specific role. Reverts\n * with a standardized message including the required role.\n *\n * The format of the revert reason is given by the following regular expression:\n *\n * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/\n *\n * _Available since v4.1._\n */\n modifier onlyRole(bytes32 role) {\n _checkRole(role);\n _;\n }\n\n /**\n * @dev See {IERC165-supportsInterface}.\n */\n function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);\n }\n\n /**\n * @dev Returns `true` if `account` has been granted `role`.\n */\n function hasRole(bytes32 role, address account) public view virtual override returns (bool) {\n return _roles[role].members[account];\n }\n\n /**\n * @dev Revert with a standard message if `_msgSender()` is missing `role`.\n * Overriding this function changes the behavior of the {onlyRole} modifier.\n *\n * Format of the revert message is described in {_checkRole}.\n *\n * _Available since v4.6._\n */\n function _checkRole(bytes32 role) internal view virtual {\n _checkRole(role, _msgSender());\n }\n\n /**\n * @dev Revert with a standard message if `account` is missing `role`.\n *\n * The format of the revert reason is given by the following regular expression:\n *\n * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/\n */\n function _checkRole(bytes32 role, address account) internal view virtual {\n if (!hasRole(role, account)) {\n revert(\n string(\n abi.encodePacked(\n \"AccessControl: account \",\n Strings.toHexString(account),\n \" is missing role \",\n Strings.toHexString(uint256(role), 32)\n )\n )\n );\n }\n }\n\n /**\n * @dev Returns the admin role that controls `role`. See {grantRole} and\n * {revokeRole}.\n *\n * To change a role's admin, use {_setRoleAdmin}.\n */\n function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) {\n return _roles[role].adminRole;\n }\n\n /**\n * @dev Grants `role` to `account`.\n *\n * If `account` had not been already granted `role`, emits a {RoleGranted}\n * event.\n *\n * Requirements:\n *\n * - the caller must have ``role``'s admin role.\n *\n * May emit a {RoleGranted} event.\n */\n function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {\n _grantRole(role, account);\n }\n\n /**\n * @dev Revokes `role` from `account`.\n *\n * If `account` had been granted `role`, emits a {RoleRevoked} event.\n *\n * Requirements:\n *\n * - the caller must have ``role``'s admin role.\n *\n * May emit a {RoleRevoked} event.\n */\n function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {\n _revokeRole(role, account);\n }\n\n /**\n * @dev Revokes `role` from the calling account.\n *\n * Roles are often managed via {grantRole} and {revokeRole}: this function's\n * purpose is to provide a mechanism for accounts to lose their privileges\n * if they are compromised (such as when a trusted device is misplaced).\n *\n * If the calling account had been revoked `role`, emits a {RoleRevoked}\n * event.\n *\n * Requirements:\n *\n * - the caller must be `account`.\n *\n * May emit a {RoleRevoked} event.\n */\n function renounceRole(bytes32 role, address account) public virtual override {\n require(account == _msgSender(), \"AccessControl: can only renounce roles for self\");\n\n _revokeRole(role, account);\n }\n\n /**\n * @dev Grants `role` to `account`.\n *\n * If `account` had not been already granted `role`, emits a {RoleGranted}\n * event. Note that unlike {grantRole}, this function doesn't perform any\n * checks on the calling account.\n *\n * May emit a {RoleGranted} event.\n *\n * [WARNING]\n * ====\n * This function should only be called from the constructor when setting\n * up the initial roles for the system.\n *\n * Using this function in any other way is effectively circumventing the admin\n * system imposed by {AccessControl}.\n * ====\n *\n * NOTE: This function is deprecated in favor of {_grantRole}.\n */\n function _setupRole(bytes32 role, address account) internal virtual {\n _grantRole(role, account);\n }\n\n /**\n * @dev Sets `adminRole` as ``role``'s admin role.\n *\n * Emits a {RoleAdminChanged} event.\n */\n function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {\n bytes32 previousAdminRole = getRoleAdmin(role);\n _roles[role].adminRole = adminRole;\n emit RoleAdminChanged(role, previousAdminRole, adminRole);\n }\n\n /**\n * @dev Grants `role` to `account`.\n *\n * Internal function without access restriction.\n *\n * May emit a {RoleGranted} event.\n */\n function _grantRole(bytes32 role, address account) internal virtual {\n if (!hasRole(role, account)) {\n _roles[role].members[account] = true;\n emit RoleGranted(role, account, _msgSender());\n }\n }\n\n /**\n * @dev Revokes `role` from `account`.\n *\n * Internal function without access restriction.\n *\n * May emit a {RoleRevoked} event.\n */\n function _revokeRole(bytes32 role, address account) internal virtual {\n if (hasRole(role, account)) {\n _roles[role].members[account] = false;\n emit RoleRevoked(role, account, _msgSender());\n }\n }\n}\n" + }, + "@openzeppelin/contracts/access/IAccessControl.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev External interface of AccessControl declared to support ERC165 detection.\n */\ninterface IAccessControl {\n /**\n * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`\n *\n * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite\n * {RoleAdminChanged} not being emitted signaling this.\n *\n * _Available since v3.1._\n */\n event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);\n\n /**\n * @dev Emitted when `account` is granted `role`.\n *\n * `sender` is the account that originated the contract call, an admin role\n * bearer except when using {AccessControl-_setupRole}.\n */\n event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);\n\n /**\n * @dev Emitted when `account` is revoked `role`.\n *\n * `sender` is the account that originated the contract call:\n * - if using `revokeRole`, it is the admin role bearer\n * - if using `renounceRole`, it is the role bearer (i.e. `account`)\n */\n event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);\n\n /**\n * @dev Returns `true` if `account` has been granted `role`.\n */\n function hasRole(bytes32 role, address account) external view returns (bool);\n\n /**\n * @dev Returns the admin role that controls `role`. See {grantRole} and\n * {revokeRole}.\n *\n * To change a role's admin, use {AccessControl-_setRoleAdmin}.\n */\n function getRoleAdmin(bytes32 role) external view returns (bytes32);\n\n /**\n * @dev Grants `role` to `account`.\n *\n * If `account` had not been already granted `role`, emits a {RoleGranted}\n * event.\n *\n * Requirements:\n *\n * - the caller must have ``role``'s admin role.\n */\n function grantRole(bytes32 role, address account) external;\n\n /**\n * @dev Revokes `role` from `account`.\n *\n * If `account` had been granted `role`, emits a {RoleRevoked} event.\n *\n * Requirements:\n *\n * - the caller must have ``role``'s admin role.\n */\n function revokeRole(bytes32 role, address account) external;\n\n /**\n * @dev Revokes `role` from the calling account.\n *\n * Roles are often managed via {grantRole} and {revokeRole}: this function's\n * purpose is to provide a mechanism for accounts to lose their privileges\n * if they are compromised (such as when a trusted device is misplaced).\n *\n * If the calling account had been granted `role`, emits a {RoleRevoked}\n * event.\n *\n * Requirements:\n *\n * - the caller must be `account`.\n */\n function renounceRole(bytes32 role, address account) external;\n}\n" + }, + "@openzeppelin/contracts/access/Ownable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../utils/Context.sol\";\n\n/**\n * @dev Contract module which provides a basic access control mechanism, where\n * there is an account (an owner) that can be granted exclusive access to\n * specific functions.\n *\n * By default, the owner account will be the one that deploys the contract. This\n * can later be changed with {transferOwnership}.\n *\n * This module is used through inheritance. It will make available the modifier\n * `onlyOwner`, which can be applied to your functions to restrict their use to\n * the owner.\n */\nabstract contract Ownable is Context {\n address private _owner;\n\n event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n\n /**\n * @dev Initializes the contract setting the deployer as the initial owner.\n */\n constructor() {\n _transferOwnership(_msgSender());\n }\n\n /**\n * @dev Throws if called by any account other than the owner.\n */\n modifier onlyOwner() {\n _checkOwner();\n _;\n }\n\n /**\n * @dev Returns the address of the current owner.\n */\n function owner() public view virtual returns (address) {\n return _owner;\n }\n\n /**\n * @dev Throws if the sender is not the owner.\n */\n function _checkOwner() internal view virtual {\n require(owner() == _msgSender(), \"Ownable: caller is not the owner\");\n }\n\n /**\n * @dev Leaves the contract without owner. It will not be possible to call\n * `onlyOwner` functions. Can only be called by the current owner.\n *\n * NOTE: Renouncing ownership will leave the contract without an owner,\n * thereby disabling any functionality that is only available to the owner.\n */\n function renounceOwnership() public virtual onlyOwner {\n _transferOwnership(address(0));\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Can only be called by the current owner.\n */\n function transferOwnership(address newOwner) public virtual onlyOwner {\n require(newOwner != address(0), \"Ownable: new owner is the zero address\");\n _transferOwnership(newOwner);\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Internal function without access restriction.\n */\n function _transferOwnership(address newOwner) internal virtual {\n address oldOwner = _owner;\n _owner = newOwner;\n emit OwnershipTransferred(oldOwner, newOwner);\n }\n}\n" + }, + "@openzeppelin/contracts/access/Ownable2Step.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable2Step.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./Ownable.sol\";\n\n/**\n * @dev Contract module which provides access control mechanism, where\n * there is an account (an owner) that can be granted exclusive access to\n * specific functions.\n *\n * By default, the owner account will be the one that deploys the contract. This\n * can later be changed with {transferOwnership} and {acceptOwnership}.\n *\n * This module is used through inheritance. It will make available all functions\n * from parent (Ownable).\n */\nabstract contract Ownable2Step is Ownable {\n address private _pendingOwner;\n\n event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner);\n\n /**\n * @dev Returns the address of the pending owner.\n */\n function pendingOwner() public view virtual returns (address) {\n return _pendingOwner;\n }\n\n /**\n * @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one.\n * Can only be called by the current owner.\n */\n function transferOwnership(address newOwner) public virtual override onlyOwner {\n _pendingOwner = newOwner;\n emit OwnershipTransferStarted(owner(), newOwner);\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner.\n * Internal function without access restriction.\n */\n function _transferOwnership(address newOwner) internal virtual override {\n delete _pendingOwner;\n super._transferOwnership(newOwner);\n }\n\n /**\n * @dev The new owner accepts the ownership transfer.\n */\n function acceptOwnership() public virtual {\n address sender = _msgSender();\n require(pendingOwner() == sender, \"Ownable2Step: caller is not the new owner\");\n _transferOwnership(sender);\n }\n}\n" + }, + "@openzeppelin/contracts/interfaces/draft-IERC1822.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.5.0) (interfaces/draft-IERC1822.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev ERC1822: Universal Upgradeable Proxy Standard (UUPS) documents a method for upgradeability through a simplified\n * proxy whose upgrades are fully controlled by the current implementation.\n */\ninterface IERC1822Proxiable {\n /**\n * @dev Returns the storage slot that the proxiable contract assumes is being used to store the implementation\n * address.\n *\n * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks\n * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this\n * function revert if invoked through a proxy.\n */\n function proxiableUUID() external view returns (bytes32);\n}\n" + }, + "@openzeppelin/contracts/interfaces/IERC1967.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.0) (interfaces/IERC1967.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev ERC-1967: Proxy Storage Slots. This interface contains the events defined in the ERC.\n *\n * _Available since v4.8.3._\n */\ninterface IERC1967 {\n /**\n * @dev Emitted when the implementation is upgraded.\n */\n event Upgraded(address indexed implementation);\n\n /**\n * @dev Emitted when the admin account has changed.\n */\n event AdminChanged(address previousAdmin, address newAdmin);\n\n /**\n * @dev Emitted when the beacon is changed.\n */\n event BeaconUpgraded(address indexed beacon);\n}\n" + }, + "@openzeppelin/contracts/proxy/beacon/BeaconProxy.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.7.0) (proxy/beacon/BeaconProxy.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IBeacon.sol\";\nimport \"../Proxy.sol\";\nimport \"../ERC1967/ERC1967Upgrade.sol\";\n\n/**\n * @dev This contract implements a proxy that gets the implementation address for each call from an {UpgradeableBeacon}.\n *\n * The beacon address is stored in storage slot `uint256(keccak256('eip1967.proxy.beacon')) - 1`, so that it doesn't\n * conflict with the storage layout of the implementation behind the proxy.\n *\n * _Available since v3.4._\n */\ncontract BeaconProxy is Proxy, ERC1967Upgrade {\n /**\n * @dev Initializes the proxy with `beacon`.\n *\n * If `data` is nonempty, it's used as data in a delegate call to the implementation returned by the beacon. This\n * will typically be an encoded function call, and allows initializing the storage of the proxy like a Solidity\n * constructor.\n *\n * Requirements:\n *\n * - `beacon` must be a contract with the interface {IBeacon}.\n */\n constructor(address beacon, bytes memory data) payable {\n _upgradeBeaconToAndCall(beacon, data, false);\n }\n\n /**\n * @dev Returns the current beacon address.\n */\n function _beacon() internal view virtual returns (address) {\n return _getBeacon();\n }\n\n /**\n * @dev Returns the current implementation address of the associated beacon.\n */\n function _implementation() internal view virtual override returns (address) {\n return IBeacon(_getBeacon()).implementation();\n }\n\n /**\n * @dev Changes the proxy to use a new beacon. Deprecated: see {_upgradeBeaconToAndCall}.\n *\n * If `data` is nonempty, it's used as data in a delegate call to the implementation returned by the beacon.\n *\n * Requirements:\n *\n * - `beacon` must be a contract.\n * - The implementation returned by `beacon` must be a contract.\n */\n function _setBeacon(address beacon, bytes memory data) internal virtual {\n _upgradeBeaconToAndCall(beacon, data, false);\n }\n}\n" + }, + "@openzeppelin/contracts/proxy/beacon/IBeacon.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (proxy/beacon/IBeacon.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev This is the interface that {BeaconProxy} expects of its beacon.\n */\ninterface IBeacon {\n /**\n * @dev Must return an address that can be used as a delegate call target.\n *\n * {BeaconProxy} will check that this address is a contract.\n */\n function implementation() external view returns (address);\n}\n" + }, + "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (proxy/beacon/UpgradeableBeacon.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IBeacon.sol\";\nimport \"../../access/Ownable.sol\";\nimport \"../../utils/Address.sol\";\n\n/**\n * @dev This contract is used in conjunction with one or more instances of {BeaconProxy} to determine their\n * implementation contract, which is where they will delegate all function calls.\n *\n * An owner is able to change the implementation the beacon points to, thus upgrading the proxies that use this beacon.\n */\ncontract UpgradeableBeacon is IBeacon, Ownable {\n address private _implementation;\n\n /**\n * @dev Emitted when the implementation returned by the beacon is changed.\n */\n event Upgraded(address indexed implementation);\n\n /**\n * @dev Sets the address of the initial implementation, and the deployer account as the owner who can upgrade the\n * beacon.\n */\n constructor(address implementation_) {\n _setImplementation(implementation_);\n }\n\n /**\n * @dev Returns the current implementation address.\n */\n function implementation() public view virtual override returns (address) {\n return _implementation;\n }\n\n /**\n * @dev Upgrades the beacon to a new implementation.\n *\n * Emits an {Upgraded} event.\n *\n * Requirements:\n *\n * - msg.sender must be the owner of the contract.\n * - `newImplementation` must be a contract.\n */\n function upgradeTo(address newImplementation) public virtual onlyOwner {\n _setImplementation(newImplementation);\n emit Upgraded(newImplementation);\n }\n\n /**\n * @dev Sets the implementation contract address for this beacon\n *\n * Requirements:\n *\n * - `newImplementation` must be a contract.\n */\n function _setImplementation(address newImplementation) private {\n require(Address.isContract(newImplementation), \"UpgradeableBeacon: implementation is not a contract\");\n _implementation = newImplementation;\n }\n}\n" + }, + "@openzeppelin/contracts/proxy/ERC1967/ERC1967Upgrade.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.0) (proxy/ERC1967/ERC1967Upgrade.sol)\n\npragma solidity ^0.8.2;\n\nimport \"../beacon/IBeacon.sol\";\nimport \"../../interfaces/IERC1967.sol\";\nimport \"../../interfaces/draft-IERC1822.sol\";\nimport \"../../utils/Address.sol\";\nimport \"../../utils/StorageSlot.sol\";\n\n/**\n * @dev This abstract contract provides getters and event emitting update functions for\n * https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots.\n *\n * _Available since v4.1._\n */\nabstract contract ERC1967Upgrade is IERC1967 {\n // This is the keccak-256 hash of \"eip1967.proxy.rollback\" subtracted by 1\n bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143;\n\n /**\n * @dev Storage slot with the address of the current implementation.\n * This is the keccak-256 hash of \"eip1967.proxy.implementation\" subtracted by 1, and is\n * validated in the constructor.\n */\n bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;\n\n /**\n * @dev Returns the current implementation address.\n */\n function _getImplementation() internal view returns (address) {\n return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;\n }\n\n /**\n * @dev Stores a new address in the EIP1967 implementation slot.\n */\n function _setImplementation(address newImplementation) private {\n require(Address.isContract(newImplementation), \"ERC1967: new implementation is not a contract\");\n StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;\n }\n\n /**\n * @dev Perform implementation upgrade\n *\n * Emits an {Upgraded} event.\n */\n function _upgradeTo(address newImplementation) internal {\n _setImplementation(newImplementation);\n emit Upgraded(newImplementation);\n }\n\n /**\n * @dev Perform implementation upgrade with additional setup call.\n *\n * Emits an {Upgraded} event.\n */\n function _upgradeToAndCall(address newImplementation, bytes memory data, bool forceCall) internal {\n _upgradeTo(newImplementation);\n if (data.length > 0 || forceCall) {\n Address.functionDelegateCall(newImplementation, data);\n }\n }\n\n /**\n * @dev Perform implementation upgrade with security checks for UUPS proxies, and additional setup call.\n *\n * Emits an {Upgraded} event.\n */\n function _upgradeToAndCallUUPS(address newImplementation, bytes memory data, bool forceCall) internal {\n // Upgrades from old implementations will perform a rollback test. This test requires the new\n // implementation to upgrade back to the old, non-ERC1822 compliant, implementation. Removing\n // this special case will break upgrade paths from old UUPS implementation to new ones.\n if (StorageSlot.getBooleanSlot(_ROLLBACK_SLOT).value) {\n _setImplementation(newImplementation);\n } else {\n try IERC1822Proxiable(newImplementation).proxiableUUID() returns (bytes32 slot) {\n require(slot == _IMPLEMENTATION_SLOT, \"ERC1967Upgrade: unsupported proxiableUUID\");\n } catch {\n revert(\"ERC1967Upgrade: new implementation is not UUPS\");\n }\n _upgradeToAndCall(newImplementation, data, forceCall);\n }\n }\n\n /**\n * @dev Storage slot with the admin of the contract.\n * This is the keccak-256 hash of \"eip1967.proxy.admin\" subtracted by 1, and is\n * validated in the constructor.\n */\n bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;\n\n /**\n * @dev Returns the current admin.\n */\n function _getAdmin() internal view returns (address) {\n return StorageSlot.getAddressSlot(_ADMIN_SLOT).value;\n }\n\n /**\n * @dev Stores a new address in the EIP1967 admin slot.\n */\n function _setAdmin(address newAdmin) private {\n require(newAdmin != address(0), \"ERC1967: new admin is the zero address\");\n StorageSlot.getAddressSlot(_ADMIN_SLOT).value = newAdmin;\n }\n\n /**\n * @dev Changes the admin of the proxy.\n *\n * Emits an {AdminChanged} event.\n */\n function _changeAdmin(address newAdmin) internal {\n emit AdminChanged(_getAdmin(), newAdmin);\n _setAdmin(newAdmin);\n }\n\n /**\n * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.\n * This is bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)) and is validated in the constructor.\n */\n bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;\n\n /**\n * @dev Returns the current beacon.\n */\n function _getBeacon() internal view returns (address) {\n return StorageSlot.getAddressSlot(_BEACON_SLOT).value;\n }\n\n /**\n * @dev Stores a new beacon in the EIP1967 beacon slot.\n */\n function _setBeacon(address newBeacon) private {\n require(Address.isContract(newBeacon), \"ERC1967: new beacon is not a contract\");\n require(\n Address.isContract(IBeacon(newBeacon).implementation()),\n \"ERC1967: beacon implementation is not a contract\"\n );\n StorageSlot.getAddressSlot(_BEACON_SLOT).value = newBeacon;\n }\n\n /**\n * @dev Perform beacon upgrade with additional setup call. Note: This upgrades the address of the beacon, it does\n * not upgrade the implementation contained in the beacon (see {UpgradeableBeacon-_setImplementation} for that).\n *\n * Emits a {BeaconUpgraded} event.\n */\n function _upgradeBeaconToAndCall(address newBeacon, bytes memory data, bool forceCall) internal {\n _setBeacon(newBeacon);\n emit BeaconUpgraded(newBeacon);\n if (data.length > 0 || forceCall) {\n Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data);\n }\n }\n}\n" + }, + "@openzeppelin/contracts/proxy/Proxy.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.6.0) (proxy/Proxy.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM\n * instruction `delegatecall`. We refer to the second contract as the _implementation_ behind the proxy, and it has to\n * be specified by overriding the virtual {_implementation} function.\n *\n * Additionally, delegation to the implementation can be triggered manually through the {_fallback} function, or to a\n * different contract through the {_delegate} function.\n *\n * The success and return data of the delegated call will be returned back to the caller of the proxy.\n */\nabstract contract Proxy {\n /**\n * @dev Delegates the current call to `implementation`.\n *\n * This function does not return to its internal call site, it will return directly to the external caller.\n */\n function _delegate(address implementation) internal virtual {\n assembly {\n // Copy msg.data. We take full control of memory in this inline assembly\n // block because it will not return to Solidity code. We overwrite the\n // Solidity scratch pad at memory position 0.\n calldatacopy(0, 0, calldatasize())\n\n // Call the implementation.\n // out and outsize are 0 because we don't know the size yet.\n let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)\n\n // Copy the returned data.\n returndatacopy(0, 0, returndatasize())\n\n switch result\n // delegatecall returns 0 on error.\n case 0 {\n revert(0, returndatasize())\n }\n default {\n return(0, returndatasize())\n }\n }\n }\n\n /**\n * @dev This is a virtual function that should be overridden so it returns the address to which the fallback function\n * and {_fallback} should delegate.\n */\n function _implementation() internal view virtual returns (address);\n\n /**\n * @dev Delegates the current call to the address returned by `_implementation()`.\n *\n * This function does not return to its internal call site, it will return directly to the external caller.\n */\n function _fallback() internal virtual {\n _beforeFallback();\n _delegate(_implementation());\n }\n\n /**\n * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other\n * function in the contract matches the call data.\n */\n fallback() external payable virtual {\n _fallback();\n }\n\n /**\n * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if call data\n * is empty.\n */\n receive() external payable virtual {\n _fallback();\n }\n\n /**\n * @dev Hook that is called before falling back to the implementation. Can happen as part of a manual `_fallback`\n * call, or as part of the Solidity `fallback` or `receive` functions.\n *\n * If overridden should call `super._beforeFallback()`.\n */\n function _beforeFallback() internal virtual {}\n}\n" + }, + "@openzeppelin/contracts/security/ReentrancyGuard.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.0) (security/ReentrancyGuard.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Contract module that helps prevent reentrant calls to a function.\n *\n * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier\n * available, which can be applied to functions to make sure there are no nested\n * (reentrant) calls to them.\n *\n * Note that because there is a single `nonReentrant` guard, functions marked as\n * `nonReentrant` may not call one another. This can be worked around by making\n * those functions `private`, and then adding `external` `nonReentrant` entry\n * points to them.\n *\n * TIP: If you would like to learn more about reentrancy and alternative ways\n * to protect against it, check out our blog post\n * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].\n */\nabstract contract ReentrancyGuard {\n // Booleans are more expensive than uint256 or any type that takes up a full\n // word because each write operation emits an extra SLOAD to first read the\n // slot's contents, replace the bits taken up by the boolean, and then write\n // back. This is the compiler's defense against contract upgrades and\n // pointer aliasing, and it cannot be disabled.\n\n // The values being non-zero value makes deployment a bit more expensive,\n // but in exchange the refund on every call to nonReentrant will be lower in\n // amount. Since refunds are capped to a percentage of the total\n // transaction's gas, it is best to keep them low in cases like this one, to\n // increase the likelihood of the full refund coming into effect.\n uint256 private constant _NOT_ENTERED = 1;\n uint256 private constant _ENTERED = 2;\n\n uint256 private _status;\n\n constructor() {\n _status = _NOT_ENTERED;\n }\n\n /**\n * @dev Prevents a contract from calling itself, directly or indirectly.\n * Calling a `nonReentrant` function from another `nonReentrant`\n * function is not supported. It is possible to prevent this from happening\n * by making the `nonReentrant` function external, and making it call a\n * `private` function that does the actual work.\n */\n modifier nonReentrant() {\n _nonReentrantBefore();\n _;\n _nonReentrantAfter();\n }\n\n function _nonReentrantBefore() private {\n // On the first call to nonReentrant, _status will be _NOT_ENTERED\n require(_status != _ENTERED, \"ReentrancyGuard: reentrant call\");\n\n // Any calls to nonReentrant after this point will fail\n _status = _ENTERED;\n }\n\n function _nonReentrantAfter() private {\n // By storing the original value once again, a refund is triggered (see\n // https://eips.ethereum.org/EIPS/eip-2200)\n _status = _NOT_ENTERED;\n }\n\n /**\n * @dev Returns true if the reentrancy guard is currently set to \"entered\", which indicates there is a\n * `nonReentrant` function in the call stack.\n */\n function _reentrancyGuardEntered() internal view returns (bool) {\n return _status == _ENTERED;\n }\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/ERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/ERC20.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IERC20.sol\";\nimport \"./extensions/IERC20Metadata.sol\";\nimport \"../../utils/Context.sol\";\n\n/**\n * @dev Implementation of the {IERC20} interface.\n *\n * This implementation is agnostic to the way tokens are created. This means\n * that a supply mechanism has to be added in a derived contract using {_mint}.\n * For a generic mechanism see {ERC20PresetMinterPauser}.\n *\n * TIP: For a detailed writeup see our guide\n * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How\n * to implement supply mechanisms].\n *\n * The default value of {decimals} is 18. To change this, you should override\n * this function so it returns a different value.\n *\n * We have followed general OpenZeppelin Contracts guidelines: functions revert\n * instead returning `false` on failure. This behavior is nonetheless\n * conventional and does not conflict with the expectations of ERC20\n * applications.\n *\n * Additionally, an {Approval} event is emitted on calls to {transferFrom}.\n * This allows applications to reconstruct the allowance for all accounts just\n * by listening to said events. Other implementations of the EIP may not emit\n * these events, as it isn't required by the specification.\n *\n * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}\n * functions have been added to mitigate the well-known issues around setting\n * allowances. See {IERC20-approve}.\n */\ncontract ERC20 is Context, IERC20, IERC20Metadata {\n mapping(address => uint256) private _balances;\n\n mapping(address => mapping(address => uint256)) private _allowances;\n\n uint256 private _totalSupply;\n\n string private _name;\n string private _symbol;\n\n /**\n * @dev Sets the values for {name} and {symbol}.\n *\n * All two of these values are immutable: they can only be set once during\n * construction.\n */\n constructor(string memory name_, string memory symbol_) {\n _name = name_;\n _symbol = symbol_;\n }\n\n /**\n * @dev Returns the name of the token.\n */\n function name() public view virtual override returns (string memory) {\n return _name;\n }\n\n /**\n * @dev Returns the symbol of the token, usually a shorter version of the\n * name.\n */\n function symbol() public view virtual override returns (string memory) {\n return _symbol;\n }\n\n /**\n * @dev Returns the number of decimals used to get its user representation.\n * For example, if `decimals` equals `2`, a balance of `505` tokens should\n * be displayed to a user as `5.05` (`505 / 10 ** 2`).\n *\n * Tokens usually opt for a value of 18, imitating the relationship between\n * Ether and Wei. This is the default value returned by this function, unless\n * it's overridden.\n *\n * NOTE: This information is only used for _display_ purposes: it in\n * no way affects any of the arithmetic of the contract, including\n * {IERC20-balanceOf} and {IERC20-transfer}.\n */\n function decimals() public view virtual override returns (uint8) {\n return 18;\n }\n\n /**\n * @dev See {IERC20-totalSupply}.\n */\n function totalSupply() public view virtual override returns (uint256) {\n return _totalSupply;\n }\n\n /**\n * @dev See {IERC20-balanceOf}.\n */\n function balanceOf(address account) public view virtual override returns (uint256) {\n return _balances[account];\n }\n\n /**\n * @dev See {IERC20-transfer}.\n *\n * Requirements:\n *\n * - `to` cannot be the zero address.\n * - the caller must have a balance of at least `amount`.\n */\n function transfer(address to, uint256 amount) public virtual override returns (bool) {\n address owner = _msgSender();\n _transfer(owner, to, amount);\n return true;\n }\n\n /**\n * @dev See {IERC20-allowance}.\n */\n function allowance(address owner, address spender) public view virtual override returns (uint256) {\n return _allowances[owner][spender];\n }\n\n /**\n * @dev See {IERC20-approve}.\n *\n * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on\n * `transferFrom`. This is semantically equivalent to an infinite approval.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n */\n function approve(address spender, uint256 amount) public virtual override returns (bool) {\n address owner = _msgSender();\n _approve(owner, spender, amount);\n return true;\n }\n\n /**\n * @dev See {IERC20-transferFrom}.\n *\n * Emits an {Approval} event indicating the updated allowance. This is not\n * required by the EIP. See the note at the beginning of {ERC20}.\n *\n * NOTE: Does not update the allowance if the current allowance\n * is the maximum `uint256`.\n *\n * Requirements:\n *\n * - `from` and `to` cannot be the zero address.\n * - `from` must have a balance of at least `amount`.\n * - the caller must have allowance for ``from``'s tokens of at least\n * `amount`.\n */\n function transferFrom(address from, address to, uint256 amount) public virtual override returns (bool) {\n address spender = _msgSender();\n _spendAllowance(from, spender, amount);\n _transfer(from, to, amount);\n return true;\n }\n\n /**\n * @dev Atomically increases the allowance granted to `spender` by the caller.\n *\n * This is an alternative to {approve} that can be used as a mitigation for\n * problems described in {IERC20-approve}.\n *\n * Emits an {Approval} event indicating the updated allowance.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n */\n function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {\n address owner = _msgSender();\n _approve(owner, spender, allowance(owner, spender) + addedValue);\n return true;\n }\n\n /**\n * @dev Atomically decreases the allowance granted to `spender` by the caller.\n *\n * This is an alternative to {approve} that can be used as a mitigation for\n * problems described in {IERC20-approve}.\n *\n * Emits an {Approval} event indicating the updated allowance.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n * - `spender` must have allowance for the caller of at least\n * `subtractedValue`.\n */\n function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {\n address owner = _msgSender();\n uint256 currentAllowance = allowance(owner, spender);\n require(currentAllowance >= subtractedValue, \"ERC20: decreased allowance below zero\");\n unchecked {\n _approve(owner, spender, currentAllowance - subtractedValue);\n }\n\n return true;\n }\n\n /**\n * @dev Moves `amount` of tokens from `from` to `to`.\n *\n * This internal function is equivalent to {transfer}, and can be used to\n * e.g. implement automatic token fees, slashing mechanisms, etc.\n *\n * Emits a {Transfer} event.\n *\n * Requirements:\n *\n * - `from` cannot be the zero address.\n * - `to` cannot be the zero address.\n * - `from` must have a balance of at least `amount`.\n */\n function _transfer(address from, address to, uint256 amount) internal virtual {\n require(from != address(0), \"ERC20: transfer from the zero address\");\n require(to != address(0), \"ERC20: transfer to the zero address\");\n\n _beforeTokenTransfer(from, to, amount);\n\n uint256 fromBalance = _balances[from];\n require(fromBalance >= amount, \"ERC20: transfer amount exceeds balance\");\n unchecked {\n _balances[from] = fromBalance - amount;\n // Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by\n // decrementing then incrementing.\n _balances[to] += amount;\n }\n\n emit Transfer(from, to, amount);\n\n _afterTokenTransfer(from, to, amount);\n }\n\n /** @dev Creates `amount` tokens and assigns them to `account`, increasing\n * the total supply.\n *\n * Emits a {Transfer} event with `from` set to the zero address.\n *\n * Requirements:\n *\n * - `account` cannot be the zero address.\n */\n function _mint(address account, uint256 amount) internal virtual {\n require(account != address(0), \"ERC20: mint to the zero address\");\n\n _beforeTokenTransfer(address(0), account, amount);\n\n _totalSupply += amount;\n unchecked {\n // Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above.\n _balances[account] += amount;\n }\n emit Transfer(address(0), account, amount);\n\n _afterTokenTransfer(address(0), account, amount);\n }\n\n /**\n * @dev Destroys `amount` tokens from `account`, reducing the\n * total supply.\n *\n * Emits a {Transfer} event with `to` set to the zero address.\n *\n * Requirements:\n *\n * - `account` cannot be the zero address.\n * - `account` must have at least `amount` tokens.\n */\n function _burn(address account, uint256 amount) internal virtual {\n require(account != address(0), \"ERC20: burn from the zero address\");\n\n _beforeTokenTransfer(account, address(0), amount);\n\n uint256 accountBalance = _balances[account];\n require(accountBalance >= amount, \"ERC20: burn amount exceeds balance\");\n unchecked {\n _balances[account] = accountBalance - amount;\n // Overflow not possible: amount <= accountBalance <= totalSupply.\n _totalSupply -= amount;\n }\n\n emit Transfer(account, address(0), amount);\n\n _afterTokenTransfer(account, address(0), amount);\n }\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.\n *\n * This internal function is equivalent to `approve`, and can be used to\n * e.g. set automatic allowances for certain subsystems, etc.\n *\n * Emits an {Approval} event.\n *\n * Requirements:\n *\n * - `owner` cannot be the zero address.\n * - `spender` cannot be the zero address.\n */\n function _approve(address owner, address spender, uint256 amount) internal virtual {\n require(owner != address(0), \"ERC20: approve from the zero address\");\n require(spender != address(0), \"ERC20: approve to the zero address\");\n\n _allowances[owner][spender] = amount;\n emit Approval(owner, spender, amount);\n }\n\n /**\n * @dev Updates `owner` s allowance for `spender` based on spent `amount`.\n *\n * Does not update the allowance amount in case of infinite allowance.\n * Revert if not enough allowance is available.\n *\n * Might emit an {Approval} event.\n */\n function _spendAllowance(address owner, address spender, uint256 amount) internal virtual {\n uint256 currentAllowance = allowance(owner, spender);\n if (currentAllowance != type(uint256).max) {\n require(currentAllowance >= amount, \"ERC20: insufficient allowance\");\n unchecked {\n _approve(owner, spender, currentAllowance - amount);\n }\n }\n }\n\n /**\n * @dev Hook that is called before any transfer of tokens. This includes\n * minting and burning.\n *\n * Calling conditions:\n *\n * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens\n * will be transferred to `to`.\n * - when `from` is zero, `amount` tokens will be minted for `to`.\n * - when `to` is zero, `amount` of ``from``'s tokens will be burned.\n * - `from` and `to` are never both zero.\n *\n * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].\n */\n function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual {}\n\n /**\n * @dev Hook that is called after any transfer of tokens. This includes\n * minting and burning.\n *\n * Calling conditions:\n *\n * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens\n * has been transferred to `to`.\n * - when `from` is zero, `amount` tokens have been minted for `to`.\n * - when `to` is zero, `amount` of ``from``'s tokens have been burned.\n * - `from` and `to` are never both zero.\n *\n * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].\n */\n function _afterTokenTransfer(address from, address to, uint256 amount) internal virtual {}\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC20.sol\";\n\n/**\n * @dev Interface for the optional metadata functions from the ERC20 standard.\n *\n * _Available since v4.1._\n */\ninterface IERC20Metadata is IERC20 {\n /**\n * @dev Returns the name of the token.\n */\n function name() external view returns (string memory);\n\n /**\n * @dev Returns the symbol of the token.\n */\n function symbol() external view returns (string memory);\n\n /**\n * @dev Returns the decimals places of the token.\n */\n function decimals() external view returns (uint8);\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.4) (token/ERC20/extensions/IERC20Permit.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in\n * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].\n *\n * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by\n * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't\n * need to send a transaction, and thus is not required to hold Ether at all.\n *\n * ==== Security Considerations\n *\n * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature\n * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be\n * considered as an intention to spend the allowance in any specific way. The second is that because permits have\n * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should\n * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be\n * generally recommended is:\n *\n * ```solidity\n * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {\n * try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}\n * doThing(..., value);\n * }\n *\n * function doThing(..., uint256 value) public {\n * token.safeTransferFrom(msg.sender, address(this), value);\n * ...\n * }\n * ```\n *\n * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of\n * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also\n * {SafeERC20-safeTransferFrom}).\n *\n * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so\n * contracts should have entry points that don't rely on permit.\n */\ninterface IERC20Permit {\n /**\n * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,\n * given ``owner``'s signed approval.\n *\n * IMPORTANT: The same issues {IERC20-approve} has related to transaction\n * ordering also apply here.\n *\n * Emits an {Approval} event.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n * - `deadline` must be a timestamp in the future.\n * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`\n * over the EIP712-formatted function arguments.\n * - the signature must use ``owner``'s current nonce (see {nonces}).\n *\n * For more information on the signature format, see the\n * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP\n * section].\n *\n * CAUTION: See Security Considerations above.\n */\n function permit(\n address owner,\n address spender,\n uint256 value,\n uint256 deadline,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) external;\n\n /**\n * @dev Returns the current nonce for `owner`. This value must be\n * included whenever a signature is generated for {permit}.\n *\n * Every successful call to {permit} increases ``owner``'s nonce by one. This\n * prevents a signature from being used multiple times.\n */\n function nonces(address owner) external view returns (uint256);\n\n /**\n * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.\n */\n // solhint-disable-next-line func-name-mixedcase\n function DOMAIN_SEPARATOR() external view returns (bytes32);\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/IERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n\n /**\n * @dev Returns the amount of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves `amount` tokens from the caller's account to `to`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address to, uint256 amount) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender's allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @dev Moves `amount` tokens from `from` to `to` using the\n * allowance mechanism. `amount` is then deducted from the caller's\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(address from, address to, uint256 amount) external returns (bool);\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.3) (token/ERC20/utils/SafeERC20.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC20.sol\";\nimport \"../extensions/IERC20Permit.sol\";\nimport \"../../../utils/Address.sol\";\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n using Address for address;\n\n /**\n * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,\n * non-reverting calls are assumed to be successful.\n */\n function safeTransfer(IERC20 token, address to, uint256 value) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\n }\n\n /**\n * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the\n * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.\n */\n function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\n }\n\n /**\n * @dev Deprecated. This function has issues similar to the ones found in\n * {IERC20-approve}, and its usage is discouraged.\n *\n * Whenever possible, use {safeIncreaseAllowance} and\n * {safeDecreaseAllowance} instead.\n */\n function safeApprove(IERC20 token, address spender, uint256 value) internal {\n // safeApprove should only be called when setting an initial allowance,\n // or when resetting it to zero. To increase and decrease it, use\n // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\n require(\n (value == 0) || (token.allowance(address(this), spender) == 0),\n \"SafeERC20: approve from non-zero to non-zero allowance\"\n );\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\n }\n\n /**\n * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,\n * non-reverting calls are assumed to be successful.\n */\n function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {\n uint256 oldAllowance = token.allowance(address(this), spender);\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));\n }\n\n /**\n * @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,\n * non-reverting calls are assumed to be successful.\n */\n function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {\n unchecked {\n uint256 oldAllowance = token.allowance(address(this), spender);\n require(oldAllowance >= value, \"SafeERC20: decreased allowance below zero\");\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));\n }\n }\n\n /**\n * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,\n * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval\n * to be set to zero before setting it to a non-zero value, such as USDT.\n */\n function forceApprove(IERC20 token, address spender, uint256 value) internal {\n bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);\n\n if (!_callOptionalReturnBool(token, approvalCall)) {\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));\n _callOptionalReturn(token, approvalCall);\n }\n }\n\n /**\n * @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.\n * Revert on invalid signature.\n */\n function safePermit(\n IERC20Permit token,\n address owner,\n address spender,\n uint256 value,\n uint256 deadline,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) internal {\n uint256 nonceBefore = token.nonces(owner);\n token.permit(owner, spender, value, deadline, v, r, s);\n uint256 nonceAfter = token.nonces(owner);\n require(nonceAfter == nonceBefore + 1, \"SafeERC20: permit did not succeed\");\n }\n\n /**\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n * on the return value: the return value is optional (but if data is returned, it must not be false).\n * @param token The token targeted by the call.\n * @param data The call data (encoded using abi.encode or one of its variants).\n */\n function _callOptionalReturn(IERC20 token, bytes memory data) private {\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that\n // the target address contains contract code and also asserts for success in the low-level call.\n\n bytes memory returndata = address(token).functionCall(data, \"SafeERC20: low-level call failed\");\n require(returndata.length == 0 || abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n }\n\n /**\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n * on the return value: the return value is optional (but if data is returned, it must not be false).\n * @param token The token targeted by the call.\n * @param data The call data (encoded using abi.encode or one of its variants).\n *\n * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.\n */\n function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false\n // and not revert is the subcall reverts.\n\n (bool success, bytes memory returndata) = address(token).call(data);\n return\n success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token));\n }\n}\n" + }, + "@openzeppelin/contracts/utils/Address.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)\n\npragma solidity ^0.8.1;\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n /**\n * @dev Returns true if `account` is a contract.\n *\n * [IMPORTANT]\n * ====\n * It is unsafe to assume that an address for which this function returns\n * false is an externally-owned account (EOA) and not a contract.\n *\n * Among others, `isContract` will return false for the following\n * types of addresses:\n *\n * - an externally-owned account\n * - a contract in construction\n * - an address where a contract will be created\n * - an address where a contract lived, but was destroyed\n *\n * Furthermore, `isContract` will also return true if the target contract within\n * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,\n * which only has an effect at the end of a transaction.\n * ====\n *\n * [IMPORTANT]\n * ====\n * You shouldn't rely on `isContract` to protect against flash loan attacks!\n *\n * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets\n * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract\n * constructor.\n * ====\n */\n function isContract(address account) internal view returns (bool) {\n // This method relies on extcodesize/address.code.length, which returns 0\n // for contracts in construction, since the code is only stored at the end\n // of the constructor execution.\n\n return account.code.length > 0;\n }\n\n /**\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n * `recipient`, forwarding all available gas and reverting on errors.\n *\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\n * imposed by `transfer`, making them unable to receive funds via\n * `transfer`. {sendValue} removes this limitation.\n *\n * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n *\n * IMPORTANT: because control is transferred to `recipient`, care must be\n * taken to not create reentrancy vulnerabilities. Consider using\n * {ReentrancyGuard} or the\n * https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n */\n function sendValue(address payable recipient, uint256 amount) internal {\n require(address(this).balance >= amount, \"Address: insufficient balance\");\n\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Address: unable to send value, recipient may have reverted\");\n }\n\n /**\n * @dev Performs a Solidity function call using a low level `call`. A\n * plain `call` is an unsafe replacement for a function call: use this\n * function instead.\n *\n * If `target` reverts with a revert reason, it is bubbled up by this\n * function (like regular Solidity function calls).\n *\n * Returns the raw returned data. To convert to the expected return value,\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n *\n * Requirements:\n *\n * - `target` must be a contract.\n * - calling `target` with `data` must not revert.\n *\n * _Available since v3.1._\n */\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, \"Address: low-level call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n * `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but also transferring `value` wei to `target`.\n *\n * Requirements:\n *\n * - the calling contract must have an ETH balance of at least `value`.\n * - the called Solidity function must be `payable`.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {\n return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n * with `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(address(this).balance >= value, \"Address: insufficient balance for call\");\n (bool success, bytes memory returndata) = target.call{value: value}(data);\n return verifyCallResultFromTarget(target, success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n return functionStaticCall(target, data, \"Address: low-level static call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n (bool success, bytes memory returndata) = target.staticcall(data);\n return verifyCallResultFromTarget(target, success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n (bool success, bytes memory returndata) = target.delegatecall(data);\n return verifyCallResultFromTarget(target, success, returndata, errorMessage);\n }\n\n /**\n * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling\n * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.\n *\n * _Available since v4.8._\n */\n function verifyCallResultFromTarget(\n address target,\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n if (success) {\n if (returndata.length == 0) {\n // only check isContract if the call was successful and the return data is empty\n // otherwise we already know that it was a contract\n require(isContract(target), \"Address: call to non-contract\");\n }\n return returndata;\n } else {\n _revert(returndata, errorMessage);\n }\n }\n\n /**\n * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the\n * revert reason or using the provided one.\n *\n * _Available since v4.3._\n */\n function verifyCallResult(\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal pure returns (bytes memory) {\n if (success) {\n return returndata;\n } else {\n _revert(returndata, errorMessage);\n }\n }\n\n function _revert(bytes memory returndata, string memory errorMessage) private pure {\n // Look for revert reason and bubble it up if present\n if (returndata.length > 0) {\n // The easiest way to bubble the revert reason is using memory via assembly\n /// @solidity memory-safe-assembly\n assembly {\n let returndata_size := mload(returndata)\n revert(add(32, returndata), returndata_size)\n }\n } else {\n revert(errorMessage);\n }\n }\n}\n" + }, + "@openzeppelin/contracts/utils/Context.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.4) (utils/Context.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract Context {\n function _msgSender() internal view virtual returns (address) {\n return msg.sender;\n }\n\n function _msgData() internal view virtual returns (bytes calldata) {\n return msg.data;\n }\n\n function _contextSuffixLength() internal view virtual returns (uint256) {\n return 0;\n }\n}\n" + }, + "@openzeppelin/contracts/utils/introspection/ERC165.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IERC165.sol\";\n\n/**\n * @dev Implementation of the {IERC165} interface.\n *\n * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check\n * for the additional interface id that will be supported. For example:\n *\n * ```solidity\n * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);\n * }\n * ```\n *\n * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.\n */\nabstract contract ERC165 is IERC165 {\n /**\n * @dev See {IERC165-supportsInterface}.\n */\n function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n return interfaceId == type(IERC165).interfaceId;\n }\n}\n" + }, + "@openzeppelin/contracts/utils/introspection/IERC165.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC165 standard, as defined in the\n * https://eips.ethereum.org/EIPS/eip-165[EIP].\n *\n * Implementers can declare support of contract interfaces, which can then be\n * queried by others ({ERC165Checker}).\n *\n * For an implementation, see {ERC165}.\n */\ninterface IERC165 {\n /**\n * @dev Returns true if this contract implements the interface defined by\n * `interfaceId`. See the corresponding\n * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]\n * to learn more about how these ids are created.\n *\n * This function call must use less than 30 000 gas.\n */\n function supportsInterface(bytes4 interfaceId) external view returns (bool);\n}\n" + }, + "@openzeppelin/contracts/utils/math/Math.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Standard math utilities missing in the Solidity language.\n */\nlibrary Math {\n enum Rounding {\n Down, // Toward negative infinity\n Up, // Toward infinity\n Zero // Toward zero\n }\n\n /**\n * @dev Returns the largest of two numbers.\n */\n function max(uint256 a, uint256 b) internal pure returns (uint256) {\n return a > b ? a : b;\n }\n\n /**\n * @dev Returns the smallest of two numbers.\n */\n function min(uint256 a, uint256 b) internal pure returns (uint256) {\n return a < b ? a : b;\n }\n\n /**\n * @dev Returns the average of two numbers. The result is rounded towards\n * zero.\n */\n function average(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b) / 2 can overflow.\n return (a & b) + (a ^ b) / 2;\n }\n\n /**\n * @dev Returns the ceiling of the division of two numbers.\n *\n * This differs from standard division with `/` in that it rounds up instead\n * of rounding down.\n */\n function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {\n // (a + b - 1) / b can overflow on addition, so we distribute.\n return a == 0 ? 0 : (a - 1) / b + 1;\n }\n\n /**\n * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0\n * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)\n * with further edits by Uniswap Labs also under MIT license.\n */\n function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {\n unchecked {\n // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use\n // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256\n // variables such that product = prod1 * 2^256 + prod0.\n uint256 prod0; // Least significant 256 bits of the product\n uint256 prod1; // Most significant 256 bits of the product\n assembly {\n let mm := mulmod(x, y, not(0))\n prod0 := mul(x, y)\n prod1 := sub(sub(mm, prod0), lt(mm, prod0))\n }\n\n // Handle non-overflow cases, 256 by 256 division.\n if (prod1 == 0) {\n // Solidity will revert if denominator == 0, unlike the div opcode on its own.\n // The surrounding unchecked block does not change this fact.\n // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.\n return prod0 / denominator;\n }\n\n // Make sure the result is less than 2^256. Also prevents denominator == 0.\n require(denominator > prod1, \"Math: mulDiv overflow\");\n\n ///////////////////////////////////////////////\n // 512 by 256 division.\n ///////////////////////////////////////////////\n\n // Make division exact by subtracting the remainder from [prod1 prod0].\n uint256 remainder;\n assembly {\n // Compute remainder using mulmod.\n remainder := mulmod(x, y, denominator)\n\n // Subtract 256 bit number from 512 bit number.\n prod1 := sub(prod1, gt(remainder, prod0))\n prod0 := sub(prod0, remainder)\n }\n\n // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.\n // See https://cs.stackexchange.com/q/138556/92363.\n\n // Does not overflow because the denominator cannot be zero at this stage in the function.\n uint256 twos = denominator & (~denominator + 1);\n assembly {\n // Divide denominator by twos.\n denominator := div(denominator, twos)\n\n // Divide [prod1 prod0] by twos.\n prod0 := div(prod0, twos)\n\n // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.\n twos := add(div(sub(0, twos), twos), 1)\n }\n\n // Shift in bits from prod1 into prod0.\n prod0 |= prod1 * twos;\n\n // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such\n // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for\n // four bits. That is, denominator * inv = 1 mod 2^4.\n uint256 inverse = (3 * denominator) ^ 2;\n\n // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works\n // in modular arithmetic, doubling the correct bits in each step.\n inverse *= 2 - denominator * inverse; // inverse mod 2^8\n inverse *= 2 - denominator * inverse; // inverse mod 2^16\n inverse *= 2 - denominator * inverse; // inverse mod 2^32\n inverse *= 2 - denominator * inverse; // inverse mod 2^64\n inverse *= 2 - denominator * inverse; // inverse mod 2^128\n inverse *= 2 - denominator * inverse; // inverse mod 2^256\n\n // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.\n // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is\n // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1\n // is no longer required.\n result = prod0 * inverse;\n return result;\n }\n }\n\n /**\n * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.\n */\n function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {\n uint256 result = mulDiv(x, y, denominator);\n if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {\n result += 1;\n }\n return result;\n }\n\n /**\n * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.\n *\n * Inspired by Henry S. Warren, Jr.'s \"Hacker's Delight\" (Chapter 11).\n */\n function sqrt(uint256 a) internal pure returns (uint256) {\n if (a == 0) {\n return 0;\n }\n\n // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.\n //\n // We know that the \"msb\" (most significant bit) of our target number `a` is a power of 2 such that we have\n // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.\n //\n // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`\n // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`\n // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`\n //\n // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.\n uint256 result = 1 << (log2(a) >> 1);\n\n // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,\n // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at\n // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision\n // into the expected uint128 result.\n unchecked {\n result = (result + a / result) >> 1;\n result = (result + a / result) >> 1;\n result = (result + a / result) >> 1;\n result = (result + a / result) >> 1;\n result = (result + a / result) >> 1;\n result = (result + a / result) >> 1;\n result = (result + a / result) >> 1;\n return min(result, a / result);\n }\n }\n\n /**\n * @notice Calculates sqrt(a), following the selected rounding direction.\n */\n function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {\n unchecked {\n uint256 result = sqrt(a);\n return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);\n }\n }\n\n /**\n * @dev Return the log in base 2, rounded down, of a positive value.\n * Returns 0 if given 0.\n */\n function log2(uint256 value) internal pure returns (uint256) {\n uint256 result = 0;\n unchecked {\n if (value >> 128 > 0) {\n value >>= 128;\n result += 128;\n }\n if (value >> 64 > 0) {\n value >>= 64;\n result += 64;\n }\n if (value >> 32 > 0) {\n value >>= 32;\n result += 32;\n }\n if (value >> 16 > 0) {\n value >>= 16;\n result += 16;\n }\n if (value >> 8 > 0) {\n value >>= 8;\n result += 8;\n }\n if (value >> 4 > 0) {\n value >>= 4;\n result += 4;\n }\n if (value >> 2 > 0) {\n value >>= 2;\n result += 2;\n }\n if (value >> 1 > 0) {\n result += 1;\n }\n }\n return result;\n }\n\n /**\n * @dev Return the log in base 2, following the selected rounding direction, of a positive value.\n * Returns 0 if given 0.\n */\n function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {\n unchecked {\n uint256 result = log2(value);\n return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);\n }\n }\n\n /**\n * @dev Return the log in base 10, rounded down, of a positive value.\n * Returns 0 if given 0.\n */\n function log10(uint256 value) internal pure returns (uint256) {\n uint256 result = 0;\n unchecked {\n if (value >= 10 ** 64) {\n value /= 10 ** 64;\n result += 64;\n }\n if (value >= 10 ** 32) {\n value /= 10 ** 32;\n result += 32;\n }\n if (value >= 10 ** 16) {\n value /= 10 ** 16;\n result += 16;\n }\n if (value >= 10 ** 8) {\n value /= 10 ** 8;\n result += 8;\n }\n if (value >= 10 ** 4) {\n value /= 10 ** 4;\n result += 4;\n }\n if (value >= 10 ** 2) {\n value /= 10 ** 2;\n result += 2;\n }\n if (value >= 10 ** 1) {\n result += 1;\n }\n }\n return result;\n }\n\n /**\n * @dev Return the log in base 10, following the selected rounding direction, of a positive value.\n * Returns 0 if given 0.\n */\n function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {\n unchecked {\n uint256 result = log10(value);\n return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);\n }\n }\n\n /**\n * @dev Return the log in base 256, rounded down, of a positive value.\n * Returns 0 if given 0.\n *\n * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.\n */\n function log256(uint256 value) internal pure returns (uint256) {\n uint256 result = 0;\n unchecked {\n if (value >> 128 > 0) {\n value >>= 128;\n result += 16;\n }\n if (value >> 64 > 0) {\n value >>= 64;\n result += 8;\n }\n if (value >> 32 > 0) {\n value >>= 32;\n result += 4;\n }\n if (value >> 16 > 0) {\n value >>= 16;\n result += 2;\n }\n if (value >> 8 > 0) {\n result += 1;\n }\n }\n return result;\n }\n\n /**\n * @dev Return the log in base 256, following the selected rounding direction, of a positive value.\n * Returns 0 if given 0.\n */\n function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {\n unchecked {\n uint256 result = log256(value);\n return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);\n }\n }\n}\n" + }, + "@openzeppelin/contracts/utils/math/SignedMath.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Standard signed math utilities missing in the Solidity language.\n */\nlibrary SignedMath {\n /**\n * @dev Returns the largest of two signed numbers.\n */\n function max(int256 a, int256 b) internal pure returns (int256) {\n return a > b ? a : b;\n }\n\n /**\n * @dev Returns the smallest of two signed numbers.\n */\n function min(int256 a, int256 b) internal pure returns (int256) {\n return a < b ? a : b;\n }\n\n /**\n * @dev Returns the average of two signed numbers without overflow.\n * The result is rounded towards zero.\n */\n function average(int256 a, int256 b) internal pure returns (int256) {\n // Formula from the book \"Hacker's Delight\"\n int256 x = (a & b) + ((a ^ b) >> 1);\n return x + (int256(uint256(x) >> 255) & (a ^ b));\n }\n\n /**\n * @dev Returns the absolute unsigned value of a signed value.\n */\n function abs(int256 n) internal pure returns (uint256) {\n unchecked {\n // must be unchecked in order to support `n = type(int256).min`\n return uint256(n >= 0 ? n : -n);\n }\n }\n}\n" + }, + "@openzeppelin/contracts/utils/StorageSlot.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.0) (utils/StorageSlot.sol)\n// This file was procedurally generated from scripts/generate/templates/StorageSlot.js.\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Library for reading and writing primitive types to specific storage slots.\n *\n * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.\n * This library helps with reading and writing to such slots without the need for inline assembly.\n *\n * The functions in this library return Slot structs that contain a `value` member that can be used to read or write.\n *\n * Example usage to set ERC1967 implementation slot:\n * ```solidity\n * contract ERC1967 {\n * bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;\n *\n * function _getImplementation() internal view returns (address) {\n * return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;\n * }\n *\n * function _setImplementation(address newImplementation) internal {\n * require(Address.isContract(newImplementation), \"ERC1967: new implementation is not a contract\");\n * StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;\n * }\n * }\n * ```\n *\n * _Available since v4.1 for `address`, `bool`, `bytes32`, `uint256`._\n * _Available since v4.9 for `string`, `bytes`._\n */\nlibrary StorageSlot {\n struct AddressSlot {\n address value;\n }\n\n struct BooleanSlot {\n bool value;\n }\n\n struct Bytes32Slot {\n bytes32 value;\n }\n\n struct Uint256Slot {\n uint256 value;\n }\n\n struct StringSlot {\n string value;\n }\n\n struct BytesSlot {\n bytes value;\n }\n\n /**\n * @dev Returns an `AddressSlot` with member `value` located at `slot`.\n */\n function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {\n /// @solidity memory-safe-assembly\n assembly {\n r.slot := slot\n }\n }\n\n /**\n * @dev Returns an `BooleanSlot` with member `value` located at `slot`.\n */\n function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {\n /// @solidity memory-safe-assembly\n assembly {\n r.slot := slot\n }\n }\n\n /**\n * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.\n */\n function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {\n /// @solidity memory-safe-assembly\n assembly {\n r.slot := slot\n }\n }\n\n /**\n * @dev Returns an `Uint256Slot` with member `value` located at `slot`.\n */\n function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {\n /// @solidity memory-safe-assembly\n assembly {\n r.slot := slot\n }\n }\n\n /**\n * @dev Returns an `StringSlot` with member `value` located at `slot`.\n */\n function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {\n /// @solidity memory-safe-assembly\n assembly {\n r.slot := slot\n }\n }\n\n /**\n * @dev Returns an `StringSlot` representation of the string storage pointer `store`.\n */\n function getStringSlot(string storage store) internal pure returns (StringSlot storage r) {\n /// @solidity memory-safe-assembly\n assembly {\n r.slot := store.slot\n }\n }\n\n /**\n * @dev Returns an `BytesSlot` with member `value` located at `slot`.\n */\n function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {\n /// @solidity memory-safe-assembly\n assembly {\n r.slot := slot\n }\n }\n\n /**\n * @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.\n */\n function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) {\n /// @solidity memory-safe-assembly\n assembly {\n r.slot := store.slot\n }\n }\n}\n" + }, + "@openzeppelin/contracts/utils/Strings.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.0) (utils/Strings.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./math/Math.sol\";\nimport \"./math/SignedMath.sol\";\n\n/**\n * @dev String operations.\n */\nlibrary Strings {\n bytes16 private constant _SYMBOLS = \"0123456789abcdef\";\n uint8 private constant _ADDRESS_LENGTH = 20;\n\n /**\n * @dev Converts a `uint256` to its ASCII `string` decimal representation.\n */\n function toString(uint256 value) internal pure returns (string memory) {\n unchecked {\n uint256 length = Math.log10(value) + 1;\n string memory buffer = new string(length);\n uint256 ptr;\n /// @solidity memory-safe-assembly\n assembly {\n ptr := add(buffer, add(32, length))\n }\n while (true) {\n ptr--;\n /// @solidity memory-safe-assembly\n assembly {\n mstore8(ptr, byte(mod(value, 10), _SYMBOLS))\n }\n value /= 10;\n if (value == 0) break;\n }\n return buffer;\n }\n }\n\n /**\n * @dev Converts a `int256` to its ASCII `string` decimal representation.\n */\n function toString(int256 value) internal pure returns (string memory) {\n return string(abi.encodePacked(value < 0 ? \"-\" : \"\", toString(SignedMath.abs(value))));\n }\n\n /**\n * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.\n */\n function toHexString(uint256 value) internal pure returns (string memory) {\n unchecked {\n return toHexString(value, Math.log256(value) + 1);\n }\n }\n\n /**\n * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.\n */\n function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {\n bytes memory buffer = new bytes(2 * length + 2);\n buffer[0] = \"0\";\n buffer[1] = \"x\";\n for (uint256 i = 2 * length + 1; i > 1; --i) {\n buffer[i] = _SYMBOLS[value & 0xf];\n value >>= 4;\n }\n require(value == 0, \"Strings: hex length insufficient\");\n return string(buffer);\n }\n\n /**\n * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.\n */\n function toHexString(address addr) internal pure returns (string memory) {\n return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);\n }\n\n /**\n * @dev Returns true if the two strings are equal.\n */\n function equal(string memory a, string memory b) internal pure returns (bool) {\n return keccak256(bytes(a)) == keccak256(bytes(b));\n }\n}\n" + }, + "@venusprotocol/governance-contracts/contracts/Governance/AccessControlledV8.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\n\nimport \"@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol\";\nimport \"@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol\";\n\nimport \"./IAccessControlManagerV8.sol\";\n\n/**\n * @title AccessControlledV8\n * @author Venus\n * @notice This contract is helper between access control manager and actual contract. This contract further inherited by other contract (using solidity 0.8.13)\n * to integrate access controlled mechanism. It provides initialise methods and verifying access methods.\n */\nabstract contract AccessControlledV8 is Initializable, Ownable2StepUpgradeable {\n /// @notice Access control manager contract\n IAccessControlManagerV8 internal _accessControlManager;\n\n /**\n * @dev This empty reserved space is put in place to allow future versions to add new\n * variables without shifting down storage in the inheritance chain.\n * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\n */\n uint256[49] private __gap;\n\n /// @notice Emitted when access control manager contract address is changed\n event NewAccessControlManager(address oldAccessControlManager, address newAccessControlManager);\n\n /// @notice Thrown when the action is prohibited by AccessControlManager\n error Unauthorized(address sender, address calledContract, string methodSignature);\n\n function __AccessControlled_init(address accessControlManager_) internal onlyInitializing {\n __Ownable2Step_init();\n __AccessControlled_init_unchained(accessControlManager_);\n }\n\n function __AccessControlled_init_unchained(address accessControlManager_) internal onlyInitializing {\n _setAccessControlManager(accessControlManager_);\n }\n\n /**\n * @notice Sets the address of AccessControlManager\n * @dev Admin function to set address of AccessControlManager\n * @param accessControlManager_ The new address of the AccessControlManager\n * @custom:event Emits NewAccessControlManager event\n * @custom:access Only Governance\n */\n function setAccessControlManager(address accessControlManager_) external onlyOwner {\n _setAccessControlManager(accessControlManager_);\n }\n\n /**\n * @notice Returns the address of the access control manager contract\n */\n function accessControlManager() external view returns (IAccessControlManagerV8) {\n return _accessControlManager;\n }\n\n /**\n * @dev Internal function to set address of AccessControlManager\n * @param accessControlManager_ The new address of the AccessControlManager\n */\n function _setAccessControlManager(address accessControlManager_) internal {\n require(address(accessControlManager_) != address(0), \"invalid acess control manager address\");\n address oldAccessControlManager = address(_accessControlManager);\n _accessControlManager = IAccessControlManagerV8(accessControlManager_);\n emit NewAccessControlManager(oldAccessControlManager, accessControlManager_);\n }\n\n /**\n * @notice Reverts if the call is not allowed by AccessControlManager\n * @param signature Method signature\n */\n function _checkAccessAllowed(string memory signature) internal view {\n bool isAllowedToCall = _accessControlManager.isAllowedToCall(msg.sender, signature);\n\n if (!isAllowedToCall) {\n revert Unauthorized(msg.sender, address(this), signature);\n }\n }\n}\n" + }, + "@venusprotocol/governance-contracts/contracts/Governance/AccessControlManager.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\nimport \"@openzeppelin/contracts/access/AccessControl.sol\";\nimport \"./IAccessControlManagerV8.sol\";\n\n/**\n * @title AccessControlManager\n * @author Venus\n * @dev This contract is a wrapper of OpenZeppelin AccessControl extending it in a way to standartize access control within Venus Smart Contract Ecosystem.\n * @notice Access control plays a crucial role in the Venus governance model. It is used to restrict functions so that they can only be called from one\n * account or list of accounts (EOA or Contract Accounts).\n *\n * The implementation of `AccessControlManager`(https://github.com/VenusProtocol/governance-contracts/blob/main/contracts/Governance/AccessControlManager.sol)\n * inherits the [Open Zeppelin AccessControl](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/access/AccessControl.sol)\n * contract as a base for role management logic. There are two role types: admin and granular permissions.\n * \n * ## Granular Roles\n * \n * Granular roles are built by hashing the contract address and its function signature. For example, given contract `Foo` with function `Foo.bar()` which\n * is guarded by ACM, calling `giveRolePermission` for account B do the following:\n * \n * 1. Compute `keccak256(contractFooAddress,functionSignatureBar)`\n * 1. Add the computed role to the roles of account B\n * 1. Account B now can call `ContractFoo.bar()`\n * \n * ## Admin Roles\n * \n * Admin roles allow for an address to call a function signature on any contract guarded by the `AccessControlManager`. This is particularly useful for\n * contracts created by factories.\n * \n * For Admin roles a null address is hashed in place of the contract address (`keccak256(0x0000000000000000000000000000000000000000,functionSignatureBar)`.\n * \n * In the previous example, giving account B the admin role, account B will have permissions to call the `bar()` function on any contract that is guarded by\n * ACM, not only contract A.\n * \n * ## Protocol Integration\n * \n * All restricted functions in Venus Protocol use a hook to ACM in order to check if the caller has the right permission to call the guarded function.\n * `AccessControlledV5` and `AccessControlledV8` abstract contract makes this integration easier. They call ACM's external method\n * `isAllowedToCall(address caller, string functionSig)`. Here is an example of how `setCollateralFactor` function in `Comptroller` is integrated with ACM:\n\n```\n contract Comptroller is [...] AccessControlledV8 {\n [...]\n function setCollateralFactor(VToken vToken, uint256 newCollateralFactorMantissa, uint256 newLiquidationThresholdMantissa) external {\n _checkAccessAllowed(\"setCollateralFactor(address,uint256,uint256)\");\n [...]\n }\n }\n```\n */\ncontract AccessControlManager is AccessControl, IAccessControlManagerV8 {\n /// @notice Emitted when an account is given a permission to a certain contract function\n /// @dev If contract address is 0x000..0 this means that the account is a default admin of this function and\n /// can call any contract function with this signature\n event PermissionGranted(address account, address contractAddress, string functionSig);\n\n /// @notice Emitted when an account is revoked a permission to a certain contract function\n event PermissionRevoked(address account, address contractAddress, string functionSig);\n\n constructor() {\n // Grant the contract deployer the default admin role: it will be able\n // to grant and revoke any roles\n _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);\n }\n\n /**\n * @notice Gives a function call permission to one single account\n * @dev this function can be called only from Role Admin or DEFAULT_ADMIN_ROLE\n * @param contractAddress address of contract for which call permissions will be granted\n * @dev if contractAddress is zero address, the account can access the specified function\n * on **any** contract managed by this ACL\n * @param functionSig signature e.g. \"functionName(uint256,bool)\"\n * @param accountToPermit account that will be given access to the contract function\n * @custom:event Emits a {RoleGranted} and {PermissionGranted} events.\n */\n function giveCallPermission(address contractAddress, string calldata functionSig, address accountToPermit) public {\n bytes32 role = keccak256(abi.encodePacked(contractAddress, functionSig));\n grantRole(role, accountToPermit);\n emit PermissionGranted(accountToPermit, contractAddress, functionSig);\n }\n\n /**\n * @notice Revokes an account's permission to a particular function call\n * @dev this function can be called only from Role Admin or DEFAULT_ADMIN_ROLE\n * \t\tMay emit a {RoleRevoked} event.\n * @param contractAddress address of contract for which call permissions will be revoked\n * @param functionSig signature e.g. \"functionName(uint256,bool)\"\n * @custom:event Emits {RoleRevoked} and {PermissionRevoked} events.\n */\n function revokeCallPermission(\n address contractAddress,\n string calldata functionSig,\n address accountToRevoke\n ) public {\n bytes32 role = keccak256(abi.encodePacked(contractAddress, functionSig));\n revokeRole(role, accountToRevoke);\n emit PermissionRevoked(accountToRevoke, contractAddress, functionSig);\n }\n\n /**\n * @notice Verifies if the given account can call a contract's guarded function\n * @dev Since restricted contracts using this function as a permission hook, we can get contracts address with msg.sender\n * @param account for which call permissions will be checked\n * @param functionSig restricted function signature e.g. \"functionName(uint256,bool)\"\n * @return false if the user account cannot call the particular contract function\n *\n */\n function isAllowedToCall(address account, string calldata functionSig) public view returns (bool) {\n bytes32 role = keccak256(abi.encodePacked(msg.sender, functionSig));\n\n if (hasRole(role, account)) {\n return true;\n } else {\n role = keccak256(abi.encodePacked(address(0), functionSig));\n return hasRole(role, account);\n }\n }\n\n /**\n * @notice Verifies if the given account can call a contract's guarded function\n * @dev This function is used as a view function to check permissions rather than contract hook for access restriction check.\n * @param account for which call permissions will be checked against\n * @param contractAddress address of the restricted contract\n * @param functionSig signature of the restricted function e.g. \"functionName(uint256,bool)\"\n * @return false if the user account cannot call the particular contract function\n */\n function hasPermission(\n address account,\n address contractAddress,\n string calldata functionSig\n ) public view returns (bool) {\n bytes32 role = keccak256(abi.encodePacked(contractAddress, functionSig));\n return hasRole(role, account);\n }\n}\n" + }, + "@venusprotocol/governance-contracts/contracts/Governance/IAccessControlManagerV8.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity ^0.8.25;\n\nimport \"@openzeppelin/contracts/access/IAccessControl.sol\";\n\n/**\n * @title IAccessControlManagerV8\n * @author Venus\n * @notice Interface implemented by the `AccessControlManagerV8` contract.\n */\ninterface IAccessControlManagerV8 is IAccessControl {\n function giveCallPermission(address contractAddress, string calldata functionSig, address accountToPermit) external;\n\n function revokeCallPermission(\n address contractAddress,\n string calldata functionSig,\n address accountToRevoke\n ) external;\n\n function isAllowedToCall(address account, string calldata functionSig) external view returns (bool);\n\n function hasPermission(\n address account,\n address contractAddress,\n string calldata functionSig\n ) external view returns (bool);\n}\n" + }, + "@venusprotocol/oracle/contracts/interfaces/FeedRegistryInterface.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity ^0.8.25;\n\ninterface FeedRegistryInterface {\n function latestRoundDataByName(\n string memory base,\n string memory quote\n )\n external\n view\n returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);\n\n function decimalsByName(string memory base, string memory quote) external view returns (uint8);\n}\n" + }, + "@venusprotocol/oracle/contracts/interfaces/IDeviationBoundedOracle.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\n\ninterface IDeviationBoundedOracle {\n // --- Enums ---\n\n /// @notice Identifies whether a price bound is a minimum or maximum\n enum PriceBoundType {\n MIN,\n MAX\n }\n\n /// @notice Identifies which keeper action a single syncPriceBoundsAndProtections item performs\n enum KeeperAction {\n SetMinPrice,\n SetMaxPrice,\n ExitProtectionMode\n }\n\n // --- Structs ---\n\n /// @notice Per-asset protection state tracking the min/max price window\n struct MarketProtectionState {\n /// @notice Lowest price observed in the current window (packed with maxPrice in one slot)\n uint128 minPrice;\n /// @notice Highest price observed in the current window\n uint128 maxPrice;\n /// @notice Whether protected price is currently being used\n bool currentlyUsingProtectedPrice;\n /// @notice Whether this market is whitelisted for bounded pricing\n bool isBoundedPricingEnabled;\n /// @notice Timestamp of the last protection trigger — reset on every trigger\n uint64 lastProtectionTriggeredAt;\n /// @notice Minimum time protection stays active after last trigger\n uint64 cooldownPeriod;\n /// @notice The underlying asset address, used to verify initialization\n address asset;\n /// @notice Entry deviation threshold (mantissa, e.g. 0.1667e18 = 16.67%); packed with resetThreshold\n uint128 triggerThreshold;\n /// @notice Exit threshold (mantissa); window must converge below this for protection to be disabled\n uint128 resetThreshold;\n /// @notice Whether transient caching of the bounded (collateral, debt) pair is enabled for this asset\n bool cachingEnabled;\n }\n\n /// @notice One item in an syncPriceBoundsAndProtections payload\n /// @dev `value` is interpreted per-action: the new bound price for SetMinPrice / SetMaxPrice, ignored for ExitProtectionMode\n struct KeeperActionItem {\n address asset;\n KeeperAction action;\n uint256 value;\n }\n\n /// @notice One item in a setTokenConfigs payload\n struct TokenConfigInput {\n /// @notice The underlying asset address\n address asset;\n /// @notice Minimum time protection stays active after the last trigger (seconds)\n uint64 cooldownPeriod;\n /// @notice Entry deviation threshold (mantissa). Must be between 5% and 50%.\n uint256 triggerThreshold;\n /// @notice Exit deviation threshold (mantissa). Must be non-zero and below triggerThreshold.\n uint256 resetThreshold;\n /// @notice Whether to enable bounded pricing immediately upon initialization\n bool enableBoundedPricing;\n /// @notice Whether transient caching of the bounded (collateral, debt) pair is enabled for this asset\n bool enableCaching;\n }\n\n // --- Events ---\n\n /// @notice Emitted when protection is initialized for an asset\n event ProtectionInitialized(\n address indexed asset,\n uint128 minPrice,\n uint128 maxPrice,\n uint64 cooldownPeriod,\n uint256 triggerThreshold\n );\n\n /// @notice Emitted when protection mode is triggered for an asset\n event ProtectionTriggered(address indexed asset, uint256 spotPrice, uint128 minPrice, uint128 maxPrice);\n\n /// @notice Emitted when protection mode is disabled for an asset\n event ProtectionModeExited(address indexed asset);\n\n /// @notice Emitted when the keeper updates the minimum price for an asset\n event MinPriceUpdated(address indexed asset, uint128 oldMin, uint128 newMin);\n\n /// @notice Emitted when the keeper updates the maximum price for an asset\n event MaxPriceUpdated(address indexed asset, uint128 oldMax, uint128 newMax);\n\n /// @notice Emitted when the entry threshold is updated for an asset\n event TriggerThresholdSet(address indexed asset, uint256 oldThreshold, uint256 newThreshold);\n\n /// @notice Emitted when the exit threshold is updated for an asset\n event ResetThresholdSet(address indexed asset, uint256 oldExitThreshold, uint256 newExitThreshold);\n\n /// @notice Emitted when the cooldown period is updated for an asset\n event CooldownPeriodSet(address indexed asset, uint64 oldCooldown, uint64 newCooldown);\n\n /// @notice Emitted when an asset's whitelist status changes\n event BoundedPricingWhitelistUpdated(address indexed asset, bool whitelisted);\n\n /// @notice Emitted when the per-asset transient caching flag is toggled\n event CachingEnabledUpdated(address indexed asset, bool oldEnabled, bool newEnabled);\n\n // --- Errors ---\n\n /// @notice Thrown when trying to use or update protection for an asset that has not been initialized\n error MarketNotInitialized(address asset);\n\n /// @notice Thrown when trying to initialize an already initialized market\n error MarketAlreadyInitialized(address asset);\n\n /// @notice Thrown when trying to disable protection that is not active\n error ProtectedPriceInactive(address asset);\n\n /// @notice Thrown when trying to disable protection before cooldown has elapsed\n error CooldownNotElapsed(address asset, uint64 lastProtectionTriggeredAt, uint64 cooldownPeriod);\n\n /// @notice Thrown when trying to disable protection before price range has converged\n error PriceRangeNotConverged(address asset, uint256 currentRangeRatio, uint256 resetThreshold);\n\n /// @notice Thrown when keeper tries to set minPrice above current spot\n error InvalidMinPrice(address asset, uint128 newMin, uint256 currentSpot);\n\n /// @notice Thrown when keeper tries to set maxPrice below current spot\n error InvalidMaxPrice(address asset, uint128 newMax, uint256 currentSpot);\n\n /// @notice Thrown when threshold is set below the minimum allowed value\n error ThresholdBelowMinimum(uint256 threshold, uint256 minimum);\n\n /// @notice Thrown when threshold is set above the maximum allowed value\n error ThresholdAboveMaximum(uint256 threshold, uint256 maximum);\n\n /// @notice Thrown when a price exceeds uint128 max\n error PriceExceedsUint128(uint256 price);\n\n /// @notice Thrown when a zero price is provided where a non-zero price is required\n error ZeroPriceNotAllowed();\n\n /// @notice Thrown when trying to initialize protection for VAI\n error VAINotAllowed();\n\n /// @notice Thrown when trying to disable bounded pricing for an asset while protection is active\n error ProtectedPriceActive(address asset);\n\n /// @notice Thrown when the lengths of the arrays are not equal\n error InvalidArrayLength();\n\n /// @notice Thrown when the exit threshold is set at or above the trigger threshold\n error InvalidResetThreshold(uint256 resetThreshold);\n\n /// @notice Thrown when an syncPriceBoundsAndProtections item carries an unsupported action enum value\n error InvalidKeeperAction(uint8 action);\n\n // --- Non-view price functions (update window + trigger protection) ---\n\n /**\n * @notice Gets the bounded collateral price for a given vToken, updating protection state\n * @param vToken vToken address\n * @return collateralPrice The bounded collateral price\n * @custom:error PriceExceedsUint128 if the spot price overflows uint128\n * @custom:event MinPriceUpdated if a new window minimum is recorded\n * @custom:event MaxPriceUpdated if a new window maximum is recorded\n * @custom:event ProtectionTriggered if the spot price deviates beyond the threshold\n */\n function getBoundedCollateralPrice(address vToken) external returns (uint256 collateralPrice);\n\n /**\n * @notice Gets the bounded debt price for a given vToken, updating protection state\n * @param vToken vToken address\n * @return debtPrice The bounded debt price\n * @custom:error PriceExceedsUint128 if the spot price overflows uint128\n * @custom:event MinPriceUpdated if a new window minimum is recorded\n * @custom:event MaxPriceUpdated if a new window maximum is recorded\n * @custom:event ProtectionTriggered if the spot price deviates beyond the threshold\n */\n function getBoundedDebtPrice(address vToken) external returns (uint256 debtPrice);\n\n /**\n * @notice Gets both the bounded collateral and debt prices for a given vToken, updating protection state\n * @param vToken vToken address\n * @return collateralPrice The bounded collateral price\n * @return debtPrice The bounded debt price\n * @custom:error PriceExceedsUint128 if the spot price overflows uint128\n * @custom:event MinPriceUpdated if a new window minimum is recorded\n * @custom:event MaxPriceUpdated if a new window maximum is recorded\n * @custom:event ProtectionTriggered if the spot price deviates beyond the threshold\n */\n function getBoundedPrices(address vToken) external returns (uint256 collateralPrice, uint256 debtPrice);\n\n // --- State update (call before view price reads to populate transient cache) ---\n\n /**\n * @notice Updates the protection state for a given vToken, caching the resolved collateral and debt prices\n * @dev Called by PolicyFacet before liquidity calculations so subsequent view price\n * reads in the same transaction are served from transient storage. The transient\n * cache is only populated when the asset's `cachingEnabled` flag is `true`.\n * @param vToken vToken address\n * @custom:error PriceExceedsUint128 if the spot price overflows uint128\n * @custom:event MinPriceUpdated if a new window minimum is recorded\n * @custom:event MaxPriceUpdated if a new window maximum is recorded\n * @custom:event ProtectionTriggered if the spot price deviates beyond the threshold\n */\n function updateProtectionState(address vToken) external;\n\n // --- View price functions (read stored/cached state only) ---\n\n /**\n * @notice Gets the bounded collateral price for a given vToken (view variant)\n * @dev Reads from transient cache first when the asset's `cachingEnabled` flag is `true`;\n * falls back to ResilientOracle on cache miss or when caching is disabled.\n * @param vToken vToken address\n * @return price The bounded collateral price\n * @custom:error PriceExceedsUint128 if the spot price overflows uint128 (cache miss path only)\n */\n function getBoundedCollateralPriceView(address vToken) external view returns (uint256 price);\n\n /**\n * @notice Gets the bounded debt price for a given vToken (view variant)\n * @dev Reads from transient cache first when the asset's `cachingEnabled` flag is `true`;\n * falls back to ResilientOracle on cache miss or when caching is disabled.\n * @param vToken vToken address\n * @return price The bounded debt price\n * @custom:error PriceExceedsUint128 if the spot price overflows uint128 (cache miss path only)\n */\n function getBoundedDebtPriceView(address vToken) external view returns (uint256 price);\n\n /**\n * @notice Gets both the bounded collateral and debt prices for a given vToken (view variant)\n * @dev Reads from transient cache first when the asset's `cachingEnabled` flag is `true`;\n * falls back to ResilientOracle on cache miss or when caching is disabled.\n * @param vToken vToken address\n * @return collateralPrice The bounded collateral price\n * @return debtPrice The bounded debt price\n * @custom:error PriceExceedsUint128 if the spot price overflows uint128 (cache miss path only)\n */\n function getBoundedPricesView(address vToken) external view returns (uint256 collateralPrice, uint256 debtPrice);\n\n // --- Keeper functions ---\n\n /**\n * @notice Updates the minimum price in the rolling window for a given asset\n * @param asset The underlying asset address\n * @param newMin The new minimum price; must be at or below the current spot and below maxPrice\n * @custom:access Only authorized keeper addresses\n * @custom:error ZeroPriceNotAllowed if newMin is zero\n * @custom:error MarketNotInitialized if the asset has not been initialized\n * @custom:error InvalidMinPrice if newMin exceeds the current spot or is at or above maxPrice\n * @custom:event MinPriceUpdated\n */\n function updateMinPrice(address asset, uint128 newMin) external;\n\n /**\n * @notice Updates the maximum price in the rolling window for a given asset\n * @param asset The underlying asset address\n * @param newMax The new maximum price; must be at or above the current spot and above minPrice\n * @custom:access Only authorized keeper addresses\n * @custom:error ZeroPriceNotAllowed if newMax is zero\n * @custom:error MarketNotInitialized if the asset has not been initialized\n * @custom:error InvalidMaxPrice if newMax is below the current spot or is at or below minPrice\n * @custom:event MaxPriceUpdated\n */\n function updateMaxPrice(address asset, uint128 newMax) external;\n\n /**\n * @notice Exits protection mode for a given asset once conditions are met\n * @param asset The underlying asset address\n * @custom:access Only authorized monitor/keeper addresses\n * @custom:error ProtectedPriceInactive if protection is not currently active\n * @custom:error CooldownNotElapsed if the cooldown period has not elapsed since the last trigger\n * @custom:error PriceRangeNotConverged if the window range is still above the exit threshold\n * @custom:event ProtectionModeExited\n */\n function exitProtectionMode(address asset) external;\n\n /**\n * @notice Dispatches a batch of keeper-only actions (set min, set max, or exit protection) under a single ACM check\n * @dev Each item is processed in array order; any item revert rolls back the whole batch.\n * `value` is interpreted as the new bound price for SetMinPrice / SetMaxPrice and ignored for ExitProtectionMode.\n * Empty `actions` is a no-op success.\n * @param actions The list of keeper actions to apply\n * @custom:access Only authorized keeper addresses\n * @custom:error InvalidKeeperAction if an item carries an unsupported action enum value\n * @custom:error PriceExceedsUint128 if a SetMin/SetMax item value overflows uint128\n * @custom:error ZeroPriceNotAllowed if a SetMin/SetMax item value is zero\n * @custom:error MarketNotInitialized if any referenced asset has not been initialized\n * @custom:error InvalidMinPrice if a SetMinPrice item violates the spot/maxPrice constraints\n * @custom:error InvalidMaxPrice if a SetMaxPrice item violates the spot/minPrice constraints\n * @custom:error ProtectedPriceInactive if an ExitProtectionMode item targets an asset whose protection is not active\n * @custom:error CooldownNotElapsed if an ExitProtectionMode item is submitted before cooldown elapsed\n * @custom:error PriceRangeNotConverged if an ExitProtectionMode item is submitted before window convergence\n * @custom:event MinPriceUpdated, MaxPriceUpdated, ProtectionModeExited\n */\n function syncPriceBoundsAndProtections(KeeperActionItem[] calldata actions) external;\n\n // --- Admin functions (governance-gated) ---\n\n /**\n * @notice Initializes protection parameters for a new asset\n * @dev Seeds the initial min/max window from the current ResilientOracle spot price,\n * confirming the oracle is live for this asset before it is listed.\n * @param tokenConfig_ Token config input for the asset\n * @custom:access Only Governance\n * @custom:error ZeroAddressNotAllowed if asset is the zero address\n * @custom:error ZeroValueNotAllowed if cooldownPeriod, triggerThreshold, or resetThreshold is zero\n * @custom:error MarketAlreadyInitialized if the asset has already been initialized\n * @custom:error ThresholdBelowMinimum if triggerThreshold is below 5%\n * @custom:error ThresholdAboveMaximum if triggerThreshold is above 50%\n * @custom:error InvalidResetThreshold if resetThreshold is at or above triggerThreshold\n * @custom:error VAINotAllowed if asset is the VAI token\n * @custom:error PriceExceedsUint128 if the spot price overflows uint128\n * @custom:event ProtectionInitialized\n * @custom:event BoundedPricingWhitelistUpdated\n */\n function setTokenConfig(TokenConfigInput calldata tokenConfig_) external;\n\n /**\n * @notice Batch-initializes protection parameters for multiple assets in a single transaction\n * @param tokenConfigs_ Array of token config inputs, one per asset\n * @custom:access Only Governance\n * @custom:error InvalidArrayLength if the input array is empty\n * @custom:error ZeroAddressNotAllowed if any asset is the zero address\n * @custom:error ZeroValueNotAllowed if any cooldownPeriod, triggerThreshold, or resetThreshold is zero\n * @custom:error MarketAlreadyInitialized if any asset has already been initialized\n * @custom:error ThresholdBelowMinimum if any triggerThreshold is below 5%\n * @custom:error ThresholdAboveMaximum if any triggerThreshold is above 50%\n * @custom:error InvalidResetThreshold if any resetThreshold is at or above its triggerThreshold\n * @custom:error VAINotAllowed if any asset is the VAI token\n * @custom:error PriceExceedsUint128 if the spot price for any asset overflows uint128\n * @custom:event ProtectionInitialized for each asset\n * @custom:event BoundedPricingWhitelistUpdated for each asset\n */\n function setTokenConfigs(TokenConfigInput[] calldata tokenConfigs_) external;\n\n /**\n * @notice Sets the cooldown period for an asset\n * @param asset The underlying asset address\n * @param newCooldown The new cooldown period in seconds; must be non-zero\n * @custom:access Only Governance\n * @custom:error MarketNotInitialized if the asset has not been initialized\n * @custom:event CooldownPeriodSet\n */\n function setCooldownPeriod(address asset, uint64 newCooldown) external;\n\n /**\n * @notice Sets the trigger and reset thresholds for an asset\n * @param asset The underlying asset address\n * @param newTriggerThreshold The new trigger threshold (mantissa). Must be between 5% and 50% and above the reset threshold.\n * @param newResetThreshold The new reset threshold (mantissa). Must be non-zero and below the trigger threshold.\n * @custom:access Only Governance\n * @custom:error MarketNotInitialized if the asset has not been initialized\n * @custom:error ThresholdBelowMinimum if newTriggerThreshold is below 5%\n * @custom:error ThresholdAboveMaximum if newTriggerThreshold is above 50%\n * @custom:error InvalidResetThreshold if newResetThreshold is at or above newTriggerThreshold\n * @custom:event TriggerThresholdSet if the trigger threshold changed\n * @custom:event ResetThresholdSet if the reset threshold changed\n */\n function setThresholds(address asset, uint256 newTriggerThreshold, uint256 newResetThreshold) external;\n\n /**\n * @notice Sets whether bounded pricing is enabled for an asset\n * @param asset The underlying asset address\n * @param enabled Whether bounded pricing should be enabled for the asset\n * @custom:access Only Governance\n * @custom:error MarketNotInitialized if the asset has not been initialized\n * @custom:error ProtectedPriceActive if trying to disable an asset while protection is active\n * @custom:event BoundedPricingWhitelistUpdated\n */\n function setAssetBoundedPricingEnabled(address asset, bool enabled) external;\n\n /**\n * @notice Toggles transient caching of the bounded (collateral, debt) pair for an asset\n * @dev When disabled, each view/non-view price call recomputes bounded prices from the\n * live spot instead of reading or writing the transient slots. The initial value is\n * set via the `enableCaching` argument of `setTokenConfig`.\n * @param asset The underlying asset address\n * @param enabled Whether transient caching is enabled for this asset\n * @custom:access Only Governance\n * @custom:error MarketNotInitialized if the asset has not been initialized\n * @custom:event CachingEnabledUpdated\n */\n function setCachingEnabled(address asset, bool enabled) external;\n\n // --- View helpers ---\n\n /**\n * @notice Returns the full protection state for an asset\n * @param asset The underlying asset address\n * @return minPrice Lowest price observed in the current window\n * @return maxPrice Highest price observed in the current window\n * @return currentlyUsingProtectedPrice Whether protected price is currently active\n * @return isBoundedPricingEnabled Whether the asset is whitelisted for bounded pricing\n * @return lastProtectionTriggeredAt Timestamp of the last protection trigger\n * @return cooldownPeriod Minimum time protection stays active after last trigger\n * @return assetAddr The underlying asset address stored in the struct\n * @return triggerThreshold Entry deviation threshold (mantissa) that activates protection\n * @return resetThreshold Exit deviation threshold (mantissa) below which protection can be disabled\n * @return cachingEnabled Whether transient caching of the bounded pair is enabled for the asset\n */\n function assetProtectionConfig(\n address asset\n )\n external\n view\n returns (\n uint128 minPrice,\n uint128 maxPrice,\n bool currentlyUsingProtectedPrice,\n bool isBoundedPricingEnabled,\n uint64 lastProtectionTriggeredAt,\n uint64 cooldownPeriod,\n address assetAddr,\n uint128 triggerThreshold,\n uint128 resetThreshold,\n bool cachingEnabled\n );\n\n /**\n * @notice Checks if an asset is whitelisted for bounded pricing\n * @param asset The underlying asset address\n * @return True if the asset is whitelisted\n */\n function isBoundedPricingEnabled(address asset) external view returns (bool);\n\n /**\n * @notice Checks if the asset is currently using the protected (bounded) price\n * @param asset The underlying asset address\n * @return True if the asset is currently using the protected price instead of spot\n */\n function currentlyUsingProtectedPrice(address asset) external view returns (bool);\n\n /**\n * @notice Checks if protection can be exited for a given asset\n * @param asset The underlying asset address\n * @return True if both the cooldown has elapsed and the price range has converged below the exit threshold\n */\n function canExitProtection(address asset) external view returns (bool);\n\n /**\n * @notice Returns the initialized asset at the given index (auto-generated array getter)\n * @param index Array index\n * @return The asset address at the given index\n */\n function allAssets(uint256 index) external view returns (address);\n\n /**\n * @notice Returns all currently whitelisted asset addresses\n * @return result Array of whitelisted asset addresses\n */\n function getAllBoundedPricingEnabledAssets() external view returns (address[] memory result);\n\n /**\n * @notice Returns all asset addresses that have ever been initialized\n * @return Array of all initialized asset addresses\n */\n function getInitializedAssets() external view returns (address[] memory);\n\n /**\n * @notice Batch-checks which assets' on-chain min/max have drifted beyond the keeper deadband\n * @param assets Array of asset addresses to check\n * @param proposedMins Keeper's proposed window minimum prices\n * @param proposedMaxs Keeper's proposed window maximum prices\n * @return needsMinUpdate Whether minPrice drift exceeds the deadband for each asset\n * @return needsMaxUpdate Whether maxPrice drift exceeds the deadband for each asset\n * @custom:error InvalidArrayLength if the input array lengths do not match\n */\n function checkAndGetWindowDrift(\n address[] calldata assets,\n uint128[] calldata proposedMins,\n uint128[] calldata proposedMaxs\n ) external view returns (bool[] memory needsMinUpdate, bool[] memory needsMaxUpdate);\n}\n" + }, + "@venusprotocol/oracle/contracts/interfaces/OracleInterface.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity ^0.8.25;\n\ninterface OracleInterface {\n function getPrice(address asset) external view returns (uint256);\n}\n\ninterface ResilientOracleInterface is OracleInterface {\n function updatePrice(address vToken) external;\n\n function updateAssetPrice(address asset) external;\n\n function getUnderlyingPrice(address vToken) external view returns (uint256);\n}\n\ninterface BoundValidatorInterface {\n function validatePriceWithAnchorPrice(\n address asset,\n uint256 reporterPrice,\n uint256 anchorPrice\n ) external view returns (bool);\n}\n" + }, + "@venusprotocol/oracle/contracts/interfaces/PublicResolverInterface.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\n// SPDX-FileCopyrightText: 2022 Venus\npragma solidity ^0.8.25;\n\ninterface PublicResolverInterface {\n function addr(bytes32 node) external view returns (address payable);\n}\n" + }, + "@venusprotocol/oracle/contracts/interfaces/SIDRegistryInterface.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\n// SPDX-FileCopyrightText: 2022 Venus\npragma solidity ^0.8.25;\n\ninterface SIDRegistryInterface {\n function resolver(bytes32 node) external view returns (address);\n}\n" + }, + "@venusprotocol/oracle/contracts/interfaces/VBep20Interface.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity ^0.8.25;\n\nimport \"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol\";\n\ninterface VBep20Interface is IERC20Metadata {\n /**\n * @notice Underlying asset for this VToken\n */\n function underlying() external view returns (address);\n}\n" + }, + "@venusprotocol/oracle/contracts/oracles/BinanceOracle.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\n\nimport \"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol\";\nimport \"../interfaces/VBep20Interface.sol\";\nimport \"../interfaces/SIDRegistryInterface.sol\";\nimport \"../interfaces/FeedRegistryInterface.sol\";\nimport \"../interfaces/PublicResolverInterface.sol\";\nimport \"../interfaces/OracleInterface.sol\";\nimport \"@venusprotocol/governance-contracts/contracts/Governance/AccessControlledV8.sol\";\nimport \"../interfaces/OracleInterface.sol\";\n\n/**\n * @title BinanceOracle\n * @author Venus\n * @notice This oracle fetches price of assets from Binance.\n */\ncontract BinanceOracle is AccessControlledV8, OracleInterface {\n /// @notice Used to fetch feed registry address.\n address public sidRegistryAddress;\n\n /// @notice Set this as asset address for BNB. This is the underlying address for vBNB\n address public constant BNB_ADDR = 0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB;\n\n /// @notice Max stale period configuration for assets\n mapping(string => uint256) public maxStalePeriod;\n\n /// @notice Override symbols to be compatible with Binance feed registry\n mapping(string => string) public symbols;\n\n /// @notice Used to fetch price of assets used directly when space ID is not supported by current chain.\n address public feedRegistryAddress;\n\n /// @notice Emits when asset stale period is updated.\n event MaxStalePeriodAdded(string indexed asset, uint256 maxStalePeriod);\n\n /// @notice Emits when symbol of the asset is updated.\n event SymbolOverridden(string indexed symbol, string overriddenSymbol);\n\n /// @notice Emits when address of feed registry is updated.\n event FeedRegistryUpdated(address indexed oldFeedRegistry, address indexed newFeedRegistry);\n\n /**\n * @notice Checks whether an address is null or not\n */\n modifier notNullAddress(address someone) {\n if (someone == address(0)) revert(\"can't be zero address\");\n _;\n }\n\n /// @notice Constructor for the implementation contract.\n /// @custom:oz-upgrades-unsafe-allow constructor\n constructor() {\n _disableInitializers();\n }\n\n /**\n * @notice Sets the contracts required to fetch prices\n * @param _sidRegistryAddress Address of SID registry\n * @param _acm Address of the access control manager contract\n */\n function initialize(address _sidRegistryAddress, address _acm) external initializer {\n sidRegistryAddress = _sidRegistryAddress;\n __AccessControlled_init(_acm);\n }\n\n /**\n * @notice Used to set the max stale period of an asset\n * @param symbol The symbol of the asset\n * @param _maxStalePeriod The max stake period\n */\n function setMaxStalePeriod(string memory symbol, uint256 _maxStalePeriod) external {\n _checkAccessAllowed(\"setMaxStalePeriod(string,uint256)\");\n if (_maxStalePeriod == 0) revert(\"stale period can't be zero\");\n if (bytes(symbol).length == 0) revert(\"symbol cannot be empty\");\n\n maxStalePeriod[symbol] = _maxStalePeriod;\n emit MaxStalePeriodAdded(symbol, _maxStalePeriod);\n }\n\n /**\n * @notice Used to override a symbol when fetching price\n * @param symbol The symbol to override\n * @param overrideSymbol The symbol after override\n */\n function setSymbolOverride(string calldata symbol, string calldata overrideSymbol) external {\n _checkAccessAllowed(\"setSymbolOverride(string,string)\");\n if (bytes(symbol).length == 0) revert(\"symbol cannot be empty\");\n\n symbols[symbol] = overrideSymbol;\n emit SymbolOverridden(symbol, overrideSymbol);\n }\n\n /**\n * @notice Used to set feed registry address when current chain does not support space ID.\n * @param newfeedRegistryAddress Address of new feed registry.\n */\n function setFeedRegistryAddress(\n address newfeedRegistryAddress\n ) external notNullAddress(newfeedRegistryAddress) onlyOwner {\n if (sidRegistryAddress != address(0)) revert(\"sidRegistryAddress must be zero\");\n emit FeedRegistryUpdated(feedRegistryAddress, newfeedRegistryAddress);\n feedRegistryAddress = newfeedRegistryAddress;\n }\n\n /**\n * @notice Uses Space ID to fetch the feed registry address\n * @return feedRegistryAddress Address of binance oracle feed registry.\n */\n function getFeedRegistryAddress() public view returns (address) {\n bytes32 nodeHash = 0x94fe3821e0768eb35012484db4df61890f9a6ca5bfa984ef8ff717e73139faff;\n\n SIDRegistryInterface sidRegistry = SIDRegistryInterface(sidRegistryAddress);\n address publicResolverAddress = sidRegistry.resolver(nodeHash);\n PublicResolverInterface publicResolver = PublicResolverInterface(publicResolverAddress);\n\n return publicResolver.addr(nodeHash);\n }\n\n /**\n * @notice Gets the price of a asset from the binance oracle\n * @param asset Address of the asset\n * @return Price in USD\n */\n function getPrice(address asset) public view returns (uint256) {\n string memory symbol;\n uint256 decimals;\n\n if (asset == BNB_ADDR) {\n symbol = \"BNB\";\n decimals = 18;\n } else {\n IERC20Metadata token = IERC20Metadata(asset);\n symbol = token.symbol();\n decimals = token.decimals();\n }\n\n string memory overrideSymbol = symbols[symbol];\n\n if (bytes(overrideSymbol).length != 0) {\n symbol = overrideSymbol;\n }\n\n return _getPrice(symbol, decimals);\n }\n\n function _getPrice(string memory symbol, uint256 decimals) internal view returns (uint256) {\n FeedRegistryInterface feedRegistry;\n\n if (sidRegistryAddress != address(0)) {\n // If sidRegistryAddress is available, fetch feedRegistryAddress from sidRegistry\n feedRegistry = FeedRegistryInterface(getFeedRegistryAddress());\n } else {\n // Use feedRegistry directly if sidRegistryAddress is not available\n feedRegistry = FeedRegistryInterface(feedRegistryAddress);\n }\n\n (, int256 answer, , uint256 updatedAt, ) = feedRegistry.latestRoundDataByName(symbol, \"USD\");\n if (answer <= 0) revert(\"invalid binance oracle price\");\n if (block.timestamp < updatedAt) revert(\"updatedAt exceeds block time\");\n\n uint256 deltaTime;\n unchecked {\n deltaTime = block.timestamp - updatedAt;\n }\n if (deltaTime > maxStalePeriod[symbol]) revert(\"binance oracle price expired\");\n\n uint256 decimalDelta = feedRegistry.decimalsByName(symbol, \"USD\");\n return (uint256(answer) * (10 ** (18 - decimalDelta))) * (10 ** (18 - decimals));\n }\n}\n" + }, + "@venusprotocol/oracle/contracts/oracles/ChainlinkOracle.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\n\nimport \"../interfaces/VBep20Interface.sol\";\nimport \"../interfaces/OracleInterface.sol\";\nimport \"@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol\";\nimport \"@venusprotocol/governance-contracts/contracts/Governance/AccessControlledV8.sol\";\n\n/**\n * @title ChainlinkOracle\n * @author Venus\n * @notice This oracle fetches prices of assets from the Chainlink oracle.\n */\ncontract ChainlinkOracle is AccessControlledV8, OracleInterface {\n struct TokenConfig {\n /// @notice Underlying token address, which can't be a null address\n /// @notice Used to check if a token is supported\n /// @notice 0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB address for native tokens\n /// (e.g BNB for BNB chain, ETH for Ethereum network)\n address asset;\n /// @notice Chainlink feed address\n address feed;\n /// @notice Price expiration period of this asset\n uint256 maxStalePeriod;\n }\n\n /// @notice Set this as asset address for native token on each chain.\n /// This is the underlying address for vBNB on BNB chain or an underlying asset for a native market on any chain.\n address public constant NATIVE_TOKEN_ADDR = 0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB;\n\n /// @notice Manually set an override price, useful under extenuating conditions such as price feed failure\n mapping(address => uint256) public prices;\n\n /// @notice Token config by assets\n mapping(address => TokenConfig) public tokenConfigs;\n\n /// @notice Emit when a price is manually set\n event PricePosted(address indexed asset, uint256 previousPriceMantissa, uint256 newPriceMantissa);\n\n /// @notice Emit when a token config is added\n event TokenConfigAdded(address indexed asset, address feed, uint256 maxStalePeriod);\n\n modifier notNullAddress(address someone) {\n if (someone == address(0)) revert(\"can't be zero address\");\n _;\n }\n\n /// @notice Constructor for the implementation contract.\n /// @custom:oz-upgrades-unsafe-allow constructor\n constructor() {\n _disableInitializers();\n }\n\n /**\n * @notice Initializes the owner of the contract\n * @param accessControlManager_ Address of the access control manager contract\n */\n function initialize(address accessControlManager_) external initializer {\n __AccessControlled_init(accessControlManager_);\n }\n\n /**\n * @notice Manually set the price of a given asset\n * @param asset Asset address\n * @param price Asset price in 18 decimals\n * @custom:access Only Governance\n * @custom:event Emits PricePosted event on successfully setup of asset price\n */\n function setDirectPrice(address asset, uint256 price) external notNullAddress(asset) {\n _checkAccessAllowed(\"setDirectPrice(address,uint256)\");\n\n uint256 previousPriceMantissa = prices[asset];\n prices[asset] = price;\n emit PricePosted(asset, previousPriceMantissa, price);\n }\n\n /**\n * @notice Add multiple token configs at the same time\n * @param tokenConfigs_ config array\n * @custom:access Only Governance\n * @custom:error Zero length error thrown, if length of the array in parameter is 0\n */\n function setTokenConfigs(TokenConfig[] memory tokenConfigs_) external {\n if (tokenConfigs_.length == 0) revert(\"length can't be 0\");\n uint256 numTokenConfigs = tokenConfigs_.length;\n for (uint256 i; i < numTokenConfigs; ++i) {\n setTokenConfig(tokenConfigs_[i]);\n }\n }\n\n /**\n * @notice Add single token config. asset & feed cannot be null addresses and maxStalePeriod must be positive\n * @param tokenConfig Token config struct\n * @custom:access Only Governance\n * @custom:error NotNullAddress error is thrown if asset address is null\n * @custom:error NotNullAddress error is thrown if token feed address is null\n * @custom:error Range error is thrown if maxStale period of token is not greater than zero\n * @custom:event Emits TokenConfigAdded event on successfully setting of the token config\n */\n function setTokenConfig(\n TokenConfig memory tokenConfig\n ) public notNullAddress(tokenConfig.asset) notNullAddress(tokenConfig.feed) {\n _checkAccessAllowed(\"setTokenConfig(TokenConfig)\");\n\n if (tokenConfig.maxStalePeriod == 0) revert(\"stale period can't be zero\");\n tokenConfigs[tokenConfig.asset] = tokenConfig;\n emit TokenConfigAdded(tokenConfig.asset, tokenConfig.feed, tokenConfig.maxStalePeriod);\n }\n\n /**\n * @notice Gets the price of a asset from the chainlink oracle\n * @param asset Address of the asset\n * @return Price in USD from Chainlink or a manually set price for the asset\n */\n function getPrice(address asset) public view virtual returns (uint256) {\n uint256 decimals;\n\n if (asset == NATIVE_TOKEN_ADDR) {\n decimals = 18;\n } else {\n IERC20Metadata token = IERC20Metadata(asset);\n decimals = token.decimals();\n }\n\n return _getPriceInternal(asset, decimals);\n }\n\n /**\n * @notice Gets the Chainlink price for a given asset\n * @param asset address of the asset\n * @param decimals decimals of the asset\n * @return price Asset price in USD or a manually set price of the asset\n */\n function _getPriceInternal(address asset, uint256 decimals) internal view returns (uint256 price) {\n uint256 tokenPrice = prices[asset];\n if (tokenPrice != 0) {\n price = tokenPrice;\n } else {\n price = _getChainlinkPrice(asset);\n }\n\n uint256 decimalDelta = 18 - decimals;\n return price * (10 ** decimalDelta);\n }\n\n /**\n * @notice Get the Chainlink price for an asset, revert if token config doesn't exist\n * @dev The precision of the price feed is used to ensure the returned price has 18 decimals of precision\n * @param asset Address of the asset\n * @return price Price in USD, with 18 decimals of precision\n * @custom:error NotNullAddress error is thrown if the asset address is null\n * @custom:error Price error is thrown if the Chainlink price of asset is not greater than zero\n * @custom:error Timing error is thrown if current timestamp is less than the last updatedAt timestamp\n * @custom:error Timing error is thrown if time difference between current time and last updated time\n * is greater than maxStalePeriod\n */\n function _getChainlinkPrice(\n address asset\n ) private view notNullAddress(tokenConfigs[asset].asset) returns (uint256) {\n TokenConfig memory tokenConfig = tokenConfigs[asset];\n AggregatorV3Interface feed = AggregatorV3Interface(tokenConfig.feed);\n\n // note: maxStalePeriod cannot be 0\n uint256 maxStalePeriod = tokenConfig.maxStalePeriod;\n\n // Chainlink USD-denominated feeds store answers at 8 decimals, mostly\n uint256 decimalDelta = 18 - feed.decimals();\n\n (, int256 answer, , uint256 updatedAt, ) = feed.latestRoundData();\n if (answer <= 0) revert(\"chainlink price must be positive\");\n if (block.timestamp < updatedAt) revert(\"updatedAt exceeds block time\");\n\n uint256 deltaTime;\n unchecked {\n deltaTime = block.timestamp - updatedAt;\n }\n\n if (deltaTime > maxStalePeriod) revert(\"chainlink price expired\");\n\n return uint256(answer) * (10 ** decimalDelta);\n }\n}\n" + }, + "@venusprotocol/protocol-reserve/contracts/Interfaces/IComptroller.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity ^0.8.25;\n\ninterface IComptroller {\n function isComptroller() external view returns (bool);\n\n function markets(address) external view returns (bool);\n\n function getAllMarkets() external view returns (address[] memory);\n}\n" + }, + "@venusprotocol/protocol-reserve/contracts/Interfaces/IIncomeDestination.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity ^0.8.25;\n\ninterface IIncomeDestination {\n function updateAssetsState(address comptroller, address asset) external;\n}\n" + }, + "@venusprotocol/protocol-reserve/contracts/Interfaces/IPoolRegistry.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity ^0.8.25;\n\ninterface IPoolRegistry {\n /// @notice Get VToken in the Pool for an Asset\n function getVTokenForAsset(address comptroller, address asset) external view returns (address);\n\n /// @notice Get the addresss of the Pools supported that include a market for the provided asset\n function getPoolsSupportedByAsset(address asset) external view returns (address[] memory);\n}\n" + }, + "@venusprotocol/protocol-reserve/contracts/Interfaces/IProtocolShareReserve.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity ^0.8.25;\n\ninterface IProtocolShareReserve {\n /// @notice it represents the type of vToken income\n enum IncomeType {\n SPREAD,\n LIQUIDATION,\n ERC4626_WRAPPER_REWARDS,\n FLASHLOAN\n }\n\n function updateAssetsState(\n address comptroller,\n address asset,\n IncomeType incomeType\n ) external;\n}\n" + }, + "@venusprotocol/protocol-reserve/contracts/Interfaces/IVToken.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity ^0.8.25;\n\ninterface IVToken {\n function underlying() external view returns (address);\n}\n" + }, + "@venusprotocol/protocol-reserve/contracts/ProtocolReserve/ProtocolShareReserve.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\n\nimport { SafeERC20Upgradeable, IERC20Upgradeable } from \"@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol\";\nimport { AccessControlledV8 } from \"@venusprotocol/governance-contracts/contracts/Governance/AccessControlledV8.sol\";\nimport { ReentrancyGuardUpgradeable } from \"@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol\";\nimport { MaxLoopsLimitHelper } from \"@venusprotocol/solidity-utilities/contracts/MaxLoopsLimitHelper.sol\";\nimport { ensureNonzeroAddress } from \"@venusprotocol/solidity-utilities/contracts/validators.sol\";\n\nimport { IProtocolShareReserve } from \"../Interfaces/IProtocolShareReserve.sol\";\nimport { IComptroller } from \"../Interfaces/IComptroller.sol\";\nimport { IPoolRegistry } from \"../Interfaces/IPoolRegistry.sol\";\nimport { IVToken } from \"../Interfaces/IVToken.sol\";\nimport { IIncomeDestination } from \"../Interfaces/IIncomeDestination.sol\";\n\nerror InvalidAddress();\nerror UnsupportedAsset();\nerror InvalidTotalPercentage();\nerror InvalidMaxLoopsLimit();\n\ncontract ProtocolShareReserve is\n AccessControlledV8,\n ReentrancyGuardUpgradeable,\n MaxLoopsLimitHelper,\n IProtocolShareReserve\n{\n using SafeERC20Upgradeable for IERC20Upgradeable;\n\n /// @notice protocol income is categorized into two schemas.\n /// The first schema is for spread income\n /// The second schema is for liquidation income\n enum Schema {\n PROTOCOL_RESERVES,\n ADDITIONAL_REVENUE\n }\n\n struct DistributionConfig {\n Schema schema;\n /// @dev percenatge is represented without any scale\n uint16 percentage;\n address destination;\n }\n\n /// @notice address of core pool comptroller contract\n /// @custom:oz-upgrades-unsafe-allow state-variable-immutable\n address public immutable CORE_POOL_COMPTROLLER;\n\n /// @notice address of WBNB contract\n /// @custom:oz-upgrades-unsafe-allow state-variable-immutable\n address public immutable WBNB;\n\n /// @notice address of vBNB contract\n /// @custom:oz-upgrades-unsafe-allow state-variable-immutable\n address public immutable vBNB;\n\n /// @notice address of pool registry contract\n address public poolRegistry;\n\n uint16 public constant MAX_PERCENT = 1e4;\n\n /// @notice comptroller => asset => schema => balance\n mapping(address => mapping(address => mapping(Schema => uint256))) public assetsReserves;\n\n /// @notice asset => balance\n mapping(address => uint256) public totalAssetReserve;\n\n /// @notice configuration for different income distribution targets\n DistributionConfig[] public distributionTargets;\n\n /// @notice Emitted when pool registry address is updated\n event PoolRegistryUpdated(address indexed oldPoolRegistry, address indexed newPoolRegistry);\n\n /// @notice Event emitted after updating of the assets reserves.\n event AssetsReservesUpdated(\n address indexed comptroller,\n address indexed asset,\n uint256 amount,\n IncomeType incomeType,\n Schema schema\n );\n\n /// @notice Event emitted when an asset is released to a target\n event AssetReleased(\n address indexed destination,\n address indexed asset,\n Schema schema,\n uint256 percent,\n uint256 amount\n );\n\n /// @notice Event emitted when asset reserves state is updated\n event ReservesUpdated(\n address indexed comptroller,\n address indexed asset,\n Schema schema,\n uint256 oldBalance,\n uint256 newBalance\n );\n\n /// @notice Event emitted when distribution configuration is updated\n event DistributionConfigUpdated(\n address indexed destination,\n uint16 oldPercentage,\n uint16 newPercentage,\n Schema schema\n );\n\n /// @notice Event emitted when distribution configuration is added\n event DistributionConfigAdded(address indexed destination, uint16 percentage, Schema schema);\n\n /// @notice Event emitted when distribution configuration is removed\n event DistributionConfigRemoved(address indexed destination, uint16 percentage, Schema schema);\n\n /**\n * @dev Constructor to initialize the immutable variables\n * @param _corePoolComptroller The address of core pool comptroller\n * @param _wbnb The address of WBNB\n * @param _vbnb The address of vBNB\n */\n /// @custom:oz-upgrades-unsafe-allow constructor\n constructor(\n address _corePoolComptroller,\n address _wbnb,\n address _vbnb\n ) {\n ensureNonzeroAddress(_corePoolComptroller);\n ensureNonzeroAddress(_wbnb);\n ensureNonzeroAddress(_vbnb);\n\n CORE_POOL_COMPTROLLER = _corePoolComptroller;\n WBNB = _wbnb;\n vBNB = _vbnb;\n\n // Note that the contract is upgradeable. Use initialize() or reinitializers\n // to set the state variables.\n _disableInitializers();\n }\n\n /**\n * @dev Initializes the deployer to owner.\n * @param _accessControlManager The address of ACM contract\n * @param _loopsLimit Limit for the loops in the contract to avoid DOS\n */\n function initialize(address _accessControlManager, uint256 _loopsLimit) external initializer {\n __AccessControlled_init(_accessControlManager);\n __ReentrancyGuard_init();\n _setMaxLoopsLimit(_loopsLimit);\n }\n\n /**\n * @dev Pool registry setter.\n * @param _poolRegistry Address of the pool registry\n * @custom:error ZeroAddressNotAllowed is thrown when pool registry address is zero\n */\n function setPoolRegistry(address _poolRegistry) external onlyOwner {\n ensureNonzeroAddress(_poolRegistry);\n emit PoolRegistryUpdated(poolRegistry, _poolRegistry);\n poolRegistry = _poolRegistry;\n }\n\n /**\n * @dev Add or update destination targets based on destination address\n * @param configs configurations of the destinations.\n */\n function addOrUpdateDistributionConfigs(DistributionConfig[] calldata configs) external nonReentrant {\n _checkAccessAllowed(\"addOrUpdateDistributionConfigs(DistributionConfig[])\");\n\n for (uint256 i = 0; i < configs.length; ) {\n DistributionConfig memory _config = configs[i];\n ensureNonzeroAddress(_config.destination);\n\n bool updated = false;\n uint256 distributionTargetsLength = distributionTargets.length;\n for (uint256 j = 0; j < distributionTargetsLength; ) {\n DistributionConfig storage config = distributionTargets[j];\n\n if (_config.schema == config.schema && config.destination == _config.destination) {\n emit DistributionConfigUpdated(\n _config.destination,\n config.percentage,\n _config.percentage,\n _config.schema\n );\n config.percentage = _config.percentage;\n updated = true;\n break;\n }\n\n unchecked {\n ++j;\n }\n }\n\n if (!updated) {\n distributionTargets.push(_config);\n emit DistributionConfigAdded(_config.destination, _config.percentage, _config.schema);\n }\n\n unchecked {\n ++i;\n }\n }\n\n _ensurePercentages();\n _ensureMaxLoops(distributionTargets.length);\n }\n\n /**\n * @dev Remove destionation target if percentage is 0\n * @param schema schema of the configuration\n * @param destination destination address of the configuration\n */\n function removeDistributionConfig(Schema schema, address destination) external {\n _checkAccessAllowed(\"removeDistributionConfig(Schema,address)\");\n\n uint256 distributionIndex;\n bool found = false;\n for (uint256 i = 0; i < distributionTargets.length; ) {\n DistributionConfig storage config = distributionTargets[i];\n\n if (schema == config.schema && destination == config.destination && config.percentage == 0) {\n found = true;\n distributionIndex = i;\n break;\n }\n\n unchecked {\n ++i;\n }\n }\n\n if (found) {\n emit DistributionConfigRemoved(\n distributionTargets[distributionIndex].destination,\n distributionTargets[distributionIndex].percentage,\n distributionTargets[distributionIndex].schema\n );\n\n distributionTargets[distributionIndex] = distributionTargets[distributionTargets.length - 1];\n distributionTargets.pop();\n }\n\n _ensurePercentages();\n }\n\n /**\n * @dev Release funds\n * @param comptroller the comptroller address of the pool\n * @param assets assets to be released to distribution targets\n */\n function releaseFunds(address comptroller, address[] calldata assets) external nonReentrant {\n for (uint256 i = 0; i < assets.length; ) {\n _releaseFund(comptroller, assets[i]);\n\n unchecked {\n ++i;\n }\n }\n }\n\n /**\n * @dev Used to find out the amount of funds that's going to be released when release funds is called.\n * @param comptroller the comptroller address of the pool\n * @param schema the schema of the distribution target\n * @param destination the destination address of the distribution target\n * @param asset the asset address which will be released\n */\n function getUnreleasedFunds(\n address comptroller,\n Schema schema,\n address destination,\n address asset\n ) external view returns (uint256) {\n uint256 distributionTargetsLength = distributionTargets.length;\n for (uint256 i = 0; i < distributionTargetsLength; ) {\n DistributionConfig storage _config = distributionTargets[i];\n if (_config.schema == schema && _config.destination == destination) {\n uint256 total = assetsReserves[comptroller][asset][schema];\n return (total * _config.percentage) / MAX_PERCENT;\n }\n\n unchecked {\n ++i;\n }\n }\n }\n\n /**\n * @dev Returns the total number of distribution targets\n */\n function totalDistributions() external view returns (uint256) {\n return distributionTargets.length;\n }\n\n /**\n * @dev Used to find out the percentage distribution for a particular destination based on schema\n * @param destination the destination address of the distribution target\n * @param schema the schema of the distribution target\n * @return percentage percentage distribution\n */\n function getPercentageDistribution(address destination, Schema schema) external view returns (uint256) {\n uint256 distributionTargetsLength = distributionTargets.length;\n for (uint256 i = 0; i < distributionTargetsLength; ) {\n DistributionConfig memory config = distributionTargets[i];\n\n if (config.destination == destination && config.schema == schema) {\n return config.percentage;\n }\n\n unchecked {\n ++i;\n }\n }\n }\n\n /**\n * @dev Update the reserve of the asset for the specific pool after transferring to the protocol share reserve.\n * @param comptroller Comptroller address (pool)\n * @param asset Asset address.\n * @param incomeType type of income\n */\n function updateAssetsState(\n address comptroller,\n address asset,\n IncomeType incomeType\n ) public override(IProtocolShareReserve) nonReentrant {\n if (!IComptroller(comptroller).isComptroller()) revert InvalidAddress();\n ensureNonzeroAddress(asset);\n\n if (\n comptroller != CORE_POOL_COMPTROLLER &&\n IPoolRegistry(poolRegistry).getVTokenForAsset(comptroller, asset) == address(0)\n ) revert InvalidAddress();\n\n Schema schema = _getSchema(incomeType);\n uint256 currentBalance = IERC20Upgradeable(asset).balanceOf(address(this));\n uint256 assetReserve = totalAssetReserve[asset];\n\n if (currentBalance > assetReserve) {\n uint256 balanceDifference;\n unchecked {\n balanceDifference = currentBalance - assetReserve;\n }\n\n assetsReserves[comptroller][asset][schema] += balanceDifference;\n totalAssetReserve[asset] += balanceDifference;\n emit AssetsReservesUpdated(comptroller, asset, balanceDifference, incomeType, schema);\n }\n }\n\n /**\n * @dev asset from a particular pool to be release to distribution targets\n * @param comptroller Comptroller address(pool)\n * @param asset Asset address.\n */\n function _releaseFund(address comptroller, address asset) internal {\n uint256 totalSchemas = uint256(type(Schema).max) + 1;\n uint256[] memory schemaBalances = new uint256[](totalSchemas);\n uint256 totalBalance;\n for (uint256 schemaValue; schemaValue < totalSchemas; ) {\n schemaBalances[schemaValue] = assetsReserves[comptroller][asset][Schema(schemaValue)];\n totalBalance += schemaBalances[schemaValue];\n\n unchecked {\n ++schemaValue;\n }\n }\n\n if (totalBalance == 0) {\n return;\n }\n\n uint256[] memory totalTransferAmounts = new uint256[](totalSchemas);\n for (uint256 i = 0; i < distributionTargets.length; ) {\n DistributionConfig memory _config = distributionTargets[i];\n\n uint256 transferAmount = (schemaBalances[uint256(_config.schema)] * _config.percentage) / MAX_PERCENT;\n totalTransferAmounts[uint256(_config.schema)] += transferAmount;\n\n if (transferAmount != 0) {\n IERC20Upgradeable(asset).safeTransfer(_config.destination, transferAmount);\n IIncomeDestination(_config.destination).updateAssetsState(comptroller, asset);\n\n emit AssetReleased(_config.destination, asset, _config.schema, _config.percentage, transferAmount);\n }\n\n unchecked {\n ++i;\n }\n }\n\n uint256[] memory newSchemaBalances = new uint256[](totalSchemas);\n for (uint256 schemaValue = 0; schemaValue < totalSchemas; ) {\n newSchemaBalances[schemaValue] = schemaBalances[schemaValue] - totalTransferAmounts[schemaValue];\n assetsReserves[comptroller][asset][Schema(schemaValue)] = newSchemaBalances[schemaValue];\n totalAssetReserve[asset] = totalAssetReserve[asset] - totalTransferAmounts[schemaValue];\n\n emit ReservesUpdated(\n comptroller,\n asset,\n Schema(schemaValue),\n schemaBalances[schemaValue],\n newSchemaBalances[schemaValue]\n );\n\n unchecked {\n ++schemaValue;\n }\n }\n }\n\n /**\n * @dev Returns the schema based on income type\n * @param incomeType type of income\n * @return schema schema for distribution\n */\n function _getSchema(IncomeType incomeType) internal view returns (Schema schema) {\n schema = Schema.ADDITIONAL_REVENUE;\n\n if (incomeType == IncomeType.SPREAD) {\n schema = Schema.PROTOCOL_RESERVES;\n }\n }\n\n /**\n * @dev This ensures that the total percentage of all the distribution targets is 100% or 0%\n */\n function _ensurePercentages() internal view {\n uint256 totalSchemas = uint256(type(Schema).max) + 1;\n uint16[] memory totalPercentages = new uint16[](totalSchemas);\n\n uint256 distributionTargetsLength = distributionTargets.length;\n for (uint256 i = 0; i < distributionTargetsLength; ) {\n DistributionConfig memory config = distributionTargets[i];\n totalPercentages[uint256(config.schema)] += config.percentage;\n\n unchecked {\n ++i;\n }\n }\n for (uint256 schemaValue = 0; schemaValue < totalSchemas; ) {\n if (totalPercentages[schemaValue] != MAX_PERCENT && totalPercentages[schemaValue] != 0)\n revert InvalidTotalPercentage();\n\n unchecked {\n ++schemaValue;\n }\n }\n }\n\n /**\n * @dev Returns the underlying asset address for the vToken\n * @param vToken vToken address\n * @return asset address of asset\n */\n function _getUnderlying(address vToken) internal view returns (address) {\n if (vToken == vBNB) {\n return WBNB;\n } else {\n return IVToken(vToken).underlying();\n }\n }\n}\n" + }, + "@venusprotocol/solidity-utilities/contracts/constants.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity ^0.8.25;\n\n/// @dev Base unit for computations, usually used in scaling (multiplications, divisions)\nuint256 constant EXP_SCALE = 1e18;\n\n/// @dev A unit (literal one) in EXP_SCALE, usually used in additions/subtractions\nuint256 constant MANTISSA_ONE = EXP_SCALE;\n\n/// @dev The approximate number of seconds per year\nuint256 constant SECONDS_PER_YEAR = 31_536_000;\n" + }, + "@venusprotocol/solidity-utilities/contracts/MaxLoopsLimitHelper.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\n\n/**\n * @title MaxLoopsLimitHelper\n * @author Venus\n * @notice Abstract contract used to avoid collection with too many items that would generate gas errors and DoS.\n */\nabstract contract MaxLoopsLimitHelper {\n // Limit for the loops to avoid the DOS\n uint256 public maxLoopsLimit;\n\n /**\n * @dev This empty reserved space is put in place to allow future versions to add new\n * variables without shifting down storage in the inheritance chain.\n * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\n */\n uint256[49] private __gap;\n\n /// @notice Emitted when max loops limit is set\n event MaxLoopsLimitUpdated(uint256 oldMaxLoopsLimit, uint256 newmaxLoopsLimit);\n\n /// @notice Thrown an error on maxLoopsLimit exceeds for any loop\n error MaxLoopsLimitExceeded(uint256 loopsLimit, uint256 requiredLoops);\n\n /**\n * @notice Set the limit for the loops can iterate to avoid the DOS\n * @param limit Limit for the max loops can execute at a time\n */\n function _setMaxLoopsLimit(uint256 limit) internal {\n require(limit > maxLoopsLimit, \"Comptroller: Invalid maxLoopsLimit\");\n\n uint256 oldMaxLoopsLimit = maxLoopsLimit;\n maxLoopsLimit = limit;\n\n emit MaxLoopsLimitUpdated(oldMaxLoopsLimit, limit);\n }\n\n /**\n * @notice Compare the maxLoopsLimit with number of the times loop iterate\n * @param len Length of the loops iterate\n * @custom:error MaxLoopsLimitExceeded error is thrown when loops length exceeds maxLoopsLimit\n */\n function _ensureMaxLoops(uint256 len) internal view {\n if (len > maxLoopsLimit) {\n revert MaxLoopsLimitExceeded(maxLoopsLimit, len);\n }\n }\n}\n" + }, + "@venusprotocol/solidity-utilities/contracts/TimeManagerV8.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\n\nimport { SECONDS_PER_YEAR } from \"./constants.sol\";\n\nabstract contract TimeManagerV8 {\n /// @notice Stores blocksPerYear if isTimeBased is true else secondsPerYear is stored\n /// @custom:oz-upgrades-unsafe-allow state-variable-immutable\n uint256 public immutable blocksOrSecondsPerYear;\n\n /// @notice Acknowledges if a contract is time based or not\n /// @custom:oz-upgrades-unsafe-allow state-variable-immutable\n bool public immutable isTimeBased;\n\n /// @notice Stores the current block timestamp or block number depending on isTimeBased\n /// @custom:oz-upgrades-unsafe-allow state-variable-immutable\n function() view returns (uint256) private immutable _getCurrentSlot;\n\n /**\n * @dev This empty reserved space is put in place to allow future versions to add new\n * variables without shifting down storage in the inheritance chain\n * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\n */\n uint256[48] private __gap;\n\n /// @notice Thrown when blocks per year is invalid\n error InvalidBlocksPerYear();\n\n /// @notice Thrown when time based but blocks per year is provided\n error InvalidTimeBasedConfiguration();\n\n /**\n * @param timeBased_ A boolean indicating whether the contract is based on time or block\n * If timeBased is true than blocksPerYear_ param is ignored as blocksOrSecondsPerYear is set to SECONDS_PER_YEAR\n * @param blocksPerYear_ The number of blocks per year\n * @custom:error InvalidBlocksPerYear is thrown if blocksPerYear entered is zero and timeBased is false\n * @custom:error InvalidTimeBasedConfiguration is thrown if blocksPerYear entered is non zero and timeBased is true\n * @custom:oz-upgrades-unsafe-allow constructor\n */\n constructor(bool timeBased_, uint256 blocksPerYear_) {\n if (!timeBased_ && blocksPerYear_ == 0) {\n revert InvalidBlocksPerYear();\n }\n\n if (timeBased_ && blocksPerYear_ != 0) {\n revert InvalidTimeBasedConfiguration();\n }\n\n isTimeBased = timeBased_;\n blocksOrSecondsPerYear = timeBased_ ? SECONDS_PER_YEAR : blocksPerYear_;\n _getCurrentSlot = timeBased_ ? _getBlockTimestamp : _getBlockNumber;\n }\n\n /**\n * @dev Function to simply retrieve block number or block timestamp\n * @return Current block number or block timestamp\n */\n function getBlockNumberOrTimestamp() public view virtual returns (uint256) {\n return _getCurrentSlot();\n }\n\n /**\n * @dev Returns the current timestamp in seconds\n * @return The current timestamp\n */\n function _getBlockTimestamp() private view returns (uint256) {\n return block.timestamp;\n }\n\n /**\n * @dev Returns the current block number\n * @return The current block number\n */\n function _getBlockNumber() private view returns (uint256) {\n return block.number;\n }\n}\n" + }, + "@venusprotocol/solidity-utilities/contracts/validators.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\n\n/// @notice Thrown if the supplied address is a zero address where it is not allowed\nerror ZeroAddressNotAllowed();\n\n/// @notice Thrown if the supplied value is 0 where it is not allowed\nerror ZeroValueNotAllowed();\n\n/// @notice Checks if the provided address is nonzero, reverts otherwise\n/// @param address_ Address to check\n/// @custom:error ZeroAddressNotAllowed is thrown if the provided address is a zero address\nfunction ensureNonzeroAddress(address address_) pure {\n if (address_ == address(0)) {\n revert ZeroAddressNotAllowed();\n }\n}\n\n/// @notice Checks if the provided value is nonzero, reverts otherwise\n/// @param value_ Value to check\n/// @custom:error ZeroValueNotAllowed is thrown if the provided value is 0\nfunction ensureNonzeroValue(uint256 value_) pure {\n if (value_ == 0) {\n revert ZeroValueNotAllowed();\n }\n}\n" + }, + "@venusprotocol/venus-protocol/contracts/Tokens/Prime/Interfaces/InterfaceComptroller.sol": { + "content": "// SPDX-License-Identifier: GPL-3.0-or-later\npragma solidity ^0.8.25;\n\ninterface InterfaceComptroller {\n function markets(address) external view returns (bool);\n}\n" + }, + "@venusprotocol/venus-protocol/contracts/Tokens/Prime/Interfaces/IPoolRegistry.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity ^0.8.25;\n\n/**\n * @title PoolRegistryInterface\n * @author Venus\n * @notice Interface implemented by `PoolRegistry`.\n */\ninterface PoolRegistryInterface {\n /**\n * @notice Struct for a Venus interest rate pool.\n */\n struct VenusPool {\n string name;\n address creator;\n address comptroller;\n uint256 blockPosted;\n uint256 timestampPosted;\n }\n\n /// @notice Get a pool by comptroller address\n function getPoolByComptroller(address comptroller) external view returns (VenusPool memory);\n}\n" + }, + "@venusprotocol/venus-protocol/contracts/Tokens/Prime/Interfaces/IPrime.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity ^0.8.25;\n\nimport { PrimeStorageV1 } from \"../PrimeStorage.sol\";\n\n/**\n * @title IPrime\n * @author Venus\n * @notice Interface for Prime Token\n */\ninterface IPrime {\n struct APRInfo {\n // supply APR of the user in BPS\n uint256 supplyAPR;\n // borrow APR of the user in BPS\n uint256 borrowAPR;\n // total score of the market\n uint256 totalScore;\n // score of the user\n uint256 userScore;\n // capped XVS balance of the user\n uint256 xvsBalanceForScore;\n // capital of the user\n uint256 capital;\n // capped supply of the user\n uint256 cappedSupply;\n // capped borrow of the user\n uint256 cappedBorrow;\n // capped supply of user in USD\n uint256 supplyCapUSD;\n // capped borrow of user in USD\n uint256 borrowCapUSD;\n }\n\n struct Capital {\n // capital of the user\n uint256 capital;\n // capped supply of the user\n uint256 cappedSupply;\n // capped borrow of the user\n uint256 cappedBorrow;\n // capped supply of user in USD\n uint256 supplyCapUSD;\n // capped borrow of user in USD\n uint256 borrowCapUSD;\n }\n\n /**\n * @notice Returns boosted pending interest accrued for a user for all markets\n * @param user the account for which to get the accrued interests\n * @return pendingRewards the number of underlying tokens accrued by the user for all markets\n */\n function getPendingRewards(address user) external returns (PrimeStorageV1.PendingReward[] memory pendingRewards);\n\n /**\n * @notice Update total score of multiple users and market\n * @param users accounts for which we need to update score\n */\n function updateScores(address[] memory users) external;\n\n /**\n * @notice Update value of alpha\n * @param _alphaNumerator numerator of alpha. If alpha is 0.5 then numerator is 1\n * @param _alphaDenominator denominator of alpha. If alpha is 0.5 then denominator is 2\n */\n function updateAlpha(uint128 _alphaNumerator, uint128 _alphaDenominator) external;\n\n /**\n * @notice Update multipliers for a market\n * @param market address of the market vToken\n * @param supplyMultiplier new supply multiplier for the market, scaled by 1e18\n * @param borrowMultiplier new borrow multiplier for the market, scaled by 1e18\n */\n function updateMultipliers(address market, uint256 supplyMultiplier, uint256 borrowMultiplier) external;\n\n /**\n * @notice Add a market to prime program\n * @param comptroller address of the comptroller\n * @param market address of the market vToken\n * @param supplyMultiplier the multiplier for supply cap. It should be converted to 1e18\n * @param borrowMultiplier the multiplier for borrow cap. It should be converted to 1e18\n */\n function addMarket(\n address comptroller,\n address market,\n uint256 supplyMultiplier,\n uint256 borrowMultiplier\n ) external;\n\n /**\n * @notice Set limits for total tokens that can be minted\n * @param _irrevocableLimit total number of irrevocable tokens that can be minted\n * @param _revocableLimit total number of revocable tokens that can be minted\n */\n function setLimit(uint256 _irrevocableLimit, uint256 _revocableLimit) external;\n\n /**\n * @notice Directly issue prime tokens to users\n * @param isIrrevocable are the tokens being issued\n * @param users list of address to issue tokens to\n */\n function issue(bool isIrrevocable, address[] calldata users) external;\n\n /**\n * @notice Executed by XVSVault whenever user's XVSVault balance changes\n * @param user the account address whose balance was updated\n */\n function xvsUpdated(address user) external;\n\n /**\n * @notice accrues interest and updates score for an user for a specific market\n * @param user the account address for which to accrue interest and update score\n * @param market the market for which to accrue interest and update score\n */\n function accrueInterestAndUpdateScore(address user, address market) external;\n\n /**\n * @notice For claiming prime token when staking period is completed\n */\n function claim() external;\n\n /**\n * @notice For burning any prime token\n * @param user the account address for which the prime token will be burned\n */\n function burn(address user) external;\n\n /**\n * @notice To pause or unpause claiming of interest\n */\n function togglePause() external;\n\n /**\n * @notice For user to claim boosted yield\n * @param vToken the market for which claim the accrued interest\n * @return amount the amount of tokens transferred to the user\n */\n function claimInterest(address vToken) external returns (uint256);\n\n /**\n * @notice For user to claim boosted yield\n * @param vToken the market for which claim the accrued interest\n * @param user the user for which to claim the accrued interest\n * @return amount the amount of tokens transferred to the user\n */\n function claimInterest(address vToken, address user) external returns (uint256);\n\n /**\n * @notice Distributes income from market since last distribution\n * @param vToken the market for which to distribute the income\n */\n function accrueInterest(address vToken) external;\n\n /**\n * @notice Returns boosted interest accrued for a user\n * @param vToken the market for which to fetch the accrued interest\n * @param user the account for which to get the accrued interest\n * @return interestAccrued the number of underlying tokens accrued by the user since the last accrual\n */\n function getInterestAccrued(address vToken, address user) external returns (uint256);\n\n /**\n * @notice Retrieves an array of all available markets\n * @return an array of addresses representing all available markets\n */\n function getAllMarkets() external view returns (address[] memory);\n\n /**\n * @notice fetch the numbers of seconds remaining for staking period to complete\n * @param user the account address for which we are checking the remaining time\n * @return timeRemaining the number of seconds the user needs to wait to claim prime token\n */\n function claimTimeRemaining(address user) external view returns (uint256);\n\n /**\n * @notice Returns supply and borrow APR for user for a given market\n * @param market the market for which to fetch the APR\n * @param user the account for which to get the APR\n * @return aprInfo APR information for the user for the given market\n */\n function calculateAPR(address market, address user) external view returns (APRInfo memory aprInfo);\n\n /**\n * @notice Returns supply and borrow APR for estimated supply, borrow and XVS staked\n * @param market the market for which to fetch the APR\n * @param user the account for which to get the APR\n * @param borrow hypothetical borrow amount\n * @param supply hypothetical supply amount\n * @param xvsStaked hypothetical staked XVS amount\n * @return aprInfo APR information for the user for the given market\n */\n function estimateAPR(\n address market,\n address user,\n uint256 borrow,\n uint256 supply,\n uint256 xvsStaked\n ) external view returns (APRInfo memory aprInfo);\n\n /**\n * @notice the total income that's going to be distributed in a year to prime token holders\n * @param vToken the market for which to fetch the total income that's going to distributed in a year\n * @return amount the total income\n */\n function incomeDistributionYearly(address vToken) external view returns (uint256 amount);\n\n /**\n * @notice Returns if user is a prime holder\n * @return isPrimeHolder true if user is a prime holder\n */\n function isUserPrimeHolder(address user) external view returns (bool);\n\n /**\n * @notice Set the limit for the loops can iterate to avoid the DOS\n * @param loopsLimit Number of loops limit\n */\n function setMaxLoopsLimit(uint256 loopsLimit) external;\n\n /**\n * @notice Update staked at timestamp for multiple users\n * @param users accounts for which we need to update staked at timestamp\n * @param timestamps new staked at timestamp for the users\n */\n function setStakedAt(address[] calldata users, uint256[] calldata timestamps) external;\n}\n" + }, + "@venusprotocol/venus-protocol/contracts/Tokens/Prime/Interfaces/IPrimeLiquidityProvider.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity ^0.8.25;\n\nimport { IERC20Upgradeable } from \"@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol\";\n\n/**\n * @title IPrimeLiquidityProvider\n * @author Venus\n * @notice Interface for PrimeLiquidityProvider\n */\ninterface IPrimeLiquidityProvider {\n /**\n * @notice Initialize the distribution of the token\n * @param tokens_ Array of addresses of the tokens to be intialized\n */\n function initializeTokens(address[] calldata tokens_) external;\n\n /**\n * @notice Pause fund transfer of tokens to Prime contract\n */\n function pauseFundsTransfer() external;\n\n /**\n * @notice Resume fund transfer of tokens to Prime contract\n */\n function resumeFundsTransfer() external;\n\n /**\n * @notice Set distribution speed (amount of token distribute per block or second)\n * @param tokens_ Array of addresses of the tokens\n * @param distributionSpeeds_ New distribution speeds for tokens\n */\n function setTokensDistributionSpeed(address[] calldata tokens_, uint256[] calldata distributionSpeeds_) external;\n\n /**\n * @notice Set max distribution speed for token (amount of maximum token distribute per block or second)\n * @param tokens_ Array of addresses of the tokens\n * @param maxDistributionSpeeds_ New distribution speeds for tokens\n */\n function setMaxTokensDistributionSpeed(\n address[] calldata tokens_,\n uint256[] calldata maxDistributionSpeeds_\n ) external;\n\n /**\n * @notice Set the prime token contract address\n * @param prime_ The new address of the prime token contract\n */\n function setPrimeToken(address prime_) external;\n\n /**\n * @notice Claim all the token accrued till last block or second\n * @param token_ The token to release to the Prime contract\n */\n function releaseFunds(address token_) external;\n\n /**\n * @notice A public function to sweep accidental ERC-20 transfers to this contract. Tokens are sent to user\n * @param token_ The address of the ERC-20 token to sweep\n * @param to_ The address of the recipient\n * @param amount_ The amount of tokens needs to transfer\n */\n function sweepToken(IERC20Upgradeable token_, address to_, uint256 amount_) external;\n\n /**\n * @notice Accrue token by updating the distribution state\n * @param token_ Address of the token\n */\n function accrueTokens(address token_) external;\n\n /**\n * @notice Set the limit for the loops can iterate to avoid the DOS\n * @param loopsLimit Limit for the max loops can execute at a time\n */\n function setMaxLoopsLimit(uint256 loopsLimit) external;\n\n /**\n * @notice Get rewards per block or second for token\n * @param token_ Address of the token\n * @return speed returns the per block or second reward\n */\n function getEffectiveDistributionSpeed(address token_) external view returns (uint256);\n\n /**\n * @notice Get the amount of tokens accrued\n * @param token_ Address of the token\n * @return Amount of tokens that are accrued\n */\n function tokenAmountAccrued(address token_) external view returns (uint256);\n}\n" + }, + "@venusprotocol/venus-protocol/contracts/Tokens/Prime/Interfaces/IVToken.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity ^0.8.25;\n\ninterface IVToken {\n function borrowBalanceStored(address account) external view returns (uint256);\n\n function exchangeRateStored() external view returns (uint256);\n\n function balanceOf(address account) external view returns (uint256);\n\n function underlying() external view returns (address);\n\n function totalBorrows() external view returns (uint256);\n\n function borrowRatePerBlock() external view returns (uint256);\n\n function reserveFactorMantissa() external view returns (uint256);\n\n function decimals() external view returns (uint8);\n}\n" + }, + "@venusprotocol/venus-protocol/contracts/Tokens/Prime/Interfaces/IXVSVault.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity ^0.8.25;\n\ninterface IXVSVault {\n function getUserInfo(\n address _rewardToken,\n uint256 _pid,\n address _user\n ) external view returns (uint256 amount, uint256 rewardDebt, uint256 pendingWithdrawals);\n\n function xvsAddress() external view returns (address);\n}\n" + }, + "@venusprotocol/venus-protocol/contracts/Tokens/Prime/libs/FixedMath.sol": { + "content": "// SPDX-License-Identifier: MIT\n// solhint-disable var-name-mixedcase\n\npragma solidity 0.8.25;\n\nimport { SafeCastUpgradeable } from \"@openzeppelin/contracts-upgradeable/utils/math/SafeCastUpgradeable.sol\";\nimport { FixedMath0x } from \"./FixedMath0x.sol\";\n\nusing SafeCastUpgradeable for uint256;\n\nerror InvalidFixedPoint();\n\n/**\n * @title FixedMath\n * @author Venus\n * @notice FixedMath library is used for complex mathematical operations\n */\nlibrary FixedMath {\n error InvalidFraction(uint256 n, uint256 d);\n\n /**\n * @notice Convert some uint256 fraction `n` numerator / `d` denominator to a fixed-point number `f`.\n * @param n numerator\n * @param d denominator\n * @return fixed-point number\n */\n function _toFixed(uint256 n, uint256 d) internal pure returns (int256) {\n if (d.toInt256() < n.toInt256()) revert InvalidFraction(n, d);\n\n return (n.toInt256() * FixedMath0x.FIXED_1) / int256(d.toInt256());\n }\n\n /**\n * @notice Divide some unsigned int `u` by a fixed point number `f`\n * @param u unsigned dividend\n * @param f fixed point divisor, in FIXED_1 units\n * @return unsigned int quotient\n */\n function _uintDiv(uint256 u, int256 f) internal pure returns (uint256) {\n if (f < 0) revert InvalidFixedPoint();\n // multiply `u` by FIXED_1 to cancel out the built-in FIXED_1 in f\n return uint256((u.toInt256() * FixedMath0x.FIXED_1) / f);\n }\n\n /**\n * @notice Multiply some unsigned int `u` by a fixed point number `f`\n * @param u unsigned multiplicand\n * @param f fixed point multiplier, in FIXED_1 units\n * @return unsigned int product\n */\n function _uintMul(uint256 u, int256 f) internal pure returns (uint256) {\n if (f < 0) revert InvalidFixedPoint();\n // divide the product by FIXED_1 to cancel out the built-in FIXED_1 in f\n return uint256((u.toInt256() * f) / FixedMath0x.FIXED_1);\n }\n\n /// @notice see FixedMath0x\n function _ln(int256 x) internal pure returns (int256) {\n return FixedMath0x._ln(x);\n }\n\n /// @notice see FixedMath0x\n function _exp(int256 x) internal pure returns (int256) {\n return FixedMath0x._exp(x);\n }\n}\n" + }, + "@venusprotocol/venus-protocol/contracts/Tokens/Prime/libs/FixedMath0x.sol": { + "content": "// SPDX-License-Identifier: MIT\n// solhint-disable max-line-length\n\npragma solidity 0.8.25;\n\n// Below is code from 0x's LibFixedMath.sol. Changes:\n// - addition of 0.8-style errors\n// - removal of unused functions\n// - added comments for clarity\n// https://github.com/0xProject/exchange-v3/blob/aae46bef841bfd1cc31028f41793db4fe7197084/contracts/staking/contracts/src/libs/LibFixedMath.sol\n\n/*\n\n Copyright 2017 Bprotocol Foundation, 2019 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n/// Thrown when the natural log function is given too large of an argument\nerror LnTooLarge(int256 x);\n/// Thrown when the natural log would have returned a number outside of ℝ\nerror LnNonRealResult(int256 x);\n/// Thrown when exp is given too large of an argument\nerror ExpTooLarge(int256 x);\n/// Thrown when an unsigned value is too large to be converted to a signed value\nerror UnsignedValueTooLarge(uint256 x);\n\n/**\n * @title FixedMath0x\n * @notice Signed, fixed-point, 127-bit precision math library\n */\nlibrary FixedMath0x {\n // Base for the fixed point numbers (this is our 1)\n int256 internal constant FIXED_1 = int256(0x0000000000000000000000000000000080000000000000000000000000000000);\n // Maximum ln argument (1)\n int256 private constant LN_MAX_VAL = FIXED_1;\n // Minimum ln argument. Notice this is related to EXP_MIN_VAL (e ^ -63.875)\n int256 private constant LN_MIN_VAL = int256(0x0000000000000000000000000000000000000000000000000000000733048c5a);\n // Maximum exp argument (0)\n int256 private constant EXP_MAX_VAL = 0;\n // Minimum exp argument. Notice this is related to LN_MIN_VAL (-63.875)\n int256 private constant EXP_MIN_VAL = -int256(0x0000000000000000000000000000001ff0000000000000000000000000000000);\n\n /// @dev Get the natural logarithm of a fixed-point number 0 < `x` <= LN_MAX_VAL\n function _ln(int256 x) internal pure returns (int256 r) {\n if (x > LN_MAX_VAL) {\n revert LnTooLarge(x);\n }\n if (x <= 0) {\n revert LnNonRealResult(x);\n }\n if (x == FIXED_1) {\n return 0;\n }\n if (x <= LN_MIN_VAL) {\n return EXP_MIN_VAL;\n }\n\n int256 y;\n int256 z;\n int256 w;\n\n // Rewrite the input as a quotient of negative natural exponents and a single residual q, such that 1 < q < 2\n // For example: log(0.3) = log(e^-1 * e^-0.25 * 1.0471028872385522)\n // = 1 - 0.25 - log(1 + 0.0471028872385522)\n // e ^ -32\n if (x <= int256(0x00000000000000000000000000000000000000000001c8464f76164760000000)) {\n r -= int256(0x0000000000000000000000000000001000000000000000000000000000000000); // - 32\n x = (x * FIXED_1) / int256(0x00000000000000000000000000000000000000000001c8464f76164760000000); // / e ^ -32\n }\n // e ^ -16\n if (x <= int256(0x00000000000000000000000000000000000000f1aaddd7742e90000000000000)) {\n r -= int256(0x0000000000000000000000000000000800000000000000000000000000000000); // - 16\n x = (x * FIXED_1) / int256(0x00000000000000000000000000000000000000f1aaddd7742e90000000000000); // / e ^ -16\n }\n // e ^ -8\n if (x <= int256(0x00000000000000000000000000000000000afe10820813d78000000000000000)) {\n r -= int256(0x0000000000000000000000000000000400000000000000000000000000000000); // - 8\n x = (x * FIXED_1) / int256(0x00000000000000000000000000000000000afe10820813d78000000000000000); // / e ^ -8\n }\n // e ^ -4\n if (x <= int256(0x0000000000000000000000000000000002582ab704279ec00000000000000000)) {\n r -= int256(0x0000000000000000000000000000000200000000000000000000000000000000); // - 4\n x = (x * FIXED_1) / int256(0x0000000000000000000000000000000002582ab704279ec00000000000000000); // / e ^ -4\n }\n // e ^ -2\n if (x <= int256(0x000000000000000000000000000000001152aaa3bf81cc000000000000000000)) {\n r -= int256(0x0000000000000000000000000000000100000000000000000000000000000000); // - 2\n x = (x * FIXED_1) / int256(0x000000000000000000000000000000001152aaa3bf81cc000000000000000000); // / e ^ -2\n }\n // e ^ -1\n if (x <= int256(0x000000000000000000000000000000002f16ac6c59de70000000000000000000)) {\n r -= int256(0x0000000000000000000000000000000080000000000000000000000000000000); // - 1\n x = (x * FIXED_1) / int256(0x000000000000000000000000000000002f16ac6c59de70000000000000000000); // / e ^ -1\n }\n // e ^ -0.5\n if (x <= int256(0x000000000000000000000000000000004da2cbf1be5828000000000000000000)) {\n r -= int256(0x0000000000000000000000000000000040000000000000000000000000000000); // - 0.5\n x = (x * FIXED_1) / int256(0x000000000000000000000000000000004da2cbf1be5828000000000000000000); // / e ^ -0.5\n }\n // e ^ -0.25\n if (x <= int256(0x0000000000000000000000000000000063afbe7ab2082c000000000000000000)) {\n r -= int256(0x0000000000000000000000000000000020000000000000000000000000000000); // - 0.25\n x = (x * FIXED_1) / int256(0x0000000000000000000000000000000063afbe7ab2082c000000000000000000); // / e ^ -0.25\n }\n // e ^ -0.125\n if (x <= int256(0x0000000000000000000000000000000070f5a893b608861e1f58934f97aea57d)) {\n r -= int256(0x0000000000000000000000000000000010000000000000000000000000000000); // - 0.125\n x = (x * FIXED_1) / int256(0x0000000000000000000000000000000070f5a893b608861e1f58934f97aea57d); // / e ^ -0.125\n }\n // `x` is now our residual in the range of 1 <= x <= 2 (or close enough).\n\n // Add the taylor series for log(1 + z), where z = x - 1\n z = y = x - FIXED_1;\n w = (y * y) / FIXED_1;\n r += (z * (0x100000000000000000000000000000000 - y)) / 0x100000000000000000000000000000000;\n z = (z * w) / FIXED_1; // add y^01 / 01 - y^02 / 02\n r += (z * (0x0aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa - y)) / 0x200000000000000000000000000000000;\n z = (z * w) / FIXED_1; // add y^03 / 03 - y^04 / 04\n r += (z * (0x099999999999999999999999999999999 - y)) / 0x300000000000000000000000000000000;\n z = (z * w) / FIXED_1; // add y^05 / 05 - y^06 / 06\n r += (z * (0x092492492492492492492492492492492 - y)) / 0x400000000000000000000000000000000;\n z = (z * w) / FIXED_1; // add y^07 / 07 - y^08 / 08\n r += (z * (0x08e38e38e38e38e38e38e38e38e38e38e - y)) / 0x500000000000000000000000000000000;\n z = (z * w) / FIXED_1; // add y^09 / 09 - y^10 / 10\n r += (z * (0x08ba2e8ba2e8ba2e8ba2e8ba2e8ba2e8b - y)) / 0x600000000000000000000000000000000;\n z = (z * w) / FIXED_1; // add y^11 / 11 - y^12 / 12\n r += (z * (0x089d89d89d89d89d89d89d89d89d89d89 - y)) / 0x700000000000000000000000000000000;\n z = (z * w) / FIXED_1; // add y^13 / 13 - y^14 / 14\n r += (z * (0x088888888888888888888888888888888 - y)) / 0x800000000000000000000000000000000; // add y^15 / 15 - y^16 / 16\n }\n\n /// @dev Compute the natural exponent for a fixed-point number EXP_MIN_VAL <= `x` <= 1\n function _exp(int256 x) internal pure returns (int256 r) {\n if (x < EXP_MIN_VAL) {\n // Saturate to zero below EXP_MIN_VAL.\n return 0;\n }\n if (x == 0) {\n return FIXED_1;\n }\n if (x > EXP_MAX_VAL) {\n revert ExpTooLarge(x);\n }\n\n // Rewrite the input as a product of natural exponents and a\n // single residual q, where q is a number of small magnitude.\n // For example: e^-34.419 = e^(-32 - 2 - 0.25 - 0.125 - 0.044)\n // = e^-32 * e^-2 * e^-0.25 * e^-0.125 * e^-0.044\n // -> q = -0.044\n\n // Multiply with the taylor series for e^q\n int256 y;\n int256 z;\n // q = x % 0.125 (the residual)\n z = y = x % 0x0000000000000000000000000000000010000000000000000000000000000000;\n z = (z * y) / FIXED_1;\n r += z * 0x10e1b3be415a0000; // add y^02 * (20! / 02!)\n z = (z * y) / FIXED_1;\n r += z * 0x05a0913f6b1e0000; // add y^03 * (20! / 03!)\n z = (z * y) / FIXED_1;\n r += z * 0x0168244fdac78000; // add y^04 * (20! / 04!)\n z = (z * y) / FIXED_1;\n r += z * 0x004807432bc18000; // add y^05 * (20! / 05!)\n z = (z * y) / FIXED_1;\n r += z * 0x000c0135dca04000; // add y^06 * (20! / 06!)\n z = (z * y) / FIXED_1;\n r += z * 0x0001b707b1cdc000; // add y^07 * (20! / 07!)\n z = (z * y) / FIXED_1;\n r += z * 0x000036e0f639b800; // add y^08 * (20! / 08!)\n z = (z * y) / FIXED_1;\n r += z * 0x00000618fee9f800; // add y^09 * (20! / 09!)\n z = (z * y) / FIXED_1;\n r += z * 0x0000009c197dcc00; // add y^10 * (20! / 10!)\n z = (z * y) / FIXED_1;\n r += z * 0x0000000e30dce400; // add y^11 * (20! / 11!)\n z = (z * y) / FIXED_1;\n r += z * 0x000000012ebd1300; // add y^12 * (20! / 12!)\n z = (z * y) / FIXED_1;\n r += z * 0x0000000017499f00; // add y^13 * (20! / 13!)\n z = (z * y) / FIXED_1;\n r += z * 0x0000000001a9d480; // add y^14 * (20! / 14!)\n z = (z * y) / FIXED_1;\n r += z * 0x00000000001c6380; // add y^15 * (20! / 15!)\n z = (z * y) / FIXED_1;\n r += z * 0x000000000001c638; // add y^16 * (20! / 16!)\n z = (z * y) / FIXED_1;\n r += z * 0x0000000000001ab8; // add y^17 * (20! / 17!)\n z = (z * y) / FIXED_1;\n r += z * 0x000000000000017c; // add y^18 * (20! / 18!)\n z = (z * y) / FIXED_1;\n r += z * 0x0000000000000014; // add y^19 * (20! / 19!)\n z = (z * y) / FIXED_1;\n r += z * 0x0000000000000001; // add y^20 * (20! / 20!)\n r = r / 0x21c3677c82b40000 + y + FIXED_1; // divide by 20! and then add y^1 / 1! + y^0 / 0!\n\n // Multiply with the non-residual terms.\n x = -x;\n // e ^ -32\n if ((x & int256(0x0000000000000000000000000000001000000000000000000000000000000000)) != 0) {\n r =\n (r * int256(0x00000000000000000000000000000000000000f1aaddd7742e56d32fb9f99744)) /\n int256(0x0000000000000000000000000043cbaf42a000812488fc5c220ad7b97bf6e99e); // * e ^ -32\n }\n // e ^ -16\n if ((x & int256(0x0000000000000000000000000000000800000000000000000000000000000000)) != 0) {\n r =\n (r * int256(0x00000000000000000000000000000000000afe10820813d65dfe6a33c07f738f)) /\n int256(0x000000000000000000000000000005d27a9f51c31b7c2f8038212a0574779991); // * e ^ -16\n }\n // e ^ -8\n if ((x & int256(0x0000000000000000000000000000000400000000000000000000000000000000)) != 0) {\n r =\n (r * int256(0x0000000000000000000000000000000002582ab704279e8efd15e0265855c47a)) /\n int256(0x0000000000000000000000000000001b4c902e273a58678d6d3bfdb93db96d02); // * e ^ -8\n }\n // e ^ -4\n if ((x & int256(0x0000000000000000000000000000000200000000000000000000000000000000)) != 0) {\n r =\n (r * int256(0x000000000000000000000000000000001152aaa3bf81cb9fdb76eae12d029571)) /\n int256(0x00000000000000000000000000000003b1cc971a9bb5b9867477440d6d157750); // * e ^ -4\n }\n // e ^ -2\n if ((x & int256(0x0000000000000000000000000000000100000000000000000000000000000000)) != 0) {\n r =\n (r * int256(0x000000000000000000000000000000002f16ac6c59de6f8d5d6f63c1482a7c86)) /\n int256(0x000000000000000000000000000000015bf0a8b1457695355fb8ac404e7a79e3); // * e ^ -2\n }\n // e ^ -1\n if ((x & int256(0x0000000000000000000000000000000080000000000000000000000000000000)) != 0) {\n r =\n (r * int256(0x000000000000000000000000000000004da2cbf1be5827f9eb3ad1aa9866ebb3)) /\n int256(0x00000000000000000000000000000000d3094c70f034de4b96ff7d5b6f99fcd8); // * e ^ -1\n }\n // e ^ -0.5\n if ((x & int256(0x0000000000000000000000000000000040000000000000000000000000000000)) != 0) {\n r =\n (r * int256(0x0000000000000000000000000000000063afbe7ab2082ba1a0ae5e4eb1b479dc)) /\n int256(0x00000000000000000000000000000000a45af1e1f40c333b3de1db4dd55f29a7); // * e ^ -0.5\n }\n // e ^ -0.25\n if ((x & int256(0x0000000000000000000000000000000020000000000000000000000000000000)) != 0) {\n r =\n (r * int256(0x0000000000000000000000000000000070f5a893b608861e1f58934f97aea57d)) /\n int256(0x00000000000000000000000000000000910b022db7ae67ce76b441c27035c6a1); // * e ^ -0.25\n }\n // e ^ -0.125\n if ((x & int256(0x0000000000000000000000000000000010000000000000000000000000000000)) != 0) {\n r =\n (r * int256(0x00000000000000000000000000000000783eafef1c0a8f3978c7f81824d62ebf)) /\n int256(0x0000000000000000000000000000000088415abbe9a76bead8d00cf112e4d4a8); // * e ^ -0.125\n }\n }\n}\n" + }, + "@venusprotocol/venus-protocol/contracts/Tokens/Prime/libs/Scores.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.25;\n\nimport { SafeCastUpgradeable } from \"@openzeppelin/contracts-upgradeable/utils/math/SafeCastUpgradeable.sol\";\nimport { FixedMath } from \"./FixedMath.sol\";\n\nusing SafeCastUpgradeable for uint256;\n\n/**\n * @title Scores\n * @author Venus\n * @notice Scores library is used to calculate score of users\n */\nlibrary Scores {\n /**\n * @notice Calculate a membership score given some amount of `xvs` and `capital`, along\n * with some 𝝰 = `alphaNumerator` / `alphaDenominator`.\n * @param xvs amount of xvs (xvs, 1e18 decimal places)\n * @param capital amount of capital (1e18 decimal places)\n * @param alphaNumerator alpha param numerator\n * @param alphaDenominator alpha param denominator\n * @return membership score with 1e18 decimal places\n *\n * @dev 𝝰 must be in the range [0, 1]\n */\n function _calculateScore(\n uint256 xvs,\n uint256 capital,\n uint256 alphaNumerator,\n uint256 alphaDenominator\n ) internal pure returns (uint256) {\n // Score function is:\n // xvs^𝝰 * capital^(1-𝝰)\n // = capital * capital^(-𝝰) * xvs^𝝰\n // = capital * (xvs / capital)^𝝰\n // = capital * (e ^ (ln(xvs / capital))) ^ 𝝰\n // = capital * e ^ (𝝰 * ln(xvs / capital)) (1)\n // or\n // = capital / ( 1 / e ^ (𝝰 * ln(xvs / capital)))\n // = capital / (e ^ (𝝰 * ln(xvs / capital)) ^ -1)\n // = capital / e ^ (𝝰 * -1 * ln(xvs / capital))\n // = capital / e ^ (𝝰 * ln(capital / xvs)) (2)\n //\n // To avoid overflows, use (1) when xvs < capital and\n // use (2) when capital < xvs\n\n // If any side is 0, exit early\n if (xvs == 0 || capital == 0) return 0;\n\n // If both sides are equal, we have:\n // xvs^𝝰 * capital^(1-𝝰)\n // = xvs^𝝰 * xvs^(1-𝝰)\n // = xvs^(𝝰 + 1 - 𝝰) = xvs\n if (xvs == capital) return xvs;\n\n bool lessxvsThanCapital = xvs < capital;\n\n // (xvs / capital) or (capital / xvs), always in range (0, 1)\n int256 ratio = lessxvsThanCapital ? FixedMath._toFixed(xvs, capital) : FixedMath._toFixed(capital, xvs);\n\n // e ^ ( ln(ratio) * 𝝰 )\n int256 exponentiation = FixedMath._exp(\n (FixedMath._ln(ratio) * alphaNumerator.toInt256()) / alphaDenominator.toInt256()\n );\n\n if (lessxvsThanCapital) {\n // capital * e ^ (𝝰 * ln(xvs / capital))\n return FixedMath._uintMul(capital, exponentiation);\n }\n\n // capital / e ^ (𝝰 * ln(capital / xvs))\n return FixedMath._uintDiv(capital, exponentiation);\n }\n}\n" + }, + "@venusprotocol/venus-protocol/contracts/Tokens/Prime/Prime.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\n\nimport { SafeERC20Upgradeable, IERC20Upgradeable } from \"@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol\";\nimport { AccessControlledV8 } from \"@venusprotocol/governance-contracts/contracts/Governance/AccessControlledV8.sol\";\nimport { ResilientOracleInterface } from \"@venusprotocol/oracle/contracts/interfaces/OracleInterface.sol\";\nimport { PausableUpgradeable } from \"@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol\";\nimport { MaxLoopsLimitHelper } from \"@venusprotocol/solidity-utilities/contracts/MaxLoopsLimitHelper.sol\";\nimport { TimeManagerV8 } from \"@venusprotocol/solidity-utilities/contracts/TimeManagerV8.sol\";\n\nimport { IERC20MetadataUpgradeable } from \"@openzeppelin/contracts-upgradeable/token/ERC20/extensions/IERC20MetadataUpgradeable.sol\";\n\nimport { PrimeStorageV1 } from \"./PrimeStorage.sol\";\nimport { Scores } from \"./libs/Scores.sol\";\n\nimport { IPrimeLiquidityProvider } from \"./Interfaces/IPrimeLiquidityProvider.sol\";\nimport { IPrime } from \"./Interfaces/IPrime.sol\";\nimport { IXVSVault } from \"./Interfaces/IXVSVault.sol\";\nimport { IVToken } from \"./Interfaces/IVToken.sol\";\nimport { InterfaceComptroller } from \"./Interfaces/InterfaceComptroller.sol\";\nimport { PoolRegistryInterface } from \"./Interfaces/IPoolRegistry.sol\";\n\n/**\n * @title Prime\n * @author Venus\n * @notice Prime Token is used to provide extra rewards to the users who have staked a minimum of `MINIMUM_STAKED_XVS` XVS in the XVSVault for `STAKING_PERIOD` days\n * @custom:security-contact https://github.com/VenusProtocol/venus-protocol\n */\ncontract Prime is IPrime, AccessControlledV8, PausableUpgradeable, MaxLoopsLimitHelper, PrimeStorageV1, TimeManagerV8 {\n using SafeERC20Upgradeable for IERC20Upgradeable;\n\n /// @notice address of wrapped native token contract\n /// @custom:oz-upgrades-unsafe-allow state-variable-immutable\n address public immutable WRAPPED_NATIVE_TOKEN;\n\n /// @notice address of native market contract\n /// @custom:oz-upgrades-unsafe-allow state-variable-immutable\n address public immutable NATIVE_MARKET;\n\n /// @notice minimum amount of XVS user needs to stake to become a prime member\n /// @custom:oz-upgrades-unsafe-allow state-variable-immutable\n uint256 public immutable MINIMUM_STAKED_XVS;\n\n /// @notice maximum XVS taken in account when calculating user score\n /// @custom:oz-upgrades-unsafe-allow state-variable-immutable\n uint256 public immutable MAXIMUM_XVS_CAP;\n\n /// @notice number of days user need to stake to claim prime token\n /// @custom:oz-upgrades-unsafe-allow state-variable-immutable\n uint256 public immutable STAKING_PERIOD;\n\n /// @notice Emitted when prime token is minted\n event Mint(address indexed user, bool isIrrevocable);\n\n /// @notice Emitted when prime token is burned\n event Burn(address indexed user);\n\n /// @notice Emitted when a market is added to prime program\n event MarketAdded(\n address indexed comptroller,\n address indexed market,\n uint256 supplyMultiplier,\n uint256 borrowMultiplier\n );\n\n /// @notice Emitted when mint limits are updated\n event MintLimitsUpdated(\n uint256 indexed oldIrrevocableLimit,\n uint256 indexed oldRevocableLimit,\n uint256 indexed newIrrevocableLimit,\n uint256 newRevocableLimit\n );\n\n /// @notice Emitted when user score is updated\n event UserScoreUpdated(address indexed user);\n\n /// @notice Emitted when alpha is updated\n event AlphaUpdated(\n uint128 indexed oldNumerator,\n uint128 indexed oldDenominator,\n uint128 indexed newNumerator,\n uint128 newDenominator\n );\n\n /// @notice Emitted when multiplier is updated\n event MultiplierUpdated(\n address indexed market,\n uint256 indexed oldSupplyMultiplier,\n uint256 indexed oldBorrowMultiplier,\n uint256 newSupplyMultiplier,\n uint256 newBorrowMultiplier\n );\n\n /// @notice Emitted when interest is claimed\n event InterestClaimed(address indexed user, address indexed market, uint256 amount);\n\n /// @notice Emitted when revocable token is upgraded to irrevocable token\n event TokenUpgraded(address indexed user);\n\n /// @notice Emitted when stakedAt is updated\n event StakedAtUpdated(address indexed user, uint256 timestamp);\n\n /// @notice Error thrown when market is not supported\n error MarketNotSupported();\n\n /// @notice Error thrown when mint limit is reached\n error InvalidLimit();\n\n /// @notice Error thrown when user is not eligible to claim prime token\n error IneligibleToClaim();\n\n /// @notice Error thrown when user needs to wait more time to claim prime token\n error WaitMoreTime();\n\n /// @notice Error thrown when user has no prime token\n error UserHasNoPrimeToken();\n\n /// @notice Error thrown when no score updates are required\n error NoScoreUpdatesRequired();\n\n /// @notice Error thrown when market already exists\n error MarketAlreadyExists();\n\n /// @notice Error thrown when asset already exists\n error AssetAlreadyExists();\n\n /// @notice Error thrown when invalid address is passed\n error InvalidAddress();\n\n /// @notice Error thrown when invalid alpha arguments are passed\n error InvalidAlphaArguments();\n\n /// @notice Error thrown when invalid vToken is passed\n error InvalidVToken();\n\n /// @notice Error thrown when invalid length is passed\n error InvalidLength();\n\n /// @notice Error thrown when timestamp is invalid\n error InvalidTimestamp();\n\n /// @notice Error thrown when invalid comptroller is passed\n error InvalidComptroller();\n\n /**\n * @notice Prime constructor\n * @param _wrappedNativeToken Address of wrapped native token\n * @param _nativeMarket Address of native market\n * @param _blocksPerYear total blocks per year\n * @param _stakingPeriod total number of seconds for which user needs to stake to claim prime token\n * @param _minimumStakedXVS minimum amount of XVS user needs to stake to become a prime member (scaled by 1e18)\n * @param _maximumXVSCap maximum XVS taken in account when calculating user score (scaled by 1e18)\n * @param _timeBased A boolean indicating whether the contract is based on time or block.\n */\n /// @custom:oz-upgrades-unsafe-allow constructor\n constructor(\n address _wrappedNativeToken,\n address _nativeMarket,\n uint256 _blocksPerYear,\n uint256 _stakingPeriod,\n uint256 _minimumStakedXVS,\n uint256 _maximumXVSCap,\n bool _timeBased\n ) TimeManagerV8(_timeBased, _blocksPerYear) {\n WRAPPED_NATIVE_TOKEN = _wrappedNativeToken;\n NATIVE_MARKET = _nativeMarket;\n STAKING_PERIOD = _stakingPeriod;\n MINIMUM_STAKED_XVS = _minimumStakedXVS;\n MAXIMUM_XVS_CAP = _maximumXVSCap;\n\n // Note that the contract is upgradeable. Use initialize() or reinitializers\n // to set the state variables.\n _disableInitializers();\n }\n\n /**\n * @notice Prime initializer\n * @param xvsVault_ Address of XVSVault\n * @param xvsVaultRewardToken_ Address of XVSVault reward token\n * @param xvsVaultPoolId_ Pool id of XVSVault\n * @param alphaNumerator_ numerator of alpha. If alpha is 0.5 then numerator is 1.\n alphaDenominator_ must be greater than alphaNumerator_, alphaDenominator_ cannot be zero and alphaNumerator_ cannot be zero\n * @param alphaDenominator_ denominator of alpha. If alpha is 0.5 then denominator is 2.\n alpha is alphaNumerator_/alphaDenominator_. So, 0 < alpha < 1\n * @param accessControlManager_ Address of AccessControlManager\n * @param primeLiquidityProvider_ Address of PrimeLiquidityProvider\n * @param comptroller_ Address of core pool comptroller\n * @param oracle_ Address of Oracle\n * @param loopsLimit_ Maximum number of loops allowed in a single transaction\n * @custom:error Throw InvalidAddress if any of the address is invalid\n */\n function initialize(\n address xvsVault_,\n address xvsVaultRewardToken_,\n uint256 xvsVaultPoolId_,\n uint128 alphaNumerator_,\n uint128 alphaDenominator_,\n address accessControlManager_,\n address primeLiquidityProvider_,\n address comptroller_,\n address oracle_,\n uint256 loopsLimit_\n ) external initializer {\n if (xvsVault_ == address(0)) revert InvalidAddress();\n if (xvsVaultRewardToken_ == address(0)) revert InvalidAddress();\n if (oracle_ == address(0)) revert InvalidAddress();\n if (primeLiquidityProvider_ == address(0)) revert InvalidAddress();\n\n _checkAlphaArguments(alphaNumerator_, alphaDenominator_);\n\n alphaNumerator = alphaNumerator_;\n alphaDenominator = alphaDenominator_;\n xvsVaultRewardToken = xvsVaultRewardToken_;\n xvsVaultPoolId = xvsVaultPoolId_;\n xvsVault = xvsVault_;\n nextScoreUpdateRoundId = 0;\n primeLiquidityProvider = primeLiquidityProvider_;\n corePoolComptroller = comptroller_;\n oracle = ResilientOracleInterface(oracle_);\n\n __AccessControlled_init(accessControlManager_);\n __Pausable_init();\n _setMaxLoopsLimit(loopsLimit_);\n\n _pause();\n }\n\n /**\n * @notice Prime initializer V2 for initializing pool registry\n * @param poolRegistry_ Address of IL pool registry\n */\n function initializeV2(address poolRegistry_) external reinitializer(2) {\n poolRegistry = poolRegistry_;\n }\n\n /**\n * @notice Returns boosted pending interest accrued for a user for all markets\n * @param user the account for which to get the accrued interests\n * @return pendingRewards the number of underlying tokens accrued by the user for all markets\n */\n function getPendingRewards(address user) external returns (PendingReward[] memory pendingRewards) {\n address[] storage allMarkets = _allMarkets;\n uint256 marketsLength = allMarkets.length;\n\n pendingRewards = new PendingReward[](marketsLength);\n for (uint256 i; i < marketsLength; ) {\n address market = allMarkets[i];\n uint256 interestAccrued = getInterestAccrued(market, user);\n uint256 accrued = interests[market][user].accrued;\n\n pendingRewards[i] = PendingReward({\n vToken: market,\n rewardToken: _getUnderlying(market),\n amount: interestAccrued + accrued\n });\n\n unchecked {\n ++i;\n }\n }\n }\n\n /**\n * @notice Update total score of multiple users and market\n * @param users accounts for which we need to update score\n * @custom:error Throw NoScoreUpdatesRequired if no score updates are required\n * @custom:error Throw UserHasNoPrimeToken if user has no prime token\n * @custom:event Emits UserScoreUpdated event\n */\n function updateScores(address[] calldata users) external {\n if (pendingScoreUpdates == 0) revert NoScoreUpdatesRequired();\n if (nextScoreUpdateRoundId == 0) revert NoScoreUpdatesRequired();\n\n for (uint256 i; i < users.length; ) {\n address user = users[i];\n\n if (!tokens[user].exists) revert UserHasNoPrimeToken();\n if (isScoreUpdated[nextScoreUpdateRoundId][user]) {\n unchecked {\n ++i;\n }\n continue;\n }\n\n address[] storage allMarkets = _allMarkets;\n uint256 marketsLength = allMarkets.length;\n\n for (uint256 j; j < marketsLength; ) {\n address market = allMarkets[j];\n _executeBoost(user, market);\n _updateScore(user, market);\n\n unchecked {\n ++j;\n }\n }\n\n --pendingScoreUpdates;\n isScoreUpdated[nextScoreUpdateRoundId][user] = true;\n\n unchecked {\n ++i;\n }\n\n emit UserScoreUpdated(user);\n }\n }\n\n /**\n * @notice Update value of alpha\n * @param _alphaNumerator numerator of alpha. If alpha is 0.5 then numerator is 1\n * @param _alphaDenominator denominator of alpha. If alpha is 0.5 then denominator is 2\n * @custom:event Emits AlphaUpdated event\n * @custom:access Controlled by ACM\n */\n function updateAlpha(uint128 _alphaNumerator, uint128 _alphaDenominator) external {\n _checkAccessAllowed(\"updateAlpha(uint128,uint128)\");\n _checkAlphaArguments(_alphaNumerator, _alphaDenominator);\n\n emit AlphaUpdated(alphaNumerator, alphaDenominator, _alphaNumerator, _alphaDenominator);\n\n alphaNumerator = _alphaNumerator;\n alphaDenominator = _alphaDenominator;\n\n uint256 marketslength = _allMarkets.length;\n\n for (uint256 i; i < marketslength; ) {\n accrueInterest(_allMarkets[i]);\n\n unchecked {\n ++i;\n }\n }\n\n _startScoreUpdateRound();\n }\n\n /**\n * @notice Update multipliers for a market\n * @param market address of the market vToken\n * @param supplyMultiplier new supply multiplier for the market, scaled by 1e18\n * @param borrowMultiplier new borrow multiplier for the market, scaled by 1e18\n * @custom:error Throw MarketNotSupported if market is not supported\n * @custom:event Emits MultiplierUpdated event\n * @custom:access Controlled by ACM\n */\n function updateMultipliers(address market, uint256 supplyMultiplier, uint256 borrowMultiplier) external {\n _checkAccessAllowed(\"updateMultipliers(address,uint256,uint256)\");\n\n Market storage _market = markets[market];\n if (!_market.exists) revert MarketNotSupported();\n\n accrueInterest(market);\n\n emit MultiplierUpdated(\n market,\n _market.supplyMultiplier,\n _market.borrowMultiplier,\n supplyMultiplier,\n borrowMultiplier\n );\n _market.supplyMultiplier = supplyMultiplier;\n _market.borrowMultiplier = borrowMultiplier;\n\n _startScoreUpdateRound();\n }\n\n /**\n * @notice Update staked at timestamp for multiple users\n * @param users accounts for which we need to update staked at timestamp\n * @param timestamps new staked at timestamp for the users\n * @custom:error Throw InvalidLength if users and timestamps length are not equal\n * @custom:event Emits StakedAtUpdated event for each user\n * @custom:access Controlled by ACM\n */\n function setStakedAt(address[] calldata users, uint256[] calldata timestamps) external {\n _checkAccessAllowed(\"setStakedAt(address[],uint256[])\");\n if (users.length != timestamps.length) revert InvalidLength();\n\n for (uint256 i; i < users.length; ) {\n if (timestamps[i] > block.timestamp) revert InvalidTimestamp();\n\n stakedAt[users[i]] = timestamps[i];\n emit StakedAtUpdated(users[i], timestamps[i]);\n\n unchecked {\n ++i;\n }\n }\n }\n\n /**\n * @notice Add a market to prime program\n * @param comptroller address of the comptroller\n * @param market address of the market vToken\n * @param supplyMultiplier the multiplier for supply cap. It should be converted to 1e18\n * @param borrowMultiplier the multiplier for borrow cap. It should be converted to 1e18\n * @custom:error Throw MarketAlreadyExists if market already exists\n * @custom:error Throw InvalidVToken if market is not valid\n * @custom:event Emits MarketAdded event\n * @custom:access Controlled by ACM\n */\n function addMarket(\n address comptroller,\n address market,\n uint256 supplyMultiplier,\n uint256 borrowMultiplier\n ) external {\n _checkAccessAllowed(\"addMarket(address,address,uint256,uint256)\");\n\n if (comptroller == address(0)) revert InvalidComptroller();\n\n if (\n comptroller != corePoolComptroller &&\n PoolRegistryInterface(poolRegistry).getPoolByComptroller(comptroller).comptroller != comptroller\n ) revert InvalidComptroller();\n\n Market storage _market = markets[market];\n if (_market.exists) revert MarketAlreadyExists();\n\n bool isMarketExist = InterfaceComptroller(comptroller).markets(market);\n if (!isMarketExist) revert InvalidVToken();\n\n delete _market.rewardIndex;\n _market.supplyMultiplier = supplyMultiplier;\n _market.borrowMultiplier = borrowMultiplier;\n delete _market.sumOfMembersScore;\n _market.exists = true;\n\n address underlying = _getUnderlying(market);\n\n if (vTokenForAsset[underlying] != address(0)) revert AssetAlreadyExists();\n vTokenForAsset[underlying] = market;\n\n _allMarkets.push(market);\n _startScoreUpdateRound();\n\n _ensureMaxLoops(_allMarkets.length);\n\n emit MarketAdded(comptroller, market, supplyMultiplier, borrowMultiplier);\n }\n\n /**\n * @notice Set limits for total tokens that can be minted\n * @param _irrevocableLimit total number of irrevocable tokens that can be minted\n * @param _revocableLimit total number of revocable tokens that can be minted\n * @custom:error Throw InvalidLimit if any of the limit is less than total tokens minted\n * @custom:event Emits MintLimitsUpdated event\n * @custom:access Controlled by ACM\n */\n function setLimit(uint256 _irrevocableLimit, uint256 _revocableLimit) external {\n _checkAccessAllowed(\"setLimit(uint256,uint256)\");\n if (_irrevocableLimit < totalIrrevocable || _revocableLimit < totalRevocable) revert InvalidLimit();\n\n emit MintLimitsUpdated(irrevocableLimit, revocableLimit, _irrevocableLimit, _revocableLimit);\n\n revocableLimit = _revocableLimit;\n irrevocableLimit = _irrevocableLimit;\n }\n\n /**\n * @notice Set the limit for the loops can iterate to avoid the DOS\n * @param loopsLimit Number of loops limit\n * @custom:event Emits MaxLoopsLimitUpdated event on success\n * @custom:access Controlled by ACM\n */\n function setMaxLoopsLimit(uint256 loopsLimit) external {\n _checkAccessAllowed(\"setMaxLoopsLimit(uint256)\");\n _setMaxLoopsLimit(loopsLimit);\n }\n\n /**\n * @notice Directly issue prime tokens to users\n * @param isIrrevocable are the tokens being issued\n * @param users list of address to issue tokens to\n * @custom:access Controlled by ACM\n */\n function issue(bool isIrrevocable, address[] calldata users) external {\n _checkAccessAllowed(\"issue(bool,address[])\");\n\n if (isIrrevocable) {\n for (uint256 i; i < users.length; ) {\n Token storage userToken = tokens[users[i]];\n if (userToken.exists && !userToken.isIrrevocable) {\n _upgrade(users[i]);\n } else {\n _mint(true, users[i]);\n _initializeMarkets(users[i]);\n }\n\n unchecked {\n ++i;\n }\n }\n } else {\n for (uint256 i; i < users.length; ) {\n _mint(false, users[i]);\n _initializeMarkets(users[i]);\n\n unchecked {\n ++i;\n }\n }\n }\n }\n\n /**\n * @notice Executed by XVSVault whenever user's XVSVault balance changes\n * @param user the account address whose balance was updated\n */\n function xvsUpdated(address user) external {\n uint256 totalStaked = _xvsBalanceOfUser(user);\n bool isAccountEligible = _isEligible(totalStaked);\n\n uint256 userStakedAt = stakedAt[user];\n Token memory token = tokens[user];\n\n if (token.exists && !isAccountEligible) {\n delete stakedAt[user];\n emit StakedAtUpdated(user, 0);\n\n if (token.isIrrevocable) {\n _accrueInterestAndUpdateScore(user);\n } else {\n _burn(user);\n }\n } else if (!isAccountEligible && !token.exists && userStakedAt != 0) {\n delete stakedAt[user];\n emit StakedAtUpdated(user, 0);\n } else if (userStakedAt == 0 && isAccountEligible && !token.exists) {\n stakedAt[user] = block.timestamp;\n emit StakedAtUpdated(user, block.timestamp);\n } else if (token.exists && isAccountEligible) {\n _accrueInterestAndUpdateScore(user);\n\n if (stakedAt[user] == 0) {\n stakedAt[user] = block.timestamp;\n emit StakedAtUpdated(user, block.timestamp);\n }\n }\n }\n\n /**\n * @notice accrues interes and updates score for an user for a specific market\n * @param user the account address for which to accrue interest and update score\n * @param market the market for which to accrue interest and update score\n */\n function accrueInterestAndUpdateScore(address user, address market) external {\n _executeBoost(user, market);\n _updateScore(user, market);\n }\n\n /**\n * @notice For claiming prime token when staking period is completed\n */\n function claim() external {\n uint256 userStakedAt = stakedAt[msg.sender];\n if (userStakedAt == 0) revert IneligibleToClaim();\n if (block.timestamp - userStakedAt < STAKING_PERIOD) revert WaitMoreTime();\n\n _mint(false, msg.sender);\n _initializeMarkets(msg.sender);\n }\n\n /**\n * @notice For burning any prime token\n * @param user the account address for which the prime token will be burned\n * @custom:access Controlled by ACM\n */\n function burn(address user) external {\n _checkAccessAllowed(\"burn(address)\");\n _burn(user);\n }\n\n /**\n * @notice To pause or unpause claiming of interest\n * @custom:access Controlled by ACM\n */\n function togglePause() external {\n _checkAccessAllowed(\"togglePause()\");\n if (paused()) {\n _unpause();\n } else {\n _pause();\n }\n }\n\n /**\n * @notice For user to claim boosted yield\n * @param vToken the market for which claim the accrued interest\n * @return amount the amount of tokens transferred to the msg.sender\n */\n function claimInterest(address vToken) external whenNotPaused returns (uint256) {\n return _claimInterest(vToken, msg.sender);\n }\n\n /**\n * @notice For user to claim boosted yield\n * @param vToken the market for which claim the accrued interest\n * @param user the user for which to claim the accrued interest\n * @return amount the amount of tokens transferred to the user\n */\n function claimInterest(address vToken, address user) external whenNotPaused returns (uint256) {\n return _claimInterest(vToken, user);\n }\n\n /**\n * @notice Retrieves an array of all available markets\n * @return an array of addresses representing all available markets\n */\n function getAllMarkets() external view returns (address[] memory) {\n return _allMarkets;\n }\n\n /**\n * @notice Retrieves the core pool comptroller address\n * @return the core pool comptroller address\n */\n function comptroller() external view returns (address) {\n return corePoolComptroller;\n }\n\n /**\n * @notice fetch the numbers of seconds remaining for staking period to complete\n * @param user the account address for which we are checking the remaining time\n * @return timeRemaining the number of seconds the user needs to wait to claim prime token\n */\n function claimTimeRemaining(address user) external view returns (uint256) {\n uint256 userStakedAt = stakedAt[user];\n if (userStakedAt == 0) return STAKING_PERIOD;\n\n uint256 totalTimeStaked;\n unchecked {\n totalTimeStaked = block.timestamp - userStakedAt;\n }\n\n if (totalTimeStaked < STAKING_PERIOD) {\n unchecked {\n return STAKING_PERIOD - totalTimeStaked;\n }\n }\n return 0;\n }\n\n /**\n * @notice Returns if user is a prime holder\n * @return isPrimeHolder true if user is a prime holder\n */\n function isUserPrimeHolder(address user) external view returns (bool) {\n return tokens[user].exists;\n }\n\n /**\n * @notice Returns supply and borrow APR for user for a given market\n * @param market the market for which to fetch the APR\n * @param user the account for which to get the APR\n * @return aprInfo APR information for the user for the given market\n */\n function calculateAPR(address market, address user) external view returns (APRInfo memory aprInfo) {\n IVToken vToken = IVToken(market);\n uint256 borrow = vToken.borrowBalanceStored(user);\n uint256 exchangeRate = vToken.exchangeRateStored();\n uint256 balanceOfAccount = vToken.balanceOf(user);\n uint256 supply = (exchangeRate * balanceOfAccount) / EXP_SCALE;\n\n aprInfo.userScore = interests[market][user].score;\n aprInfo.totalScore = markets[market].sumOfMembersScore;\n\n aprInfo.xvsBalanceForScore = _xvsBalanceForScore(_xvsBalanceOfUser(user));\n Capital memory capital = _capitalForScore(aprInfo.xvsBalanceForScore, borrow, supply, address(vToken));\n\n aprInfo.capital = capital.capital;\n aprInfo.cappedSupply = capital.cappedSupply;\n aprInfo.cappedBorrow = capital.cappedBorrow;\n aprInfo.supplyCapUSD = capital.supplyCapUSD;\n aprInfo.borrowCapUSD = capital.borrowCapUSD;\n\n (aprInfo.supplyAPR, aprInfo.borrowAPR) = _calculateUserAPR(\n market,\n supply,\n borrow,\n aprInfo.cappedSupply,\n aprInfo.cappedBorrow,\n aprInfo.userScore,\n aprInfo.totalScore\n );\n }\n\n /**\n * @notice Returns supply and borrow APR for estimated supply, borrow and XVS staked\n * @param market the market for which to fetch the APR\n * @param user the account for which to get the APR\n * @return aprInfo APR information for the user for the given market\n */\n function estimateAPR(\n address market,\n address user,\n uint256 borrow,\n uint256 supply,\n uint256 xvsStaked\n ) external view returns (APRInfo memory aprInfo) {\n aprInfo.totalScore = markets[market].sumOfMembersScore - interests[market][user].score;\n\n aprInfo.xvsBalanceForScore = _xvsBalanceForScore(xvsStaked);\n Capital memory capital = _capitalForScore(aprInfo.xvsBalanceForScore, borrow, supply, market);\n\n aprInfo.capital = capital.capital;\n aprInfo.cappedSupply = capital.cappedSupply;\n aprInfo.cappedBorrow = capital.cappedBorrow;\n aprInfo.supplyCapUSD = capital.supplyCapUSD;\n aprInfo.borrowCapUSD = capital.borrowCapUSD;\n\n uint256 decimals = IERC20MetadataUpgradeable(_getUnderlying(market)).decimals();\n aprInfo.capital = aprInfo.capital * (10 ** (18 - decimals));\n\n aprInfo.userScore = Scores._calculateScore(\n aprInfo.xvsBalanceForScore,\n aprInfo.capital,\n alphaNumerator,\n alphaDenominator\n );\n\n aprInfo.totalScore = aprInfo.totalScore + aprInfo.userScore;\n\n (aprInfo.supplyAPR, aprInfo.borrowAPR) = _calculateUserAPR(\n market,\n supply,\n borrow,\n aprInfo.cappedSupply,\n aprInfo.cappedBorrow,\n aprInfo.userScore,\n aprInfo.totalScore\n );\n }\n\n /**\n * @notice Distributes income from market since last distribution\n * @param vToken the market for which to distribute the income\n * @custom:error Throw MarketNotSupported if market is not supported\n */\n function accrueInterest(address vToken) public {\n Market storage market = markets[vToken];\n\n if (!market.exists) revert MarketNotSupported();\n\n address underlying = _getUnderlying(vToken);\n\n IPrimeLiquidityProvider _primeLiquidityProvider = IPrimeLiquidityProvider(primeLiquidityProvider);\n _primeLiquidityProvider.accrueTokens(underlying);\n uint256 totalAccruedInPLP = _primeLiquidityProvider.tokenAmountAccrued(underlying);\n uint256 unreleasedPLPAccruedInterest = totalAccruedInPLP - unreleasedPLPIncome[underlying];\n uint256 distributionIncome = unreleasedPLPAccruedInterest;\n\n if (distributionIncome == 0) {\n return;\n }\n\n unreleasedPLPIncome[underlying] = totalAccruedInPLP;\n\n uint256 delta;\n if (market.sumOfMembersScore != 0) {\n delta = ((distributionIncome * EXP_SCALE) / market.sumOfMembersScore);\n }\n\n market.rewardIndex += delta;\n }\n\n /**\n * @notice Returns boosted interest accrued for a user\n * @param vToken the market for which to fetch the accrued interest\n * @param user the account for which to get the accrued interest\n * @return interestAccrued the number of underlying tokens accrued by the user since the last accrual\n */\n function getInterestAccrued(address vToken, address user) public returns (uint256) {\n accrueInterest(vToken);\n\n return _interestAccrued(vToken, user);\n }\n\n /**\n * @notice accrues interest and updates score of all markets for an user\n * @param user the account address for which to accrue interest and update score\n */\n function _accrueInterestAndUpdateScore(address user) internal {\n address[] storage allMarkets = _allMarkets;\n uint256 marketsLength = allMarkets.length;\n\n for (uint256 i; i < marketsLength; ) {\n address market = allMarkets[i];\n _executeBoost(user, market);\n _updateScore(user, market);\n\n unchecked {\n ++i;\n }\n }\n }\n\n /**\n * @notice Initializes all the markets for the user when a prime token is minted\n * @param account the account address for which markets needs to be initialized\n */\n function _initializeMarkets(address account) internal {\n address[] storage allMarkets = _allMarkets;\n uint256 marketsLength = allMarkets.length;\n\n for (uint256 i; i < marketsLength; ) {\n address market = allMarkets[i];\n accrueInterest(market);\n\n interests[market][account].rewardIndex = markets[market].rewardIndex;\n\n uint256 score = _calculateScore(market, account);\n interests[market][account].score = score;\n markets[market].sumOfMembersScore = markets[market].sumOfMembersScore + score;\n\n unchecked {\n ++i;\n }\n }\n }\n\n /**\n * @notice calculate the current score of user\n * @param market the market for which to calculate the score\n * @param user the account for which to calculate the score\n * @return score the score of the user\n */\n function _calculateScore(address market, address user) internal returns (uint256) {\n uint256 xvsBalanceForScore = _xvsBalanceForScore(_xvsBalanceOfUser(user));\n\n IVToken vToken = IVToken(market);\n uint256 borrow = vToken.borrowBalanceStored(user);\n uint256 exchangeRate = vToken.exchangeRateStored();\n uint256 balanceOfAccount = vToken.balanceOf(user);\n uint256 supply = (exchangeRate * balanceOfAccount) / EXP_SCALE;\n\n address xvsToken = IXVSVault(xvsVault).xvsAddress();\n oracle.updateAssetPrice(xvsToken);\n oracle.updatePrice(market);\n\n Capital memory capital = _capitalForScore(xvsBalanceForScore, borrow, supply, market);\n\n uint256 decimals = IERC20MetadataUpgradeable(_getUnderlying(market)).decimals();\n\n capital.capital = capital.capital * (10 ** (18 - decimals));\n\n return Scores._calculateScore(xvsBalanceForScore, capital.capital, alphaNumerator, alphaDenominator);\n }\n\n /**\n * @notice To transfer the accrued interest to user\n * @param vToken the market for which to claim\n * @param user the account for which to get the accrued interest\n * @return amount the amount of tokens transferred to the user\n * @custom:event Emits InterestClaimed event\n */\n function _claimInterest(address vToken, address user) internal returns (uint256) {\n uint256 amount = getInterestAccrued(vToken, user);\n amount += interests[vToken][user].accrued;\n\n interests[vToken][user].rewardIndex = markets[vToken].rewardIndex;\n delete interests[vToken][user].accrued;\n\n address underlying = _getUnderlying(vToken);\n IERC20Upgradeable asset = IERC20Upgradeable(underlying);\n\n if (amount > asset.balanceOf(address(this))) {\n delete unreleasedPLPIncome[underlying];\n IPrimeLiquidityProvider(primeLiquidityProvider).releaseFunds(address(asset));\n }\n\n asset.safeTransfer(user, amount);\n\n emit InterestClaimed(user, vToken, amount);\n\n return amount;\n }\n\n /**\n * @notice Used to mint a new prime token\n * @param isIrrevocable is the tokens being issued is irrevocable\n * @param user token owner\n * @custom:error Throw IneligibleToClaim if user is not eligible to claim prime token\n * @custom:event Emits Mint event\n */\n function _mint(bool isIrrevocable, address user) internal {\n Token storage token = tokens[user];\n if (token.exists) revert IneligibleToClaim();\n\n token.exists = true;\n token.isIrrevocable = isIrrevocable;\n\n if (isIrrevocable) {\n ++totalIrrevocable;\n } else {\n ++totalRevocable;\n }\n\n if (totalIrrevocable > irrevocableLimit || totalRevocable > revocableLimit) revert InvalidLimit();\n _updateRoundAfterTokenMinted(user);\n\n emit Mint(user, isIrrevocable);\n }\n\n /**\n * @notice Used to burn a new prime token\n * @param user owner whose prime token to burn\n * @custom:error Throw UserHasNoPrimeToken if user has no prime token\n * @custom:event Emits Burn event\n */\n function _burn(address user) internal {\n Token memory token = tokens[user];\n if (!token.exists) revert UserHasNoPrimeToken();\n\n address[] storage allMarkets = _allMarkets;\n uint256 marketsLength = allMarkets.length;\n\n for (uint256 i; i < marketsLength; ) {\n address market = allMarkets[i];\n _executeBoost(user, market);\n markets[market].sumOfMembersScore = markets[market].sumOfMembersScore - interests[market][user].score;\n\n delete interests[market][user].score;\n delete interests[market][user].rewardIndex;\n\n unchecked {\n ++i;\n }\n }\n\n if (token.isIrrevocable) {\n --totalIrrevocable;\n } else {\n --totalRevocable;\n }\n\n delete tokens[user].exists;\n delete tokens[user].isIrrevocable;\n\n _updateRoundAfterTokenBurned(user);\n\n emit Burn(user);\n }\n\n /**\n * @notice Used to upgrade an token\n * @param user owner whose prime token to upgrade\n * @custom:error Throw InvalidLimit if total irrevocable tokens exceeds the limit\n * @custom:event Emits TokenUpgraded event\n */\n function _upgrade(address user) internal {\n Token storage userToken = tokens[user];\n\n userToken.isIrrevocable = true;\n ++totalIrrevocable;\n --totalRevocable;\n\n if (totalIrrevocable > irrevocableLimit) revert InvalidLimit();\n\n emit TokenUpgraded(user);\n }\n\n /**\n * @notice Accrue rewards for the user. Must be called before updating score\n * @param user account for which we need to accrue rewards\n * @param vToken the market for which we need to accrue rewards\n */\n function _executeBoost(address user, address vToken) internal {\n if (!markets[vToken].exists || !tokens[user].exists) {\n return;\n }\n\n accrueInterest(vToken);\n interests[vToken][user].accrued += _interestAccrued(vToken, user);\n interests[vToken][user].rewardIndex = markets[vToken].rewardIndex;\n }\n\n /**\n * @notice Update total score of user and market. Must be called after changing account's borrow or supply balance.\n * @param user account for which we need to update score\n * @param market the market for which we need to score\n */\n function _updateScore(address user, address market) internal {\n Market storage _market = markets[market];\n if (!_market.exists || !tokens[user].exists) {\n return;\n }\n\n uint256 score = _calculateScore(market, user);\n _market.sumOfMembersScore = _market.sumOfMembersScore - interests[market][user].score + score;\n\n interests[market][user].score = score;\n }\n\n /**\n * @notice Verify new alpha arguments\n * @param _alphaNumerator numerator of alpha. If alpha is 0.5 then numerator is 1\n * @param _alphaDenominator denominator of alpha. If alpha is 0.5 then denominator is 2\n * @custom:error Throw InvalidAlphaArguments if alpha is invalid\n */\n function _checkAlphaArguments(uint128 _alphaNumerator, uint128 _alphaDenominator) internal pure {\n if (_alphaNumerator >= _alphaDenominator || _alphaNumerator == 0) {\n revert InvalidAlphaArguments();\n }\n }\n\n /**\n * @notice starts round to update scores of a particular or all markets\n */\n function _startScoreUpdateRound() internal {\n nextScoreUpdateRoundId++;\n totalScoreUpdatesRequired = totalIrrevocable + totalRevocable;\n pendingScoreUpdates = totalScoreUpdatesRequired;\n }\n\n /**\n * @notice update the required score updates when token is burned before round is completed\n */\n function _updateRoundAfterTokenBurned(address user) internal {\n if (totalScoreUpdatesRequired != 0) --totalScoreUpdatesRequired;\n\n if (pendingScoreUpdates != 0 && !isScoreUpdated[nextScoreUpdateRoundId][user]) {\n --pendingScoreUpdates;\n }\n }\n\n /**\n * @notice update the required score updates when token is minted before round is completed\n */\n function _updateRoundAfterTokenMinted(address user) internal {\n if (totalScoreUpdatesRequired != 0) isScoreUpdated[nextScoreUpdateRoundId][user] = true;\n }\n\n /**\n * @notice fetch the current XVS balance of user in the XVSVault\n * @param user the account address\n * @return xvsBalance the XVS balance of user\n */\n function _xvsBalanceOfUser(address user) internal view returns (uint256) {\n (uint256 xvs, , uint256 pendingWithdrawals) = IXVSVault(xvsVault).getUserInfo(\n xvsVaultRewardToken,\n xvsVaultPoolId,\n user\n );\n return (xvs - pendingWithdrawals);\n }\n\n /**\n * @notice calculate the current XVS balance that will be used in calculation of score\n * @param xvs the actual XVS balance of user\n * @return xvsBalanceForScore the XVS balance to use in score\n */\n function _xvsBalanceForScore(uint256 xvs) internal view returns (uint256) {\n if (xvs > MAXIMUM_XVS_CAP) {\n return MAXIMUM_XVS_CAP;\n }\n return xvs;\n }\n\n /**\n * @notice calculate the capital for calculation of score\n * @param xvs the actual XVS balance of user\n * @param borrow the borrow balance of user\n * @param supply the supply balance of user\n * @param market the market vToken address\n * @return capital the capital to use in calculation of score\n */\n function _capitalForScore(\n uint256 xvs,\n uint256 borrow,\n uint256 supply,\n address market\n ) internal view returns (Capital memory capital) {\n address xvsToken = IXVSVault(xvsVault).xvsAddress();\n\n uint256 xvsPrice = oracle.getPrice(xvsToken);\n capital.borrowCapUSD = (xvsPrice * ((xvs * markets[market].borrowMultiplier) / EXP_SCALE)) / EXP_SCALE;\n capital.supplyCapUSD = (xvsPrice * ((xvs * markets[market].supplyMultiplier) / EXP_SCALE)) / EXP_SCALE;\n\n uint256 tokenPrice = oracle.getUnderlyingPrice(market);\n uint256 supplyUSD = (tokenPrice * supply) / EXP_SCALE;\n uint256 borrowUSD = (tokenPrice * borrow) / EXP_SCALE;\n\n if (supplyUSD >= capital.supplyCapUSD) {\n supply = supplyUSD != 0 ? (supply * capital.supplyCapUSD) / supplyUSD : 0;\n }\n\n if (borrowUSD >= capital.borrowCapUSD) {\n borrow = borrowUSD != 0 ? (borrow * capital.borrowCapUSD) / borrowUSD : 0;\n }\n\n capital.capital = supply + borrow;\n capital.cappedSupply = supply;\n capital.cappedBorrow = borrow;\n }\n\n /**\n * @notice Used to get if the XVS balance is eligible for prime token\n * @param amount amount of XVS\n * @return isEligible true if the staked XVS amount is enough to consider the associated user eligible for a Prime token, false otherwise\n */\n function _isEligible(uint256 amount) internal view returns (bool) {\n if (amount >= MINIMUM_STAKED_XVS) {\n return true;\n }\n\n return false;\n }\n\n /**\n * @notice Calculate the interests accrued by the user in the market, since the last accrual\n * @param vToken the market for which to calculate the accrued interest\n * @param user the user for which to calculate the accrued interest\n * @return interestAccrued the number of underlying tokens accrued by the user since the last accrual\n */\n function _interestAccrued(address vToken, address user) internal view returns (uint256) {\n Interest memory interest = interests[vToken][user];\n uint256 index = markets[vToken].rewardIndex - interest.rewardIndex;\n\n uint256 score = interest.score;\n\n return (index * score) / EXP_SCALE;\n }\n\n /**\n * @notice Returns the underlying token associated with the VToken, or wrapped native token if the market is native market\n * @param vToken the market whose underlying token will be returned\n * @return underlying The address of the underlying token associated with the VToken, or the address of the WRAPPED_NATIVE_TOKEN token if the market is NATIVE_MARKET\n */\n function _getUnderlying(address vToken) internal view returns (address) {\n if (vToken == NATIVE_MARKET) {\n return WRAPPED_NATIVE_TOKEN;\n }\n return IVToken(vToken).underlying();\n }\n\n //////////////////////////////////////////////////\n //////////////// APR Calculation ////////////////\n ////////////////////////////////////////////////\n\n /**\n * @notice the total income that's going to be distributed in a year to prime token holders\n * @param vToken the market for which to fetch the total income that's going to distributed in a year\n * @return amount the total income\n */\n function incomeDistributionYearly(address vToken) public view returns (uint256 amount) {\n uint256 totalIncomePerBlockOrSecondFromPLP = IPrimeLiquidityProvider(primeLiquidityProvider)\n .getEffectiveDistributionSpeed(_getUnderlying(vToken));\n amount = blocksOrSecondsPerYear * totalIncomePerBlockOrSecondFromPLP;\n }\n\n /**\n * @notice used to calculate the supply and borrow APR of the user\n * @param vToken the market for which to fetch the APR\n * @param totalSupply the total token supply of the user\n * @param totalBorrow the total tokens borrowed by the user\n * @param totalCappedSupply the total token capped supply of the user\n * @param totalCappedBorrow the total capped tokens borrowed by the user\n * @param userScore the score of the user\n * @param totalScore the total market score\n * @return supplyAPR the supply APR of the user\n * @return borrowAPR the borrow APR of the user\n */\n function _calculateUserAPR(\n address vToken,\n uint256 totalSupply,\n uint256 totalBorrow,\n uint256 totalCappedSupply,\n uint256 totalCappedBorrow,\n uint256 userScore,\n uint256 totalScore\n ) internal view returns (uint256 supplyAPR, uint256 borrowAPR) {\n if (totalScore == 0) return (0, 0);\n\n uint256 userYearlyIncome = (userScore * incomeDistributionYearly(vToken)) / totalScore;\n\n uint256 totalCappedValue = totalCappedSupply + totalCappedBorrow;\n\n if (totalCappedValue == 0) return (0, 0);\n\n uint256 maximumBps = MAXIMUM_BPS;\n uint256 userSupplyIncomeYearly;\n uint256 userBorrowIncomeYearly;\n userSupplyIncomeYearly = (userYearlyIncome * totalCappedSupply) / totalCappedValue;\n userBorrowIncomeYearly = (userYearlyIncome * totalCappedBorrow) / totalCappedValue;\n supplyAPR = totalSupply == 0 ? 0 : ((userSupplyIncomeYearly * maximumBps) / totalSupply);\n borrowAPR = totalBorrow == 0 ? 0 : ((userBorrowIncomeYearly * maximumBps) / totalBorrow);\n }\n}\n" + }, + "@venusprotocol/venus-protocol/contracts/Tokens/Prime/PrimeLiquidityProvider.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\n\nimport { PrimeLiquidityProviderStorageV1 } from \"./PrimeLiquidityProviderStorage.sol\";\nimport { SafeERC20Upgradeable, IERC20Upgradeable } from \"@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol\";\nimport { AccessControlledV8 } from \"@venusprotocol/governance-contracts/contracts/Governance/AccessControlledV8.sol\";\nimport { PausableUpgradeable } from \"@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol\";\nimport { IPrimeLiquidityProvider } from \"./Interfaces/IPrimeLiquidityProvider.sol\";\nimport { MaxLoopsLimitHelper } from \"@venusprotocol/solidity-utilities/contracts/MaxLoopsLimitHelper.sol\";\nimport { TimeManagerV8 } from \"@venusprotocol/solidity-utilities/contracts/TimeManagerV8.sol\";\n\n/**\n * @title PrimeLiquidityProvider\n * @author Venus\n * @notice PrimeLiquidityProvider is used to fund Prime\n */\ncontract PrimeLiquidityProvider is\n IPrimeLiquidityProvider,\n AccessControlledV8,\n PausableUpgradeable,\n MaxLoopsLimitHelper,\n PrimeLiquidityProviderStorageV1,\n TimeManagerV8\n{\n using SafeERC20Upgradeable for IERC20Upgradeable;\n\n /// @notice The default max token distribution speed\n uint256 public constant DEFAULT_MAX_DISTRIBUTION_SPEED = 1e18;\n\n /// @notice Emitted when a token distribution is initialized\n event TokenDistributionInitialized(address indexed token);\n\n /// @notice Emitted when a new token distribution speed is set\n event TokenDistributionSpeedUpdated(address indexed token, uint256 oldSpeed, uint256 newSpeed);\n\n /// @notice Emitted when a new max distribution speed for token is set\n event MaxTokenDistributionSpeedUpdated(address indexed token, uint256 oldSpeed, uint256 newSpeed);\n\n /// @notice Emitted when prime token contract address is changed\n event PrimeTokenUpdated(address indexed oldPrimeToken, address indexed newPrimeToken);\n\n /// @notice Emitted when distribution state(Index and block or second) is updated\n event TokensAccrued(address indexed token, uint256 amount);\n\n /// @notice Emitted when token is transferred to the prime contract\n event TokenTransferredToPrime(address indexed token, uint256 amount);\n\n /// @notice Emitted on sweep token success\n event SweepToken(address indexed token, address indexed to, uint256 sweepAmount);\n\n /// @notice Thrown when arguments are passed are invalid\n error InvalidArguments();\n\n /// @notice Thrown when distribution speed is greater than maxTokenDistributionSpeeds[tokenAddress]\n error InvalidDistributionSpeed(uint256 speed, uint256 maxSpeed);\n\n /// @notice Thrown when caller is not the desired caller\n error InvalidCaller();\n\n /// @notice Thrown when token is initialized\n error TokenAlreadyInitialized(address token);\n\n ///@notice Error thrown when PrimeLiquidityProvider's balance is less than sweep amount\n error InsufficientBalance(uint256 sweepAmount, uint256 balance);\n\n /// @notice Error thrown when funds transfer is paused\n error FundsTransferIsPaused();\n\n /// @notice Error thrown when accrueTokens is called for an uninitialized token\n error TokenNotInitialized(address token_);\n\n /// @notice Error thrown when argument value in setter is same as previous value\n error AddressesMustDiffer();\n\n /**\n * @notice Compares two addresses to ensure they are different\n * @param oldAddress The original address to compare\n * @param newAddress The new address to compare\n */\n modifier compareAddress(address oldAddress, address newAddress) {\n if (newAddress == oldAddress) {\n revert AddressesMustDiffer();\n }\n _;\n }\n\n /**\n * @notice Prime Liquidity Provider constructor\n * @param _timeBased A boolean indicating whether the contract is based on time or block.\n * @param _blocksPerYear total blocks per year\n */\n /// @custom:oz-upgrades-unsafe-allow constructor\n constructor(bool _timeBased, uint256 _blocksPerYear) TimeManagerV8(_timeBased, _blocksPerYear) {\n _disableInitializers();\n }\n\n /**\n * @notice PrimeLiquidityProvider initializer\n * @dev Initializes the deployer to owner\n * @param accessControlManager_ AccessControlManager contract address\n * @param tokens_ Array of addresses of the tokens\n * @param distributionSpeeds_ New distribution speeds for tokens\n * @param loopsLimit_ Maximum number of loops allowed in a single transaction\n * @custom:error Throw InvalidArguments on different length of tokens and speeds array\n */\n function initialize(\n address accessControlManager_,\n address[] calldata tokens_,\n uint256[] calldata distributionSpeeds_,\n uint256[] calldata maxDistributionSpeeds_,\n uint256 loopsLimit_\n ) external initializer {\n _ensureZeroAddress(accessControlManager_);\n\n __AccessControlled_init(accessControlManager_);\n __Pausable_init();\n _setMaxLoopsLimit(loopsLimit_);\n\n uint256 numTokens = tokens_.length;\n _ensureMaxLoops(numTokens);\n\n if ((numTokens != distributionSpeeds_.length) || (numTokens != maxDistributionSpeeds_.length)) {\n revert InvalidArguments();\n }\n\n for (uint256 i; i < numTokens; ) {\n _initializeToken(tokens_[i]);\n _setMaxTokenDistributionSpeed(tokens_[i], maxDistributionSpeeds_[i]);\n _setTokenDistributionSpeed(tokens_[i], distributionSpeeds_[i]);\n\n unchecked {\n ++i;\n }\n }\n }\n\n /**\n * @notice Initialize the distribution of the token\n * @param tokens_ Array of addresses of the tokens to be intialized\n * @custom:access Only Governance\n */\n function initializeTokens(address[] calldata tokens_) external onlyOwner {\n uint256 tokensLength = tokens_.length;\n _ensureMaxLoops(tokensLength);\n\n for (uint256 i; i < tokensLength; ) {\n _initializeToken(tokens_[i]);\n\n unchecked {\n ++i;\n }\n }\n }\n\n /**\n * @notice Pause fund transfer of tokens to Prime contract\n * @custom:access Controlled by ACM\n */\n function pauseFundsTransfer() external {\n _checkAccessAllowed(\"pauseFundsTransfer()\");\n _pause();\n }\n\n /**\n * @notice Resume fund transfer of tokens to Prime contract\n * @custom:access Controlled by ACM\n */\n function resumeFundsTransfer() external {\n _checkAccessAllowed(\"resumeFundsTransfer()\");\n _unpause();\n }\n\n /**\n * @notice Set distribution speed (amount of token distribute per block or second)\n * @param tokens_ Array of addresses of the tokens\n * @param distributionSpeeds_ New distribution speeds for tokens\n * @custom:access Controlled by ACM\n * @custom:error Throw InvalidArguments on different length of tokens and speeds array\n */\n function setTokensDistributionSpeed(address[] calldata tokens_, uint256[] calldata distributionSpeeds_) external {\n _checkAccessAllowed(\"setTokensDistributionSpeed(address[],uint256[])\");\n uint256 numTokens = tokens_.length;\n _ensureMaxLoops(numTokens);\n\n if (numTokens != distributionSpeeds_.length) {\n revert InvalidArguments();\n }\n\n for (uint256 i; i < numTokens; ) {\n _ensureTokenInitialized(tokens_[i]);\n _setTokenDistributionSpeed(tokens_[i], distributionSpeeds_[i]);\n\n unchecked {\n ++i;\n }\n }\n }\n\n /**\n * @notice Set max distribution speed for token (amount of maximum token distribute per block or second)\n * @param tokens_ Array of addresses of the tokens\n * @param maxDistributionSpeeds_ New distribution speeds for tokens\n * @custom:access Controlled by ACM\n * @custom:error Throw InvalidArguments on different length of tokens and speeds array\n */\n function setMaxTokensDistributionSpeed(\n address[] calldata tokens_,\n uint256[] calldata maxDistributionSpeeds_\n ) external {\n _checkAccessAllowed(\"setMaxTokensDistributionSpeed(address[],uint256[])\");\n uint256 numTokens = tokens_.length;\n _ensureMaxLoops(numTokens);\n\n if (numTokens != maxDistributionSpeeds_.length) {\n revert InvalidArguments();\n }\n\n for (uint256 i; i < numTokens; ) {\n _setMaxTokenDistributionSpeed(tokens_[i], maxDistributionSpeeds_[i]);\n\n unchecked {\n ++i;\n }\n }\n }\n\n /**\n * @notice Set the prime token contract address\n * @param prime_ The new address of the prime token contract\n * @custom:event Emits PrimeTokenUpdated event\n * @custom:access Only owner\n */\n function setPrimeToken(address prime_) external onlyOwner compareAddress(prime, prime_) {\n _ensureZeroAddress(prime_);\n\n emit PrimeTokenUpdated(prime, prime_);\n prime = prime_;\n }\n\n /**\n * @notice Set the limit for the loops can iterate to avoid the DOS\n * @param loopsLimit Limit for the max loops can execute at a time\n * @custom:event Emits MaxLoopsLimitUpdated event on success\n * @custom:access Controlled by ACM\n */\n function setMaxLoopsLimit(uint256 loopsLimit) external {\n _checkAccessAllowed(\"setMaxLoopsLimit(uint256)\");\n _setMaxLoopsLimit(loopsLimit);\n }\n\n /**\n * @notice Claim all the token accrued till last block or second\n * @param token_ The token to release to the Prime contract\n * @custom:event Emits TokenTransferredToPrime event\n * @custom:error Throw InvalidArguments on Zero address(token)\n * @custom:error Throw FundsTransferIsPaused is paused\n * @custom:error Throw InvalidCaller if the sender is not the Prime contract\n */\n function releaseFunds(address token_) external {\n address _prime = prime;\n if (msg.sender != _prime) revert InvalidCaller();\n if (paused()) {\n revert FundsTransferIsPaused();\n }\n\n accrueTokens(token_);\n uint256 accruedAmount = _tokenAmountAccrued[token_];\n delete _tokenAmountAccrued[token_];\n\n emit TokenTransferredToPrime(token_, accruedAmount);\n\n IERC20Upgradeable(token_).safeTransfer(_prime, accruedAmount);\n }\n\n /**\n * @notice A public function to sweep accidental ERC-20 transfers to this contract. Tokens are sent to user\n * @param token_ The address of the ERC-20 token to sweep\n * @param to_ The address of the recipient\n * @param amount_ The amount of tokens needs to transfer\n * @custom:event Emits SweepToken event\n * @custom:error Throw InsufficientBalance if amount_ is greater than the available balance of the token in the contract\n * @custom:access Only Governance\n */\n function sweepToken(IERC20Upgradeable token_, address to_, uint256 amount_) external onlyOwner {\n uint256 balance = token_.balanceOf(address(this));\n if (amount_ > balance) {\n revert InsufficientBalance(amount_, balance);\n }\n\n emit SweepToken(address(token_), to_, amount_);\n\n token_.safeTransfer(to_, amount_);\n }\n\n /**\n * @notice Get rewards per block or second for token\n * @param token_ Address of the token\n * @return speed returns the per block or second reward\n */\n function getEffectiveDistributionSpeed(address token_) external view returns (uint256) {\n uint256 distributionSpeed = tokenDistributionSpeeds[token_];\n uint256 balance = IERC20Upgradeable(token_).balanceOf(address(this));\n uint256 accrued = _tokenAmountAccrued[token_];\n\n if (balance > accrued) {\n return distributionSpeed;\n }\n\n return 0;\n }\n\n /**\n * @notice Accrue token by updating the distribution state\n * @param token_ Address of the token\n * @custom:event Emits TokensAccrued event\n */\n function accrueTokens(address token_) public {\n _ensureZeroAddress(token_);\n\n _ensureTokenInitialized(token_);\n\n uint256 blockNumberOrSecond = getBlockNumberOrTimestamp();\n uint256 deltaBlocksOrSeconds;\n unchecked {\n deltaBlocksOrSeconds = blockNumberOrSecond - lastAccruedBlockOrSecond[token_];\n }\n\n if (deltaBlocksOrSeconds != 0) {\n uint256 distributionSpeed = tokenDistributionSpeeds[token_];\n uint256 balance = IERC20Upgradeable(token_).balanceOf(address(this));\n\n uint256 balanceDiff = balance - _tokenAmountAccrued[token_];\n if (distributionSpeed != 0 && balanceDiff != 0) {\n uint256 accruedSinceUpdate = deltaBlocksOrSeconds * distributionSpeed;\n uint256 tokenAccrued = (balanceDiff <= accruedSinceUpdate ? balanceDiff : accruedSinceUpdate);\n\n _tokenAmountAccrued[token_] += tokenAccrued;\n emit TokensAccrued(token_, tokenAccrued);\n }\n\n lastAccruedBlockOrSecond[token_] = blockNumberOrSecond;\n }\n }\n\n /**\n * @notice Get the last accrued block or second for token\n * @param token_ Address of the token\n * @return blockNumberOrSecond returns the last accrued block or second\n */\n function lastAccruedBlock(address token_) external view returns (uint256) {\n return lastAccruedBlockOrSecond[token_];\n }\n\n /**\n * @notice Get the tokens accrued\n * @param token_ Address of the token\n * @return returns the amount of accrued tokens for the token provided\n */\n function tokenAmountAccrued(address token_) external view returns (uint256) {\n return _tokenAmountAccrued[token_];\n }\n\n /**\n * @notice Initialize the distribution of the token\n * @param token_ Address of the token to be intialized\n * @custom:event Emits TokenDistributionInitialized event\n * @custom:error Throw TokenAlreadyInitialized if token is already initialized\n */\n function _initializeToken(address token_) internal {\n _ensureZeroAddress(token_);\n uint256 blockNumberOrSecond = getBlockNumberOrTimestamp();\n uint256 initializedBlockOrSecond = lastAccruedBlockOrSecond[token_];\n\n if (initializedBlockOrSecond != 0) {\n revert TokenAlreadyInitialized(token_);\n }\n\n /*\n * Update token state block number or second\n */\n lastAccruedBlockOrSecond[token_] = blockNumberOrSecond;\n\n emit TokenDistributionInitialized(token_);\n }\n\n /**\n * @notice Set distribution speed (amount of token distribute per block or second)\n * @param token_ Address of the token\n * @param distributionSpeed_ New distribution speed for token\n * @custom:event Emits TokenDistributionSpeedUpdated event\n * @custom:error Throw InvalidDistributionSpeed if speed is greater than max speed\n */\n function _setTokenDistributionSpeed(address token_, uint256 distributionSpeed_) internal {\n uint256 maxDistributionSpeed = maxTokenDistributionSpeeds[token_];\n if (maxDistributionSpeed == 0) {\n maxTokenDistributionSpeeds[token_] = maxDistributionSpeed = DEFAULT_MAX_DISTRIBUTION_SPEED;\n }\n\n if (distributionSpeed_ > maxDistributionSpeed) {\n revert InvalidDistributionSpeed(distributionSpeed_, maxDistributionSpeed);\n }\n\n uint256 oldDistributionSpeed = tokenDistributionSpeeds[token_];\n if (oldDistributionSpeed != distributionSpeed_) {\n // Distribution speed updated so let's update distribution state to ensure that\n // 1. Token accrued properly for the old speed, and\n // 2. Token accrued at the new speed starts after this block or second.\n accrueTokens(token_);\n\n // Update speed\n tokenDistributionSpeeds[token_] = distributionSpeed_;\n\n emit TokenDistributionSpeedUpdated(token_, oldDistributionSpeed, distributionSpeed_);\n }\n }\n\n /**\n * @notice Set max distribution speed (amount of maximum token distribute per block or second)\n * @param token_ Address of the token\n * @param maxDistributionSpeed_ New max distribution speed for token\n * @custom:event Emits MaxTokenDistributionSpeedUpdated event\n */\n function _setMaxTokenDistributionSpeed(address token_, uint256 maxDistributionSpeed_) internal {\n emit MaxTokenDistributionSpeedUpdated(token_, tokenDistributionSpeeds[token_], maxDistributionSpeed_);\n maxTokenDistributionSpeeds[token_] = maxDistributionSpeed_;\n }\n\n /**\n * @notice Revert on non initialized token\n * @param token_ Token Address to be verified for\n */\n function _ensureTokenInitialized(address token_) internal view {\n uint256 lastBlockOrSecondAccrued = lastAccruedBlockOrSecond[token_];\n\n if (lastBlockOrSecondAccrued == 0) {\n revert TokenNotInitialized(token_);\n }\n }\n\n /**\n * @notice Revert on zero address\n * @param address_ Address to be verified\n */\n function _ensureZeroAddress(address address_) internal pure {\n if (address_ == address(0)) {\n revert InvalidArguments();\n }\n }\n}\n" + }, + "@venusprotocol/venus-protocol/contracts/Tokens/Prime/PrimeLiquidityProviderStorage.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\n\n/**\n * @title PrimeLiquidityProviderStorageV1\n * @author Venus\n * @notice Storage for Prime Liquidity Provider\n */\ncontract PrimeLiquidityProviderStorageV1 {\n /// @notice Address of the Prime contract\n address public prime;\n\n /// @notice The rate at which token is distributed (per block or second)\n mapping(address => uint256) public tokenDistributionSpeeds;\n\n /// @notice The max token distribution speed for token\n mapping(address => uint256) public maxTokenDistributionSpeeds;\n\n /// @notice The block or second till which rewards are distributed for an asset\n mapping(address => uint256) public lastAccruedBlockOrSecond;\n\n /// @notice The token accrued but not yet transferred to prime contract\n mapping(address => uint256) internal _tokenAmountAccrued;\n\n /// @dev This empty reserved space is put in place to allow future versions to add new\n /// variables without shifting down storage in the inheritance chain.\n uint256[45] private __gap;\n}\n" + }, + "@venusprotocol/venus-protocol/contracts/Tokens/Prime/PrimeStorage.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\n\nimport { ResilientOracleInterface } from \"@venusprotocol/oracle/contracts/interfaces/OracleInterface.sol\";\n\n/**\n * @title PrimeStorageV1\n * @author Venus\n * @notice Storage for Prime Token\n */\ncontract PrimeStorageV1 {\n struct Token {\n bool exists;\n bool isIrrevocable;\n }\n\n struct Market {\n uint256 supplyMultiplier;\n uint256 borrowMultiplier;\n uint256 rewardIndex;\n uint256 sumOfMembersScore;\n bool exists;\n }\n\n struct Interest {\n uint256 accrued;\n uint256 score;\n uint256 rewardIndex;\n }\n\n struct PendingReward {\n address vToken;\n address rewardToken;\n uint256 amount;\n }\n\n /// @notice Base unit for computations, usually used in scaling (multiplications, divisions)\n uint256 internal constant EXP_SCALE = 1e18;\n\n /// @notice maximum BPS = 100%\n uint256 internal constant MAXIMUM_BPS = 1e4;\n\n /// @notice Mapping to get prime token's metadata\n mapping(address => Token) public tokens;\n\n /// @notice Tracks total irrevocable tokens minted\n uint256 public totalIrrevocable;\n\n /// @notice Tracks total revocable tokens minted\n uint256 public totalRevocable;\n\n /// @notice Indicates maximum revocable tokens that can be minted\n uint256 public revocableLimit;\n\n /// @notice Indicates maximum irrevocable tokens that can be minted\n uint256 public irrevocableLimit;\n\n /// @notice Tracks when prime token eligible users started staking for claiming prime token\n mapping(address => uint256) public stakedAt;\n\n /// @notice vToken to market configuration\n mapping(address => Market) public markets;\n\n /// @notice vToken to user to user index\n mapping(address => mapping(address => Interest)) public interests;\n\n /// @notice A list of boosted markets\n address[] internal _allMarkets;\n\n /// @notice numerator of alpha. Ex: if alpha is 0.5 then this will be 1\n uint128 public alphaNumerator;\n\n /// @notice denominator of alpha. Ex: if alpha is 0.5 then this will be 2\n uint128 public alphaDenominator;\n\n /// @notice address of XVS vault\n address public xvsVault;\n\n /// @notice address of XVS vault reward token\n address public xvsVaultRewardToken;\n\n /// @notice address of XVS vault pool id\n uint256 public xvsVaultPoolId;\n\n /// @notice mapping to check if a account's score was updated in the round\n mapping(uint256 => mapping(address => bool)) public isScoreUpdated;\n\n /// @notice unique id for next round\n uint256 public nextScoreUpdateRoundId;\n\n /// @notice total number of accounts whose score needs to be updated\n uint256 public totalScoreUpdatesRequired;\n\n /// @notice total number of accounts whose score is yet to be updated\n uint256 public pendingScoreUpdates;\n\n /// @notice mapping used to find if an asset is part of prime markets\n mapping(address => address) public vTokenForAsset;\n\n /// @notice Address of core pool comptroller contract\n address internal corePoolComptroller;\n\n /// @notice unreleased income from PLP that's already distributed to prime holders\n /// @dev mapping of asset address => amount\n mapping(address => uint256) public unreleasedPLPIncome;\n\n /// @notice The address of PLP contract\n address public primeLiquidityProvider;\n\n /// @notice The address of ResilientOracle contract\n ResilientOracleInterface public oracle;\n\n /// @notice The address of PoolRegistry contract\n address public poolRegistry;\n\n /// @dev This empty reserved space is put in place to allow future versions to add new\n /// variables without shifting down storage in the inheritance chain.\n uint256[26] private __gap;\n}\n" + }, + "contracts/CheckpointView.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\n\nimport { ensureNonzeroAddress } from \"@venusprotocol/solidity-utilities/contracts/validators.sol\";\nimport { Address } from \"@openzeppelin/contracts/utils/Address.sol\";\n\n/**\n * @title Venus CheckpointView Contract\n * @notice A contract that calls a view function from two different contracts\n * based on whether a checkpoint in time has passed. Using this contract, we\n * can change dependencies at a certain timestamp, which is useful for\n * scheduled changes in, e.g., interest rate models.\n * @author Venus\n */\ncontract CheckpointView {\n using Address for address;\n\n address public immutable DATA_SOURCE_1;\n address public immutable DATA_SOURCE_2;\n uint256 public immutable CHECKPOINT_TIMESTAMP;\n\n /**\n * @notice Constructor\n * @param dataSource1 Data source to use before the checkpoint\n * @param dataSource2 Data source to use after the checkpoint\n * @param checkpointTimestamp Checkpoint timestamp\n */\n constructor(address dataSource1, address dataSource2, uint256 checkpointTimestamp) {\n ensureNonzeroAddress(address(dataSource1));\n ensureNonzeroAddress(address(dataSource2));\n DATA_SOURCE_1 = dataSource1;\n DATA_SOURCE_2 = dataSource2;\n CHECKPOINT_TIMESTAMP = checkpointTimestamp;\n }\n\n /**\n * @notice Fallback function that proxies the view calls to the current data source\n * @param input Input data (with a function selector) for the call\n */\n fallback(bytes calldata input) external returns (bytes memory) {\n return _getCurrentDataSource().functionStaticCall(input);\n }\n\n /**\n * @notice Returns the current data source contract (either the old one or the new one)\n * @return Data source contract in use\n */\n function currentDataSource() external view returns (address) {\n return _getCurrentDataSource();\n }\n\n /**\n * @dev Returns the current data source contract (either the old one or the new one)\n * @return Data source contract in use\n */\n function _getCurrentDataSource() internal view returns (address) {\n return (block.timestamp < CHECKPOINT_TIMESTAMP) ? DATA_SOURCE_1 : DATA_SOURCE_2;\n }\n}\n" + }, + "contracts/Comptroller.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\n\nimport { Ownable2StepUpgradeable } from \"@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol\";\nimport { ResilientOracleInterface } from \"@venusprotocol/oracle/contracts/interfaces/OracleInterface.sol\";\nimport { AccessControlledV8 } from \"@venusprotocol/governance-contracts/contracts/Governance/AccessControlledV8.sol\";\nimport { IPrime } from \"@venusprotocol/venus-protocol/contracts/Tokens/Prime/Interfaces/IPrime.sol\";\n\nimport { ComptrollerInterface, Action } from \"./ComptrollerInterface.sol\";\nimport { ComptrollerStorage } from \"./ComptrollerStorage.sol\";\nimport { ExponentialNoError } from \"./ExponentialNoError.sol\";\nimport { VToken } from \"./VToken.sol\";\nimport { RewardsDistributor } from \"./Rewards/RewardsDistributor.sol\";\nimport { MaxLoopsLimitHelper } from \"./MaxLoopsLimitHelper.sol\";\nimport { ensureNonzeroAddress } from \"./lib/validators.sol\";\n\n/**\n * @title Comptroller\n * @author Venus\n * @notice The Comptroller is designed to provide checks for all minting, redeeming, transferring, borrowing, lending, repaying, liquidating,\n * and seizing done by the `vToken` contract. Each pool has one `Comptroller` checking these interactions across markets. When a user interacts\n * with a given market by one of these main actions, a call is made to a corresponding hook in the associated `Comptroller`, which either allows\n * or reverts the transaction. These hooks also update supply and borrow rewards as they are called. The comptroller holds the logic for assessing\n * liquidity snapshots of an account via the collateral factor and liquidation threshold. This check determines the collateral needed for a borrow,\n * as well as how much of a borrow may be liquidated. A user may borrow a portion of their collateral with the maximum amount determined by the\n * markets collateral factor. However, if their borrowed amount exceeds an amount calculated using the market’s corresponding liquidation threshold,\n * the borrow is eligible for liquidation.\n *\n * The `Comptroller` also includes two functions `liquidateAccount()` and `healAccount()`, which are meant to handle accounts that do not exceed\n * the `minLiquidatableCollateral` for the `Comptroller`:\n *\n * - `healAccount()`: This function is called to seize all of a given user’s collateral, requiring the `msg.sender` repay a certain percentage\n * of the debt calculated by `collateral/(borrows*liquidationIncentive)`. The function can only be called if the calculated percentage does not exceed\n * 100%, because otherwise no `badDebt` would be created and `liquidateAccount()` should be used instead. The difference in the actual amount of debt\n * and debt paid off is recorded as `badDebt` for each market, which can then be auctioned off for the risk reserves of the associated pool.\n * - `liquidateAccount()`: This function can only be called if the collateral seized will cover all borrows of an account, as well as the liquidation\n * incentive. Otherwise, the pool will incur bad debt, in which case the function `healAccount()` should be used instead. This function skips the logic\n * verifying that the repay amount does not exceed the close factor.\n */\ncontract Comptroller is\n Ownable2StepUpgradeable,\n AccessControlledV8,\n ComptrollerStorage,\n ComptrollerInterface,\n ExponentialNoError,\n MaxLoopsLimitHelper\n{\n // PoolRegistry, immutable to save on gas\n /// @custom:oz-upgrades-unsafe-allow state-variable-immutable\n address public immutable poolRegistry;\n\n /// @notice Emitted when an account enters a market\n event MarketEntered(VToken indexed vToken, address indexed account);\n\n /// @notice Emitted when an account exits a market\n event MarketExited(VToken indexed vToken, address indexed account);\n\n /// @notice Emitted when close factor is changed by admin\n event NewCloseFactor(uint256 oldCloseFactorMantissa, uint256 newCloseFactorMantissa);\n\n /// @notice Emitted when a collateral factor is changed by admin\n event NewCollateralFactor(VToken vToken, uint256 oldCollateralFactorMantissa, uint256 newCollateralFactorMantissa);\n\n /// @notice Emitted when liquidation threshold is changed by admin\n event NewLiquidationThreshold(\n VToken vToken,\n uint256 oldLiquidationThresholdMantissa,\n uint256 newLiquidationThresholdMantissa\n );\n\n /// @notice Emitted when liquidation incentive is changed by admin\n event NewLiquidationIncentive(uint256 oldLiquidationIncentiveMantissa, uint256 newLiquidationIncentiveMantissa);\n\n /// @notice Emitted when price oracle is changed\n event NewPriceOracle(ResilientOracleInterface oldPriceOracle, ResilientOracleInterface newPriceOracle);\n\n /// @notice Emitted when an action is paused on a market\n event ActionPausedMarket(VToken vToken, Action action, bool pauseState);\n\n /// @notice Emitted when borrow cap for a vToken is changed\n event NewBorrowCap(VToken indexed vToken, uint256 newBorrowCap);\n\n /// @notice Emitted when the collateral threshold (in USD) for non-batch liquidations is changed\n event NewMinLiquidatableCollateral(uint256 oldMinLiquidatableCollateral, uint256 newMinLiquidatableCollateral);\n\n /// @notice Emitted when supply cap for a vToken is changed\n event NewSupplyCap(VToken indexed vToken, uint256 newSupplyCap);\n\n /// @notice Emitted when a rewards distributor is added\n event NewRewardsDistributor(address indexed rewardsDistributor, address indexed rewardToken);\n\n /// @notice Emitted when a market is supported\n event MarketSupported(VToken vToken);\n\n /// @notice Emitted when prime token contract address is changed\n event NewPrimeToken(IPrime oldPrimeToken, IPrime newPrimeToken);\n\n /// @notice Emitted when forced liquidation is enabled or disabled for a market\n event IsForcedLiquidationEnabledUpdated(address indexed vToken, bool enable);\n\n /// @notice Emitted when a market is unlisted\n event MarketUnlisted(address indexed vToken);\n /// @notice Emitted when the borrowing or redeeming delegate rights are updated for an account\n event DelegateUpdated(address indexed approver, address indexed delegate, bool approved);\n\n /// @notice Thrown when collateral factor exceeds the upper bound\n error InvalidCollateralFactor();\n\n /// @notice Thrown when liquidation threshold exceeds the collateral factor\n error InvalidLiquidationThreshold();\n\n /// @notice Thrown when the action is only available to specific sender, but the real sender was different\n error UnexpectedSender(address expectedSender, address actualSender);\n\n /// @notice Thrown when the oracle returns an invalid price for some asset\n error PriceError(address vToken);\n\n /// @notice Thrown if VToken unexpectedly returned a nonzero error code while trying to get account snapshot\n error SnapshotError(address vToken, address user);\n\n /// @notice Thrown when the market is not listed\n error MarketNotListed(address market);\n\n /// @notice Thrown when a market has an unexpected comptroller\n error ComptrollerMismatch();\n\n /// @notice Thrown when user is not member of market\n error MarketNotCollateral(address vToken, address user);\n\n /// @notice Thrown when borrow action is not paused\n error BorrowActionNotPaused();\n\n /// @notice Thrown when mint action is not paused\n error MintActionNotPaused();\n\n /// @notice Thrown when redeem action is not paused\n error RedeemActionNotPaused();\n\n /// @notice Thrown when repay action is not paused\n error RepayActionNotPaused();\n\n /// @notice Thrown when seize action is not paused\n error SeizeActionNotPaused();\n\n /// @notice Thrown when exit market action is not paused\n error ExitMarketActionNotPaused();\n\n /// @notice Thrown when transfer action is not paused\n error TransferActionNotPaused();\n\n /// @notice Thrown when enter market action is not paused\n error EnterMarketActionNotPaused();\n\n /// @notice Thrown when liquidate action is not paused\n error LiquidateActionNotPaused();\n\n /// @notice Thrown when borrow cap is not zero\n error BorrowCapIsNotZero();\n\n /// @notice Thrown when supply cap is not zero\n error SupplyCapIsNotZero();\n\n /// @notice Thrown when collateral factor is not zero\n error CollateralFactorIsNotZero();\n\n /**\n * @notice Thrown during the liquidation if user's total collateral amount is lower than\n * a predefined threshold. In this case only batch liquidations (either liquidateAccount\n * or healAccount) are available.\n */\n error MinimalCollateralViolated(uint256 expectedGreaterThan, uint256 actual);\n error CollateralExceedsThreshold(uint256 expectedLessThanOrEqualTo, uint256 actual);\n error InsufficientCollateral(uint256 collateralToSeize, uint256 availableCollateral);\n\n /// @notice Thrown when the account doesn't have enough liquidity to redeem or borrow\n error InsufficientLiquidity();\n\n /// @notice Thrown when trying to liquidate a healthy account\n error InsufficientShortfall();\n\n /// @notice Thrown when trying to repay more than allowed by close factor\n error TooMuchRepay();\n\n /// @notice Thrown if the user is trying to exit a market in which they have an outstanding debt\n error NonzeroBorrowBalance();\n\n /// @notice Thrown when trying to perform an action that is paused\n error ActionPaused(address market, Action action);\n\n /// @notice Thrown when trying to add a market that is already listed\n error MarketAlreadyListed(address market);\n\n /// @notice Thrown if the supply cap is exceeded\n error SupplyCapExceeded(address market, uint256 cap);\n\n /// @notice Thrown if the borrow cap is exceeded\n error BorrowCapExceeded(address market, uint256 cap);\n\n /// @notice Thrown if delegate approval status is already set to the requested value\n error DelegationStatusUnchanged();\n\n /// @param poolRegistry_ Pool registry address\n /// @custom:oz-upgrades-unsafe-allow constructor\n /// @custom:error ZeroAddressNotAllowed is thrown when pool registry address is zero\n constructor(address poolRegistry_) {\n ensureNonzeroAddress(poolRegistry_);\n\n poolRegistry = poolRegistry_;\n _disableInitializers();\n }\n\n /**\n * @param loopLimit Limit for the loops can iterate to avoid the DOS\n * @param accessControlManager Access control manager contract address\n */\n function initialize(uint256 loopLimit, address accessControlManager) external initializer {\n __Ownable2Step_init();\n __AccessControlled_init_unchained(accessControlManager);\n\n _setMaxLoopsLimit(loopLimit);\n }\n\n /**\n * @notice Add assets to be included in account liquidity calculation; enabling them to be used as collateral\n * @param vTokens The list of addresses of the vToken markets to be enabled\n * @return errors An array of NO_ERROR for compatibility with Venus core tooling\n * @custom:event MarketEntered is emitted for each market on success\n * @custom:error ActionPaused error is thrown if entering any of the markets is paused\n * @custom:error MarketNotListed error is thrown if any of the markets is not listed\n * @custom:access Not restricted\n */\n function enterMarkets(address[] memory vTokens) external override returns (uint256[] memory) {\n uint256 len = vTokens.length;\n\n uint256[] memory results = new uint256[](len);\n for (uint256 i; i < len; ++i) {\n VToken vToken = VToken(vTokens[i]);\n\n _addToMarket(vToken, msg.sender);\n results[i] = NO_ERROR;\n }\n\n return results;\n }\n\n /**\n * @notice Unlist a market by setting isListed to false\n * @dev Checks if all actions are paused, borrow/supply caps is set to 0 and collateral factor is to 0.\n * @param market The address of the market (token) to unlist\n * @return uint256 Always NO_ERROR for compatibility with Venus core tooling\n * @custom:event MarketUnlisted is emitted on success\n * @custom:error MarketNotListed error is thrown when the market is not listed\n * @custom:error BorrowActionNotPaused error is thrown if borrow action is not paused\n * @custom:error MintActionNotPaused error is thrown if mint action is not paused\n * @custom:error RedeemActionNotPaused error is thrown if redeem action is not paused\n * @custom:error RepayActionNotPaused error is thrown if repay action is not paused\n * @custom:error EnterMarketActionNotPaused error is thrown if enter market action is not paused\n * @custom:error LiquidateActionNotPaused error is thrown if liquidate action is not paused\n * @custom:error BorrowCapIsNotZero error is thrown if borrow cap is not zero\n * @custom:error SupplyCapIsNotZero error is thrown if supply cap is not zero\n * @custom:error CollateralFactorIsNotZero error is thrown if collateral factor is not zero\n */\n function unlistMarket(address market) external returns (uint256) {\n _checkAccessAllowed(\"unlistMarket(address)\");\n\n Market storage _market = markets[market];\n\n if (!_market.isListed) {\n revert MarketNotListed(market);\n }\n\n if (!actionPaused(market, Action.BORROW)) {\n revert BorrowActionNotPaused();\n }\n\n if (!actionPaused(market, Action.MINT)) {\n revert MintActionNotPaused();\n }\n\n if (!actionPaused(market, Action.REDEEM)) {\n revert RedeemActionNotPaused();\n }\n\n if (!actionPaused(market, Action.REPAY)) {\n revert RepayActionNotPaused();\n }\n\n if (!actionPaused(market, Action.SEIZE)) {\n revert SeizeActionNotPaused();\n }\n\n if (!actionPaused(market, Action.ENTER_MARKET)) {\n revert EnterMarketActionNotPaused();\n }\n\n if (!actionPaused(market, Action.LIQUIDATE)) {\n revert LiquidateActionNotPaused();\n }\n\n if (!actionPaused(market, Action.TRANSFER)) {\n revert TransferActionNotPaused();\n }\n\n if (!actionPaused(market, Action.EXIT_MARKET)) {\n revert ExitMarketActionNotPaused();\n }\n\n if (borrowCaps[market] != 0) {\n revert BorrowCapIsNotZero();\n }\n\n if (supplyCaps[market] != 0) {\n revert SupplyCapIsNotZero();\n }\n\n if (_market.collateralFactorMantissa != 0) {\n revert CollateralFactorIsNotZero();\n }\n\n _market.isListed = false;\n emit MarketUnlisted(market);\n\n return NO_ERROR;\n }\n\n /**\n * @notice Grants or revokes the borrowing or redeeming delegate rights to / from an account\n * If allowed, the delegate will be able to borrow funds on behalf of the sender\n * Upon a delegated borrow, the delegate will receive the funds, and the borrower\n * will see the debt on their account\n * Upon a delegated redeem, the delegate will receive the redeemed amount and the approver\n * will see a deduction in his vToken balance\n * @param delegate The address to update the rights for\n * @param approved Whether to grant (true) or revoke (false) the borrowing or redeeming rights\n * @custom:event DelegateUpdated emits on success\n * @custom:error ZeroAddressNotAllowed is thrown when delegate address is zero\n * @custom:error DelegationStatusUnchanged is thrown if approval status is already set to the requested value\n * @custom:access Not restricted\n */\n function updateDelegate(address delegate, bool approved) external {\n ensureNonzeroAddress(delegate);\n if (approvedDelegates[msg.sender][delegate] == approved) {\n revert DelegationStatusUnchanged();\n }\n\n approvedDelegates[msg.sender][delegate] = approved;\n emit DelegateUpdated(msg.sender, delegate, approved);\n }\n\n /**\n * @notice Removes asset from sender's account liquidity calculation; disabling them as collateral\n * @dev Sender must not have an outstanding borrow balance in the asset,\n * or be providing necessary collateral for an outstanding borrow.\n * @param vTokenAddress The address of the asset to be removed\n * @return error Always NO_ERROR for compatibility with Venus core tooling\n * @custom:event MarketExited is emitted on success\n * @custom:error ActionPaused error is thrown if exiting the market is paused\n * @custom:error NonzeroBorrowBalance error is thrown if the user has an outstanding borrow in this market\n * @custom:error MarketNotListed error is thrown when the market is not listed\n * @custom:error InsufficientLiquidity error is thrown if exiting the market would lead to user's insolvency\n * @custom:error SnapshotError is thrown if some vToken fails to return the account's supply and borrows\n * @custom:error PriceError is thrown if the oracle returns an incorrect price for some asset\n * @custom:access Not restricted\n */\n function exitMarket(address vTokenAddress) external override returns (uint256) {\n _checkActionPauseState(vTokenAddress, Action.EXIT_MARKET);\n VToken vToken = VToken(vTokenAddress);\n /* Get sender tokensHeld and amountOwed underlying from the vToken */\n (uint256 tokensHeld, uint256 amountOwed, ) = _safeGetAccountSnapshot(vToken, msg.sender);\n\n /* Fail if the sender has a borrow balance */\n if (amountOwed != 0) {\n revert NonzeroBorrowBalance();\n }\n\n /* Fail if the sender is not permitted to redeem all of their tokens */\n _checkRedeemAllowed(vTokenAddress, msg.sender, tokensHeld);\n\n Market storage marketToExit = markets[address(vToken)];\n\n /* Return true if the sender is not already ‘in’ the market */\n if (!marketToExit.accountMembership[msg.sender]) {\n return NO_ERROR;\n }\n\n /* Set vToken account membership to false */\n delete marketToExit.accountMembership[msg.sender];\n\n /* Delete vToken from the account’s list of assets */\n // load into memory for faster iteration\n VToken[] memory userAssetList = accountAssets[msg.sender];\n uint256 len = userAssetList.length;\n\n uint256 assetIndex = len;\n for (uint256 i; i < len; ++i) {\n if (userAssetList[i] == vToken) {\n assetIndex = i;\n break;\n }\n }\n\n // We *must* have found the asset in the list or our redundant data structure is broken\n assert(assetIndex < len);\n\n // copy last item in list to location of item to be removed, reduce length by 1\n VToken[] storage storedList = accountAssets[msg.sender];\n storedList[assetIndex] = storedList[storedList.length - 1];\n storedList.pop();\n\n emit MarketExited(vToken, msg.sender);\n\n return NO_ERROR;\n }\n\n /*** Policy Hooks ***/\n\n /**\n * @notice Checks if the account should be allowed to mint tokens in the given market\n * @param vToken The market to verify the mint against\n * @param minter The account which would get the minted tokens\n * @param mintAmount The amount of underlying being supplied to the market in exchange for tokens\n * @custom:error ActionPaused error is thrown if supplying to this market is paused\n * @custom:error MarketNotListed error is thrown when the market is not listed\n * @custom:error SupplyCapExceeded error is thrown if the total supply exceeds the cap after minting\n * @custom:access Not restricted\n */\n function preMintHook(address vToken, address minter, uint256 mintAmount) external override {\n _checkActionPauseState(vToken, Action.MINT);\n\n if (!markets[vToken].isListed) {\n revert MarketNotListed(address(vToken));\n }\n\n uint256 supplyCap = supplyCaps[vToken];\n // Skipping the cap check for uncapped coins to save some gas\n if (supplyCap != type(uint256).max) {\n uint256 vTokenSupply = VToken(vToken).totalSupply();\n Exp memory exchangeRate = Exp({ mantissa: VToken(vToken).exchangeRateStored() });\n uint256 nextTotalSupply = mul_ScalarTruncateAddUInt(exchangeRate, vTokenSupply, mintAmount);\n if (nextTotalSupply > supplyCap) {\n revert SupplyCapExceeded(vToken, supplyCap);\n }\n }\n\n // Keep the flywheel moving\n uint256 rewardDistributorsCount = rewardsDistributors.length;\n\n for (uint256 i; i < rewardDistributorsCount; ++i) {\n RewardsDistributor rewardsDistributor = rewardsDistributors[i];\n rewardsDistributor.updateRewardTokenSupplyIndex(vToken);\n rewardsDistributor.distributeSupplierRewardToken(vToken, minter);\n }\n }\n\n /**\n * @notice Validates mint, accrues interest and updates score in prime. Reverts on rejection. May emit logs.\n * @param vToken Asset being minted\n * @param minter The address minting the tokens\n * @param actualMintAmount The amount of the underlying asset being minted\n * @param mintTokens The number of tokens being minted\n */\n // solhint-disable-next-line no-unused-vars\n function mintVerify(address vToken, address minter, uint256 actualMintAmount, uint256 mintTokens) external {\n if (address(prime) != address(0)) {\n prime.accrueInterestAndUpdateScore(minter, vToken);\n }\n }\n\n /**\n * @notice Checks if the account should be allowed to redeem tokens in the given market\n * @param vToken The market to verify the redeem against\n * @param redeemer The account which would redeem the tokens\n * @param redeemTokens The number of vTokens to exchange for the underlying asset in the market\n * @custom:error ActionPaused error is thrown if withdrawals are paused in this market\n * @custom:error MarketNotListed error is thrown when the market is not listed\n * @custom:error InsufficientLiquidity error is thrown if the withdrawal would lead to user's insolvency\n * @custom:error SnapshotError is thrown if some vToken fails to return the account's supply and borrows\n * @custom:error PriceError is thrown if the oracle returns an incorrect price for some asset\n * @custom:access Not restricted\n */\n function preRedeemHook(address vToken, address redeemer, uint256 redeemTokens) external override {\n _checkActionPauseState(vToken, Action.REDEEM);\n\n _checkRedeemAllowed(vToken, redeemer, redeemTokens);\n\n // Keep the flywheel moving\n uint256 rewardDistributorsCount = rewardsDistributors.length;\n\n for (uint256 i; i < rewardDistributorsCount; ++i) {\n RewardsDistributor rewardsDistributor = rewardsDistributors[i];\n rewardsDistributor.updateRewardTokenSupplyIndex(vToken);\n rewardsDistributor.distributeSupplierRewardToken(vToken, redeemer);\n }\n }\n\n /**\n * @notice Validates redeem, accrues interest and updates score in prime. Reverts on rejection. May emit logs.\n * @param vToken Asset being redeemed\n * @param redeemer The address redeeming the tokens\n * @param redeemAmount The amount of the underlying asset being redeemed\n * @param redeemTokens The number of tokens being redeemed\n */\n function redeemVerify(address vToken, address redeemer, uint256 redeemAmount, uint256 redeemTokens) external {\n if (address(prime) != address(0)) {\n prime.accrueInterestAndUpdateScore(redeemer, vToken);\n }\n }\n\n /**\n * @notice Validates repayBorrow, accrues interest and updates score in prime. Reverts on rejection. May emit logs.\n * @param vToken Asset being repaid\n * @param payer The address repaying the borrow\n * @param borrower The address of the borrower\n * @param actualRepayAmount The amount of underlying being repaid\n */\n function repayBorrowVerify(\n address vToken,\n address payer, // solhint-disable-line no-unused-vars\n address borrower,\n uint256 actualRepayAmount, // solhint-disable-line no-unused-vars\n uint256 borrowerIndex // solhint-disable-line no-unused-vars\n ) external {\n if (address(prime) != address(0)) {\n prime.accrueInterestAndUpdateScore(borrower, vToken);\n }\n }\n\n /**\n * @notice Validates liquidateBorrow, accrues interest and updates score in prime. Reverts on rejection. May emit logs.\n * @param vTokenBorrowed Asset which was borrowed by the borrower\n * @param vTokenCollateral Asset which was used as collateral and will be seized\n * @param liquidator The address repaying the borrow and seizing the collateral\n * @param borrower The address of the borrower\n * @param actualRepayAmount The amount of underlying being repaid\n * @param seizeTokens The amount of collateral token that will be seized\n */\n function liquidateBorrowVerify(\n address vTokenBorrowed,\n address vTokenCollateral, // solhint-disable-line no-unused-vars\n address liquidator,\n address borrower,\n uint256 actualRepayAmount, // solhint-disable-line no-unused-vars\n uint256 seizeTokens // solhint-disable-line no-unused-vars\n ) external {\n if (address(prime) != address(0)) {\n prime.accrueInterestAndUpdateScore(borrower, vTokenBorrowed);\n prime.accrueInterestAndUpdateScore(liquidator, vTokenBorrowed);\n }\n }\n\n /**\n * @notice Validates seize, accrues interest and updates score in prime. Reverts on rejection. May emit logs.\n * @param vTokenCollateral Asset which was used as collateral and will be seized\n * @param vTokenBorrowed Asset which was borrowed by the borrower\n * @param liquidator The address repaying the borrow and seizing the collateral\n * @param borrower The address of the borrower\n * @param seizeTokens The number of collateral tokens to seize\n */\n function seizeVerify(\n address vTokenCollateral,\n address vTokenBorrowed, // solhint-disable-line no-unused-vars\n address liquidator,\n address borrower,\n uint256 seizeTokens // solhint-disable-line no-unused-vars\n ) external {\n if (address(prime) != address(0)) {\n prime.accrueInterestAndUpdateScore(borrower, vTokenCollateral);\n prime.accrueInterestAndUpdateScore(liquidator, vTokenCollateral);\n }\n }\n\n /**\n * @notice Validates transfer, accrues interest and updates score in prime. Reverts on rejection. May emit logs.\n * @param vToken Asset being transferred\n * @param src The account which sources the tokens\n * @param dst The account which receives the tokens\n * @param transferTokens The number of vTokens to transfer\n */\n // solhint-disable-next-line no-unused-vars\n function transferVerify(address vToken, address src, address dst, uint256 transferTokens) external {\n if (address(prime) != address(0)) {\n prime.accrueInterestAndUpdateScore(src, vToken);\n prime.accrueInterestAndUpdateScore(dst, vToken);\n }\n }\n\n /**\n * @notice Checks if the account should be allowed to borrow the underlying asset of the given market\n * @param vToken The market to verify the borrow against\n * @param borrower The account which would borrow the asset\n * @param borrowAmount The amount of underlying the account would borrow\n * @custom:error ActionPaused error is thrown if borrowing is paused in this market\n * @custom:error MarketNotListed error is thrown when the market is not listed\n * @custom:error InsufficientLiquidity error is thrown if there is not enough collateral to borrow\n * @custom:error BorrowCapExceeded is thrown if the borrow cap will be exceeded should this borrow succeed\n * @custom:error SnapshotError is thrown if some vToken fails to return the account's supply and borrows\n * @custom:error PriceError is thrown if the oracle returns an incorrect price for some asset\n * @custom:access Not restricted if vToken is enabled as collateral, otherwise only vToken\n */\n /// disable-eslint\n function preBorrowHook(address vToken, address borrower, uint256 borrowAmount) external override {\n _checkActionPauseState(vToken, Action.BORROW);\n\n if (!markets[vToken].isListed) {\n revert MarketNotListed(address(vToken));\n }\n\n if (!markets[vToken].accountMembership[borrower]) {\n // only vTokens may call borrowAllowed if borrower not in market\n _checkSenderIs(vToken);\n\n // attempt to add borrower to the market or revert\n _addToMarket(VToken(msg.sender), borrower);\n }\n\n // Update the prices of tokens\n updatePrices(borrower);\n\n if (oracle.getUnderlyingPrice(vToken) == 0) {\n revert PriceError(address(vToken));\n }\n\n uint256 borrowCap = borrowCaps[vToken];\n // Skipping the cap check for uncapped coins to save some gas\n if (borrowCap != type(uint256).max) {\n uint256 totalBorrows = VToken(vToken).totalBorrows();\n uint256 badDebt = VToken(vToken).badDebt();\n uint256 nextTotalBorrows = totalBorrows + borrowAmount + badDebt;\n if (nextTotalBorrows > borrowCap) {\n revert BorrowCapExceeded(vToken, borrowCap);\n }\n }\n\n AccountLiquiditySnapshot memory snapshot = _getHypotheticalLiquiditySnapshot(\n borrower,\n VToken(vToken),\n 0,\n borrowAmount,\n _getCollateralFactor\n );\n\n if (snapshot.shortfall > 0) {\n revert InsufficientLiquidity();\n }\n\n Exp memory borrowIndex = Exp({ mantissa: VToken(vToken).borrowIndex() });\n\n // Keep the flywheel moving\n uint256 rewardDistributorsCount = rewardsDistributors.length;\n\n for (uint256 i; i < rewardDistributorsCount; ++i) {\n RewardsDistributor rewardsDistributor = rewardsDistributors[i];\n rewardsDistributor.updateRewardTokenBorrowIndex(vToken, borrowIndex);\n rewardsDistributor.distributeBorrowerRewardToken(vToken, borrower, borrowIndex);\n }\n }\n\n /**\n * @notice Validates borrow, accrues interest and updates score in prime. Reverts on rejection. May emit logs.\n * @param vToken Asset whose underlying is being borrowed\n * @param borrower The address borrowing the underlying\n * @param borrowAmount The amount of the underlying asset requested to borrow\n */\n // solhint-disable-next-line no-unused-vars\n function borrowVerify(address vToken, address borrower, uint256 borrowAmount) external {\n if (address(prime) != address(0)) {\n prime.accrueInterestAndUpdateScore(borrower, vToken);\n }\n }\n\n /**\n * @notice Checks if the account should be allowed to repay a borrow in the given market\n * @param vToken The market to verify the repay against\n * @param borrower The account which would borrowed the asset\n * @custom:error ActionPaused error is thrown if repayments are paused in this market\n * @custom:error MarketNotListed error is thrown when the market is not listed\n * @custom:access Not restricted\n */\n function preRepayHook(address vToken, address borrower) external override {\n _checkActionPauseState(vToken, Action.REPAY);\n\n oracle.updatePrice(vToken);\n\n if (!markets[vToken].isListed) {\n revert MarketNotListed(address(vToken));\n }\n\n // Keep the flywheel moving\n uint256 rewardDistributorsCount = rewardsDistributors.length;\n\n for (uint256 i; i < rewardDistributorsCount; ++i) {\n Exp memory borrowIndex = Exp({ mantissa: VToken(vToken).borrowIndex() });\n RewardsDistributor rewardsDistributor = rewardsDistributors[i];\n rewardsDistributor.updateRewardTokenBorrowIndex(vToken, borrowIndex);\n rewardsDistributor.distributeBorrowerRewardToken(vToken, borrower, borrowIndex);\n }\n }\n\n /**\n * @notice Checks if the liquidation should be allowed to occur\n * @param vTokenBorrowed Asset which was borrowed by the borrower\n * @param vTokenCollateral Asset which was used as collateral and will be seized\n * @param borrower The address of the borrower\n * @param repayAmount The amount of underlying being repaid\n * @param skipLiquidityCheck Allows the borrow to be liquidated regardless of the account liquidity\n * @custom:error ActionPaused error is thrown if liquidations are paused in this market\n * @custom:error MarketNotListed error is thrown if either collateral or borrowed token is not listed\n * @custom:error TooMuchRepay error is thrown if the liquidator is trying to repay more than allowed by close factor\n * @custom:error MinimalCollateralViolated is thrown if the users' total collateral is lower than the threshold for non-batch liquidations\n * @custom:error InsufficientShortfall is thrown when trying to liquidate a healthy account\n * @custom:error SnapshotError is thrown if some vToken fails to return the account's supply and borrows\n * @custom:error PriceError is thrown if the oracle returns an incorrect price for some asset\n */\n function preLiquidateHook(\n address vTokenBorrowed,\n address vTokenCollateral,\n address borrower,\n uint256 repayAmount,\n bool skipLiquidityCheck\n ) external override {\n // Pause Action.LIQUIDATE on BORROWED TOKEN to prevent liquidating it.\n // If we want to pause liquidating to vTokenCollateral, we should pause\n // Action.SEIZE on it\n _checkActionPauseState(vTokenBorrowed, Action.LIQUIDATE);\n\n // Update the prices of tokens\n updatePrices(borrower);\n\n if (!markets[vTokenBorrowed].isListed) {\n revert MarketNotListed(address(vTokenBorrowed));\n }\n if (!markets[vTokenCollateral].isListed) {\n revert MarketNotListed(address(vTokenCollateral));\n }\n\n uint256 borrowBalance = VToken(vTokenBorrowed).borrowBalanceStored(borrower);\n\n /* Allow accounts to be liquidated if it is a forced liquidation */\n if (skipLiquidityCheck || isForcedLiquidationEnabled[vTokenBorrowed]) {\n if (repayAmount > borrowBalance) {\n revert TooMuchRepay();\n }\n return;\n }\n\n /* The borrower must have shortfall and collateral > threshold in order to be liquidatable */\n AccountLiquiditySnapshot memory snapshot = _getCurrentLiquiditySnapshot(borrower, _getLiquidationThreshold);\n\n if (snapshot.totalCollateral <= minLiquidatableCollateral) {\n /* The liquidator should use either liquidateAccount or healAccount */\n revert MinimalCollateralViolated(minLiquidatableCollateral, snapshot.totalCollateral);\n }\n\n if (snapshot.shortfall == 0) {\n revert InsufficientShortfall();\n }\n\n /* The liquidator may not repay more than what is allowed by the closeFactor */\n uint256 maxClose = mul_ScalarTruncate(Exp({ mantissa: closeFactorMantissa }), borrowBalance);\n if (repayAmount > maxClose) {\n revert TooMuchRepay();\n }\n }\n\n /**\n * @notice Checks if the seizing of assets should be allowed to occur\n * @param vTokenCollateral Asset which was used as collateral and will be seized\n * @param seizerContract Contract that tries to seize the asset (either borrowed vToken or Comptroller)\n * @param liquidator The address repaying the borrow and seizing the collateral\n * @param borrower The address of the borrower\n * @custom:error ActionPaused error is thrown if seizing this type of collateral is paused\n * @custom:error MarketNotListed error is thrown if either collateral or borrowed token is not listed\n * @custom:error ComptrollerMismatch error is when seizer contract or seized asset belong to different pools\n * @custom:access Not restricted\n */\n function preSeizeHook(\n address vTokenCollateral,\n address seizerContract,\n address liquidator,\n address borrower\n ) external override {\n // Pause Action.SEIZE on COLLATERAL to prevent seizing it.\n // If we want to pause liquidating vTokenBorrowed, we should pause\n // Action.LIQUIDATE on it\n _checkActionPauseState(vTokenCollateral, Action.SEIZE);\n\n Market storage market = markets[vTokenCollateral];\n\n if (!market.isListed) {\n revert MarketNotListed(vTokenCollateral);\n }\n\n if (seizerContract == address(this)) {\n // If Comptroller is the seizer, just check if collateral's comptroller\n // is equal to the current address\n if (address(VToken(vTokenCollateral).comptroller()) != address(this)) {\n revert ComptrollerMismatch();\n }\n } else {\n // If the seizer is not the Comptroller, check that the seizer is a\n // listed market, and that the markets' comptrollers match\n if (!markets[seizerContract].isListed) {\n revert MarketNotListed(seizerContract);\n }\n if (VToken(vTokenCollateral).comptroller() != VToken(seizerContract).comptroller()) {\n revert ComptrollerMismatch();\n }\n }\n\n if (!market.accountMembership[borrower]) {\n revert MarketNotCollateral(vTokenCollateral, borrower);\n }\n\n // Keep the flywheel moving\n uint256 rewardDistributorsCount = rewardsDistributors.length;\n\n for (uint256 i; i < rewardDistributorsCount; ++i) {\n RewardsDistributor rewardsDistributor = rewardsDistributors[i];\n rewardsDistributor.updateRewardTokenSupplyIndex(vTokenCollateral);\n rewardsDistributor.distributeSupplierRewardToken(vTokenCollateral, borrower);\n rewardsDistributor.distributeSupplierRewardToken(vTokenCollateral, liquidator);\n }\n }\n\n /**\n * @notice Checks if the account should be allowed to transfer tokens in the given market\n * @param vToken The market to verify the transfer against\n * @param src The account which sources the tokens\n * @param dst The account which receives the tokens\n * @param transferTokens The number of vTokens to transfer\n * @custom:error ActionPaused error is thrown if withdrawals are paused in this market\n * @custom:error MarketNotListed error is thrown when the market is not listed\n * @custom:error InsufficientLiquidity error is thrown if the withdrawal would lead to user's insolvency\n * @custom:error SnapshotError is thrown if some vToken fails to return the account's supply and borrows\n * @custom:error PriceError is thrown if the oracle returns an incorrect price for some asset\n * @custom:access Not restricted\n */\n function preTransferHook(address vToken, address src, address dst, uint256 transferTokens) external override {\n _checkActionPauseState(vToken, Action.TRANSFER);\n\n // Currently the only consideration is whether or not\n // the src is allowed to redeem this many tokens\n _checkRedeemAllowed(vToken, src, transferTokens);\n\n // Keep the flywheel moving\n uint256 rewardDistributorsCount = rewardsDistributors.length;\n\n for (uint256 i; i < rewardDistributorsCount; ++i) {\n RewardsDistributor rewardsDistributor = rewardsDistributors[i];\n rewardsDistributor.updateRewardTokenSupplyIndex(vToken);\n rewardsDistributor.distributeSupplierRewardToken(vToken, src);\n rewardsDistributor.distributeSupplierRewardToken(vToken, dst);\n }\n }\n\n /*** Pool-level operations ***/\n\n /**\n * @notice Seizes all the remaining collateral, makes msg.sender repay the existing\n * borrows, and treats the rest of the debt as bad debt (for each market).\n * The sender has to repay a certain percentage of the debt, computed as\n * collateral / (borrows * liquidationIncentive).\n * @param user account to heal\n * @custom:error CollateralExceedsThreshold error is thrown when the collateral is too big for healing\n * @custom:error SnapshotError is thrown if some vToken fails to return the account's supply and borrows\n * @custom:error PriceError is thrown if the oracle returns an incorrect price for some asset\n * @custom:access Not restricted\n */\n function healAccount(address user) external {\n VToken[] memory userAssets = getAssetsIn(user);\n uint256 userAssetsCount = userAssets.length;\n\n address liquidator = msg.sender;\n {\n ResilientOracleInterface oracle_ = oracle;\n // We need all user's markets to be fresh for the computations to be correct\n for (uint256 i; i < userAssetsCount; ++i) {\n userAssets[i].accrueInterest();\n oracle_.updatePrice(address(userAssets[i]));\n }\n }\n\n AccountLiquiditySnapshot memory snapshot = _getCurrentLiquiditySnapshot(user, _getLiquidationThreshold);\n\n if (snapshot.totalCollateral > minLiquidatableCollateral) {\n revert CollateralExceedsThreshold(minLiquidatableCollateral, snapshot.totalCollateral);\n }\n\n if (snapshot.shortfall == 0) {\n revert InsufficientShortfall();\n }\n\n // percentage = collateral / (borrows * liquidation incentive)\n Exp memory collateral = Exp({ mantissa: snapshot.totalCollateral });\n Exp memory scaledBorrows = mul_(\n Exp({ mantissa: snapshot.borrows }),\n Exp({ mantissa: liquidationIncentiveMantissa })\n );\n\n Exp memory percentage = div_(collateral, scaledBorrows);\n if (lessThanExp(Exp({ mantissa: MANTISSA_ONE }), percentage)) {\n revert CollateralExceedsThreshold(scaledBorrows.mantissa, collateral.mantissa);\n }\n\n for (uint256 i; i < userAssetsCount; ++i) {\n VToken market = userAssets[i];\n\n (uint256 tokens, uint256 borrowBalance, ) = _safeGetAccountSnapshot(market, user);\n uint256 repaymentAmount = mul_ScalarTruncate(percentage, borrowBalance);\n\n // Seize the entire collateral\n if (tokens != 0) {\n market.seize(liquidator, user, tokens);\n }\n // Repay a certain percentage of the borrow, forgive the rest\n if (borrowBalance != 0) {\n market.healBorrow(liquidator, user, repaymentAmount);\n }\n }\n }\n\n /**\n * @notice Liquidates all borrows of the borrower. Callable only if the collateral is less than\n * a predefined threshold, and the account collateral can be seized to cover all borrows. If\n * the collateral is higher than the threshold, use regular liquidations. If the collateral is\n * below the threshold, and the account is insolvent, use healAccount.\n * @param borrower the borrower address\n * @param orders an array of liquidation orders\n * @custom:error CollateralExceedsThreshold error is thrown when the collateral is too big for a batch liquidation\n * @custom:error InsufficientCollateral error is thrown when there is not enough collateral to cover the debt\n * @custom:error SnapshotError is thrown if some vToken fails to return the account's supply and borrows\n * @custom:error PriceError is thrown if the oracle returns an incorrect price for some asset\n * @custom:access Not restricted\n */\n function liquidateAccount(address borrower, LiquidationOrder[] calldata orders) external {\n // We will accrue interest and update the oracle prices later during the liquidation\n\n AccountLiquiditySnapshot memory snapshot = _getCurrentLiquiditySnapshot(borrower, _getLiquidationThreshold);\n\n if (snapshot.totalCollateral > minLiquidatableCollateral) {\n // You should use the regular vToken.liquidateBorrow(...) call\n revert CollateralExceedsThreshold(minLiquidatableCollateral, snapshot.totalCollateral);\n }\n\n uint256 collateralToSeize = mul_ScalarTruncate(\n Exp({ mantissa: liquidationIncentiveMantissa }),\n snapshot.borrows\n );\n if (collateralToSeize >= snapshot.totalCollateral) {\n // There is not enough collateral to seize. Use healBorrow to repay some part of the borrow\n // and record bad debt.\n revert InsufficientCollateral(collateralToSeize, snapshot.totalCollateral);\n }\n\n if (snapshot.shortfall == 0) {\n revert InsufficientShortfall();\n }\n\n uint256 ordersCount = orders.length;\n\n _ensureMaxLoops(ordersCount / 2);\n\n for (uint256 i; i < ordersCount; ++i) {\n if (!markets[address(orders[i].vTokenBorrowed)].isListed) {\n revert MarketNotListed(address(orders[i].vTokenBorrowed));\n }\n if (!markets[address(orders[i].vTokenCollateral)].isListed) {\n revert MarketNotListed(address(orders[i].vTokenCollateral));\n }\n\n LiquidationOrder calldata order = orders[i];\n order.vTokenBorrowed.forceLiquidateBorrow(\n msg.sender,\n borrower,\n order.repayAmount,\n order.vTokenCollateral,\n true\n );\n }\n\n VToken[] memory borrowMarkets = getAssetsIn(borrower);\n uint256 marketsCount = borrowMarkets.length;\n\n for (uint256 i; i < marketsCount; ++i) {\n (, uint256 borrowBalance, ) = _safeGetAccountSnapshot(borrowMarkets[i], borrower);\n require(borrowBalance == 0, \"Nonzero borrow balance after liquidation\");\n }\n }\n\n /**\n * @notice Sets the closeFactor to use when liquidating borrows\n * @param newCloseFactorMantissa New close factor, scaled by 1e18\n * @custom:event Emits NewCloseFactor on success\n * @custom:access Controlled by AccessControlManager\n */\n function setCloseFactor(uint256 newCloseFactorMantissa) external {\n _checkAccessAllowed(\"setCloseFactor(uint256)\");\n require(MAX_CLOSE_FACTOR_MANTISSA >= newCloseFactorMantissa, \"Close factor greater than maximum close factor\");\n require(MIN_CLOSE_FACTOR_MANTISSA <= newCloseFactorMantissa, \"Close factor smaller than minimum close factor\");\n\n uint256 oldCloseFactorMantissa = closeFactorMantissa;\n closeFactorMantissa = newCloseFactorMantissa;\n emit NewCloseFactor(oldCloseFactorMantissa, newCloseFactorMantissa);\n }\n\n /**\n * @notice Sets the collateralFactor for a market\n * @dev This function is restricted by the AccessControlManager\n * @param vToken The market to set the factor on\n * @param newCollateralFactorMantissa The new collateral factor, scaled by 1e18\n * @param newLiquidationThresholdMantissa The new liquidation threshold, scaled by 1e18\n * @custom:event Emits NewCollateralFactor when collateral factor is updated\n * and NewLiquidationThreshold when liquidation threshold is updated\n * @custom:error MarketNotListed error is thrown when the market is not listed\n * @custom:error InvalidCollateralFactor error is thrown when collateral factor is too high\n * @custom:error InvalidLiquidationThreshold error is thrown when liquidation threshold is lower than collateral factor\n * @custom:error PriceError is thrown when the oracle returns an invalid price for the asset\n * @custom:access Controlled by AccessControlManager\n */\n function setCollateralFactor(\n VToken vToken,\n uint256 newCollateralFactorMantissa,\n uint256 newLiquidationThresholdMantissa\n ) external {\n _checkAccessAllowed(\"setCollateralFactor(address,uint256,uint256)\");\n\n // Verify market is listed\n Market storage market = markets[address(vToken)];\n if (!market.isListed) {\n revert MarketNotListed(address(vToken));\n }\n\n // Check collateral factor <= 0.9\n if (newCollateralFactorMantissa > MAX_COLLATERAL_FACTOR_MANTISSA) {\n revert InvalidCollateralFactor();\n }\n\n // Ensure that liquidation threshold <= 1\n if (newLiquidationThresholdMantissa > MANTISSA_ONE) {\n revert InvalidLiquidationThreshold();\n }\n\n // Ensure that liquidation threshold >= CF\n if (newLiquidationThresholdMantissa < newCollateralFactorMantissa) {\n revert InvalidLiquidationThreshold();\n }\n\n // If collateral factor != 0, fail if price == 0\n if (newCollateralFactorMantissa != 0 && oracle.getUnderlyingPrice(address(vToken)) == 0) {\n revert PriceError(address(vToken));\n }\n\n uint256 oldCollateralFactorMantissa = market.collateralFactorMantissa;\n if (newCollateralFactorMantissa != oldCollateralFactorMantissa) {\n market.collateralFactorMantissa = newCollateralFactorMantissa;\n emit NewCollateralFactor(vToken, oldCollateralFactorMantissa, newCollateralFactorMantissa);\n }\n\n uint256 oldLiquidationThresholdMantissa = market.liquidationThresholdMantissa;\n if (newLiquidationThresholdMantissa != oldLiquidationThresholdMantissa) {\n market.liquidationThresholdMantissa = newLiquidationThresholdMantissa;\n emit NewLiquidationThreshold(vToken, oldLiquidationThresholdMantissa, newLiquidationThresholdMantissa);\n }\n }\n\n /**\n * @notice Sets liquidationIncentive\n * @dev This function is restricted by the AccessControlManager\n * @param newLiquidationIncentiveMantissa New liquidationIncentive scaled by 1e18\n * @custom:event Emits NewLiquidationIncentive on success\n * @custom:access Controlled by AccessControlManager\n */\n function setLiquidationIncentive(uint256 newLiquidationIncentiveMantissa) external {\n require(newLiquidationIncentiveMantissa >= MANTISSA_ONE, \"liquidation incentive should be greater than 1e18\");\n\n _checkAccessAllowed(\"setLiquidationIncentive(uint256)\");\n\n // Save current value for use in log\n uint256 oldLiquidationIncentiveMantissa = liquidationIncentiveMantissa;\n\n // Set liquidation incentive to new incentive\n liquidationIncentiveMantissa = newLiquidationIncentiveMantissa;\n\n // Emit event with old incentive, new incentive\n emit NewLiquidationIncentive(oldLiquidationIncentiveMantissa, newLiquidationIncentiveMantissa);\n }\n\n /**\n * @notice Add the market to the markets mapping and set it as listed\n * @dev Only callable by the PoolRegistry\n * @param vToken The address of the market (token) to list\n * @custom:error MarketAlreadyListed is thrown if the market is already listed in this pool\n * @custom:access Only PoolRegistry\n */\n function supportMarket(VToken vToken) external {\n _checkSenderIs(poolRegistry);\n\n if (markets[address(vToken)].isListed) {\n revert MarketAlreadyListed(address(vToken));\n }\n\n require(vToken.isVToken(), \"Comptroller: Invalid vToken\"); // Sanity check to make sure its really a VToken\n\n Market storage newMarket = markets[address(vToken)];\n newMarket.isListed = true;\n newMarket.collateralFactorMantissa = 0;\n newMarket.liquidationThresholdMantissa = 0;\n\n _addMarket(address(vToken));\n\n uint256 rewardDistributorsCount = rewardsDistributors.length;\n\n for (uint256 i; i < rewardDistributorsCount; ++i) {\n rewardsDistributors[i].initializeMarket(address(vToken));\n }\n\n emit MarketSupported(vToken);\n }\n\n /**\n * @notice Set the given borrow caps for the given vToken markets. Borrowing that brings total borrows to or above borrow cap will revert.\n * @dev This function is restricted by the AccessControlManager\n * @dev A borrow cap of type(uint256).max corresponds to unlimited borrowing.\n * @dev Borrow caps smaller than the current total borrows are accepted. This way, new borrows will not be allowed\n until the total borrows amount goes below the new borrow cap\n * @param vTokens The addresses of the markets (tokens) to change the borrow caps for\n * @param newBorrowCaps The new borrow cap values in underlying to be set. A value of type(uint256).max corresponds to unlimited borrowing.\n * @custom:access Controlled by AccessControlManager\n */\n function setMarketBorrowCaps(VToken[] calldata vTokens, uint256[] calldata newBorrowCaps) external {\n _checkAccessAllowed(\"setMarketBorrowCaps(address[],uint256[])\");\n\n uint256 numMarkets = vTokens.length;\n uint256 numBorrowCaps = newBorrowCaps.length;\n\n require(numMarkets != 0 && numMarkets == numBorrowCaps, \"invalid input\");\n\n _ensureMaxLoops(numMarkets);\n\n for (uint256 i; i < numMarkets; ++i) {\n borrowCaps[address(vTokens[i])] = newBorrowCaps[i];\n emit NewBorrowCap(vTokens[i], newBorrowCaps[i]);\n }\n }\n\n /**\n * @notice Set the given supply caps for the given vToken markets. Supply that brings total Supply to or above supply cap will revert.\n * @dev This function is restricted by the AccessControlManager\n * @dev A supply cap of type(uint256).max corresponds to unlimited supply.\n * @dev Supply caps smaller than the current total supplies are accepted. This way, new supplies will not be allowed\n until the total supplies amount goes below the new supply cap\n * @param vTokens The addresses of the markets (tokens) to change the supply caps for\n * @param newSupplyCaps The new supply cap values in underlying to be set. A value of type(uint256).max corresponds to unlimited supply.\n * @custom:access Controlled by AccessControlManager\n */\n function setMarketSupplyCaps(VToken[] calldata vTokens, uint256[] calldata newSupplyCaps) external {\n _checkAccessAllowed(\"setMarketSupplyCaps(address[],uint256[])\");\n uint256 vTokensCount = vTokens.length;\n\n require(vTokensCount != 0, \"invalid number of markets\");\n require(vTokensCount == newSupplyCaps.length, \"invalid number of markets\");\n\n _ensureMaxLoops(vTokensCount);\n\n for (uint256 i; i < vTokensCount; ++i) {\n supplyCaps[address(vTokens[i])] = newSupplyCaps[i];\n emit NewSupplyCap(vTokens[i], newSupplyCaps[i]);\n }\n }\n\n /**\n * @notice Pause/unpause specified actions\n * @dev This function is restricted by the AccessControlManager\n * @param marketsList Markets to pause/unpause the actions on\n * @param actionsList List of action ids to pause/unpause\n * @param paused The new paused state (true=paused, false=unpaused)\n * @custom:access Controlled by AccessControlManager\n */\n function setActionsPaused(VToken[] calldata marketsList, Action[] calldata actionsList, bool paused) external {\n _checkAccessAllowed(\"setActionsPaused(address[],uint256[],bool)\");\n\n uint256 marketsCount = marketsList.length;\n uint256 actionsCount = actionsList.length;\n\n _ensureMaxLoops(marketsCount * actionsCount);\n\n for (uint256 marketIdx; marketIdx < marketsCount; ++marketIdx) {\n for (uint256 actionIdx; actionIdx < actionsCount; ++actionIdx) {\n _setActionPaused(address(marketsList[marketIdx]), actionsList[actionIdx], paused);\n }\n }\n }\n\n /**\n * @notice Set the given collateral threshold for non-batch liquidations. Regular liquidations\n * will fail if the collateral amount is less than this threshold. Liquidators should use batch\n * operations like liquidateAccount or healAccount.\n * @dev This function is restricted by the AccessControlManager\n * @param newMinLiquidatableCollateral The new min liquidatable collateral (in USD).\n * @custom:access Controlled by AccessControlManager\n */\n function setMinLiquidatableCollateral(uint256 newMinLiquidatableCollateral) external {\n _checkAccessAllowed(\"setMinLiquidatableCollateral(uint256)\");\n\n uint256 oldMinLiquidatableCollateral = minLiquidatableCollateral;\n minLiquidatableCollateral = newMinLiquidatableCollateral;\n emit NewMinLiquidatableCollateral(oldMinLiquidatableCollateral, newMinLiquidatableCollateral);\n }\n\n /**\n * @notice Add a new RewardsDistributor and initialize it with all markets. We can add several RewardsDistributor\n * contracts with the same rewardToken, and there could be overlaping among them considering the last reward slot (block or second)\n * @dev Only callable by the admin\n * @param _rewardsDistributor Address of the RewardDistributor contract to add\n * @custom:access Only Governance\n * @custom:event Emits NewRewardsDistributor with distributor address\n */\n function addRewardsDistributor(RewardsDistributor _rewardsDistributor) external onlyOwner {\n require(!rewardsDistributorExists[address(_rewardsDistributor)], \"already exists\");\n\n uint256 rewardsDistributorsLen = rewardsDistributors.length;\n _ensureMaxLoops(rewardsDistributorsLen + 1);\n\n rewardsDistributors.push(_rewardsDistributor);\n rewardsDistributorExists[address(_rewardsDistributor)] = true;\n\n uint256 marketsCount = allMarkets.length;\n\n for (uint256 i; i < marketsCount; ++i) {\n _rewardsDistributor.initializeMarket(address(allMarkets[i]));\n }\n\n emit NewRewardsDistributor(address(_rewardsDistributor), address(_rewardsDistributor.rewardToken()));\n }\n\n /**\n * @notice Sets a new price oracle for the Comptroller\n * @dev Only callable by the admin\n * @param newOracle Address of the new price oracle to set\n * @custom:event Emits NewPriceOracle on success\n * @custom:error ZeroAddressNotAllowed is thrown when the new oracle address is zero\n */\n function setPriceOracle(ResilientOracleInterface newOracle) external onlyOwner {\n ensureNonzeroAddress(address(newOracle));\n\n ResilientOracleInterface oldOracle = oracle;\n oracle = newOracle;\n emit NewPriceOracle(oldOracle, newOracle);\n }\n\n /**\n * @notice Set the for loop iteration limit to avoid DOS\n * @param limit Limit for the max loops can execute at a time\n */\n function setMaxLoopsLimit(uint256 limit) external onlyOwner {\n _setMaxLoopsLimit(limit);\n }\n\n /**\n * @notice Sets the prime token contract for the comptroller\n * @param _prime Address of the Prime contract\n */\n function setPrimeToken(IPrime _prime) external onlyOwner {\n ensureNonzeroAddress(address(_prime));\n\n emit NewPrimeToken(prime, _prime);\n prime = _prime;\n }\n\n /**\n * @notice Enables forced liquidations for a market. If forced liquidation is enabled,\n * borrows in the market may be liquidated regardless of the account liquidity\n * @param vTokenBorrowed Borrowed vToken\n * @param enable Whether to enable forced liquidations\n */\n function setForcedLiquidation(address vTokenBorrowed, bool enable) external {\n _checkAccessAllowed(\"setForcedLiquidation(address,bool)\");\n ensureNonzeroAddress(vTokenBorrowed);\n\n if (!markets[vTokenBorrowed].isListed) {\n revert MarketNotListed(vTokenBorrowed);\n }\n\n isForcedLiquidationEnabled[vTokenBorrowed] = enable;\n emit IsForcedLiquidationEnabledUpdated(vTokenBorrowed, enable);\n }\n\n /**\n * @notice Determine the current account liquidity with respect to liquidation threshold requirements\n * @dev The interface of this function is intentionally kept compatible with Compound and Venus Core\n * @param account The account get liquidity for\n * @return error Always NO_ERROR for compatibility with Venus core tooling\n * @return liquidity Account liquidity in excess of liquidation threshold requirements,\n * @return shortfall Account shortfall below liquidation threshold requirements\n */\n function getAccountLiquidity(\n address account\n ) external view returns (uint256 error, uint256 liquidity, uint256 shortfall) {\n AccountLiquiditySnapshot memory snapshot = _getCurrentLiquiditySnapshot(account, _getLiquidationThreshold);\n return (NO_ERROR, snapshot.liquidity, snapshot.shortfall);\n }\n\n /**\n * @notice Determine the current account liquidity with respect to collateral requirements\n * @dev The interface of this function is intentionally kept compatible with Compound and Venus Core\n * @param account The account get liquidity for\n * @return error Always NO_ERROR for compatibility with Venus core tooling\n * @return liquidity Account liquidity in excess of collateral requirements,\n * @return shortfall Account shortfall below collateral requirements\n */\n function getBorrowingPower(\n address account\n ) external view returns (uint256 error, uint256 liquidity, uint256 shortfall) {\n AccountLiquiditySnapshot memory snapshot = _getCurrentLiquiditySnapshot(account, _getCollateralFactor);\n return (NO_ERROR, snapshot.liquidity, snapshot.shortfall);\n }\n\n /**\n * @notice Determine what the account liquidity would be if the given amounts were redeemed/borrowed\n * @dev The interface of this function is intentionally kept compatible with Compound and Venus Core\n * @param vTokenModify The market to hypothetically redeem/borrow in\n * @param account The account to determine liquidity for\n * @param redeemTokens The number of tokens to hypothetically redeem\n * @param borrowAmount The amount of underlying to hypothetically borrow\n * @return error Always NO_ERROR for compatibility with Venus core tooling\n * @return liquidity Hypothetical account liquidity in excess of collateral requirements,\n * @return shortfall Hypothetical account shortfall below collateral requirements\n */\n function getHypotheticalAccountLiquidity(\n address account,\n address vTokenModify,\n uint256 redeemTokens,\n uint256 borrowAmount\n ) external view returns (uint256 error, uint256 liquidity, uint256 shortfall) {\n AccountLiquiditySnapshot memory snapshot = _getHypotheticalLiquiditySnapshot(\n account,\n VToken(vTokenModify),\n redeemTokens,\n borrowAmount,\n _getCollateralFactor\n );\n return (NO_ERROR, snapshot.liquidity, snapshot.shortfall);\n }\n\n /**\n * @notice Return all of the markets\n * @dev The automatic getter may be used to access an individual market.\n * @return markets The list of market addresses\n */\n function getAllMarkets() external view override returns (VToken[] memory) {\n return allMarkets;\n }\n\n /**\n * @notice Check if a market is marked as listed (active)\n * @param vToken vToken Address for the market to check\n * @return listed True if listed otherwise false\n */\n function isMarketListed(VToken vToken) external view returns (bool) {\n return markets[address(vToken)].isListed;\n }\n\n /*** Assets You Are In ***/\n\n /**\n * @notice Returns whether the given account is entered in a given market\n * @param account The address of the account to check\n * @param vToken The vToken to check\n * @return True if the account is in the market specified, otherwise false.\n */\n function checkMembership(address account, VToken vToken) external view returns (bool) {\n return markets[address(vToken)].accountMembership[account];\n }\n\n /**\n * @notice Calculate number of tokens of collateral asset to seize given an underlying amount\n * @dev Used in liquidation (called in vToken.liquidateBorrowFresh)\n * @param vTokenBorrowed The address of the borrowed vToken\n * @param vTokenCollateral The address of the collateral vToken\n * @param actualRepayAmount The amount of vTokenBorrowed underlying to convert into vTokenCollateral tokens\n * @return error Always NO_ERROR for compatibility with Venus core tooling\n * @return tokensToSeize Number of vTokenCollateral tokens to be seized in a liquidation\n * @custom:error PriceError if the oracle returns an invalid price\n */\n function liquidateCalculateSeizeTokens(\n address vTokenBorrowed,\n address vTokenCollateral,\n uint256 actualRepayAmount\n ) external view override returns (uint256 error, uint256 tokensToSeize) {\n /* Read oracle prices for borrowed and collateral markets */\n uint256 priceBorrowedMantissa = _safeGetUnderlyingPrice(VToken(vTokenBorrowed));\n uint256 priceCollateralMantissa = _safeGetUnderlyingPrice(VToken(vTokenCollateral));\n\n /*\n * Get the exchange rate and calculate the number of collateral tokens to seize:\n * seizeAmount = actualRepayAmount * liquidationIncentive * priceBorrowed / priceCollateral\n * seizeTokens = seizeAmount / exchangeRate\n * = actualRepayAmount * (liquidationIncentive * priceBorrowed) / (priceCollateral * exchangeRate)\n */\n uint256 exchangeRateMantissa = VToken(vTokenCollateral).exchangeRateStored(); // Note: reverts on error\n uint256 seizeTokens;\n Exp memory numerator;\n Exp memory denominator;\n Exp memory ratio;\n\n numerator = mul_(Exp({ mantissa: liquidationIncentiveMantissa }), Exp({ mantissa: priceBorrowedMantissa }));\n denominator = mul_(Exp({ mantissa: priceCollateralMantissa }), Exp({ mantissa: exchangeRateMantissa }));\n ratio = div_(numerator, denominator);\n\n seizeTokens = mul_ScalarTruncate(ratio, actualRepayAmount);\n\n return (NO_ERROR, seizeTokens);\n }\n\n /**\n * @notice Returns reward speed given a vToken\n * @param vToken The vToken to get the reward speeds for\n * @return rewardSpeeds Array of total supply and borrow speeds and reward token for all reward distributors\n */\n function getRewardsByMarket(address vToken) external view returns (RewardSpeeds[] memory rewardSpeeds) {\n uint256 rewardsDistributorsLength = rewardsDistributors.length;\n rewardSpeeds = new RewardSpeeds[](rewardsDistributorsLength);\n for (uint256 i; i < rewardsDistributorsLength; ++i) {\n RewardsDistributor rewardsDistributor = rewardsDistributors[i];\n address rewardToken = address(rewardsDistributor.rewardToken());\n rewardSpeeds[i] = RewardSpeeds({\n rewardToken: rewardToken,\n supplySpeed: rewardsDistributor.rewardTokenSupplySpeeds(vToken),\n borrowSpeed: rewardsDistributor.rewardTokenBorrowSpeeds(vToken)\n });\n }\n return rewardSpeeds;\n }\n\n /**\n * @notice Return all reward distributors for this pool\n * @return Array of RewardDistributor addresses\n */\n function getRewardDistributors() external view returns (RewardsDistributor[] memory) {\n return rewardsDistributors;\n }\n\n /**\n * @notice A marker method that returns true for a valid Comptroller contract\n * @return Always true\n */\n function isComptroller() external pure override returns (bool) {\n return true;\n }\n\n /**\n * @notice Update the prices of all the tokens associated with the provided account\n * @param account Address of the account to get associated tokens with\n */\n function updatePrices(address account) public {\n VToken[] memory vTokens = getAssetsIn(account);\n uint256 vTokensCount = vTokens.length;\n\n ResilientOracleInterface oracle_ = oracle;\n\n for (uint256 i; i < vTokensCount; ++i) {\n oracle_.updatePrice(address(vTokens[i]));\n }\n }\n\n /**\n * @notice Checks if a certain action is paused on a market\n * @param market vToken address\n * @param action Action to check\n * @return paused True if the action is paused otherwise false\n */\n function actionPaused(address market, Action action) public view returns (bool) {\n return _actionPaused[market][action];\n }\n\n /**\n * @notice Returns the assets an account has entered\n * @param account The address of the account to pull assets for\n * @return A list with the assets the account has entered\n */\n function getAssetsIn(address account) public view returns (VToken[] memory) {\n uint256 len;\n VToken[] memory _accountAssets = accountAssets[account];\n uint256 _accountAssetsLength = _accountAssets.length;\n\n VToken[] memory assetsIn = new VToken[](_accountAssetsLength);\n\n for (uint256 i; i < _accountAssetsLength; ++i) {\n Market storage market = markets[address(_accountAssets[i])];\n if (market.isListed) {\n assetsIn[len] = _accountAssets[i];\n ++len;\n }\n }\n\n assembly {\n mstore(assetsIn, len)\n }\n\n return assetsIn;\n }\n\n /**\n * @notice Add the market to the borrower's \"assets in\" for liquidity calculations\n * @param vToken The market to enter\n * @param borrower The address of the account to modify\n */\n function _addToMarket(VToken vToken, address borrower) internal {\n _checkActionPauseState(address(vToken), Action.ENTER_MARKET);\n Market storage marketToJoin = markets[address(vToken)];\n\n if (!marketToJoin.isListed) {\n revert MarketNotListed(address(vToken));\n }\n\n if (marketToJoin.accountMembership[borrower]) {\n // already joined\n return;\n }\n\n // survived the gauntlet, add to list\n // NOTE: we store these somewhat redundantly as a significant optimization\n // this avoids having to iterate through the list for the most common use cases\n // that is, only when we need to perform liquidity checks\n // and not whenever we want to check if an account is in a particular market\n marketToJoin.accountMembership[borrower] = true;\n accountAssets[borrower].push(vToken);\n\n emit MarketEntered(vToken, borrower);\n }\n\n /**\n * @notice Internal function to validate that a market hasn't already been added\n * and if it hasn't adds it\n * @param vToken The market to support\n */\n function _addMarket(address vToken) internal {\n uint256 marketsCount = allMarkets.length;\n\n for (uint256 i; i < marketsCount; ++i) {\n if (allMarkets[i] == VToken(vToken)) {\n revert MarketAlreadyListed(vToken);\n }\n }\n allMarkets.push(VToken(vToken));\n marketsCount = allMarkets.length;\n _ensureMaxLoops(marketsCount);\n }\n\n /**\n * @dev Pause/unpause an action on a market\n * @param market Market to pause/unpause the action on\n * @param action Action id to pause/unpause\n * @param paused The new paused state (true=paused, false=unpaused)\n */\n function _setActionPaused(address market, Action action, bool paused) internal {\n require(markets[market].isListed, \"cannot pause a market that is not listed\");\n _actionPaused[market][action] = paused;\n emit ActionPausedMarket(VToken(market), action, paused);\n }\n\n /**\n * @dev Internal function to check that vTokens can be safely redeemed for the underlying asset.\n * @param vToken Address of the vTokens to redeem\n * @param redeemer Account redeeming the tokens\n * @param redeemTokens The number of tokens to redeem\n */\n function _checkRedeemAllowed(address vToken, address redeemer, uint256 redeemTokens) internal {\n Market storage market = markets[vToken];\n\n if (!market.isListed) {\n revert MarketNotListed(address(vToken));\n }\n\n /* If the redeemer is not 'in' the market, then we can bypass the liquidity check */\n if (!market.accountMembership[redeemer]) {\n return;\n }\n\n // Update the prices of tokens\n updatePrices(redeemer);\n\n /* Otherwise, perform a hypothetical liquidity check to guard against shortfall */\n AccountLiquiditySnapshot memory snapshot = _getHypotheticalLiquiditySnapshot(\n redeemer,\n VToken(vToken),\n redeemTokens,\n 0,\n _getCollateralFactor\n );\n if (snapshot.shortfall > 0) {\n revert InsufficientLiquidity();\n }\n }\n\n /**\n * @notice Get the total collateral, weighted collateral, borrow balance, liquidity, shortfall\n * @param account The account to get the snapshot for\n * @param weight The function to compute the weight of the collateral – either collateral factor or\n * liquidation threshold. Accepts the address of the vToken and returns the weight as Exp.\n * @dev Note that we calculate the exchangeRateStored for each collateral vToken using stored data,\n * without calculating accumulated interest.\n * @return snapshot Account liquidity snapshot\n */\n function _getCurrentLiquiditySnapshot(\n address account,\n function(VToken) internal view returns (Exp memory) weight\n ) internal view returns (AccountLiquiditySnapshot memory snapshot) {\n return _getHypotheticalLiquiditySnapshot(account, VToken(address(0)), 0, 0, weight);\n }\n\n /**\n * @notice Determine what the supply/borrow balances would be if the given amounts were redeemed/borrowed\n * @param vTokenModify The market to hypothetically redeem/borrow in\n * @param account The account to determine liquidity for\n * @param redeemTokens The number of tokens to hypothetically redeem\n * @param borrowAmount The amount of underlying to hypothetically borrow\n * @param weight The function to compute the weight of the collateral – either collateral factor or\n liquidation threshold. Accepts the address of the VToken and returns the weight\n * @dev Note that we calculate the exchangeRateStored for each collateral vToken using stored data,\n * without calculating accumulated interest.\n * @return snapshot Account liquidity snapshot\n */\n function _getHypotheticalLiquiditySnapshot(\n address account,\n VToken vTokenModify,\n uint256 redeemTokens,\n uint256 borrowAmount,\n function(VToken) internal view returns (Exp memory) weight\n ) internal view returns (AccountLiquiditySnapshot memory snapshot) {\n // For each asset the account is in\n VToken[] memory assets = getAssetsIn(account);\n uint256 assetsCount = assets.length;\n\n for (uint256 i; i < assetsCount; ++i) {\n VToken asset = assets[i];\n\n // Read the balances and exchange rate from the vToken\n (uint256 vTokenBalance, uint256 borrowBalance, uint256 exchangeRateMantissa) = _safeGetAccountSnapshot(\n asset,\n account\n );\n\n // Get the normalized price of the asset\n Exp memory oraclePrice = Exp({ mantissa: _safeGetUnderlyingPrice(asset) });\n\n // Pre-compute conversion factors from vTokens -> usd\n Exp memory vTokenPrice = mul_(Exp({ mantissa: exchangeRateMantissa }), oraclePrice);\n Exp memory weightedVTokenPrice = mul_(weight(asset), vTokenPrice);\n\n // weightedCollateral += weightedVTokenPrice * vTokenBalance\n snapshot.weightedCollateral = mul_ScalarTruncateAddUInt(\n weightedVTokenPrice,\n vTokenBalance,\n snapshot.weightedCollateral\n );\n\n // totalCollateral += vTokenPrice * vTokenBalance\n snapshot.totalCollateral = mul_ScalarTruncateAddUInt(vTokenPrice, vTokenBalance, snapshot.totalCollateral);\n\n // borrows += oraclePrice * borrowBalance\n snapshot.borrows = mul_ScalarTruncateAddUInt(oraclePrice, borrowBalance, snapshot.borrows);\n\n // Calculate effects of interacting with vTokenModify\n if (asset == vTokenModify) {\n // redeem effect\n // effects += tokensToDenom * redeemTokens\n snapshot.effects = mul_ScalarTruncateAddUInt(weightedVTokenPrice, redeemTokens, snapshot.effects);\n\n // borrow effect\n // effects += oraclePrice * borrowAmount\n snapshot.effects = mul_ScalarTruncateAddUInt(oraclePrice, borrowAmount, snapshot.effects);\n }\n }\n\n uint256 borrowPlusEffects = snapshot.borrows + snapshot.effects;\n // These are safe, as the underflow condition is checked first\n unchecked {\n if (snapshot.weightedCollateral > borrowPlusEffects) {\n snapshot.liquidity = snapshot.weightedCollateral - borrowPlusEffects;\n snapshot.shortfall = 0;\n } else {\n snapshot.liquidity = 0;\n snapshot.shortfall = borrowPlusEffects - snapshot.weightedCollateral;\n }\n }\n\n return snapshot;\n }\n\n /**\n * @dev Retrieves price from oracle for an asset and checks it is nonzero\n * @param asset Address for asset to query price\n * @return Underlying price\n */\n function _safeGetUnderlyingPrice(VToken asset) internal view returns (uint256) {\n uint256 oraclePriceMantissa = oracle.getUnderlyingPrice(address(asset));\n if (oraclePriceMantissa == 0) {\n revert PriceError(address(asset));\n }\n return oraclePriceMantissa;\n }\n\n /**\n * @dev Return collateral factor for a market\n * @param asset Address for asset\n * @return Collateral factor as exponential\n */\n function _getCollateralFactor(VToken asset) internal view returns (Exp memory) {\n return Exp({ mantissa: markets[address(asset)].collateralFactorMantissa });\n }\n\n /**\n * @dev Retrieves liquidation threshold for a market as an exponential\n * @param asset Address for asset to liquidation threshold\n * @return Liquidation threshold as exponential\n */\n function _getLiquidationThreshold(VToken asset) internal view returns (Exp memory) {\n return Exp({ mantissa: markets[address(asset)].liquidationThresholdMantissa });\n }\n\n /**\n * @dev Returns supply and borrow balances of user in vToken, reverts on failure\n * @param vToken Market to query\n * @param user Account address\n * @return vTokenBalance Balance of vTokens, the same as vToken.balanceOf(user)\n * @return borrowBalance Borrowed amount, including the interest\n * @return exchangeRateMantissa Stored exchange rate\n */\n function _safeGetAccountSnapshot(\n VToken vToken,\n address user\n ) internal view returns (uint256 vTokenBalance, uint256 borrowBalance, uint256 exchangeRateMantissa) {\n uint256 err;\n (err, vTokenBalance, borrowBalance, exchangeRateMantissa) = vToken.getAccountSnapshot(user);\n if (err != 0) {\n revert SnapshotError(address(vToken), user);\n }\n return (vTokenBalance, borrowBalance, exchangeRateMantissa);\n }\n\n /// @notice Reverts if the call is not from expectedSender\n /// @param expectedSender Expected transaction sender\n function _checkSenderIs(address expectedSender) internal view {\n if (msg.sender != expectedSender) {\n revert UnexpectedSender(expectedSender, msg.sender);\n }\n }\n\n /// @notice Reverts if a certain action is paused on a market\n /// @param market Market to check\n /// @param action Action to check\n function _checkActionPauseState(address market, Action action) private view {\n if (actionPaused(market, action)) {\n revert ActionPaused(market, action);\n }\n }\n}\n" + }, + "contracts/ComptrollerInterface.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity ^0.8.25;\n\nimport { ResilientOracleInterface } from \"@venusprotocol/oracle/contracts/interfaces/OracleInterface.sol\";\n\nimport { VToken } from \"./VToken.sol\";\nimport { RewardsDistributor } from \"./Rewards/RewardsDistributor.sol\";\n\nenum Action {\n MINT,\n REDEEM,\n BORROW,\n REPAY,\n SEIZE,\n LIQUIDATE,\n TRANSFER,\n ENTER_MARKET,\n EXIT_MARKET\n}\n\n/**\n * @title ComptrollerInterface\n * @author Venus\n * @notice Interface implemented by the `Comptroller` contract.\n */\ninterface ComptrollerInterface {\n /*** Assets You Are In ***/\n\n function enterMarkets(address[] calldata vTokens) external returns (uint256[] memory);\n\n function exitMarket(address vToken) external returns (uint256);\n\n /*** Policy Hooks ***/\n\n function preMintHook(address vToken, address minter, uint256 mintAmount) external;\n\n function preRedeemHook(address vToken, address redeemer, uint256 redeemTokens) external;\n\n function preBorrowHook(address vToken, address borrower, uint256 borrowAmount) external;\n\n function preRepayHook(address vToken, address borrower) external;\n\n function preLiquidateHook(\n address vTokenBorrowed,\n address vTokenCollateral,\n address borrower,\n uint256 repayAmount,\n bool skipLiquidityCheck\n ) external;\n\n function preSeizeHook(\n address vTokenCollateral,\n address vTokenBorrowed,\n address liquidator,\n address borrower\n ) external;\n\n function borrowVerify(address vToken, address borrower, uint borrowAmount) external;\n\n function mintVerify(address vToken, address minter, uint mintAmount, uint mintTokens) external;\n\n function redeemVerify(address vToken, address redeemer, uint redeemAmount, uint redeemTokens) external;\n\n function repayBorrowVerify(\n address vToken,\n address payer,\n address borrower,\n uint repayAmount,\n uint borrowerIndex\n ) external;\n\n function liquidateBorrowVerify(\n address vTokenBorrowed,\n address vTokenCollateral,\n address liquidator,\n address borrower,\n uint repayAmount,\n uint seizeTokens\n ) external;\n\n function seizeVerify(\n address vTokenCollateral,\n address vTokenBorrowed,\n address liquidator,\n address borrower,\n uint seizeTokens\n ) external;\n\n function transferVerify(address vToken, address src, address dst, uint transferTokens) external;\n\n function preTransferHook(address vToken, address src, address dst, uint256 transferTokens) external;\n\n function isComptroller() external view returns (bool);\n\n /*** Liquidity/Liquidation Calculations ***/\n\n function liquidateCalculateSeizeTokens(\n address vTokenBorrowed,\n address vTokenCollateral,\n uint256 repayAmount\n ) external view returns (uint256, uint256);\n\n function getAllMarkets() external view returns (VToken[] memory);\n\n function actionPaused(address market, Action action) external view returns (bool);\n}\n\n/**\n * @title ComptrollerViewInterface\n * @author Venus\n * @notice Interface implemented by the `Comptroller` contract, including only some util view functions.\n */\ninterface ComptrollerViewInterface {\n function markets(address) external view returns (bool, uint256);\n\n function oracle() external view returns (ResilientOracleInterface);\n\n function getAssetsIn(address) external view returns (VToken[] memory);\n\n function closeFactorMantissa() external view returns (uint256);\n\n function liquidationIncentiveMantissa() external view returns (uint256);\n\n function minLiquidatableCollateral() external view returns (uint256);\n\n function getRewardDistributors() external view returns (RewardsDistributor[] memory);\n\n function getAllMarkets() external view returns (VToken[] memory);\n\n function borrowCaps(address) external view returns (uint256);\n\n function supplyCaps(address) external view returns (uint256);\n\n function approvedDelegates(address user, address delegate) external view returns (bool);\n}\n" + }, + "contracts/ComptrollerStorage.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\n\nimport { ResilientOracleInterface } from \"@venusprotocol/oracle/contracts/interfaces/OracleInterface.sol\";\n\nimport { VToken } from \"./VToken.sol\";\nimport { RewardsDistributor } from \"./Rewards/RewardsDistributor.sol\";\nimport { IPrime } from \"@venusprotocol/venus-protocol/contracts/Tokens/Prime/Interfaces/IPrime.sol\";\nimport { Action } from \"./ComptrollerInterface.sol\";\n\n/**\n * @title ComptrollerStorage\n * @author Venus\n * @notice Storage layout for the `Comptroller` contract.\n */\ncontract ComptrollerStorage {\n struct LiquidationOrder {\n VToken vTokenCollateral;\n VToken vTokenBorrowed;\n uint256 repayAmount;\n }\n\n struct AccountLiquiditySnapshot {\n uint256 totalCollateral;\n uint256 weightedCollateral;\n uint256 borrows;\n uint256 effects;\n uint256 liquidity;\n uint256 shortfall;\n }\n\n struct RewardSpeeds {\n address rewardToken;\n uint256 supplySpeed;\n uint256 borrowSpeed;\n }\n\n struct Market {\n // Whether or not this market is listed\n bool isListed;\n // Multiplier representing the most one can borrow against their collateral in this market.\n // For instance, 0.9 to allow borrowing 90% of collateral value.\n // Must be between 0 and 1, and stored as a mantissa.\n uint256 collateralFactorMantissa;\n // Multiplier representing the collateralization after which the borrow is eligible\n // for liquidation. For instance, 0.8 liquidate when the borrow is 80% of collateral\n // value. Must be between 0 and collateral factor, stored as a mantissa.\n uint256 liquidationThresholdMantissa;\n // Per-market mapping of \"accounts in this asset\"\n mapping(address => bool) accountMembership;\n }\n\n /**\n * @notice Oracle which gives the price of any given asset\n */\n ResilientOracleInterface public oracle;\n\n /**\n * @notice Multiplier used to calculate the maximum repayAmount when liquidating a borrow\n */\n uint256 public closeFactorMantissa;\n\n /**\n * @notice Multiplier representing the discount on collateral that a liquidator receives\n */\n uint256 public liquidationIncentiveMantissa;\n\n /**\n * @notice Per-account mapping of \"assets you are in\"\n */\n mapping(address => VToken[]) public accountAssets;\n\n /**\n * @notice Official mapping of vTokens -> Market metadata\n * @dev Used e.g. to determine if a market is supported\n */\n mapping(address => Market) public markets;\n\n /// @notice A list of all markets\n VToken[] public allMarkets;\n\n /// @notice Borrow caps enforced by borrowAllowed for each vToken address. Defaults to zero which restricts borrowing.\n mapping(address => uint256) public borrowCaps;\n\n /// @notice Minimal collateral required for regular (non-batch) liquidations\n uint256 public minLiquidatableCollateral;\n\n /// @notice Supply caps enforced by mintAllowed for each vToken address. Defaults to zero which corresponds to minting not allowed\n mapping(address => uint256) public supplyCaps;\n\n /// @notice True if a certain action is paused on a certain market\n mapping(address => mapping(Action => bool)) internal _actionPaused;\n\n // List of Reward Distributors added\n RewardsDistributor[] internal rewardsDistributors;\n\n // Used to check if rewards distributor is added\n mapping(address => bool) internal rewardsDistributorExists;\n\n /// @notice Flag indicating whether forced liquidation enabled for a market\n mapping(address => bool) public isForcedLiquidationEnabled;\n\n uint256 internal constant NO_ERROR = 0;\n\n // closeFactorMantissa must be strictly greater than this value\n uint256 internal constant MIN_CLOSE_FACTOR_MANTISSA = 0.05e18; // 0.05\n\n // closeFactorMantissa must not exceed this value\n uint256 internal constant MAX_CLOSE_FACTOR_MANTISSA = 0.9e18; // 0.9\n\n // No collateralFactorMantissa may exceed this value\n uint256 internal constant MAX_COLLATERAL_FACTOR_MANTISSA = 0.95e18; // 0.95\n\n /// Prime token address\n IPrime public prime;\n\n /// @notice Whether the delegate is allowed to borrow or redeem on behalf of the user\n //mapping(address user => mapping (address delegate => bool approved)) public approvedDelegates;\n mapping(address => mapping(address => bool)) public approvedDelegates;\n\n /**\n * @dev This empty reserved space is put in place to allow future versions to add new\n * variables without shifting down storage in the inheritance chain.\n * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\n */\n uint256[47] private __gap;\n}\n" + }, + "contracts/ErrorReporter.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\n\n/**\n * @title TokenErrorReporter\n * @author Venus\n * @notice Errors that can be thrown by the `VToken` contract.\n */\ncontract TokenErrorReporter {\n uint256 public constant NO_ERROR = 0; // support legacy return codes\n\n error TransferNotAllowed();\n\n error MintFreshnessCheck();\n\n error RedeemFreshnessCheck();\n error RedeemTransferOutNotPossible();\n\n error BorrowFreshnessCheck();\n error BorrowCashNotAvailable();\n error DelegateNotApproved();\n\n error RepayBorrowFreshnessCheck();\n\n error HealBorrowUnauthorized();\n error ForceLiquidateBorrowUnauthorized();\n\n error LiquidateFreshnessCheck();\n error LiquidateCollateralFreshnessCheck();\n error LiquidateAccrueCollateralInterestFailed(uint256 errorCode);\n error LiquidateLiquidatorIsBorrower();\n error LiquidateCloseAmountIsZero();\n error LiquidateCloseAmountIsUintMax();\n\n error LiquidateSeizeLiquidatorIsBorrower();\n\n error ProtocolSeizeShareTooBig();\n\n error SetReserveFactorFreshCheck();\n error SetReserveFactorBoundsCheck();\n\n error AddReservesFactorFreshCheck(uint256 actualAddAmount);\n\n error ReduceReservesFreshCheck();\n error ReduceReservesCashNotAvailable();\n error ReduceReservesCashValidation();\n\n error SetInterestRateModelFreshCheck();\n}\n" + }, + "contracts/ExponentialNoError.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\n\nimport { EXP_SCALE as EXP_SCALE_, MANTISSA_ONE as MANTISSA_ONE_ } from \"./lib/constants.sol\";\n\n/**\n * @title Exponential module for storing fixed-precision decimals\n * @author Compound\n * @notice Exp is a struct which stores decimals with a fixed precision of 18 decimal places.\n * Thus, if we wanted to store the 5.1, mantissa would store 5.1e18. That is:\n * `Exp({mantissa: 5100000000000000000})`.\n */\ncontract ExponentialNoError {\n struct Exp {\n uint256 mantissa;\n }\n\n struct Double {\n uint256 mantissa;\n }\n\n uint256 internal constant EXP_SCALE = EXP_SCALE_;\n uint256 internal constant DOUBLE_SCALE = 1e36;\n uint256 internal constant HALF_EXP_SCALE = EXP_SCALE / 2;\n uint256 internal constant MANTISSA_ONE = MANTISSA_ONE_;\n\n /**\n * @dev Truncates the given exp to a whole number value.\n * For example, truncate(Exp{mantissa: 15 * EXP_SCALE}) = 15\n */\n function truncate(Exp memory exp) internal pure returns (uint256) {\n // Note: We are not using careful math here as we're performing a division that cannot fail\n return exp.mantissa / EXP_SCALE;\n }\n\n /**\n * @dev Multiply an Exp by a scalar, then truncate to return an unsigned integer.\n */\n // solhint-disable-next-line func-name-mixedcase\n function mul_ScalarTruncate(Exp memory a, uint256 scalar) internal pure returns (uint256) {\n Exp memory product = mul_(a, scalar);\n return truncate(product);\n }\n\n /**\n * @dev Multiply an Exp by a scalar, truncate, then add an to an unsigned integer, returning an unsigned integer.\n */\n // solhint-disable-next-line func-name-mixedcase\n function mul_ScalarTruncateAddUInt(Exp memory a, uint256 scalar, uint256 addend) internal pure returns (uint256) {\n Exp memory product = mul_(a, scalar);\n return add_(truncate(product), addend);\n }\n\n /**\n * @dev Checks if first Exp is less than second Exp.\n */\n function lessThanExp(Exp memory left, Exp memory right) internal pure returns (bool) {\n return left.mantissa < right.mantissa;\n }\n\n function safe224(uint256 n, string memory errorMessage) internal pure returns (uint224) {\n require(n <= type(uint224).max, errorMessage);\n return uint224(n);\n }\n\n function safe32(uint256 n, string memory errorMessage) internal pure returns (uint32) {\n require(n <= type(uint32).max, errorMessage);\n return uint32(n);\n }\n\n function add_(Exp memory a, Exp memory b) internal pure returns (Exp memory) {\n return Exp({ mantissa: add_(a.mantissa, b.mantissa) });\n }\n\n function add_(Double memory a, Double memory b) internal pure returns (Double memory) {\n return Double({ mantissa: add_(a.mantissa, b.mantissa) });\n }\n\n function add_(uint256 a, uint256 b) internal pure returns (uint256) {\n return a + b;\n }\n\n function sub_(Exp memory a, Exp memory b) internal pure returns (Exp memory) {\n return Exp({ mantissa: sub_(a.mantissa, b.mantissa) });\n }\n\n function sub_(Double memory a, Double memory b) internal pure returns (Double memory) {\n return Double({ mantissa: sub_(a.mantissa, b.mantissa) });\n }\n\n function sub_(uint256 a, uint256 b) internal pure returns (uint256) {\n return a - b;\n }\n\n function mul_(Exp memory a, Exp memory b) internal pure returns (Exp memory) {\n return Exp({ mantissa: mul_(a.mantissa, b.mantissa) / EXP_SCALE });\n }\n\n function mul_(Exp memory a, uint256 b) internal pure returns (Exp memory) {\n return Exp({ mantissa: mul_(a.mantissa, b) });\n }\n\n function mul_(uint256 a, Exp memory b) internal pure returns (uint256) {\n return mul_(a, b.mantissa) / EXP_SCALE;\n }\n\n function mul_(Double memory a, Double memory b) internal pure returns (Double memory) {\n return Double({ mantissa: mul_(a.mantissa, b.mantissa) / DOUBLE_SCALE });\n }\n\n function mul_(Double memory a, uint256 b) internal pure returns (Double memory) {\n return Double({ mantissa: mul_(a.mantissa, b) });\n }\n\n function mul_(uint256 a, Double memory b) internal pure returns (uint256) {\n return mul_(a, b.mantissa) / DOUBLE_SCALE;\n }\n\n function mul_(uint256 a, uint256 b) internal pure returns (uint256) {\n return a * b;\n }\n\n function div_(Exp memory a, Exp memory b) internal pure returns (Exp memory) {\n return Exp({ mantissa: div_(mul_(a.mantissa, EXP_SCALE), b.mantissa) });\n }\n\n function div_(Exp memory a, uint256 b) internal pure returns (Exp memory) {\n return Exp({ mantissa: div_(a.mantissa, b) });\n }\n\n function div_(uint256 a, Exp memory b) internal pure returns (uint256) {\n return div_(mul_(a, EXP_SCALE), b.mantissa);\n }\n\n function div_(Double memory a, Double memory b) internal pure returns (Double memory) {\n return Double({ mantissa: div_(mul_(a.mantissa, DOUBLE_SCALE), b.mantissa) });\n }\n\n function div_(Double memory a, uint256 b) internal pure returns (Double memory) {\n return Double({ mantissa: div_(a.mantissa, b) });\n }\n\n function div_(uint256 a, Double memory b) internal pure returns (uint256) {\n return div_(mul_(a, DOUBLE_SCALE), b.mantissa);\n }\n\n function div_(uint256 a, uint256 b) internal pure returns (uint256) {\n return a / b;\n }\n\n function fraction(uint256 a, uint256 b) internal pure returns (Double memory) {\n return Double({ mantissa: div_(mul_(a, DOUBLE_SCALE), b) });\n }\n}\n" + }, + "contracts/Gateway/INativeTokenGateway.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity ^0.8.25;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\n/**\n * @title INativeTokenGateway\n * @author Venus\n * @notice Interface for NativeTokenGateway contract\n */\ninterface INativeTokenGateway {\n /**\n * @dev Emitted when native currency is supplied\n */\n event TokensWrappedAndSupplied(address indexed sender, address indexed vToken, uint256 amount);\n\n /**\n * @dev Emitted when tokens are redeemed and then unwrapped to be sent to user\n */\n event TokensRedeemedAndUnwrapped(address indexed sender, address indexed vToken, uint256 amount);\n\n /**\n * @dev Emitted when native tokens are borrowed and unwrapped\n */\n event TokensBorrowedAndUnwrapped(address indexed sender, address indexed vToken, uint256 amount);\n\n /**\n * @dev Emitted when native currency is wrapped and repaid\n */\n event TokensWrappedAndRepaid(address indexed sender, address indexed vToken, uint256 amount);\n\n /**\n * @dev Emitted when token is swept from the contract\n */\n event SweepToken(address indexed token, address indexed receiver, uint256 amount);\n\n /**\n * @dev Emitted when native asset is swept from the contract\n */\n event SweepNative(address indexed receiver, uint256 amount);\n\n /**\n * @notice Thrown if transfer of native token fails\n */\n error NativeTokenTransferFailed();\n\n /**\n * @notice Thrown if the supplied address is a zero address where it is not allowed\n */\n error ZeroAddressNotAllowed();\n\n /**\n * @notice Thrown if the supplied value is 0 where it is not allowed\n */\n error ZeroValueNotAllowed();\n\n /**\n * @dev Wrap Native Token, get wNativeToken, mint vWNativeTokens, and supply to the market\n * @param minter The address on behalf of whom the supply is performed\n */\n function wrapAndSupply(address minter) external payable;\n\n /**\n * @dev Redeem vWNativeTokens, unwrap to Native Token, and send to the user\n * @param redeemAmount The amount of underlying tokens to redeem\n */\n function redeemUnderlyingAndUnwrap(uint256 redeemAmount) external;\n\n /**\n * @dev Redeem vWNativeTokens, unwrap to Native Token, and send to the user\n * @param redeemTokens The amount of vWNative tokens to redeem\n */\n function redeemAndUnwrap(uint256 redeemTokens) external;\n\n /**\n * @dev Borrow wNativeToken, unwrap to Native Token, and send to the user\n * @param amount The amount of underlying tokens to borrow\n */\n function borrowAndUnwrap(uint256 amount) external;\n\n /**\n * @dev Wrap Native Token, repay borrow in the market, and send remaining Native Token to the user\n */\n function wrapAndRepay() external payable;\n\n /**\n * @dev Sweeps input token address tokens from the contract and sends them to the owner\n */\n function sweepToken(IERC20 token) external;\n\n /**\n * @dev Sweeps native assets (Native Token) from the contract and sends them to the owner\n */\n function sweepNative() external;\n}\n" + }, + "contracts/Gateway/Interfaces/IVToken.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity ^0.8.25;\n\ninterface IVToken {\n function mintBehalf(address receiver, uint256 mintAmount) external returns (uint256);\n\n function redeemUnderlyingBehalf(address redeemer, uint256 redeemAmount) external returns (uint256);\n\n function redeemBehalf(address redeemer, uint256 redeemTokens) external returns (uint256);\n\n function repayBorrowBehalf(address borrower, uint256 repayAmount) external returns (uint256);\n\n function borrowBehalf(address borrower, uint256 borrowAmount) external returns (uint256);\n\n function borrowBalanceCurrent(address account) external returns (uint256);\n\n function underlying() external returns (address);\n\n function exchangeRateCurrent() external returns (uint256);\n\n function transferFrom(address from, address to, uint256 amount) external returns (bool);\n\n function redeem(uint256 redeemTokens) external returns (uint256);\n}\n" + }, + "contracts/Gateway/Interfaces/IWrappedNative.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity ^0.8.25;\n\ninterface IWrappedNative {\n function deposit() external payable;\n\n function withdraw(uint256) external;\n\n function approve(address guy, uint256 wad) external returns (bool);\n\n function transferFrom(address src, address dst, uint256 wad) external returns (bool);\n\n function transfer(address dst, uint256 wad) external returns (bool);\n\n function balanceOf(address account) external view returns (uint256);\n}\n" + }, + "contracts/Gateway/NativeTokenGateway.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\n\nimport { Ownable2Step } from \"@openzeppelin/contracts/access/Ownable2Step.sol\";\nimport { SafeERC20, IERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { ReentrancyGuard } from \"@openzeppelin/contracts/security/ReentrancyGuard.sol\";\n\nimport { IWrappedNative } from \"./Interfaces/IWrappedNative.sol\";\nimport { INativeTokenGateway } from \"./INativeTokenGateway.sol\";\nimport { IVToken } from \"./Interfaces/IVToken.sol\";\n\n/**\n * @title NativeTokenGateway\n * @author Venus\n * @notice NativeTokenGateway contract facilitates interactions with a vToken market for native tokens (Native or wNativeToken)\n */\ncontract NativeTokenGateway is INativeTokenGateway, Ownable2Step, ReentrancyGuard {\n using SafeERC20 for IERC20;\n\n /**\n * @notice Address of wrapped native token contract\n */\n IWrappedNative public immutable wNativeToken;\n\n /**\n * @notice Address of wrapped native token market\n */\n IVToken public immutable vWNativeToken;\n\n /**\n * @notice Constructor for NativeTokenGateway\n * @param vWrappedNativeToken Address of wrapped native token market\n */\n constructor(IVToken vWrappedNativeToken) {\n ensureNonzeroAddress(address(vWrappedNativeToken));\n\n vWNativeToken = vWrappedNativeToken;\n wNativeToken = IWrappedNative(vWNativeToken.underlying());\n }\n\n /**\n * @notice To receive Native when msg.data is empty\n */\n receive() external payable {}\n\n /**\n * @notice To receive Native when msg.data is not empty\n */\n fallback() external payable {}\n\n /**\n * @notice Wrap Native, get wNativeToken, mint vWNativeToken, and supply to the market.\n * @param minter The address on behalf of whom the supply is performed.\n * @custom:error ZeroAddressNotAllowed is thrown if address of minter is zero address\n * @custom:error ZeroValueNotAllowed is thrown if mintAmount is zero\n * @custom:event TokensWrappedAndSupplied is emitted when assets are supplied to the market\n */\n function wrapAndSupply(address minter) external payable nonReentrant {\n ensureNonzeroAddress(minter);\n\n uint256 mintAmount = msg.value;\n ensureNonzeroValue(mintAmount);\n\n wNativeToken.deposit{ value: mintAmount }();\n IERC20(address(wNativeToken)).forceApprove(address(vWNativeToken), mintAmount);\n\n vWNativeToken.mintBehalf(minter, mintAmount);\n\n IERC20(address(wNativeToken)).forceApprove(address(vWNativeToken), 0);\n emit TokensWrappedAndSupplied(minter, address(vWNativeToken), mintAmount);\n }\n\n /**\n * @notice Redeem vWNativeToken, unwrap to Native Token, and send to the user\n * @param redeemAmount The amount of underlying tokens to redeem\n * @custom:error ZeroValueNotAllowed is thrown if redeemAmount is zero\n * @custom:event TokensRedeemedAndUnwrapped is emitted when assets are redeemed from a market and unwrapped\n */\n function redeemUnderlyingAndUnwrap(uint256 redeemAmount) external nonReentrant {\n _redeemAndUnwrap(redeemAmount, true);\n }\n\n /**\n * @notice Redeem vWNativeToken, unwrap to Native Token, and send to the user\n * @param redeemTokens The amount of vWNative tokens to redeem\n * @custom:error ZeroValueNotAllowed is thrown if redeemTokens is zero\n * @custom:event TokensRedeemedAndUnwrapped is emitted when assets are redeemed from a market and unwrapped\n */\n function redeemAndUnwrap(uint256 redeemTokens) external nonReentrant {\n _redeemAndUnwrap(redeemTokens, false);\n }\n\n /**\n * @dev Borrow wNativeToken, unwrap to Native, and send to the user\n * @param borrowAmount The amount of underlying tokens to borrow\n * @custom:error ZeroValueNotAllowed is thrown if borrowAmount is zero\n * @custom:event TokensBorrowedAndUnwrapped is emitted when assets are borrowed from a market and unwrapped\n */\n function borrowAndUnwrap(uint256 borrowAmount) external nonReentrant {\n ensureNonzeroValue(borrowAmount);\n\n vWNativeToken.borrowBehalf(msg.sender, borrowAmount);\n\n wNativeToken.withdraw(borrowAmount);\n _safeTransferNativeTokens(msg.sender, borrowAmount);\n emit TokensBorrowedAndUnwrapped(msg.sender, address(vWNativeToken), borrowAmount);\n }\n\n /**\n * @notice Wrap Native, repay borrow in the market, and send remaining Native to the user\n * @custom:error ZeroValueNotAllowed is thrown if repayAmount is zero\n * @custom:event TokensWrappedAndRepaid is emitted when assets are repaid to a market and unwrapped\n */\n function wrapAndRepay() external payable nonReentrant {\n uint256 repayAmount = msg.value;\n ensureNonzeroValue(repayAmount);\n\n wNativeToken.deposit{ value: repayAmount }();\n IERC20(address(wNativeToken)).forceApprove(address(vWNativeToken), repayAmount);\n\n uint256 borrowBalanceBefore = vWNativeToken.borrowBalanceCurrent(msg.sender);\n vWNativeToken.repayBorrowBehalf(msg.sender, repayAmount);\n uint256 borrowBalanceAfter = vWNativeToken.borrowBalanceCurrent(msg.sender);\n\n IERC20(address(wNativeToken)).forceApprove(address(vWNativeToken), 0);\n\n if (borrowBalanceAfter == 0 && (repayAmount > borrowBalanceBefore)) {\n uint256 dust;\n unchecked {\n dust = repayAmount - borrowBalanceBefore;\n }\n\n wNativeToken.withdraw(dust);\n _safeTransferNativeTokens(msg.sender, dust);\n }\n emit TokensWrappedAndRepaid(msg.sender, address(vWNativeToken), borrowBalanceBefore - borrowBalanceAfter);\n }\n\n /**\n * @notice Sweeps native assets (Native) from the contract and sends them to the owner\n * @custom:event SweepNative is emitted when assets are swept from the contract\n * @custom:access Controlled by Governance\n */\n function sweepNative() external onlyOwner {\n uint256 balance = address(this).balance;\n\n if (balance > 0) {\n address owner_ = owner();\n _safeTransferNativeTokens(owner_, balance);\n emit SweepNative(owner_, balance);\n }\n }\n\n /**\n * @notice Sweeps the input token address tokens from the contract and sends them to the owner\n * @param token Address of the token\n * @custom:event SweepToken emits on success\n * @custom:access Controlled by Governance\n */\n function sweepToken(IERC20 token) external onlyOwner {\n uint256 balance = token.balanceOf(address(this));\n\n if (balance > 0) {\n address owner_ = owner();\n token.safeTransfer(owner_, balance);\n emit SweepToken(address(token), owner_, balance);\n }\n }\n\n /**\n * @dev Redeems tokens, unwrap them to Native Token, and send to the user\n * This function is internally called by `redeemUnderlyingAndUnwrap` and `redeemAndUnwrap`\n * @param redeemTokens The amount of tokens to be redeemed. This can refer to either the underlying tokens directly or their equivalent vTokens\n * @param isUnderlying A boolean flag indicating whether the redemption is for underlying tokens directly (`true`) or for their equivalent vTokens (`false`).\n * @custom:error ZeroValueNotAllowed is thrown if redeemTokens is zero\n * @custom:event TokensRedeemedAndUnwrapped is emitted when assets are redeemed from a market and unwrapped\n */\n function _redeemAndUnwrap(uint256 redeemTokens, bool isUnderlying) internal {\n ensureNonzeroValue(redeemTokens);\n\n uint256 balanceBefore = wNativeToken.balanceOf(address(this));\n\n if (isUnderlying) {\n vWNativeToken.redeemUnderlyingBehalf(msg.sender, redeemTokens);\n } else {\n vWNativeToken.redeemBehalf(msg.sender, redeemTokens);\n }\n\n uint256 balanceAfter = wNativeToken.balanceOf(address(this));\n uint256 redeemedAmount = balanceAfter - balanceBefore;\n wNativeToken.withdraw(redeemedAmount);\n\n _safeTransferNativeTokens(msg.sender, redeemedAmount);\n emit TokensRedeemedAndUnwrapped(msg.sender, address(vWNativeToken), redeemedAmount);\n }\n\n /**\n * @dev transfer Native tokens to an address, revert if it fails\n * @param to recipient of the transfer\n * @param value the amount to send\n * @custom:error NativeTokenTransferFailed is thrown if the Native token transfer fails\n */\n function _safeTransferNativeTokens(address to, uint256 value) internal {\n (bool success, ) = to.call{ value: value }(new bytes(0));\n\n if (!success) {\n revert NativeTokenTransferFailed();\n }\n }\n\n /**\n * @dev Checks if the provided address is nonzero, reverts otherwise\n * @param address_ Address to check\n * @custom:error ZeroAddressNotAllowed is thrown if the provided address is a zero address\n **/\n function ensureNonzeroAddress(address address_) internal pure {\n if (address_ == address(0)) {\n revert ZeroAddressNotAllowed();\n }\n }\n\n /**\n * @dev Checks if the provided value is nonzero, reverts otherwise\n * @param value_ Value to check\n * @custom:error ZeroValueNotAllowed is thrown if the provided value is 0\n */\n function ensureNonzeroValue(uint256 value_) internal pure {\n if (value_ == 0) {\n revert ZeroValueNotAllowed();\n }\n }\n}\n" + }, + "contracts/hardhat-dependency-compiler/hardhat-deploy/solc_0.8/openzeppelin/proxy/transparent/ProxyAdmin.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity >0.0.0;\nimport 'hardhat-deploy/solc_0.8/openzeppelin/proxy/transparent/ProxyAdmin.sol';\n" + }, + "contracts/hardhat-dependency-compiler/hardhat-deploy/solc_0.8/proxy/OptimizedTransparentUpgradeableProxy.sol": { + "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity >0.0.0;\nimport 'hardhat-deploy/solc_0.8/proxy/OptimizedTransparentUpgradeableProxy.sol';\n" + }, + "contracts/InterestRateModel.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\n\n/**\n * @title Compound's InterestRateModel Interface\n * @author Compound\n */\nabstract contract InterestRateModel {\n /**\n * @notice Calculates the current borrow interest rate per slot (block or second)\n * @param cash The total amount of cash the market has\n * @param borrows The total amount of borrows the market has outstanding\n * @param reserves The total amount of reserves the market has\n * @param badDebt The amount of badDebt in the market\n * @return The borrow rate percentage per slot (block or second) as a mantissa (scaled by EXP_SCALE)\n */\n function getBorrowRate(\n uint256 cash,\n uint256 borrows,\n uint256 reserves,\n uint256 badDebt\n ) external view virtual returns (uint256);\n\n /**\n * @notice Calculates the current supply interest rate per slot (block or second)\n * @param cash The total amount of cash the market has\n * @param borrows The total amount of borrows the market has outstanding\n * @param reserves The total amount of reserves the market has\n * @param reserveFactorMantissa The current reserve factor the market has\n * @param badDebt The amount of badDebt in the market\n * @return The supply rate percentage per slot (block or second) as a mantissa (scaled by EXP_SCALE)\n */\n function getSupplyRate(\n uint256 cash,\n uint256 borrows,\n uint256 reserves,\n uint256 reserveFactorMantissa,\n uint256 badDebt\n ) external view virtual returns (uint256);\n\n /**\n * @notice Indicator that this is an InterestRateModel contract (for inspection)\n * @return Always true\n */\n function isInterestRateModel() external pure virtual returns (bool) {\n return true;\n }\n}\n" + }, + "contracts/IPancakeswapV2Router.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity ^0.8.25;\n\ninterface IPancakeswapV2Router {\n function swapExactTokensForTokens(\n uint256 amountIn,\n uint256 amountOutMin,\n address[] calldata path,\n address to,\n uint256 deadline\n ) external returns (uint256[] memory amounts);\n}\n" + }, + "contracts/JumpRateModelV2.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\n\nimport { IAccessControlManagerV8 } from \"@venusprotocol/governance-contracts/contracts/Governance/IAccessControlManagerV8.sol\";\nimport { TimeManagerV8 } from \"@venusprotocol/solidity-utilities/contracts/TimeManagerV8.sol\";\nimport { InterestRateModel } from \"./InterestRateModel.sol\";\nimport { EXP_SCALE, MANTISSA_ONE } from \"./lib/constants.sol\";\n\n/**\n * @title JumpRateModelV2\n * @author Compound (modified by Dharma Labs, Arr00 and Venus)\n * @notice An interest rate model with a steep increase after a certain utilization threshold called **kink** is reached.\n * The parameters of this interest rate model can be adjusted by the owner. Version 2 modifies Version 1 by enabling updateable parameters\n */\ncontract JumpRateModelV2 is InterestRateModel, TimeManagerV8 {\n /**\n * @notice The address of the AccessControlManager contract\n */\n IAccessControlManagerV8 public accessControlManager;\n\n /**\n * @notice The multiplier of utilization rate per block or second that gives the slope of the interest rate\n */\n uint256 public multiplierPerBlock;\n\n /**\n * @notice The base interest rate per block or second which is the y-intercept when utilization rate is 0\n */\n uint256 public baseRatePerBlock;\n\n /**\n * @notice The multiplier per block or second after hitting a specified utilization point\n */\n uint256 public jumpMultiplierPerBlock;\n\n /**\n * @notice The utilization point at which the jump multiplier is applied\n */\n uint256 public kink;\n\n event NewInterestParams(\n uint256 baseRatePerBlockOrTimestamp,\n uint256 multiplierPerBlockOrTimestamp,\n uint256 jumpMultiplierPerBlockOrTimestamp,\n uint256 kink\n );\n\n /**\n * @notice Thrown when the action is prohibited by AccessControlManager\n */\n error Unauthorized(address sender, address calledContract, string methodSignature);\n\n /**\n * @notice Construct an interest rate model\n * @param baseRatePerYear_ The approximate target base APR, as a mantissa (scaled by EXP_SCALE)\n * @param multiplierPerYear_ The rate of increase in interest rate wrt utilization (scaled by EXP_SCALE)\n * @param jumpMultiplierPerYear_ The multiplier after hitting a specified utilization point\n * @param kink_ The utilization point at which the jump multiplier is applied\n * @param accessControlManager_ The address of the AccessControlManager contract\n * @param timeBased_ A boolean indicating whether the contract is based on time or block.\n * @param blocksPerYear_ The number of blocks per year\n */\n constructor(\n uint256 baseRatePerYear_,\n uint256 multiplierPerYear_,\n uint256 jumpMultiplierPerYear_,\n uint256 kink_,\n IAccessControlManagerV8 accessControlManager_,\n bool timeBased_,\n uint256 blocksPerYear_\n ) TimeManagerV8(timeBased_, blocksPerYear_) {\n require(address(accessControlManager_) != address(0), \"invalid ACM address\");\n\n accessControlManager = accessControlManager_;\n\n _updateJumpRateModel(baseRatePerYear_, multiplierPerYear_, jumpMultiplierPerYear_, kink_);\n }\n\n /**\n * @notice Update the parameters of the interest rate model\n * @param baseRatePerYear The approximate target base APR, as a mantissa (scaled by EXP_SCALE)\n * @param multiplierPerYear The rate of increase in interest rate wrt utilization (scaled by EXP_SCALE)\n * @param jumpMultiplierPerYear The multiplierPerBlockOrTimestamp after hitting a specified utilization point\n * @param kink_ The utilization point at which the jump multiplier is applied\n * @custom:error Unauthorized if the sender is not allowed to call this function\n * @custom:access Controlled by AccessControlManager\n */\n function updateJumpRateModel(\n uint256 baseRatePerYear,\n uint256 multiplierPerYear,\n uint256 jumpMultiplierPerYear,\n uint256 kink_\n ) external virtual {\n string memory signature = \"updateJumpRateModel(uint256,uint256,uint256,uint256)\";\n bool isAllowedToCall = accessControlManager.isAllowedToCall(msg.sender, signature);\n\n if (!isAllowedToCall) {\n revert Unauthorized(msg.sender, address(this), signature);\n }\n\n _updateJumpRateModel(baseRatePerYear, multiplierPerYear, jumpMultiplierPerYear, kink_);\n }\n\n /**\n * @notice Calculates the current borrow rate per slot (block or second)\n * @param cash The amount of cash in the market\n * @param borrows The amount of borrows in the market\n * @param reserves The amount of reserves in the market\n * @param badDebt The amount of badDebt in the market\n * @return The borrow rate percentage per slot (block or second) as a mantissa (scaled by 1e18)\n */\n function getBorrowRate(\n uint256 cash,\n uint256 borrows,\n uint256 reserves,\n uint256 badDebt\n ) external view override returns (uint256) {\n return _getBorrowRate(cash, borrows, reserves, badDebt);\n }\n\n /**\n * @notice Calculates the current supply rate per slot (block or second)\n * @param cash The amount of cash in the market\n * @param borrows The amount of borrows in the market\n * @param reserves The amount of reserves in the market\n * @param reserveFactorMantissa The current reserve factor for the market\n * @param badDebt The amount of badDebt in the market\n * @return The supply rate percentage per slot (block or second) as a mantissa (scaled by EXP_SCALE)\n */\n function getSupplyRate(\n uint256 cash,\n uint256 borrows,\n uint256 reserves,\n uint256 reserveFactorMantissa,\n uint256 badDebt\n ) public view virtual override returns (uint256) {\n uint256 oneMinusReserveFactor = MANTISSA_ONE - reserveFactorMantissa;\n uint256 borrowRate = _getBorrowRate(cash, borrows, reserves, badDebt);\n uint256 rateToPool = (borrowRate * oneMinusReserveFactor) / EXP_SCALE;\n uint256 incomeToDistribute = borrows * rateToPool;\n uint256 supply = cash + borrows + badDebt - reserves;\n return incomeToDistribute / supply;\n }\n\n /**\n * @notice Calculates the utilization rate of the market: `(borrows + badDebt) / (cash + borrows + badDebt - reserves)`\n * @param cash The amount of cash in the market\n * @param borrows The amount of borrows in the market\n * @param reserves The amount of reserves in the market (currently unused)\n * @param badDebt The amount of badDebt in the market\n * @return The utilization rate as a mantissa between [0, MANTISSA_ONE]\n */\n function utilizationRate(\n uint256 cash,\n uint256 borrows,\n uint256 reserves,\n uint256 badDebt\n ) public pure returns (uint256) {\n // Utilization rate is 0 when there are no borrows and badDebt\n if ((borrows + badDebt) == 0) {\n return 0;\n }\n\n uint256 rate = ((borrows + badDebt) * EXP_SCALE) / (cash + borrows + badDebt - reserves);\n\n if (rate > EXP_SCALE) {\n rate = EXP_SCALE;\n }\n\n return rate;\n }\n\n /**\n * @notice Internal function to update the parameters of the interest rate model\n * @param baseRatePerYear The approximate target base APR, as a mantissa (scaled by EXP_SCALE)\n * @param multiplierPerYear The rate of increase in interest rate wrt utilization (scaled by EXP_SCALE)\n * @param jumpMultiplierPerYear The multiplierPerBlockOrTimestamp after hitting a specified utilization point\n * @param kink_ The utilization point at which the jump multiplier is applied\n */\n function _updateJumpRateModel(\n uint256 baseRatePerYear,\n uint256 multiplierPerYear,\n uint256 jumpMultiplierPerYear,\n uint256 kink_\n ) internal {\n baseRatePerBlock = baseRatePerYear / blocksOrSecondsPerYear;\n multiplierPerBlock = multiplierPerYear / blocksOrSecondsPerYear;\n jumpMultiplierPerBlock = jumpMultiplierPerYear / blocksOrSecondsPerYear;\n kink = kink_;\n\n emit NewInterestParams(baseRatePerBlock, multiplierPerBlock, jumpMultiplierPerBlock, kink);\n }\n\n /**\n * @notice Calculates the current borrow rate per slot (block or second), with the error code expected by the market\n * @param cash The amount of cash in the market\n * @param borrows The amount of borrows in the market\n * @param reserves The amount of reserves in the market\n * @param badDebt The amount of badDebt in the market\n * @return The borrow rate percentage per slot (block or second) as a mantissa (scaled by EXP_SCALE)\n */\n function _getBorrowRate(\n uint256 cash,\n uint256 borrows,\n uint256 reserves,\n uint256 badDebt\n ) internal view returns (uint256) {\n uint256 util = utilizationRate(cash, borrows, reserves, badDebt);\n uint256 kink_ = kink;\n\n if (util <= kink_) {\n return ((util * multiplierPerBlock) / EXP_SCALE) + baseRatePerBlock;\n }\n uint256 normalRate = ((kink_ * multiplierPerBlock) / EXP_SCALE) + baseRatePerBlock;\n uint256 excessUtil;\n unchecked {\n excessUtil = util - kink_;\n }\n return ((excessUtil * jumpMultiplierPerBlock) / EXP_SCALE) + normalRate;\n }\n}\n" + }, + "contracts/legacy/RiskFund/IRiskFund.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity ^0.8.25;\n\n/**\n * @title IRiskFund\n * @author Venus\n * @notice Interface implemented by `RiskFund`.\n */\ninterface IRiskFundV1 {\n function swapPoolsAssets(\n address[] calldata markets,\n uint256[] calldata amountsOutMin,\n address[][] calldata paths,\n uint256 deadline\n ) external returns (uint256);\n\n function transferReserveForAuction(address comptroller, uint256 amount) external returns (uint256);\n\n function updateAssetsState(address comptroller, address asset) external;\n\n function convertibleBaseAsset() external view returns (address);\n\n function getPoolsBaseAssetReserves(address comptroller) external view returns (uint256);\n}\n" + }, + "contracts/legacy/RiskFund/ReserveHelpers.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\n\nimport { Ownable2StepUpgradeable } from \"@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol\";\nimport { IERC20Upgradeable } from \"@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol\";\nimport { SafeERC20Upgradeable } from \"@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol\";\n\nimport { ensureNonzeroAddress } from \"../../lib/validators.sol\";\nimport { ComptrollerInterface } from \"../../ComptrollerInterface.sol\";\nimport { PoolRegistryInterface } from \"../../Pool/PoolRegistryInterface.sol\";\nimport { VToken } from \"../../VToken.sol\";\n\ncontract ReserveHelpers is Ownable2StepUpgradeable {\n using SafeERC20Upgradeable for IERC20Upgradeable;\n\n uint256 private constant NOT_ENTERED = 1;\n\n uint256 private constant ENTERED = 2;\n\n // Address of the core pool's comptroller\n /// @custom:oz-upgrades-unsafe-allow state-variable-immutable\n address public immutable CORE_POOL_COMPTROLLER;\n\n // Address of the VBNB\n /// @custom:oz-upgrades-unsafe-allow state-variable-immutable\n address public immutable VBNB;\n\n // Address of the native wrapped token\n /// @custom:oz-upgrades-unsafe-allow state-variable-immutable\n address public immutable NATIVE_WRAPPED;\n\n // Store the previous state for the asset transferred to ProtocolShareReserve combined(for all pools).\n mapping(address => uint256) public assetsReserves;\n\n // Store the asset's reserve per pool in the ProtocolShareReserve.\n // Comptroller(pool) -> Asset -> amount\n mapping(address => mapping(address => uint256)) internal _poolsAssetsReserves;\n\n // Address of pool registry contract\n address public poolRegistry;\n\n /**\n * @dev Guard variable for re-entrancy checks\n */\n uint256 internal status;\n\n /**\n * @dev This empty reserved space is put in place to allow future versions to add new\n * variables without shifting down storage in the inheritance chain.\n */\n uint256[46] private __gap;\n\n /// @notice Event emitted after the update of the assets reserves.\n /// @param comptroller Pool's Comptroller address\n /// @param asset Token address\n /// @param amount An amount by which the reserves have increased\n event AssetsReservesUpdated(address indexed comptroller, address indexed asset, uint256 amount);\n\n /// @notice event emitted on sweep token success\n event SweepToken(address indexed token, address indexed to, uint256 amount);\n\n /**\n * @dev Prevents a contract from calling itself, directly or indirectly.\n */\n modifier nonReentrant() {\n require(status != ENTERED, \"re-entered\");\n status = ENTERED;\n _;\n status = NOT_ENTERED;\n }\n\n /// @custom:oz-upgrades-unsafe-allow constructor\n constructor(address corePoolComptroller_, address vbnb_, address nativeWrapped_) {\n ensureNonzeroAddress(corePoolComptroller_);\n ensureNonzeroAddress(vbnb_);\n ensureNonzeroAddress(nativeWrapped_);\n\n CORE_POOL_COMPTROLLER = corePoolComptroller_;\n VBNB = vbnb_;\n NATIVE_WRAPPED = nativeWrapped_;\n }\n\n /**\n * @notice A public function to sweep accidental BEP-20 transfers to this contract. Tokens are sent to the address `to`, provided in input\n * @param _token The address of the BEP-20 token to sweep\n * @param _to Recipient of the output tokens.\n * @custom:error ZeroAddressNotAllowed is thrown when asset address is zero\n * @custom:access Only Owner\n */\n function sweepToken(address _token, address _to) external onlyOwner nonReentrant {\n ensureNonzeroAddress(_to);\n uint256 balanceDfference_;\n uint256 balance_ = IERC20Upgradeable(_token).balanceOf(address(this));\n\n require(balance_ > assetsReserves[_token], \"ReserveHelpers: Zero surplus tokens\");\n unchecked {\n balanceDfference_ = balance_ - assetsReserves[_token];\n }\n\n emit SweepToken(_token, _to, balanceDfference_);\n IERC20Upgradeable(_token).safeTransfer(_to, balanceDfference_);\n }\n\n /**\n * @notice Get the Amount of the asset in the risk fund for the specific pool.\n * @param comptroller Comptroller address(pool).\n * @param asset Asset address.\n * @return Asset's reserve in risk fund.\n * @custom:error ZeroAddressNotAllowed is thrown when asset address is zero\n */\n function getPoolAssetReserve(address comptroller, address asset) external view returns (uint256) {\n ensureNonzeroAddress(asset);\n require(ComptrollerInterface(comptroller).isComptroller(), \"ReserveHelpers: Comptroller address invalid\");\n return _poolsAssetsReserves[comptroller][asset];\n }\n\n /**\n * @notice Update the reserve of the asset for the specific pool after transferring to risk fund\n * and transferring funds to the protocol share reserve\n * @param comptroller Comptroller address(pool).\n * @param asset Asset address.\n * @custom:error ZeroAddressNotAllowed is thrown when asset address is zero\n */\n function updateAssetsState(address comptroller, address asset) public virtual {\n ensureNonzeroAddress(asset);\n require(ComptrollerInterface(comptroller).isComptroller(), \"ReserveHelpers: Comptroller address invalid\");\n address poolRegistry_ = poolRegistry;\n require(poolRegistry_ != address(0), \"ReserveHelpers: Pool Registry address is not set\");\n require(ensureAssetListed(comptroller, asset), \"ReserveHelpers: The pool doesn't support the asset\");\n uint256 currentBalance = IERC20Upgradeable(asset).balanceOf(address(this));\n uint256 assetReserve = assetsReserves[asset];\n if (currentBalance > assetReserve) {\n uint256 balanceDifference;\n unchecked {\n balanceDifference = currentBalance - assetReserve;\n }\n assetsReserves[asset] += balanceDifference;\n _poolsAssetsReserves[comptroller][asset] += balanceDifference;\n emit AssetsReservesUpdated(comptroller, asset, balanceDifference);\n }\n }\n\n function isAssetListedInCore(address tokenAddress) internal view returns (bool isAssetListed) {\n VToken[] memory coreMarkets = ComptrollerInterface(CORE_POOL_COMPTROLLER).getAllMarkets();\n for (uint256 i; i < coreMarkets.length; ++i) {\n isAssetListed = (VBNB == address(coreMarkets[i]))\n ? (tokenAddress == NATIVE_WRAPPED)\n : (coreMarkets[i].underlying() == tokenAddress);\n if (isAssetListed) {\n break;\n }\n }\n }\n\n /// @notice This function checks for the given asset is listed or not\n /// @param comptroller Address of the comptroller\n /// @param asset Address of the asset\n function ensureAssetListed(address comptroller, address asset) internal view returns (bool) {\n if (comptroller == CORE_POOL_COMPTROLLER) {\n return isAssetListedInCore(asset);\n }\n return PoolRegistryInterface(poolRegistry).getVTokenForAsset(comptroller, asset) != address(0);\n }\n}\n" + }, + "contracts/legacy/RiskFund/RiskFund.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\n\nimport { IERC20Upgradeable } from \"@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol\";\nimport { SafeERC20Upgradeable } from \"@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol\";\nimport { AccessControlledV8 } from \"@venusprotocol/governance-contracts/contracts/Governance/AccessControlledV8.sol\";\nimport { ResilientOracleInterface } from \"@venusprotocol/oracle/contracts/interfaces/OracleInterface.sol\";\nimport { ComptrollerInterface } from \"../../ComptrollerInterface.sol\";\nimport { IRiskFundV1 } from \"./IRiskFund.sol\";\nimport { ReserveHelpers } from \"./ReserveHelpers.sol\";\nimport { ExponentialNoError } from \"../../ExponentialNoError.sol\";\nimport { VToken } from \"../../VToken.sol\";\nimport { ComptrollerViewInterface } from \"../../ComptrollerInterface.sol\";\nimport { Comptroller } from \"../../Comptroller.sol\";\nimport { PoolRegistry } from \"../../Pool/PoolRegistry.sol\";\nimport { IPancakeswapV2Router } from \"../../IPancakeswapV2Router.sol\";\nimport { MaxLoopsLimitHelper } from \"../../MaxLoopsLimitHelper.sol\";\nimport { ensureNonzeroAddress } from \"../../lib/validators.sol\";\nimport { ApproveOrRevert } from \"../../lib/ApproveOrRevert.sol\";\n\n/**\n * @title RiskFund\n * @author Venus\n * @notice Contract with basic features to track/hold different assets for different Comptrollers.\n * @dev This contract does not support BNB.\n */\ncontract RiskFund is AccessControlledV8, ExponentialNoError, ReserveHelpers, MaxLoopsLimitHelper, IRiskFundV1 {\n using SafeERC20Upgradeable for IERC20Upgradeable;\n using ApproveOrRevert for IERC20Upgradeable;\n\n address public convertibleBaseAsset;\n address public shortfall;\n address public pancakeSwapRouter;\n uint256 public minAmountToConvert;\n\n /// @notice Emitted when pool registry address is updated\n event PoolRegistryUpdated(address indexed oldPoolRegistry, address indexed newPoolRegistry);\n\n /// @notice Emitted when shortfall contract address is updated\n event ShortfallContractUpdated(address indexed oldShortfallContract, address indexed newShortfallContract);\n\n /// @notice Emitted when convertible base asset is updated\n event ConvertibleBaseAssetUpdated(address indexed oldConvertibleBaseAsset, address indexed newConvertibleBaseAsset);\n\n /// @notice Emitted when PancakeSwap router contract address is updated\n event PancakeSwapRouterUpdated(address indexed oldPancakeSwapRouter, address indexed newPancakeSwapRouter);\n\n /// @notice Emitted when minimum amount to convert is updated\n event MinAmountToConvertUpdated(uint256 oldMinAmountToConvert, uint256 newMinAmountToConvert);\n\n /// @notice Emitted when pools assets are swapped\n event SwappedPoolsAssets(address[] markets, uint256[] amountsOutMin, uint256 totalAmount);\n\n /// @notice Emitted when reserves are transferred for auction\n event TransferredReserveForAuction(address indexed comptroller, uint256 amount);\n\n /// @dev Note that the contract is upgradeable. Use initialize() or reinitializers\n /// to set the state variables.\n /// @custom:oz-upgrades-unsafe-allow constructor\n constructor(\n address corePoolComptroller_,\n address vbnb_,\n address nativeWrapped_\n ) ReserveHelpers(corePoolComptroller_, vbnb_, nativeWrapped_) {\n _disableInitializers();\n }\n\n /**\n * @notice Initializes the deployer to owner.\n * @param pancakeSwapRouter_ Address of the PancakeSwap router\n * @param minAmountToConvert_ Minimum amount assets must be worth to convert into base asset\n * @param convertibleBaseAsset_ Address of the base asset\n * @param accessControlManager_ Address of the access control contract\n * @param loopsLimit_ Limit for the loops in the contract to avoid DOS\n * @custom:error ZeroAddressNotAllowed is thrown when PCS router address is zero\n * @custom:error ZeroAddressNotAllowed is thrown when convertible base asset address is zero\n */\n function initialize(\n address pancakeSwapRouter_,\n uint256 minAmountToConvert_,\n address convertibleBaseAsset_,\n address accessControlManager_,\n uint256 loopsLimit_\n ) external initializer {\n ensureNonzeroAddress(pancakeSwapRouter_);\n ensureNonzeroAddress(convertibleBaseAsset_);\n require(minAmountToConvert_ > 0, \"Risk Fund: Invalid min amount to convert\");\n require(loopsLimit_ > 0, \"Risk Fund: Loops limit can not be zero\");\n\n __Ownable2Step_init();\n __AccessControlled_init_unchained(accessControlManager_);\n\n pancakeSwapRouter = pancakeSwapRouter_;\n minAmountToConvert = minAmountToConvert_;\n convertibleBaseAsset = convertibleBaseAsset_;\n\n _setMaxLoopsLimit(loopsLimit_);\n }\n\n /**\n * @notice Pool registry setter\n * @param poolRegistry_ Address of the pool registry\n * @custom:error ZeroAddressNotAllowed is thrown when pool registry address is zero\n */\n function setPoolRegistry(address poolRegistry_) external onlyOwner {\n ensureNonzeroAddress(poolRegistry_);\n address oldPoolRegistry = poolRegistry;\n poolRegistry = poolRegistry_;\n emit PoolRegistryUpdated(oldPoolRegistry, poolRegistry_);\n }\n\n /**\n * @notice Shortfall contract address setter\n * @param shortfallContractAddress_ Address of the auction contract\n * @custom:error ZeroAddressNotAllowed is thrown when shortfall contract address is zero\n */\n function setShortfallContractAddress(address shortfallContractAddress_) external onlyOwner {\n ensureNonzeroAddress(shortfallContractAddress_);\n\n address oldShortfallContractAddress = shortfall;\n shortfall = shortfallContractAddress_;\n emit ShortfallContractUpdated(oldShortfallContractAddress, shortfallContractAddress_);\n }\n\n /**\n * @notice PancakeSwap router address setter\n * @param pancakeSwapRouter_ Address of the PancakeSwap router\n * @custom:error ZeroAddressNotAllowed is thrown when PCS router address is zero\n */\n function setPancakeSwapRouter(address pancakeSwapRouter_) external onlyOwner {\n ensureNonzeroAddress(pancakeSwapRouter_);\n address oldPancakeSwapRouter = pancakeSwapRouter;\n pancakeSwapRouter = pancakeSwapRouter_;\n emit PancakeSwapRouterUpdated(oldPancakeSwapRouter, pancakeSwapRouter_);\n }\n\n /**\n * @notice Min amount to convert setter\n * @param minAmountToConvert_ Min amount to convert.\n */\n function setMinAmountToConvert(uint256 minAmountToConvert_) external {\n _checkAccessAllowed(\"setMinAmountToConvert(uint256)\");\n require(minAmountToConvert_ > 0, \"Risk Fund: Invalid min amount to convert\");\n uint256 oldMinAmountToConvert = minAmountToConvert;\n minAmountToConvert = minAmountToConvert_;\n emit MinAmountToConvertUpdated(oldMinAmountToConvert, minAmountToConvert_);\n }\n\n /**\n * @notice Sets a new convertible base asset\n * @param _convertibleBaseAsset Address for new convertible base asset.\n */\n function setConvertibleBaseAsset(address _convertibleBaseAsset) external {\n _checkAccessAllowed(\"setConvertibleBaseAsset(address)\");\n require(_convertibleBaseAsset != address(0), \"Risk Fund: new convertible base asset address invalid\");\n\n address oldConvertibleBaseAsset = convertibleBaseAsset;\n convertibleBaseAsset = _convertibleBaseAsset;\n\n emit ConvertibleBaseAssetUpdated(oldConvertibleBaseAsset, _convertibleBaseAsset);\n }\n\n /**\n * @notice Swap array of pool assets into base asset's tokens of at least a minimum amount\n * @param markets Array of vTokens whose assets to swap for base asset\n * @param amountsOutMin Minimum amount to receive for swap\n * @param paths A path consisting of PCS token pairs for each swap\n * @param deadline Deadline for the swap\n * @return Number of swapped tokens\n * @custom:error ZeroAddressNotAllowed is thrown if PoolRegistry contract address is not configured\n */\n function swapPoolsAssets(\n address[] calldata markets,\n uint256[] calldata amountsOutMin,\n address[][] calldata paths,\n uint256 deadline\n ) external override nonReentrant returns (uint256) {\n _checkAccessAllowed(\"swapPoolsAssets(address[],uint256[],address[][],uint256)\");\n require(deadline >= block.timestamp, \"Risk fund: deadline passed\");\n address poolRegistry_ = poolRegistry;\n ensureNonzeroAddress(poolRegistry_);\n require(markets.length == amountsOutMin.length, \"Risk fund: markets and amountsOutMin are unequal lengths\");\n require(markets.length == paths.length, \"Risk fund: markets and paths are unequal lengths\");\n\n uint256 totalAmount;\n uint256 marketsCount = markets.length;\n\n _ensureMaxLoops(marketsCount);\n\n for (uint256 i; i < marketsCount; ++i) {\n address comptroller = address(VToken(markets[i]).comptroller());\n\n PoolRegistry.VenusPool memory pool = PoolRegistry(poolRegistry_).getPoolByComptroller(comptroller);\n require(pool.comptroller == comptroller, \"comptroller doesn't exist pool registry\");\n require(Comptroller(comptroller).isMarketListed(VToken(markets[i])), \"market is not listed\");\n uint256 swappedTokens = _swapAsset(VToken(markets[i]), comptroller, amountsOutMin[i], paths[i]);\n _poolsAssetsReserves[comptroller][convertibleBaseAsset] += swappedTokens;\n assetsReserves[convertibleBaseAsset] += swappedTokens;\n totalAmount = totalAmount + swappedTokens;\n }\n emit SwappedPoolsAssets(markets, amountsOutMin, totalAmount);\n return totalAmount;\n }\n\n /**\n * @notice Transfer tokens for auction.\n * @param comptroller Comptroller of the pool.\n * @param amount Amount to be transferred to auction contract.\n * @return Number reserved tokens.\n */\n function transferReserveForAuction(\n address comptroller,\n uint256 amount\n ) external override nonReentrant returns (uint256) {\n address shortfall_ = shortfall;\n require(msg.sender == shortfall_, \"Risk fund: Only callable by Shortfall contract\");\n require(\n amount <= _poolsAssetsReserves[comptroller][convertibleBaseAsset],\n \"Risk Fund: Insufficient pool reserve.\"\n );\n unchecked {\n _poolsAssetsReserves[comptroller][convertibleBaseAsset] =\n _poolsAssetsReserves[comptroller][convertibleBaseAsset] -\n amount;\n }\n unchecked {\n assetsReserves[convertibleBaseAsset] = assetsReserves[convertibleBaseAsset] - amount;\n }\n emit TransferredReserveForAuction(comptroller, amount);\n IERC20Upgradeable(convertibleBaseAsset).safeTransfer(shortfall_, amount);\n return amount;\n }\n\n /**\n * @notice Set the limit for the loops can iterate to avoid the DOS\n * @param limit Limit for the max loops can execute at a time\n */\n function setMaxLoopsLimit(uint256 limit) external onlyOwner {\n _setMaxLoopsLimit(limit);\n }\n\n /**\n * @notice Get the Amount of the Base asset in the risk fund for the specific pool.\n * @param comptroller Comptroller address(pool).\n * @return Base Asset's reserve in risk fund.\n */\n function getPoolsBaseAssetReserves(address comptroller) external view returns (uint256) {\n require(ComptrollerInterface(comptroller).isComptroller(), \"Risk Fund: Comptroller address invalid\");\n return _poolsAssetsReserves[comptroller][convertibleBaseAsset];\n }\n\n /**\n * @notice Update the reserve of the asset for the specific pool after transferring to risk fund.\n * @param comptroller Comptroller address(pool).\n * @param asset Asset address.\n */\n function updateAssetsState(address comptroller, address asset) public override(IRiskFundV1, ReserveHelpers) {\n super.updateAssetsState(comptroller, asset);\n }\n\n /**\n * @dev Swap single asset to base asset.\n * @param vToken VToken\n * @param comptroller Comptroller address\n * @param amountOutMin Minimum amount to receive for swap\n * @param path A path for the swap consisting of PCS token pairs\n * @return Number of swapped tokens.\n */\n function _swapAsset(\n VToken vToken,\n address comptroller,\n uint256 amountOutMin,\n address[] calldata path\n ) internal returns (uint256) {\n require(amountOutMin != 0, \"RiskFund: amountOutMin must be greater than 0 to swap vToken\");\n uint256 totalAmount;\n\n address underlyingAsset = vToken.underlying();\n address convertibleBaseAsset_ = convertibleBaseAsset;\n uint256 balanceOfUnderlyingAsset = _poolsAssetsReserves[comptroller][underlyingAsset];\n\n if (balanceOfUnderlyingAsset == 0) {\n return 0;\n }\n\n ResilientOracleInterface oracle = ComptrollerViewInterface(comptroller).oracle();\n oracle.updateAssetPrice(convertibleBaseAsset_);\n Exp memory baseAssetPrice = Exp({ mantissa: oracle.getPrice(convertibleBaseAsset_) });\n uint256 amountOutMinInUsd = mul_ScalarTruncate(baseAssetPrice, amountOutMin);\n\n require(amountOutMinInUsd >= minAmountToConvert, \"RiskFund: minAmountToConvert violated\");\n\n assetsReserves[underlyingAsset] -= balanceOfUnderlyingAsset;\n _poolsAssetsReserves[comptroller][underlyingAsset] -= balanceOfUnderlyingAsset;\n\n if (underlyingAsset != convertibleBaseAsset_) {\n require(path[0] == underlyingAsset, \"RiskFund: swap path must start with the underlying asset\");\n require(\n path[path.length - 1] == convertibleBaseAsset_,\n \"RiskFund: finally path must be convertible base asset\"\n );\n address pancakeSwapRouter_ = pancakeSwapRouter;\n IERC20Upgradeable(underlyingAsset).approveOrRevert(pancakeSwapRouter_, 0);\n IERC20Upgradeable(underlyingAsset).approveOrRevert(pancakeSwapRouter_, balanceOfUnderlyingAsset);\n uint256[] memory amounts = IPancakeswapV2Router(pancakeSwapRouter_).swapExactTokensForTokens(\n balanceOfUnderlyingAsset,\n amountOutMin,\n path,\n address(this),\n block.timestamp\n );\n totalAmount = amounts[path.length - 1];\n } else {\n totalAmount = balanceOfUnderlyingAsset;\n }\n\n return totalAmount;\n }\n}\n" + }, + "contracts/Lens/legacy/PoolLensR1.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IERC20Metadata } from \"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol\";\nimport { ResilientOracleInterface } from \"@venusprotocol/oracle/contracts/interfaces/OracleInterface.sol\";\n\nimport { ExponentialNoError } from \"../../ExponentialNoError.sol\";\nimport { VToken } from \"../../VToken.sol\";\nimport { ComptrollerInterface, ComptrollerViewInterface } from \"../../ComptrollerInterface.sol\";\nimport { PoolRegistryInterface } from \"../../Pool/PoolRegistryInterface.sol\";\nimport { PoolRegistry } from \"../../Pool/PoolRegistry.sol\";\nimport { RewardsDistributor } from \"../../Rewards/RewardsDistributor.sol\";\n\n/**\n * @title PoolLens\n * @author Venus\n * @notice The `PoolLens` contract is designed to retrieve important information for each registered pool. A list of essential information\n * for all pools within the lending protocol can be acquired through the function `getAllPools()`. Additionally, the following records can be\n * looked up for specific pools and markets:\n- the vToken balance of a given user;\n- the pool data (oracle address, associated vToken, liquidation incentive, etc) of a pool via its associated comptroller address;\n- the vToken address in a pool for a given asset;\n- a list of all pools that support an asset;\n- the underlying asset price of a vToken;\n- the metadata (exchange/borrow/supply rate, total supply, collateral factor, etc) of any vToken.\n */\ncontract PoolLensR1 is ExponentialNoError {\n /**\n * @dev Struct for PoolDetails.\n */\n struct PoolData {\n string name;\n address creator;\n address comptroller;\n uint256 blockPosted;\n uint256 timestampPosted;\n string category;\n string logoURL;\n string description;\n address priceOracle;\n uint256 closeFactor;\n uint256 liquidationIncentive;\n uint256 minLiquidatableCollateral;\n VTokenMetadata[] vTokens;\n }\n\n /**\n * @dev Struct for VToken.\n */\n struct VTokenMetadata {\n address vToken;\n uint256 exchangeRateCurrent;\n uint256 supplyRatePerBlock;\n uint256 borrowRatePerBlock;\n uint256 reserveFactorMantissa;\n uint256 supplyCaps;\n uint256 borrowCaps;\n uint256 totalBorrows;\n uint256 totalReserves;\n uint256 totalSupply;\n uint256 totalCash;\n bool isListed;\n uint256 collateralFactorMantissa;\n address underlyingAssetAddress;\n uint256 vTokenDecimals;\n uint256 underlyingDecimals;\n }\n\n /**\n * @dev Struct for VTokenBalance.\n */\n struct VTokenBalances {\n address vToken;\n uint256 balanceOf;\n uint256 borrowBalanceCurrent;\n uint256 balanceOfUnderlying;\n uint256 tokenBalance;\n uint256 tokenAllowance;\n }\n\n /**\n * @dev Struct for underlyingPrice of VToken.\n */\n struct VTokenUnderlyingPrice {\n address vToken;\n uint256 underlyingPrice;\n }\n\n /**\n * @dev Struct with pending reward info for a market.\n */\n struct PendingReward {\n address vTokenAddress;\n uint256 amount;\n }\n\n /**\n * @dev Struct with reward distribution totals for a single reward token and distributor.\n */\n struct RewardSummary {\n address distributorAddress;\n address rewardTokenAddress;\n uint256 totalRewards;\n PendingReward[] pendingRewards;\n }\n\n /**\n * @dev Struct used in RewardDistributor to save last updated market state.\n */\n struct RewardTokenState {\n // The market's last updated rewardTokenBorrowIndex or rewardTokenSupplyIndex\n uint224 index;\n // The block number the index was last updated at\n uint32 block;\n // The block number at which to stop rewards\n uint32 lastRewardingBlock;\n }\n\n /**\n * @dev Struct with bad debt of a market denominated\n */\n struct BadDebt {\n address vTokenAddress;\n uint256 badDebtUsd;\n }\n\n /**\n * @dev Struct with bad debt total denominated in usd for a pool and an array of BadDebt structs for each market\n */\n struct BadDebtSummary {\n address comptroller;\n uint256 totalBadDebtUsd;\n BadDebt[] badDebts;\n }\n\n /**\n * @notice Queries the user's supply/borrow balances in vTokens\n * @param vTokens The list of vToken addresses\n * @param account The user Account\n * @return A list of structs containing balances data\n */\n function vTokenBalancesAll(VToken[] calldata vTokens, address account) external returns (VTokenBalances[] memory) {\n uint256 vTokenCount = vTokens.length;\n VTokenBalances[] memory res = new VTokenBalances[](vTokenCount);\n for (uint256 i; i < vTokenCount; ++i) {\n res[i] = vTokenBalances(vTokens[i], account);\n }\n return res;\n }\n\n /**\n * @notice Queries all pools with addtional details for each of them\n * @dev This function is not designed to be called in a transaction: it is too gas-intensive\n * @param poolRegistryAddress The address of the PoolRegistry contract\n * @return Arrays of all Venus pools' data\n */\n function getAllPools(address poolRegistryAddress) external view returns (PoolData[] memory) {\n PoolRegistryInterface poolRegistryInterface = PoolRegistryInterface(poolRegistryAddress);\n PoolRegistry.VenusPool[] memory venusPools = poolRegistryInterface.getAllPools();\n uint256 poolLength = venusPools.length;\n\n PoolData[] memory poolDataItems = new PoolData[](poolLength);\n\n for (uint256 i; i < poolLength; ++i) {\n PoolRegistry.VenusPool memory venusPool = venusPools[i];\n PoolData memory poolData = getPoolDataFromVenusPool(poolRegistryAddress, venusPool);\n poolDataItems[i] = poolData;\n }\n\n return poolDataItems;\n }\n\n /**\n * @notice Queries the details of a pool identified by Comptroller address\n * @param poolRegistryAddress The address of the PoolRegistry contract\n * @param comptroller The Comptroller implementation address\n * @return PoolData structure containing the details of the pool\n */\n function getPoolByComptroller(\n address poolRegistryAddress,\n address comptroller\n ) external view returns (PoolData memory) {\n PoolRegistryInterface poolRegistryInterface = PoolRegistryInterface(poolRegistryAddress);\n return getPoolDataFromVenusPool(poolRegistryAddress, poolRegistryInterface.getPoolByComptroller(comptroller));\n }\n\n /**\n * @notice Returns vToken holding the specified underlying asset in the specified pool\n * @param poolRegistryAddress The address of the PoolRegistry contract\n * @param comptroller The pool comptroller\n * @param asset The underlyingAsset of VToken\n * @return Address of the vToken\n */\n function getVTokenForAsset(\n address poolRegistryAddress,\n address comptroller,\n address asset\n ) external view returns (address) {\n PoolRegistryInterface poolRegistryInterface = PoolRegistryInterface(poolRegistryAddress);\n return poolRegistryInterface.getVTokenForAsset(comptroller, asset);\n }\n\n /**\n * @notice Returns all pools that support the specified underlying asset\n * @param poolRegistryAddress The address of the PoolRegistry contract\n * @param asset The underlying asset of vToken\n * @return A list of Comptroller contracts\n */\n function getPoolsSupportedByAsset(\n address poolRegistryAddress,\n address asset\n ) external view returns (address[] memory) {\n PoolRegistryInterface poolRegistryInterface = PoolRegistryInterface(poolRegistryAddress);\n return poolRegistryInterface.getPoolsSupportedByAsset(asset);\n }\n\n /**\n * @notice Returns the price data for the underlying assets of the specified vTokens\n * @param vTokens The list of vToken addresses\n * @return An array containing the price data for each asset\n */\n function vTokenUnderlyingPriceAll(\n VToken[] calldata vTokens\n ) external view returns (VTokenUnderlyingPrice[] memory) {\n uint256 vTokenCount = vTokens.length;\n VTokenUnderlyingPrice[] memory res = new VTokenUnderlyingPrice[](vTokenCount);\n for (uint256 i; i < vTokenCount; ++i) {\n res[i] = vTokenUnderlyingPrice(vTokens[i]);\n }\n return res;\n }\n\n /**\n * @notice Returns the pending rewards for a user for a given pool.\n * @param account The user account.\n * @param comptrollerAddress address\n * @return Pending rewards array\n */\n function getPendingRewards(\n address account,\n address comptrollerAddress\n ) external view returns (RewardSummary[] memory) {\n VToken[] memory markets = ComptrollerInterface(comptrollerAddress).getAllMarkets();\n RewardsDistributor[] memory rewardsDistributors = ComptrollerViewInterface(comptrollerAddress)\n .getRewardDistributors();\n RewardSummary[] memory rewardSummary = new RewardSummary[](rewardsDistributors.length);\n for (uint256 i; i < rewardsDistributors.length; ++i) {\n RewardSummary memory reward;\n reward.distributorAddress = address(rewardsDistributors[i]);\n reward.rewardTokenAddress = address(rewardsDistributors[i].rewardToken());\n reward.totalRewards = rewardsDistributors[i].rewardTokenAccrued(account);\n reward.pendingRewards = _calculateNotDistributedAwards(account, markets, rewardsDistributors[i]);\n rewardSummary[i] = reward;\n }\n return rewardSummary;\n }\n\n /**\n * @notice Returns a summary of a pool's bad debt broken down by market\n *\n * @param comptrollerAddress Address of the comptroller\n *\n * @return badDebtSummary A struct with comptroller address, total bad debut denominated in usd, and\n * a break down of bad debt by market\n */\n function getPoolBadDebt(address comptrollerAddress) external view returns (BadDebtSummary memory) {\n uint256 totalBadDebtUsd;\n\n // Get every market in the pool\n ComptrollerViewInterface comptroller = ComptrollerViewInterface(comptrollerAddress);\n VToken[] memory markets = comptroller.getAllMarkets();\n ResilientOracleInterface priceOracle = comptroller.oracle();\n\n BadDebt[] memory badDebts = new BadDebt[](markets.length);\n\n BadDebtSummary memory badDebtSummary;\n badDebtSummary.comptroller = comptrollerAddress;\n badDebtSummary.badDebts = badDebts;\n\n // // Calculate the bad debt is USD per market\n for (uint256 i; i < markets.length; ++i) {\n BadDebt memory badDebt;\n badDebt.vTokenAddress = address(markets[i]);\n badDebt.badDebtUsd =\n (VToken(address(markets[i])).badDebt() * priceOracle.getUnderlyingPrice(address(markets[i]))) /\n EXP_SCALE;\n badDebtSummary.badDebts[i] = badDebt;\n totalBadDebtUsd = totalBadDebtUsd + badDebt.badDebtUsd;\n }\n\n badDebtSummary.totalBadDebtUsd = totalBadDebtUsd;\n\n return badDebtSummary;\n }\n\n /**\n * @notice Queries the user's supply/borrow balances in the specified vToken\n * @param vToken vToken address\n * @param account The user Account\n * @return A struct containing the balances data\n */\n function vTokenBalances(VToken vToken, address account) public returns (VTokenBalances memory) {\n uint256 balanceOf = vToken.balanceOf(account);\n uint256 borrowBalanceCurrent = vToken.borrowBalanceCurrent(account);\n uint256 balanceOfUnderlying = vToken.balanceOfUnderlying(account);\n uint256 tokenBalance;\n uint256 tokenAllowance;\n\n IERC20 underlying = IERC20(vToken.underlying());\n tokenBalance = underlying.balanceOf(account);\n tokenAllowance = underlying.allowance(account, address(vToken));\n\n return\n VTokenBalances({\n vToken: address(vToken),\n balanceOf: balanceOf,\n borrowBalanceCurrent: borrowBalanceCurrent,\n balanceOfUnderlying: balanceOfUnderlying,\n tokenBalance: tokenBalance,\n tokenAllowance: tokenAllowance\n });\n }\n\n /**\n * @notice Queries additional information for the pool\n * @param poolRegistryAddress Address of the PoolRegistry\n * @param venusPool The VenusPool Object from PoolRegistry\n * @return Enriched PoolData\n */\n function getPoolDataFromVenusPool(\n address poolRegistryAddress,\n PoolRegistry.VenusPool memory venusPool\n ) public view returns (PoolData memory) {\n // Get tokens in the Pool\n ComptrollerInterface comptrollerInstance = ComptrollerInterface(venusPool.comptroller);\n\n VToken[] memory vTokens = comptrollerInstance.getAllMarkets();\n\n VTokenMetadata[] memory vTokenMetadataItems = vTokenMetadataAll(vTokens);\n\n PoolRegistryInterface poolRegistryInterface = PoolRegistryInterface(poolRegistryAddress);\n\n PoolRegistry.VenusPoolMetaData memory venusPoolMetaData = poolRegistryInterface.getVenusPoolMetadata(\n venusPool.comptroller\n );\n\n ComptrollerViewInterface comptrollerViewInstance = ComptrollerViewInterface(venusPool.comptroller);\n\n PoolData memory poolData = PoolData({\n name: venusPool.name,\n creator: venusPool.creator,\n comptroller: venusPool.comptroller,\n blockPosted: venusPool.blockPosted,\n timestampPosted: venusPool.timestampPosted,\n category: venusPoolMetaData.category,\n logoURL: venusPoolMetaData.logoURL,\n description: venusPoolMetaData.description,\n vTokens: vTokenMetadataItems,\n priceOracle: address(comptrollerViewInstance.oracle()),\n closeFactor: comptrollerViewInstance.closeFactorMantissa(),\n liquidationIncentive: comptrollerViewInstance.liquidationIncentiveMantissa(),\n minLiquidatableCollateral: comptrollerViewInstance.minLiquidatableCollateral()\n });\n\n return poolData;\n }\n\n /**\n * @notice Returns the metadata of VToken\n * @param vToken The address of vToken\n * @return VTokenMetadata struct\n */\n function vTokenMetadata(VToken vToken) public view returns (VTokenMetadata memory) {\n uint256 exchangeRateCurrent = vToken.exchangeRateStored();\n address comptrollerAddress = address(vToken.comptroller());\n ComptrollerViewInterface comptroller = ComptrollerViewInterface(comptrollerAddress);\n (bool isListed, uint256 collateralFactorMantissa) = comptroller.markets(address(vToken));\n\n address underlyingAssetAddress = vToken.underlying();\n uint256 underlyingDecimals = IERC20Metadata(underlyingAssetAddress).decimals();\n\n return\n VTokenMetadata({\n vToken: address(vToken),\n exchangeRateCurrent: exchangeRateCurrent,\n supplyRatePerBlock: vToken.supplyRatePerBlock(),\n borrowRatePerBlock: vToken.borrowRatePerBlock(),\n reserveFactorMantissa: vToken.reserveFactorMantissa(),\n supplyCaps: comptroller.supplyCaps(address(vToken)),\n borrowCaps: comptroller.borrowCaps(address(vToken)),\n totalBorrows: vToken.totalBorrows(),\n totalReserves: vToken.totalReserves(),\n totalSupply: vToken.totalSupply(),\n totalCash: vToken.getCash(),\n isListed: isListed,\n collateralFactorMantissa: collateralFactorMantissa,\n underlyingAssetAddress: underlyingAssetAddress,\n vTokenDecimals: vToken.decimals(),\n underlyingDecimals: underlyingDecimals\n });\n }\n\n /**\n * @notice Returns the metadata of all VTokens\n * @param vTokens The list of vToken addresses\n * @return An array of VTokenMetadata structs\n */\n function vTokenMetadataAll(VToken[] memory vTokens) public view returns (VTokenMetadata[] memory) {\n uint256 vTokenCount = vTokens.length;\n VTokenMetadata[] memory res = new VTokenMetadata[](vTokenCount);\n for (uint256 i; i < vTokenCount; ++i) {\n res[i] = vTokenMetadata(vTokens[i]);\n }\n return res;\n }\n\n /**\n * @notice Returns the price data for the underlying asset of the specified vToken\n * @param vToken vToken address\n * @return The price data for each asset\n */\n function vTokenUnderlyingPrice(VToken vToken) public view returns (VTokenUnderlyingPrice memory) {\n ComptrollerViewInterface comptroller = ComptrollerViewInterface(address(vToken.comptroller()));\n ResilientOracleInterface priceOracle = comptroller.oracle();\n\n return\n VTokenUnderlyingPrice({\n vToken: address(vToken),\n underlyingPrice: priceOracle.getUnderlyingPrice(address(vToken))\n });\n }\n\n function _calculateNotDistributedAwards(\n address account,\n VToken[] memory markets,\n RewardsDistributor rewardsDistributor\n ) internal view returns (PendingReward[] memory) {\n PendingReward[] memory pendingRewards = new PendingReward[](markets.length);\n for (uint256 i; i < markets.length; ++i) {\n // Market borrow and supply state we will modify update in-memory, in order to not modify storage\n RewardTokenState memory borrowState;\n (borrowState.index, borrowState.block, borrowState.lastRewardingBlock) = rewardsDistributor\n .rewardTokenBorrowState(address(markets[i]));\n RewardTokenState memory supplyState;\n (supplyState.index, supplyState.block, supplyState.lastRewardingBlock) = rewardsDistributor\n .rewardTokenSupplyState(address(markets[i]));\n Exp memory marketBorrowIndex = Exp({ mantissa: markets[i].borrowIndex() });\n\n // Update market supply and borrow index in-memory\n updateMarketBorrowIndex(address(markets[i]), rewardsDistributor, borrowState, marketBorrowIndex);\n updateMarketSupplyIndex(address(markets[i]), rewardsDistributor, supplyState);\n\n // Calculate pending rewards\n uint256 borrowReward = calculateBorrowerReward(\n address(markets[i]),\n rewardsDistributor,\n account,\n borrowState,\n marketBorrowIndex\n );\n uint256 supplyReward = calculateSupplierReward(\n address(markets[i]),\n rewardsDistributor,\n account,\n supplyState\n );\n\n PendingReward memory pendingReward;\n pendingReward.vTokenAddress = address(markets[i]);\n pendingReward.amount = borrowReward + supplyReward;\n pendingRewards[i] = pendingReward;\n }\n return pendingRewards;\n }\n\n function updateMarketBorrowIndex(\n address vToken,\n RewardsDistributor rewardsDistributor,\n RewardTokenState memory borrowState,\n Exp memory marketBorrowIndex\n ) internal view {\n uint256 borrowSpeed = rewardsDistributor.rewardTokenBorrowSpeeds(vToken);\n uint256 blockNumber = block.number;\n\n if (borrowState.lastRewardingBlock > 0 && blockNumber > borrowState.lastRewardingBlock) {\n blockNumber = borrowState.lastRewardingBlock;\n }\n\n uint256 deltaBlocks = sub_(blockNumber, uint256(borrowState.block));\n if (deltaBlocks > 0 && borrowSpeed > 0) {\n // Remove the total earned interest rate since the opening of the market from total borrows\n uint256 borrowAmount = div_(VToken(vToken).totalBorrows(), marketBorrowIndex);\n uint256 tokensAccrued = mul_(deltaBlocks, borrowSpeed);\n Double memory ratio = borrowAmount > 0 ? fraction(tokensAccrued, borrowAmount) : Double({ mantissa: 0 });\n Double memory index = add_(Double({ mantissa: borrowState.index }), ratio);\n borrowState.index = safe224(index.mantissa, \"new index overflows\");\n borrowState.block = safe32(blockNumber, \"block number overflows\");\n } else if (deltaBlocks > 0) {\n borrowState.block = safe32(blockNumber, \"block number overflows\");\n }\n }\n\n function updateMarketSupplyIndex(\n address vToken,\n RewardsDistributor rewardsDistributor,\n RewardTokenState memory supplyState\n ) internal view {\n uint256 supplySpeed = rewardsDistributor.rewardTokenSupplySpeeds(vToken);\n uint256 blockNumber = block.number;\n\n if (supplyState.lastRewardingBlock > 0 && blockNumber > supplyState.lastRewardingBlock) {\n blockNumber = supplyState.lastRewardingBlock;\n }\n\n uint256 deltaBlocks = sub_(blockNumber, uint256(supplyState.block));\n if (deltaBlocks > 0 && supplySpeed > 0) {\n uint256 supplyTokens = VToken(vToken).totalSupply();\n uint256 tokensAccrued = mul_(deltaBlocks, supplySpeed);\n Double memory ratio = supplyTokens > 0 ? fraction(tokensAccrued, supplyTokens) : Double({ mantissa: 0 });\n Double memory index = add_(Double({ mantissa: supplyState.index }), ratio);\n supplyState.index = safe224(index.mantissa, \"new index overflows\");\n supplyState.block = safe32(blockNumber, \"block number overflows\");\n } else if (deltaBlocks > 0) {\n supplyState.block = safe32(blockNumber, \"block number overflows\");\n }\n }\n\n function calculateBorrowerReward(\n address vToken,\n RewardsDistributor rewardsDistributor,\n address borrower,\n RewardTokenState memory borrowState,\n Exp memory marketBorrowIndex\n ) internal view returns (uint256) {\n Double memory borrowIndex = Double({ mantissa: borrowState.index });\n Double memory borrowerIndex = Double({\n mantissa: rewardsDistributor.rewardTokenBorrowerIndex(vToken, borrower)\n });\n if (borrowerIndex.mantissa == 0 && borrowIndex.mantissa >= rewardsDistributor.INITIAL_INDEX()) {\n // Covers the case where users borrowed tokens before the market's borrow state index was set\n borrowerIndex.mantissa = rewardsDistributor.INITIAL_INDEX();\n }\n Double memory deltaIndex = sub_(borrowIndex, borrowerIndex);\n uint256 borrowerAmount = div_(VToken(vToken).borrowBalanceStored(borrower), marketBorrowIndex);\n uint256 borrowerDelta = mul_(borrowerAmount, deltaIndex);\n return borrowerDelta;\n }\n\n function calculateSupplierReward(\n address vToken,\n RewardsDistributor rewardsDistributor,\n address supplier,\n RewardTokenState memory supplyState\n ) internal view returns (uint256) {\n Double memory supplyIndex = Double({ mantissa: supplyState.index });\n Double memory supplierIndex = Double({\n mantissa: rewardsDistributor.rewardTokenSupplierIndex(vToken, supplier)\n });\n if (supplierIndex.mantissa == 0 && supplyIndex.mantissa >= rewardsDistributor.INITIAL_INDEX()) {\n // Covers the case where users supplied tokens before the market's supply state index was set\n supplierIndex.mantissa = rewardsDistributor.INITIAL_INDEX();\n }\n Double memory deltaIndex = sub_(supplyIndex, supplierIndex);\n uint256 supplierTokens = VToken(vToken).balanceOf(supplier);\n uint256 supplierDelta = mul_(supplierTokens, deltaIndex);\n return supplierDelta;\n }\n}\n" + }, + "contracts/Lens/legacy/PoolLensR2.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IERC20Metadata } from \"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol\";\nimport { ResilientOracleInterface } from \"@venusprotocol/oracle/contracts/interfaces/OracleInterface.sol\";\n\nimport { ExponentialNoError } from \"../../ExponentialNoError.sol\";\nimport { VToken } from \"../../VToken.sol\";\nimport { Action, ComptrollerInterface, ComptrollerViewInterface } from \"../../ComptrollerInterface.sol\";\nimport { PoolRegistryInterface } from \"../../Pool/PoolRegistryInterface.sol\";\nimport { PoolRegistry } from \"../../Pool/PoolRegistry.sol\";\nimport { RewardsDistributor } from \"../../Rewards/RewardsDistributor.sol\";\n\n/**\n * @title PoolLens\n * @author Venus\n * @notice The `PoolLens` contract is designed to retrieve important information for each registered pool. A list of essential information\n * for all pools within the lending protocol can be acquired through the function `getAllPools()`. Additionally, the following records can be\n * looked up for specific pools and markets:\n- the vToken balance of a given user;\n- the pool data (oracle address, associated vToken, liquidation incentive, etc) of a pool via its associated comptroller address;\n- the vToken address in a pool for a given asset;\n- a list of all pools that support an asset;\n- the underlying asset price of a vToken;\n- the metadata (exchange/borrow/supply rate, total supply, collateral factor, etc) of any vToken.\n */\ncontract PoolLensR2 is ExponentialNoError {\n /**\n * @dev Struct for PoolDetails.\n */\n struct PoolData {\n string name;\n address creator;\n address comptroller;\n uint256 blockPosted;\n uint256 timestampPosted;\n string category;\n string logoURL;\n string description;\n address priceOracle;\n uint256 closeFactor;\n uint256 liquidationIncentive;\n uint256 minLiquidatableCollateral;\n VTokenMetadata[] vTokens;\n }\n\n /**\n * @dev Struct for VToken.\n */\n struct VTokenMetadata {\n address vToken;\n uint256 exchangeRateCurrent;\n uint256 supplyRatePerBlock;\n uint256 borrowRatePerBlock;\n uint256 reserveFactorMantissa;\n uint256 supplyCaps;\n uint256 borrowCaps;\n uint256 totalBorrows;\n uint256 totalReserves;\n uint256 totalSupply;\n uint256 totalCash;\n bool isListed;\n uint256 collateralFactorMantissa;\n address underlyingAssetAddress;\n uint256 vTokenDecimals;\n uint256 underlyingDecimals;\n uint256 pausedActions;\n }\n\n /**\n * @dev Struct for VTokenBalance.\n */\n struct VTokenBalances {\n address vToken;\n uint256 balanceOf;\n uint256 borrowBalanceCurrent;\n uint256 balanceOfUnderlying;\n uint256 tokenBalance;\n uint256 tokenAllowance;\n }\n\n /**\n * @dev Struct for underlyingPrice of VToken.\n */\n struct VTokenUnderlyingPrice {\n address vToken;\n uint256 underlyingPrice;\n }\n\n /**\n * @dev Struct with pending reward info for a market.\n */\n struct PendingReward {\n address vTokenAddress;\n uint256 amount;\n }\n\n /**\n * @dev Struct with reward distribution totals for a single reward token and distributor.\n */\n struct RewardSummary {\n address distributorAddress;\n address rewardTokenAddress;\n uint256 totalRewards;\n PendingReward[] pendingRewards;\n }\n\n /**\n * @dev Struct used in RewardDistributor to save last updated market state.\n */\n struct RewardTokenState {\n // The market's last updated rewardTokenBorrowIndex or rewardTokenSupplyIndex\n uint224 index;\n // The block number the index was last updated at\n uint32 block;\n // The block number at which to stop rewards\n uint32 lastRewardingBlock;\n }\n\n /**\n * @dev Struct with bad debt of a market denominated\n */\n struct BadDebt {\n address vTokenAddress;\n uint256 badDebtUsd;\n }\n\n /**\n * @dev Struct with bad debt total denominated in usd for a pool and an array of BadDebt structs for each market\n */\n struct BadDebtSummary {\n address comptroller;\n uint256 totalBadDebtUsd;\n BadDebt[] badDebts;\n }\n\n /**\n * @notice Queries the user's supply/borrow balances in vTokens\n * @param vTokens The list of vToken addresses\n * @param account The user Account\n * @return A list of structs containing balances data\n */\n function vTokenBalancesAll(VToken[] calldata vTokens, address account) external returns (VTokenBalances[] memory) {\n uint256 vTokenCount = vTokens.length;\n VTokenBalances[] memory res = new VTokenBalances[](vTokenCount);\n for (uint256 i; i < vTokenCount; ++i) {\n res[i] = vTokenBalances(vTokens[i], account);\n }\n return res;\n }\n\n /**\n * @notice Queries all pools with addtional details for each of them\n * @dev This function is not designed to be called in a transaction: it is too gas-intensive\n * @param poolRegistryAddress The address of the PoolRegistry contract\n * @return Arrays of all Venus pools' data\n */\n function getAllPools(address poolRegistryAddress) external view returns (PoolData[] memory) {\n PoolRegistryInterface poolRegistryInterface = PoolRegistryInterface(poolRegistryAddress);\n PoolRegistry.VenusPool[] memory venusPools = poolRegistryInterface.getAllPools();\n uint256 poolLength = venusPools.length;\n\n PoolData[] memory poolDataItems = new PoolData[](poolLength);\n\n for (uint256 i; i < poolLength; ++i) {\n PoolRegistry.VenusPool memory venusPool = venusPools[i];\n PoolData memory poolData = getPoolDataFromVenusPool(poolRegistryAddress, venusPool);\n poolDataItems[i] = poolData;\n }\n\n return poolDataItems;\n }\n\n /**\n * @notice Queries the details of a pool identified by Comptroller address\n * @param poolRegistryAddress The address of the PoolRegistry contract\n * @param comptroller The Comptroller implementation address\n * @return PoolData structure containing the details of the pool\n */\n function getPoolByComptroller(\n address poolRegistryAddress,\n address comptroller\n ) external view returns (PoolData memory) {\n PoolRegistryInterface poolRegistryInterface = PoolRegistryInterface(poolRegistryAddress);\n return getPoolDataFromVenusPool(poolRegistryAddress, poolRegistryInterface.getPoolByComptroller(comptroller));\n }\n\n /**\n * @notice Returns vToken holding the specified underlying asset in the specified pool\n * @param poolRegistryAddress The address of the PoolRegistry contract\n * @param comptroller The pool comptroller\n * @param asset The underlyingAsset of VToken\n * @return Address of the vToken\n */\n function getVTokenForAsset(\n address poolRegistryAddress,\n address comptroller,\n address asset\n ) external view returns (address) {\n PoolRegistryInterface poolRegistryInterface = PoolRegistryInterface(poolRegistryAddress);\n return poolRegistryInterface.getVTokenForAsset(comptroller, asset);\n }\n\n /**\n * @notice Returns all pools that support the specified underlying asset\n * @param poolRegistryAddress The address of the PoolRegistry contract\n * @param asset The underlying asset of vToken\n * @return A list of Comptroller contracts\n */\n function getPoolsSupportedByAsset(\n address poolRegistryAddress,\n address asset\n ) external view returns (address[] memory) {\n PoolRegistryInterface poolRegistryInterface = PoolRegistryInterface(poolRegistryAddress);\n return poolRegistryInterface.getPoolsSupportedByAsset(asset);\n }\n\n /**\n * @notice Returns the price data for the underlying assets of the specified vTokens\n * @param vTokens The list of vToken addresses\n * @return An array containing the price data for each asset\n */\n function vTokenUnderlyingPriceAll(\n VToken[] calldata vTokens\n ) external view returns (VTokenUnderlyingPrice[] memory) {\n uint256 vTokenCount = vTokens.length;\n VTokenUnderlyingPrice[] memory res = new VTokenUnderlyingPrice[](vTokenCount);\n for (uint256 i; i < vTokenCount; ++i) {\n res[i] = vTokenUnderlyingPrice(vTokens[i]);\n }\n return res;\n }\n\n /**\n * @notice Returns the pending rewards for a user for a given pool.\n * @param account The user account.\n * @param comptrollerAddress address\n * @return Pending rewards array\n */\n function getPendingRewards(\n address account,\n address comptrollerAddress\n ) external view returns (RewardSummary[] memory) {\n VToken[] memory markets = ComptrollerInterface(comptrollerAddress).getAllMarkets();\n RewardsDistributor[] memory rewardsDistributors = ComptrollerViewInterface(comptrollerAddress)\n .getRewardDistributors();\n RewardSummary[] memory rewardSummary = new RewardSummary[](rewardsDistributors.length);\n for (uint256 i; i < rewardsDistributors.length; ++i) {\n RewardSummary memory reward;\n reward.distributorAddress = address(rewardsDistributors[i]);\n reward.rewardTokenAddress = address(rewardsDistributors[i].rewardToken());\n reward.totalRewards = rewardsDistributors[i].rewardTokenAccrued(account);\n reward.pendingRewards = _calculateNotDistributedAwards(account, markets, rewardsDistributors[i]);\n rewardSummary[i] = reward;\n }\n return rewardSummary;\n }\n\n /**\n * @notice Returns a summary of a pool's bad debt broken down by market\n *\n * @param comptrollerAddress Address of the comptroller\n *\n * @return badDebtSummary A struct with comptroller address, total bad debut denominated in usd, and\n * a break down of bad debt by market\n */\n function getPoolBadDebt(address comptrollerAddress) external view returns (BadDebtSummary memory) {\n uint256 totalBadDebtUsd;\n\n // Get every market in the pool\n ComptrollerViewInterface comptroller = ComptrollerViewInterface(comptrollerAddress);\n VToken[] memory markets = comptroller.getAllMarkets();\n ResilientOracleInterface priceOracle = comptroller.oracle();\n\n BadDebt[] memory badDebts = new BadDebt[](markets.length);\n\n BadDebtSummary memory badDebtSummary;\n badDebtSummary.comptroller = comptrollerAddress;\n badDebtSummary.badDebts = badDebts;\n\n // // Calculate the bad debt is USD per market\n for (uint256 i; i < markets.length; ++i) {\n BadDebt memory badDebt;\n badDebt.vTokenAddress = address(markets[i]);\n badDebt.badDebtUsd =\n (VToken(address(markets[i])).badDebt() * priceOracle.getUnderlyingPrice(address(markets[i]))) /\n EXP_SCALE;\n badDebtSummary.badDebts[i] = badDebt;\n totalBadDebtUsd = totalBadDebtUsd + badDebt.badDebtUsd;\n }\n\n badDebtSummary.totalBadDebtUsd = totalBadDebtUsd;\n\n return badDebtSummary;\n }\n\n /**\n * @notice Queries the user's supply/borrow balances in the specified vToken\n * @param vToken vToken address\n * @param account The user Account\n * @return A struct containing the balances data\n */\n function vTokenBalances(VToken vToken, address account) public returns (VTokenBalances memory) {\n uint256 balanceOf = vToken.balanceOf(account);\n uint256 borrowBalanceCurrent = vToken.borrowBalanceCurrent(account);\n uint256 balanceOfUnderlying = vToken.balanceOfUnderlying(account);\n uint256 tokenBalance;\n uint256 tokenAllowance;\n\n IERC20 underlying = IERC20(vToken.underlying());\n tokenBalance = underlying.balanceOf(account);\n tokenAllowance = underlying.allowance(account, address(vToken));\n\n return\n VTokenBalances({\n vToken: address(vToken),\n balanceOf: balanceOf,\n borrowBalanceCurrent: borrowBalanceCurrent,\n balanceOfUnderlying: balanceOfUnderlying,\n tokenBalance: tokenBalance,\n tokenAllowance: tokenAllowance\n });\n }\n\n /**\n * @notice Queries additional information for the pool\n * @param poolRegistryAddress Address of the PoolRegistry\n * @param venusPool The VenusPool Object from PoolRegistry\n * @return Enriched PoolData\n */\n function getPoolDataFromVenusPool(\n address poolRegistryAddress,\n PoolRegistry.VenusPool memory venusPool\n ) public view returns (PoolData memory) {\n // Get tokens in the Pool\n ComptrollerInterface comptrollerInstance = ComptrollerInterface(venusPool.comptroller);\n\n VToken[] memory vTokens = comptrollerInstance.getAllMarkets();\n\n VTokenMetadata[] memory vTokenMetadataItems = vTokenMetadataAll(vTokens);\n\n PoolRegistryInterface poolRegistryInterface = PoolRegistryInterface(poolRegistryAddress);\n\n PoolRegistry.VenusPoolMetaData memory venusPoolMetaData = poolRegistryInterface.getVenusPoolMetadata(\n venusPool.comptroller\n );\n\n ComptrollerViewInterface comptrollerViewInstance = ComptrollerViewInterface(venusPool.comptroller);\n\n PoolData memory poolData = PoolData({\n name: venusPool.name,\n creator: venusPool.creator,\n comptroller: venusPool.comptroller,\n blockPosted: venusPool.blockPosted,\n timestampPosted: venusPool.timestampPosted,\n category: venusPoolMetaData.category,\n logoURL: venusPoolMetaData.logoURL,\n description: venusPoolMetaData.description,\n vTokens: vTokenMetadataItems,\n priceOracle: address(comptrollerViewInstance.oracle()),\n closeFactor: comptrollerViewInstance.closeFactorMantissa(),\n liquidationIncentive: comptrollerViewInstance.liquidationIncentiveMantissa(),\n minLiquidatableCollateral: comptrollerViewInstance.minLiquidatableCollateral()\n });\n\n return poolData;\n }\n\n /**\n * @notice Returns the metadata of VToken\n * @param vToken The address of vToken\n * @return VTokenMetadata struct\n */\n function vTokenMetadata(VToken vToken) public view returns (VTokenMetadata memory) {\n uint256 exchangeRateCurrent = vToken.exchangeRateStored();\n address comptrollerAddress = address(vToken.comptroller());\n ComptrollerViewInterface comptroller = ComptrollerViewInterface(comptrollerAddress);\n (bool isListed, uint256 collateralFactorMantissa) = comptroller.markets(address(vToken));\n\n address underlyingAssetAddress = vToken.underlying();\n uint256 underlyingDecimals = IERC20Metadata(underlyingAssetAddress).decimals();\n\n uint256 pausedActions;\n for (uint8 i; i <= uint8(type(Action).max); ++i) {\n uint256 paused = ComptrollerInterface(comptrollerAddress).actionPaused(address(vToken), Action(i)) ? 1 : 0;\n pausedActions |= paused << i;\n }\n\n return\n VTokenMetadata({\n vToken: address(vToken),\n exchangeRateCurrent: exchangeRateCurrent,\n supplyRatePerBlock: vToken.supplyRatePerBlock(),\n borrowRatePerBlock: vToken.borrowRatePerBlock(),\n reserveFactorMantissa: vToken.reserveFactorMantissa(),\n supplyCaps: comptroller.supplyCaps(address(vToken)),\n borrowCaps: comptroller.borrowCaps(address(vToken)),\n totalBorrows: vToken.totalBorrows(),\n totalReserves: vToken.totalReserves(),\n totalSupply: vToken.totalSupply(),\n totalCash: vToken.getCash(),\n isListed: isListed,\n collateralFactorMantissa: collateralFactorMantissa,\n underlyingAssetAddress: underlyingAssetAddress,\n vTokenDecimals: vToken.decimals(),\n underlyingDecimals: underlyingDecimals,\n pausedActions: pausedActions\n });\n }\n\n /**\n * @notice Returns the metadata of all VTokens\n * @param vTokens The list of vToken addresses\n * @return An array of VTokenMetadata structs\n */\n function vTokenMetadataAll(VToken[] memory vTokens) public view returns (VTokenMetadata[] memory) {\n uint256 vTokenCount = vTokens.length;\n VTokenMetadata[] memory res = new VTokenMetadata[](vTokenCount);\n for (uint256 i; i < vTokenCount; ++i) {\n res[i] = vTokenMetadata(vTokens[i]);\n }\n return res;\n }\n\n /**\n * @notice Returns the price data for the underlying asset of the specified vToken\n * @param vToken vToken address\n * @return The price data for each asset\n */\n function vTokenUnderlyingPrice(VToken vToken) public view returns (VTokenUnderlyingPrice memory) {\n ComptrollerViewInterface comptroller = ComptrollerViewInterface(address(vToken.comptroller()));\n ResilientOracleInterface priceOracle = comptroller.oracle();\n\n return\n VTokenUnderlyingPrice({\n vToken: address(vToken),\n underlyingPrice: priceOracle.getUnderlyingPrice(address(vToken))\n });\n }\n\n function _calculateNotDistributedAwards(\n address account,\n VToken[] memory markets,\n RewardsDistributor rewardsDistributor\n ) internal view returns (PendingReward[] memory) {\n PendingReward[] memory pendingRewards = new PendingReward[](markets.length);\n for (uint256 i; i < markets.length; ++i) {\n // Market borrow and supply state we will modify update in-memory, in order to not modify storage\n RewardTokenState memory borrowState;\n (borrowState.index, borrowState.block, borrowState.lastRewardingBlock) = rewardsDistributor\n .rewardTokenBorrowState(address(markets[i]));\n RewardTokenState memory supplyState;\n (supplyState.index, supplyState.block, supplyState.lastRewardingBlock) = rewardsDistributor\n .rewardTokenSupplyState(address(markets[i]));\n Exp memory marketBorrowIndex = Exp({ mantissa: markets[i].borrowIndex() });\n\n // Update market supply and borrow index in-memory\n updateMarketBorrowIndex(address(markets[i]), rewardsDistributor, borrowState, marketBorrowIndex);\n updateMarketSupplyIndex(address(markets[i]), rewardsDistributor, supplyState);\n\n // Calculate pending rewards\n uint256 borrowReward = calculateBorrowerReward(\n address(markets[i]),\n rewardsDistributor,\n account,\n borrowState,\n marketBorrowIndex\n );\n uint256 supplyReward = calculateSupplierReward(\n address(markets[i]),\n rewardsDistributor,\n account,\n supplyState\n );\n\n PendingReward memory pendingReward;\n pendingReward.vTokenAddress = address(markets[i]);\n pendingReward.amount = borrowReward + supplyReward;\n pendingRewards[i] = pendingReward;\n }\n return pendingRewards;\n }\n\n function updateMarketBorrowIndex(\n address vToken,\n RewardsDistributor rewardsDistributor,\n RewardTokenState memory borrowState,\n Exp memory marketBorrowIndex\n ) internal view {\n uint256 borrowSpeed = rewardsDistributor.rewardTokenBorrowSpeeds(vToken);\n uint256 blockNumber = block.number;\n\n if (borrowState.lastRewardingBlock > 0 && blockNumber > borrowState.lastRewardingBlock) {\n blockNumber = borrowState.lastRewardingBlock;\n }\n\n uint256 deltaBlocks = sub_(blockNumber, uint256(borrowState.block));\n if (deltaBlocks > 0 && borrowSpeed > 0) {\n // Remove the total earned interest rate since the opening of the market from total borrows\n uint256 borrowAmount = div_(VToken(vToken).totalBorrows(), marketBorrowIndex);\n uint256 tokensAccrued = mul_(deltaBlocks, borrowSpeed);\n Double memory ratio = borrowAmount > 0 ? fraction(tokensAccrued, borrowAmount) : Double({ mantissa: 0 });\n Double memory index = add_(Double({ mantissa: borrowState.index }), ratio);\n borrowState.index = safe224(index.mantissa, \"new index overflows\");\n borrowState.block = safe32(blockNumber, \"block number overflows\");\n } else if (deltaBlocks > 0) {\n borrowState.block = safe32(blockNumber, \"block number overflows\");\n }\n }\n\n function updateMarketSupplyIndex(\n address vToken,\n RewardsDistributor rewardsDistributor,\n RewardTokenState memory supplyState\n ) internal view {\n uint256 supplySpeed = rewardsDistributor.rewardTokenSupplySpeeds(vToken);\n uint256 blockNumber = block.number;\n\n if (supplyState.lastRewardingBlock > 0 && blockNumber > supplyState.lastRewardingBlock) {\n blockNumber = supplyState.lastRewardingBlock;\n }\n\n uint256 deltaBlocks = sub_(blockNumber, uint256(supplyState.block));\n if (deltaBlocks > 0 && supplySpeed > 0) {\n uint256 supplyTokens = VToken(vToken).totalSupply();\n uint256 tokensAccrued = mul_(deltaBlocks, supplySpeed);\n Double memory ratio = supplyTokens > 0 ? fraction(tokensAccrued, supplyTokens) : Double({ mantissa: 0 });\n Double memory index = add_(Double({ mantissa: supplyState.index }), ratio);\n supplyState.index = safe224(index.mantissa, \"new index overflows\");\n supplyState.block = safe32(blockNumber, \"block number overflows\");\n } else if (deltaBlocks > 0) {\n supplyState.block = safe32(blockNumber, \"block number overflows\");\n }\n }\n\n function calculateBorrowerReward(\n address vToken,\n RewardsDistributor rewardsDistributor,\n address borrower,\n RewardTokenState memory borrowState,\n Exp memory marketBorrowIndex\n ) internal view returns (uint256) {\n Double memory borrowIndex = Double({ mantissa: borrowState.index });\n Double memory borrowerIndex = Double({\n mantissa: rewardsDistributor.rewardTokenBorrowerIndex(vToken, borrower)\n });\n if (borrowerIndex.mantissa == 0 && borrowIndex.mantissa >= rewardsDistributor.INITIAL_INDEX()) {\n // Covers the case where users borrowed tokens before the market's borrow state index was set\n borrowerIndex.mantissa = rewardsDistributor.INITIAL_INDEX();\n }\n Double memory deltaIndex = sub_(borrowIndex, borrowerIndex);\n uint256 borrowerAmount = div_(VToken(vToken).borrowBalanceStored(borrower), marketBorrowIndex);\n uint256 borrowerDelta = mul_(borrowerAmount, deltaIndex);\n return borrowerDelta;\n }\n\n function calculateSupplierReward(\n address vToken,\n RewardsDistributor rewardsDistributor,\n address supplier,\n RewardTokenState memory supplyState\n ) internal view returns (uint256) {\n Double memory supplyIndex = Double({ mantissa: supplyState.index });\n Double memory supplierIndex = Double({\n mantissa: rewardsDistributor.rewardTokenSupplierIndex(vToken, supplier)\n });\n if (supplierIndex.mantissa == 0 && supplyIndex.mantissa >= rewardsDistributor.INITIAL_INDEX()) {\n // Covers the case where users supplied tokens before the market's supply state index was set\n supplierIndex.mantissa = rewardsDistributor.INITIAL_INDEX();\n }\n Double memory deltaIndex = sub_(supplyIndex, supplierIndex);\n uint256 supplierTokens = VToken(vToken).balanceOf(supplier);\n uint256 supplierDelta = mul_(supplierTokens, deltaIndex);\n return supplierDelta;\n }\n}\n" + }, + "contracts/Lens/PoolLens.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IERC20Metadata } from \"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol\";\nimport { ResilientOracleInterface } from \"@venusprotocol/oracle/contracts/interfaces/OracleInterface.sol\";\n\nimport { ExponentialNoError } from \"../ExponentialNoError.sol\";\nimport { VToken } from \"../VToken.sol\";\nimport { Action, ComptrollerInterface, ComptrollerViewInterface } from \"../ComptrollerInterface.sol\";\nimport { PoolRegistryInterface } from \"../Pool/PoolRegistryInterface.sol\";\nimport { PoolRegistry } from \"../Pool/PoolRegistry.sol\";\nimport { RewardsDistributor } from \"../Rewards/RewardsDistributor.sol\";\nimport { TimeManagerV8 } from \"@venusprotocol/solidity-utilities/contracts/TimeManagerV8.sol\";\n\n/**\n * @title PoolLens\n * @author Venus\n * @notice The `PoolLens` contract is designed to retrieve important information for each registered pool. A list of essential information\n * for all pools within the lending protocol can be acquired through the function `getAllPools()`. Additionally, the following records can be\n * looked up for specific pools and markets:\n- the vToken balance of a given user;\n- the pool data (oracle address, associated vToken, liquidation incentive, etc) of a pool via its associated comptroller address;\n- the vToken address in a pool for a given asset;\n- a list of all pools that support an asset;\n- the underlying asset price of a vToken;\n- the metadata (exchange/borrow/supply rate, total supply, collateral factor, etc) of any vToken.\n */\ncontract PoolLens is ExponentialNoError, TimeManagerV8 {\n /**\n * @dev Struct for PoolDetails.\n */\n struct PoolData {\n string name;\n address creator;\n address comptroller;\n uint256 blockPosted;\n uint256 timestampPosted;\n string category;\n string logoURL;\n string description;\n address priceOracle;\n uint256 closeFactor;\n uint256 liquidationIncentive;\n uint256 minLiquidatableCollateral;\n VTokenMetadata[] vTokens;\n }\n\n /**\n * @dev Struct for VToken.\n */\n struct VTokenMetadata {\n address vToken;\n uint256 exchangeRateCurrent;\n uint256 supplyRatePerBlockOrTimestamp;\n uint256 borrowRatePerBlockOrTimestamp;\n uint256 reserveFactorMantissa;\n uint256 supplyCaps;\n uint256 borrowCaps;\n uint256 totalBorrows;\n uint256 totalReserves;\n uint256 totalSupply;\n uint256 totalCash;\n bool isListed;\n uint256 collateralFactorMantissa;\n address underlyingAssetAddress;\n uint256 vTokenDecimals;\n uint256 underlyingDecimals;\n uint256 pausedActions;\n }\n\n /**\n * @dev Struct for VTokenBalance.\n */\n struct VTokenBalances {\n address vToken;\n uint256 balanceOf;\n uint256 borrowBalanceCurrent;\n uint256 balanceOfUnderlying;\n uint256 tokenBalance;\n uint256 tokenAllowance;\n }\n\n /**\n * @dev Struct for underlyingPrice of VToken.\n */\n struct VTokenUnderlyingPrice {\n address vToken;\n uint256 underlyingPrice;\n }\n\n /**\n * @dev Struct with pending reward info for a market.\n */\n struct PendingReward {\n address vTokenAddress;\n uint256 amount;\n }\n\n /**\n * @dev Struct with reward distribution totals for a single reward token and distributor.\n */\n struct RewardSummary {\n address distributorAddress;\n address rewardTokenAddress;\n uint256 totalRewards;\n PendingReward[] pendingRewards;\n }\n\n /**\n * @dev Struct used in RewardDistributor to save last updated market state.\n */\n struct RewardTokenState {\n // The market's last updated rewardTokenBorrowIndex or rewardTokenSupplyIndex\n uint224 index;\n // The block number or timestamp the index was last updated at\n uint256 blockOrTimestamp;\n // The block number or timestamp at which to stop rewards\n uint256 lastRewardingBlockOrTimestamp;\n }\n\n /**\n * @dev Struct with bad debt of a market denominated\n */\n struct BadDebt {\n address vTokenAddress;\n uint256 badDebtUsd;\n }\n\n /**\n * @dev Struct with bad debt total denominated in usd for a pool and an array of BadDebt structs for each market\n */\n struct BadDebtSummary {\n address comptroller;\n uint256 totalBadDebtUsd;\n BadDebt[] badDebts;\n }\n\n /**\n * @param timeBased_ A boolean indicating whether the contract is based on time or block.\n * @param blocksPerYear_ The number of blocks per year\n * @custom:oz-upgrades-unsafe-allow constructor\n */\n constructor(bool timeBased_, uint256 blocksPerYear_) TimeManagerV8(timeBased_, blocksPerYear_) {}\n\n /**\n * @notice Queries the user's supply/borrow balances in vTokens\n * @param vTokens The list of vToken addresses\n * @param account The user Account\n * @return A list of structs containing balances data\n */\n function vTokenBalancesAll(VToken[] calldata vTokens, address account) external returns (VTokenBalances[] memory) {\n uint256 vTokenCount = vTokens.length;\n VTokenBalances[] memory res = new VTokenBalances[](vTokenCount);\n for (uint256 i; i < vTokenCount; ++i) {\n res[i] = vTokenBalances(vTokens[i], account);\n }\n return res;\n }\n\n /**\n * @notice Queries all pools with addtional details for each of them\n * @dev This function is not designed to be called in a transaction: it is too gas-intensive\n * @param poolRegistryAddress The address of the PoolRegistry contract\n * @return Arrays of all Venus pools' data\n */\n function getAllPools(address poolRegistryAddress) external view returns (PoolData[] memory) {\n PoolRegistryInterface poolRegistryInterface = PoolRegistryInterface(poolRegistryAddress);\n PoolRegistry.VenusPool[] memory venusPools = poolRegistryInterface.getAllPools();\n uint256 poolLength = venusPools.length;\n\n PoolData[] memory poolDataItems = new PoolData[](poolLength);\n\n for (uint256 i; i < poolLength; ++i) {\n PoolRegistry.VenusPool memory venusPool = venusPools[i];\n PoolData memory poolData = getPoolDataFromVenusPool(poolRegistryAddress, venusPool);\n poolDataItems[i] = poolData;\n }\n\n return poolDataItems;\n }\n\n /**\n * @notice Queries the details of a pool identified by Comptroller address\n * @param poolRegistryAddress The address of the PoolRegistry contract\n * @param comptroller The Comptroller implementation address\n * @return PoolData structure containing the details of the pool\n */\n function getPoolByComptroller(\n address poolRegistryAddress,\n address comptroller\n ) external view returns (PoolData memory) {\n PoolRegistryInterface poolRegistryInterface = PoolRegistryInterface(poolRegistryAddress);\n return getPoolDataFromVenusPool(poolRegistryAddress, poolRegistryInterface.getPoolByComptroller(comptroller));\n }\n\n /**\n * @notice Returns vToken holding the specified underlying asset in the specified pool\n * @param poolRegistryAddress The address of the PoolRegistry contract\n * @param comptroller The pool comptroller\n * @param asset The underlyingAsset of VToken\n * @return Address of the vToken\n */\n function getVTokenForAsset(\n address poolRegistryAddress,\n address comptroller,\n address asset\n ) external view returns (address) {\n PoolRegistryInterface poolRegistryInterface = PoolRegistryInterface(poolRegistryAddress);\n return poolRegistryInterface.getVTokenForAsset(comptroller, asset);\n }\n\n /**\n * @notice Returns all pools that support the specified underlying asset\n * @param poolRegistryAddress The address of the PoolRegistry contract\n * @param asset The underlying asset of vToken\n * @return A list of Comptroller contracts\n */\n function getPoolsSupportedByAsset(\n address poolRegistryAddress,\n address asset\n ) external view returns (address[] memory) {\n PoolRegistryInterface poolRegistryInterface = PoolRegistryInterface(poolRegistryAddress);\n return poolRegistryInterface.getPoolsSupportedByAsset(asset);\n }\n\n /**\n * @notice Returns the price data for the underlying assets of the specified vTokens\n * @param vTokens The list of vToken addresses\n * @return An array containing the price data for each asset\n */\n function vTokenUnderlyingPriceAll(\n VToken[] calldata vTokens\n ) external view returns (VTokenUnderlyingPrice[] memory) {\n uint256 vTokenCount = vTokens.length;\n VTokenUnderlyingPrice[] memory res = new VTokenUnderlyingPrice[](vTokenCount);\n for (uint256 i; i < vTokenCount; ++i) {\n res[i] = vTokenUnderlyingPrice(vTokens[i]);\n }\n return res;\n }\n\n /**\n * @notice Returns the pending rewards for a user for a given pool.\n * @param account The user account.\n * @param comptrollerAddress address\n * @return Pending rewards array\n */\n function getPendingRewards(\n address account,\n address comptrollerAddress\n ) external view returns (RewardSummary[] memory) {\n VToken[] memory markets = ComptrollerInterface(comptrollerAddress).getAllMarkets();\n RewardsDistributor[] memory rewardsDistributors = ComptrollerViewInterface(comptrollerAddress)\n .getRewardDistributors();\n RewardSummary[] memory rewardSummary = new RewardSummary[](rewardsDistributors.length);\n for (uint256 i; i < rewardsDistributors.length; ++i) {\n RewardSummary memory reward;\n reward.distributorAddress = address(rewardsDistributors[i]);\n reward.rewardTokenAddress = address(rewardsDistributors[i].rewardToken());\n reward.totalRewards = rewardsDistributors[i].rewardTokenAccrued(account);\n reward.pendingRewards = _calculateNotDistributedAwards(account, markets, rewardsDistributors[i]);\n rewardSummary[i] = reward;\n }\n return rewardSummary;\n }\n\n /**\n * @notice Returns a summary of a pool's bad debt broken down by market\n *\n * @param comptrollerAddress Address of the comptroller\n *\n * @return badDebtSummary A struct with comptroller address, total bad debut denominated in usd, and\n * a break down of bad debt by market\n */\n function getPoolBadDebt(address comptrollerAddress) external view returns (BadDebtSummary memory) {\n uint256 totalBadDebtUsd;\n\n // Get every market in the pool\n ComptrollerViewInterface comptroller = ComptrollerViewInterface(comptrollerAddress);\n VToken[] memory markets = comptroller.getAllMarkets();\n ResilientOracleInterface priceOracle = comptroller.oracle();\n\n BadDebt[] memory badDebts = new BadDebt[](markets.length);\n\n BadDebtSummary memory badDebtSummary;\n badDebtSummary.comptroller = comptrollerAddress;\n badDebtSummary.badDebts = badDebts;\n\n // // Calculate the bad debt is USD per market\n for (uint256 i; i < markets.length; ++i) {\n BadDebt memory badDebt;\n badDebt.vTokenAddress = address(markets[i]);\n badDebt.badDebtUsd =\n (VToken(address(markets[i])).badDebt() * priceOracle.getUnderlyingPrice(address(markets[i]))) /\n EXP_SCALE;\n badDebtSummary.badDebts[i] = badDebt;\n totalBadDebtUsd = totalBadDebtUsd + badDebt.badDebtUsd;\n }\n\n badDebtSummary.totalBadDebtUsd = totalBadDebtUsd;\n\n return badDebtSummary;\n }\n\n /**\n * @notice Queries the user's supply/borrow balances in the specified vToken\n * @param vToken vToken address\n * @param account The user Account\n * @return A struct containing the balances data\n */\n function vTokenBalances(VToken vToken, address account) public returns (VTokenBalances memory) {\n uint256 balanceOf = vToken.balanceOf(account);\n uint256 borrowBalanceCurrent = vToken.borrowBalanceCurrent(account);\n uint256 balanceOfUnderlying = vToken.balanceOfUnderlying(account);\n uint256 tokenBalance;\n uint256 tokenAllowance;\n\n IERC20 underlying = IERC20(vToken.underlying());\n tokenBalance = underlying.balanceOf(account);\n tokenAllowance = underlying.allowance(account, address(vToken));\n\n return\n VTokenBalances({\n vToken: address(vToken),\n balanceOf: balanceOf,\n borrowBalanceCurrent: borrowBalanceCurrent,\n balanceOfUnderlying: balanceOfUnderlying,\n tokenBalance: tokenBalance,\n tokenAllowance: tokenAllowance\n });\n }\n\n /**\n * @notice Queries additional information for the pool\n * @param poolRegistryAddress Address of the PoolRegistry\n * @param venusPool The VenusPool Object from PoolRegistry\n * @return Enriched PoolData\n */\n function getPoolDataFromVenusPool(\n address poolRegistryAddress,\n PoolRegistry.VenusPool memory venusPool\n ) public view returns (PoolData memory) {\n // Get tokens in the Pool\n ComptrollerInterface comptrollerInstance = ComptrollerInterface(venusPool.comptroller);\n\n VToken[] memory vTokens = comptrollerInstance.getAllMarkets();\n\n VTokenMetadata[] memory vTokenMetadataItems = vTokenMetadataAll(vTokens);\n\n PoolRegistryInterface poolRegistryInterface = PoolRegistryInterface(poolRegistryAddress);\n\n PoolRegistry.VenusPoolMetaData memory venusPoolMetaData = poolRegistryInterface.getVenusPoolMetadata(\n venusPool.comptroller\n );\n\n ComptrollerViewInterface comptrollerViewInstance = ComptrollerViewInterface(venusPool.comptroller);\n\n PoolData memory poolData = PoolData({\n name: venusPool.name,\n creator: venusPool.creator,\n comptroller: venusPool.comptroller,\n blockPosted: venusPool.blockPosted,\n timestampPosted: venusPool.timestampPosted,\n category: venusPoolMetaData.category,\n logoURL: venusPoolMetaData.logoURL,\n description: venusPoolMetaData.description,\n vTokens: vTokenMetadataItems,\n priceOracle: address(comptrollerViewInstance.oracle()),\n closeFactor: comptrollerViewInstance.closeFactorMantissa(),\n liquidationIncentive: comptrollerViewInstance.liquidationIncentiveMantissa(),\n minLiquidatableCollateral: comptrollerViewInstance.minLiquidatableCollateral()\n });\n\n return poolData;\n }\n\n /**\n * @notice Returns the metadata of VToken\n * @param vToken The address of vToken\n * @return VTokenMetadata struct\n */\n function vTokenMetadata(VToken vToken) public view returns (VTokenMetadata memory) {\n uint256 exchangeRateCurrent = vToken.exchangeRateStored();\n address comptrollerAddress = address(vToken.comptroller());\n ComptrollerViewInterface comptroller = ComptrollerViewInterface(comptrollerAddress);\n (bool isListed, uint256 collateralFactorMantissa) = comptroller.markets(address(vToken));\n\n address underlyingAssetAddress = vToken.underlying();\n uint256 underlyingDecimals = IERC20Metadata(underlyingAssetAddress).decimals();\n\n uint256 pausedActions;\n for (uint8 i; i <= uint8(type(Action).max); ++i) {\n uint256 paused = ComptrollerInterface(comptrollerAddress).actionPaused(address(vToken), Action(i)) ? 1 : 0;\n pausedActions |= paused << i;\n }\n\n uint256 supplyRatePerBlock;\n\n if (vToken.totalSupply() > 0) {\n supplyRatePerBlock = vToken.supplyRatePerBlock();\n }\n\n return\n VTokenMetadata({\n vToken: address(vToken),\n exchangeRateCurrent: exchangeRateCurrent,\n supplyRatePerBlockOrTimestamp: supplyRatePerBlock,\n borrowRatePerBlockOrTimestamp: vToken.borrowRatePerBlock(),\n reserveFactorMantissa: vToken.reserveFactorMantissa(),\n supplyCaps: comptroller.supplyCaps(address(vToken)),\n borrowCaps: comptroller.borrowCaps(address(vToken)),\n totalBorrows: vToken.totalBorrows(),\n totalReserves: vToken.totalReserves(),\n totalSupply: vToken.totalSupply(),\n totalCash: vToken.getCash(),\n isListed: isListed,\n collateralFactorMantissa: collateralFactorMantissa,\n underlyingAssetAddress: underlyingAssetAddress,\n vTokenDecimals: vToken.decimals(),\n underlyingDecimals: underlyingDecimals,\n pausedActions: pausedActions\n });\n }\n\n /**\n * @notice Returns the metadata of all VTokens\n * @param vTokens The list of vToken addresses\n * @return An array of VTokenMetadata structs\n */\n function vTokenMetadataAll(VToken[] memory vTokens) public view returns (VTokenMetadata[] memory) {\n uint256 vTokenCount = vTokens.length;\n VTokenMetadata[] memory res = new VTokenMetadata[](vTokenCount);\n for (uint256 i; i < vTokenCount; ++i) {\n res[i] = vTokenMetadata(vTokens[i]);\n }\n return res;\n }\n\n /**\n * @notice Returns the price data for the underlying asset of the specified vToken\n * @param vToken vToken address\n * @return The price data for each asset\n */\n function vTokenUnderlyingPrice(VToken vToken) public view returns (VTokenUnderlyingPrice memory) {\n ComptrollerViewInterface comptroller = ComptrollerViewInterface(address(vToken.comptroller()));\n ResilientOracleInterface priceOracle = comptroller.oracle();\n\n return\n VTokenUnderlyingPrice({\n vToken: address(vToken),\n underlyingPrice: priceOracle.getUnderlyingPrice(address(vToken))\n });\n }\n\n function _calculateNotDistributedAwards(\n address account,\n VToken[] memory markets,\n RewardsDistributor rewardsDistributor\n ) internal view returns (PendingReward[] memory) {\n PendingReward[] memory pendingRewards = new PendingReward[](markets.length);\n\n for (uint256 i; i < markets.length; ++i) {\n // Market borrow and supply state we will modify update in-memory, in order to not modify storage\n RewardTokenState memory borrowState;\n RewardTokenState memory supplyState;\n\n if (isTimeBased) {\n (\n borrowState.index,\n borrowState.blockOrTimestamp,\n borrowState.lastRewardingBlockOrTimestamp\n ) = rewardsDistributor.rewardTokenBorrowStateTimeBased(address(markets[i]));\n (\n supplyState.index,\n supplyState.blockOrTimestamp,\n supplyState.lastRewardingBlockOrTimestamp\n ) = rewardsDistributor.rewardTokenSupplyStateTimeBased(address(markets[i]));\n } else {\n (\n borrowState.index,\n borrowState.blockOrTimestamp,\n borrowState.lastRewardingBlockOrTimestamp\n ) = rewardsDistributor.rewardTokenBorrowState(address(markets[i]));\n (\n supplyState.index,\n supplyState.blockOrTimestamp,\n supplyState.lastRewardingBlockOrTimestamp\n ) = rewardsDistributor.rewardTokenSupplyState(address(markets[i]));\n }\n\n Exp memory marketBorrowIndex = Exp({ mantissa: markets[i].borrowIndex() });\n\n // Update market supply and borrow index in-memory\n updateMarketBorrowIndex(address(markets[i]), rewardsDistributor, borrowState, marketBorrowIndex);\n updateMarketSupplyIndex(address(markets[i]), rewardsDistributor, supplyState);\n\n // Calculate pending rewards\n uint256 borrowReward = calculateBorrowerReward(\n address(markets[i]),\n rewardsDistributor,\n account,\n borrowState,\n marketBorrowIndex\n );\n uint256 supplyReward = calculateSupplierReward(\n address(markets[i]),\n rewardsDistributor,\n account,\n supplyState\n );\n\n PendingReward memory pendingReward;\n pendingReward.vTokenAddress = address(markets[i]);\n pendingReward.amount = borrowReward + supplyReward;\n pendingRewards[i] = pendingReward;\n }\n return pendingRewards;\n }\n\n function updateMarketBorrowIndex(\n address vToken,\n RewardsDistributor rewardsDistributor,\n RewardTokenState memory borrowState,\n Exp memory marketBorrowIndex\n ) internal view {\n uint256 borrowSpeed = rewardsDistributor.rewardTokenBorrowSpeeds(vToken);\n uint256 blockNumberOrTimestamp = getBlockNumberOrTimestamp();\n\n if (\n borrowState.lastRewardingBlockOrTimestamp > 0 &&\n blockNumberOrTimestamp > borrowState.lastRewardingBlockOrTimestamp\n ) {\n blockNumberOrTimestamp = borrowState.lastRewardingBlockOrTimestamp;\n }\n\n uint256 deltaBlocksOrTimestamp = sub_(blockNumberOrTimestamp, borrowState.blockOrTimestamp);\n if (deltaBlocksOrTimestamp > 0 && borrowSpeed > 0) {\n // Remove the total earned interest rate since the opening of the market from total borrows\n uint256 borrowAmount = div_(VToken(vToken).totalBorrows(), marketBorrowIndex);\n uint256 tokensAccrued = mul_(deltaBlocksOrTimestamp, borrowSpeed);\n Double memory ratio = borrowAmount > 0 ? fraction(tokensAccrued, borrowAmount) : Double({ mantissa: 0 });\n Double memory index = add_(Double({ mantissa: borrowState.index }), ratio);\n borrowState.index = safe224(index.mantissa, \"new index overflows\");\n borrowState.blockOrTimestamp = blockNumberOrTimestamp;\n } else if (deltaBlocksOrTimestamp > 0) {\n borrowState.blockOrTimestamp = blockNumberOrTimestamp;\n }\n }\n\n function updateMarketSupplyIndex(\n address vToken,\n RewardsDistributor rewardsDistributor,\n RewardTokenState memory supplyState\n ) internal view {\n uint256 supplySpeed = rewardsDistributor.rewardTokenSupplySpeeds(vToken);\n uint256 blockNumberOrTimestamp = getBlockNumberOrTimestamp();\n\n if (\n supplyState.lastRewardingBlockOrTimestamp > 0 &&\n blockNumberOrTimestamp > supplyState.lastRewardingBlockOrTimestamp\n ) {\n blockNumberOrTimestamp = supplyState.lastRewardingBlockOrTimestamp;\n }\n\n uint256 deltaBlocksOrTimestamp = sub_(blockNumberOrTimestamp, supplyState.blockOrTimestamp);\n if (deltaBlocksOrTimestamp > 0 && supplySpeed > 0) {\n uint256 supplyTokens = VToken(vToken).totalSupply();\n uint256 tokensAccrued = mul_(deltaBlocksOrTimestamp, supplySpeed);\n Double memory ratio = supplyTokens > 0 ? fraction(tokensAccrued, supplyTokens) : Double({ mantissa: 0 });\n Double memory index = add_(Double({ mantissa: supplyState.index }), ratio);\n supplyState.index = safe224(index.mantissa, \"new index overflows\");\n supplyState.blockOrTimestamp = blockNumberOrTimestamp;\n } else if (deltaBlocksOrTimestamp > 0) {\n supplyState.blockOrTimestamp = blockNumberOrTimestamp;\n }\n }\n\n function calculateBorrowerReward(\n address vToken,\n RewardsDistributor rewardsDistributor,\n address borrower,\n RewardTokenState memory borrowState,\n Exp memory marketBorrowIndex\n ) internal view returns (uint256) {\n Double memory borrowIndex = Double({ mantissa: borrowState.index });\n Double memory borrowerIndex = Double({\n mantissa: rewardsDistributor.rewardTokenBorrowerIndex(vToken, borrower)\n });\n if (borrowerIndex.mantissa == 0 && borrowIndex.mantissa >= rewardsDistributor.INITIAL_INDEX()) {\n // Covers the case where users borrowed tokens before the market's borrow state index was set\n borrowerIndex.mantissa = rewardsDistributor.INITIAL_INDEX();\n }\n Double memory deltaIndex = sub_(borrowIndex, borrowerIndex);\n uint256 borrowerAmount = div_(VToken(vToken).borrowBalanceStored(borrower), marketBorrowIndex);\n uint256 borrowerDelta = mul_(borrowerAmount, deltaIndex);\n return borrowerDelta;\n }\n\n function calculateSupplierReward(\n address vToken,\n RewardsDistributor rewardsDistributor,\n address supplier,\n RewardTokenState memory supplyState\n ) internal view returns (uint256) {\n Double memory supplyIndex = Double({ mantissa: supplyState.index });\n Double memory supplierIndex = Double({\n mantissa: rewardsDistributor.rewardTokenSupplierIndex(vToken, supplier)\n });\n if (supplierIndex.mantissa == 0 && supplyIndex.mantissa >= rewardsDistributor.INITIAL_INDEX()) {\n // Covers the case where users supplied tokens before the market's supply state index was set\n supplierIndex.mantissa = rewardsDistributor.INITIAL_INDEX();\n }\n Double memory deltaIndex = sub_(supplyIndex, supplierIndex);\n uint256 supplierTokens = VToken(vToken).balanceOf(supplier);\n uint256 supplierDelta = mul_(supplierTokens, deltaIndex);\n return supplierDelta;\n }\n}\n" + }, + "contracts/Lens/SpokePoolLens.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IERC20Metadata } from \"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol\";\nimport { ResilientOracleInterface } from \"@venusprotocol/oracle/contracts/interfaces/OracleInterface.sol\";\nimport { TimeManagerV8 } from \"@venusprotocol/solidity-utilities/contracts/TimeManagerV8.sol\";\n\nimport { ExponentialNoError } from \"../ExponentialNoError.sol\";\nimport { VToken } from \"../VToken.sol\";\nimport { Action, ComptrollerInterface, ComptrollerViewInterface } from \"../ComptrollerInterface.sol\";\nimport { PoolRegistryInterface } from \"../Pool/PoolRegistryInterface.sol\";\nimport { PoolRegistry } from \"../Pool/PoolRegistry.sol\";\nimport { RewardsDistributor } from \"../Rewards/RewardsDistributor.sol\";\nimport { SpokeComptrollerViewInterface } from \"../Spoke/SpokeComptrollerInterface.sol\";\n\n/**\n * @title SpokePoolLens\n * @notice Reads pool and market state specific to a spoke pool\n *\n * @dev Spoke pools expose additional state that is not included in the shared PoolLens data, including the\n * deviation-bounded oracle, the supply and liquidation allowlists, liquidation thresholds and liquidation incentives.\n * Those reads are named `spoke*` or `getSpokePool*` and return spoke-shaped structs.\n *\n * The rest of the surface mirrors `PoolLens` under the same names and struct shapes, so that reading a spoke pool\n * takes one address rather than two. Those reads go through getters both kinds of pool share.\n *\n * Access-controlled call permissions are deliberately not reported: `AccessControlManager` derives the role from its\n * own caller, so asked from a lens it answers `false` for an account that can in fact call.\n *\n * This contract holds no state and is versioned by redeployment.\n */\ncontract SpokePoolLens is ExponentialNoError, TimeManagerV8 {\n /**\n * @dev Mirrors `PoolLens.PoolData` with spoke-pool-specific fields.\n */\n struct SpokePoolData {\n string name;\n address creator;\n address comptroller;\n uint256 blockPosted;\n uint256 timestampPosted;\n string category;\n string logoURL;\n string description;\n address priceOracle;\n uint256 closeFactor;\n /// @notice The pool-wide liquidation incentive, scaled by 1e18\n uint256 poolLiquidationIncentiveMantissa;\n uint256 minLiquidatableCollateral;\n /// @notice Oracle used to bound collateral prices for borrowing and redeeming. Zero until governance sets it,\n /// and while it is zero both actions revert\n address deviationBoundedOracle;\n /// @notice Whether liquidation is restricted to allowlisted accounts\n bool liquidationAllowlistEnabled;\n SpokeVTokenMetadata[] vTokens;\n }\n\n /**\n * @dev Mirrors `PoolLens.VTokenMetadata` with spoke-pool-specific fields.\n */\n struct SpokeVTokenMetadata {\n address vToken;\n uint256 exchangeRateCurrent;\n uint256 supplyRatePerBlockOrTimestamp;\n uint256 borrowRatePerBlockOrTimestamp;\n uint256 reserveFactorMantissa;\n uint256 supplyCaps;\n uint256 borrowCaps;\n uint256 totalBorrows;\n uint256 totalReserves;\n uint256 totalSupply;\n uint256 totalCash;\n bool isListed;\n uint256 collateralFactorMantissa;\n /// @notice The collateral weight above which a position becomes liquidatable, scaled by 1e18\n uint256 liquidationThresholdMantissa;\n address underlyingAssetAddress;\n uint256 vTokenDecimals;\n uint256 underlyingDecimals;\n uint256 pausedActions;\n /// @notice The liquidation incentive effective for this market, scaled by 1e18\n uint256 effectiveLiquidationIncentiveMantissa;\n /// @notice The market-specific liquidation incentive, or zero when using the pool-wide value\n uint256 ownLiquidationIncentiveMantissa;\n /// @notice Whether supply is restricted to allowlisted accounts\n bool supplyAllowlistEnabled;\n /// @notice Whether the market allows full liquidation without a shortfall\n bool forcedLiquidationEnabled;\n }\n\n /**\n * @dev Returns both the market's allowlist status and whether the account is allowlisted.\n */\n struct SpokeSupplyPermission {\n address vToken;\n /// @notice Whether supply is restricted to allowlisted accounts\n bool allowlistEnabled;\n /// @notice Whether the account is allowlisted for the market\n bool accountAllowlisted;\n }\n\n /**\n * @dev Struct for VTokenBalance.\n */\n struct VTokenBalances {\n address vToken;\n uint256 balanceOf;\n uint256 borrowBalanceCurrent;\n uint256 balanceOfUnderlying;\n uint256 tokenBalance;\n uint256 tokenAllowance;\n }\n\n /**\n * @dev Struct for underlyingPrice of VToken.\n */\n struct VTokenUnderlyingPrice {\n address vToken;\n uint256 underlyingPrice;\n }\n\n /**\n * @dev Struct with pending reward info for a market.\n */\n struct PendingReward {\n address vTokenAddress;\n uint256 amount;\n }\n\n /**\n * @dev Struct with reward distribution totals for a single reward token and distributor.\n */\n struct RewardSummary {\n address distributorAddress;\n address rewardTokenAddress;\n uint256 totalRewards;\n PendingReward[] pendingRewards;\n }\n\n /**\n * @dev Struct used in RewardDistributor to save last updated market state.\n */\n struct RewardTokenState {\n // The market's last updated rewardTokenBorrowIndex or rewardTokenSupplyIndex\n uint224 index;\n // The block number or timestamp the index was last updated at\n uint256 blockOrTimestamp;\n // The block number or timestamp at which to stop rewards\n uint256 lastRewardingBlockOrTimestamp;\n }\n\n /**\n * @dev Struct with bad debt of a market denominated\n */\n struct BadDebt {\n address vTokenAddress;\n uint256 badDebtUsd;\n }\n\n /**\n * @dev Struct with bad debt total denominated in usd for a pool and an array of BadDebt structs for each market\n */\n struct BadDebtSummary {\n address comptroller;\n uint256 totalBadDebtUsd;\n BadDebt[] badDebts;\n }\n\n /**\n * @param timeBased_ A boolean indicating whether the contract is based on time or block\n * @param blocksPerYear_ The number of blocks per year\n * @custom:oz-upgrades-unsafe-allow constructor\n */\n constructor(bool timeBased_, uint256 blocksPerYear_) TimeManagerV8(timeBased_, blocksPerYear_) {}\n\n /**\n * @notice Queries the user's supply/borrow balances in vTokens\n * @param vTokens The list of vToken addresses\n * @param account The user Account\n * @return A list of structs containing balances data\n */\n function vTokenBalancesAll(VToken[] calldata vTokens, address account) external returns (VTokenBalances[] memory) {\n uint256 vTokenCount = vTokens.length;\n VTokenBalances[] memory res = new VTokenBalances[](vTokenCount);\n for (uint256 i; i < vTokenCount; ++i) {\n res[i] = vTokenBalances(vTokens[i], account);\n }\n return res;\n }\n\n /**\n * @notice Queries every pool and market in a spoke pool registry\n * @dev Not intended to be called in a transaction due to its gas cost. The registry must contain only spoke pools.\n * @param poolRegistryAddress The registry to enumerate\n * @return The data of every pool in the registry\n */\n function getAllSpokePools(address poolRegistryAddress) external view returns (SpokePoolData[] memory) {\n PoolRegistry.VenusPool[] memory pools = PoolRegistryInterface(poolRegistryAddress).getAllPools();\n uint256 poolLength = pools.length;\n\n SpokePoolData[] memory poolDataItems = new SpokePoolData[](poolLength);\n\n for (uint256 i; i < poolLength; ++i) {\n poolDataItems[i] = getSpokePoolData(poolRegistryAddress, pools[i]);\n }\n\n return poolDataItems;\n }\n\n /**\n * @notice Queries a spoke pool by its comptroller address\n * @param poolRegistryAddress The registry containing the pool\n * @param comptroller The pool's comptroller\n * @return The pool's data\n */\n function getSpokePoolByComptroller(\n address poolRegistryAddress,\n address comptroller\n ) external view returns (SpokePoolData memory) {\n PoolRegistryInterface poolRegistry = PoolRegistryInterface(poolRegistryAddress);\n return getSpokePoolData(poolRegistryAddress, poolRegistry.getPoolByComptroller(comptroller));\n }\n\n /**\n * @notice Returns whether an account may supply to each specified market\n * @dev The account checked is the one receiving the minted vTokens, which can differ from the payer when using\n * `mintBehalf`.\n * @param comptroller The spoke pool's comptroller\n * @param vTokens The markets to test\n * @param account The account to test\n * @return One entry per market, in the order given\n */\n function spokeSupplyPermissions(\n address comptroller,\n VToken[] memory vTokens,\n address account\n ) external view returns (SpokeSupplyPermission[] memory) {\n SpokeComptrollerViewInterface spoke = SpokeComptrollerViewInterface(comptroller);\n uint256 len = vTokens.length;\n\n SpokeSupplyPermission[] memory permissions = new SpokeSupplyPermission[](len);\n\n for (uint256 i; i < len; ++i) {\n address vToken = address(vTokens[i]);\n permissions[i] = SpokeSupplyPermission({\n vToken: vToken,\n allowlistEnabled: spoke.isSupplyAllowlistEnabled(vToken),\n accountAllowlisted: spoke.isAllowedSupplier(vToken, account)\n });\n }\n\n return permissions;\n }\n\n /**\n * @notice Returns whether an account may seize collateral in a spoke pool\n * @param comptroller The spoke pool's comptroller\n * @param liquidator The account to test\n * @return allowlistEnabled Whether liquidation is restricted to allowlisted accounts\n * @return accountAllowlisted Whether the account is allowlisted\n */\n function spokeLiquidationPermission(\n address comptroller,\n address liquidator\n ) external view returns (bool allowlistEnabled, bool accountAllowlisted) {\n SpokeComptrollerViewInterface spoke = SpokeComptrollerViewInterface(comptroller);\n return (spoke.isLiquidationAllowlistEnabled(), spoke.isAllowedLiquidator(liquidator));\n }\n\n /**\n * @notice Returns the price data for the underlying assets of the specified vTokens\n * @param vTokens The list of vToken addresses\n * @return An array containing the price data for each asset\n */\n function vTokenUnderlyingPriceAll(\n VToken[] calldata vTokens\n ) external view returns (VTokenUnderlyingPrice[] memory) {\n uint256 vTokenCount = vTokens.length;\n VTokenUnderlyingPrice[] memory res = new VTokenUnderlyingPrice[](vTokenCount);\n for (uint256 i; i < vTokenCount; ++i) {\n res[i] = vTokenUnderlyingPrice(vTokens[i]);\n }\n return res;\n }\n\n /**\n * @notice Returns the pending rewards for a user for a given pool.\n * @param account The user account.\n * @param comptrollerAddress address\n * @return Pending rewards array\n */\n function getPendingRewards(\n address account,\n address comptrollerAddress\n ) external view returns (RewardSummary[] memory) {\n VToken[] memory markets = ComptrollerInterface(comptrollerAddress).getAllMarkets();\n RewardsDistributor[] memory rewardsDistributors = ComptrollerViewInterface(comptrollerAddress)\n .getRewardDistributors();\n RewardSummary[] memory rewardSummary = new RewardSummary[](rewardsDistributors.length);\n for (uint256 i; i < rewardsDistributors.length; ++i) {\n RewardSummary memory reward;\n reward.distributorAddress = address(rewardsDistributors[i]);\n reward.rewardTokenAddress = address(rewardsDistributors[i].rewardToken());\n reward.totalRewards = rewardsDistributors[i].rewardTokenAccrued(account);\n reward.pendingRewards = _calculateNotDistributedAwards(account, markets, rewardsDistributors[i]);\n rewardSummary[i] = reward;\n }\n return rewardSummary;\n }\n\n /**\n * @notice Returns a summary of a pool's bad debt broken down by market\n *\n * @param comptrollerAddress Address of the comptroller\n *\n * @return badDebtSummary A struct with comptroller address, total bad debut denominated in usd, and\n * a break down of bad debt by market\n */\n function getPoolBadDebt(address comptrollerAddress) external view returns (BadDebtSummary memory) {\n uint256 totalBadDebtUsd;\n\n // Get every market in the pool\n ComptrollerViewInterface comptroller = ComptrollerViewInterface(comptrollerAddress);\n VToken[] memory markets = comptroller.getAllMarkets();\n ResilientOracleInterface priceOracle = comptroller.oracle();\n\n BadDebt[] memory badDebts = new BadDebt[](markets.length);\n\n BadDebtSummary memory badDebtSummary;\n badDebtSummary.comptroller = comptrollerAddress;\n badDebtSummary.badDebts = badDebts;\n\n // // Calculate the bad debt is USD per market\n for (uint256 i; i < markets.length; ++i) {\n BadDebt memory badDebt;\n badDebt.vTokenAddress = address(markets[i]);\n badDebt.badDebtUsd =\n (VToken(address(markets[i])).badDebt() * priceOracle.getUnderlyingPrice(address(markets[i]))) /\n EXP_SCALE;\n badDebtSummary.badDebts[i] = badDebt;\n totalBadDebtUsd = totalBadDebtUsd + badDebt.badDebtUsd;\n }\n\n badDebtSummary.totalBadDebtUsd = totalBadDebtUsd;\n\n return badDebtSummary;\n }\n\n /**\n * @notice Returns vToken holding the specified underlying asset in the specified pool\n * @param poolRegistryAddress The address of the PoolRegistry contract\n * @param comptroller The pool comptroller\n * @param asset The underlyingAsset of VToken\n * @return Address of the vToken\n */\n function getVTokenForAsset(\n address poolRegistryAddress,\n address comptroller,\n address asset\n ) external view returns (address) {\n PoolRegistryInterface poolRegistryInterface = PoolRegistryInterface(poolRegistryAddress);\n return poolRegistryInterface.getVTokenForAsset(comptroller, asset);\n }\n\n /**\n * @notice Returns all pools that support the specified underlying asset\n * @param poolRegistryAddress The address of the PoolRegistry contract\n * @param asset The underlying asset of vToken\n * @return A list of Comptroller contracts\n */\n function getPoolsSupportedByAsset(\n address poolRegistryAddress,\n address asset\n ) external view returns (address[] memory) {\n PoolRegistryInterface poolRegistryInterface = PoolRegistryInterface(poolRegistryAddress);\n return poolRegistryInterface.getPoolsSupportedByAsset(asset);\n }\n\n /**\n * @notice Queries the user's supply/borrow balances in the specified vToken\n * @param vToken vToken address\n * @param account The user Account\n * @return A struct containing the balances data\n */\n function vTokenBalances(VToken vToken, address account) public returns (VTokenBalances memory) {\n uint256 balanceOf = vToken.balanceOf(account);\n uint256 borrowBalanceCurrent = vToken.borrowBalanceCurrent(account);\n uint256 balanceOfUnderlying = vToken.balanceOfUnderlying(account);\n uint256 tokenBalance;\n uint256 tokenAllowance;\n\n IERC20 underlying = IERC20(vToken.underlying());\n tokenBalance = underlying.balanceOf(account);\n tokenAllowance = underlying.allowance(account, address(vToken));\n\n return\n VTokenBalances({\n vToken: address(vToken),\n balanceOf: balanceOf,\n borrowBalanceCurrent: borrowBalanceCurrent,\n balanceOfUnderlying: balanceOfUnderlying,\n tokenBalance: tokenBalance,\n tokenAllowance: tokenAllowance\n });\n }\n\n /**\n * @notice Queries a spoke pool from its registry entry\n * @param poolRegistryAddress The registry containing the pool\n * @param venusPool The pool's registry entry\n * @return The pool's data, including its markets\n */\n function getSpokePoolData(\n address poolRegistryAddress,\n PoolRegistry.VenusPool memory venusPool\n ) public view returns (SpokePoolData memory) {\n address comptroller = venusPool.comptroller;\n\n SpokeVTokenMetadata[] memory vTokenMetadataItems = spokeVTokenMetadataAll(\n ComptrollerInterface(comptroller).getAllMarkets()\n );\n\n PoolRegistry.VenusPoolMetaData memory metaData = PoolRegistryInterface(poolRegistryAddress)\n .getVenusPoolMetadata(comptroller);\n\n ComptrollerViewInterface pooledView = ComptrollerViewInterface(comptroller);\n SpokeComptrollerViewInterface spokeView = SpokeComptrollerViewInterface(comptroller);\n\n SpokePoolData memory poolData;\n poolData.name = venusPool.name;\n poolData.creator = venusPool.creator;\n poolData.comptroller = comptroller;\n poolData.blockPosted = venusPool.blockPosted;\n poolData.timestampPosted = venusPool.timestampPosted;\n poolData.category = metaData.category;\n poolData.logoURL = metaData.logoURL;\n poolData.description = metaData.description;\n poolData.priceOracle = address(pooledView.oracle());\n poolData.closeFactor = pooledView.closeFactorMantissa();\n // This call is made from the lens, so the comptroller returns the pool-wide incentive.\n poolData.poolLiquidationIncentiveMantissa = pooledView.liquidationIncentiveMantissa();\n poolData.minLiquidatableCollateral = pooledView.minLiquidatableCollateral();\n poolData.deviationBoundedOracle = address(spokeView.deviationBoundedOracle());\n poolData.liquidationAllowlistEnabled = spokeView.isLiquidationAllowlistEnabled();\n poolData.vTokens = vTokenMetadataItems;\n\n return poolData;\n }\n\n /**\n * @notice Queries the metadata of every given market of a spoke pool\n * @param vTokens The markets to read\n * @return One entry per market, in the order given\n */\n function spokeVTokenMetadataAll(VToken[] memory vTokens) public view returns (SpokeVTokenMetadata[] memory) {\n uint256 len = vTokens.length;\n\n SpokeVTokenMetadata[] memory metadataItems = new SpokeVTokenMetadata[](len);\n\n for (uint256 i; i < len; ++i) {\n metadataItems[i] = spokeVTokenMetadata(vTokens[i]);\n }\n\n return metadataItems;\n }\n\n /**\n * @notice Queries the metadata of one market of a spoke pool\n * @param vToken The market to read\n * @return The market's metadata\n */\n function spokeVTokenMetadata(VToken vToken) public view returns (SpokeVTokenMetadata memory) {\n address vTokenAddress = address(vToken);\n address comptroller = address(vToken.comptroller());\n\n ComptrollerViewInterface pooledView = ComptrollerViewInterface(comptroller);\n SpokeComptrollerViewInterface spokeView = SpokeComptrollerViewInterface(comptroller);\n\n // Read through the spoke interface, which declares all three returns. `ComptrollerViewInterface` declares\n // the first two, so reading it there drops the liquidation threshold with no error.\n (bool isListed, uint256 collateralFactorMantissa, uint256 liquidationThresholdMantissa) = spokeView.markets(\n vTokenAddress\n );\n\n address underlying = vToken.underlying();\n\n return\n SpokeVTokenMetadata({\n vToken: vTokenAddress,\n exchangeRateCurrent: vToken.exchangeRateStored(),\n // Zero for an empty market, as `PoolLens` reports: the rate model divides by the market's supply.\n supplyRatePerBlockOrTimestamp: vToken.totalSupply() > 0 ? vToken.supplyRatePerBlock() : 0,\n borrowRatePerBlockOrTimestamp: vToken.borrowRatePerBlock(),\n reserveFactorMantissa: vToken.reserveFactorMantissa(),\n supplyCaps: pooledView.supplyCaps(vTokenAddress),\n borrowCaps: pooledView.borrowCaps(vTokenAddress),\n totalBorrows: vToken.totalBorrows(),\n totalReserves: vToken.totalReserves(),\n totalSupply: vToken.totalSupply(),\n totalCash: vToken.getCash(),\n isListed: isListed,\n collateralFactorMantissa: collateralFactorMantissa,\n liquidationThresholdMantissa: liquidationThresholdMantissa,\n underlyingAssetAddress: underlying,\n vTokenDecimals: vToken.decimals(),\n underlyingDecimals: IERC20Metadata(underlying).decimals(),\n pausedActions: _pausedActions(comptroller, vTokenAddress),\n effectiveLiquidationIncentiveMantissa: spokeView.effectiveLiquidationIncentive(vTokenAddress),\n ownLiquidationIncentiveMantissa: spokeView.liquidationIncentives(vTokenAddress),\n supplyAllowlistEnabled: spokeView.isSupplyAllowlistEnabled(vTokenAddress),\n forcedLiquidationEnabled: spokeView.isForcedLiquidationEnabled(vTokenAddress)\n });\n }\n\n /**\n * @notice Returns the price data for the underlying asset of the specified vToken\n * @param vToken vToken address\n * @return The price data for each asset\n */\n function vTokenUnderlyingPrice(VToken vToken) public view returns (VTokenUnderlyingPrice memory) {\n ComptrollerViewInterface comptroller = ComptrollerViewInterface(address(vToken.comptroller()));\n ResilientOracleInterface priceOracle = comptroller.oracle();\n\n return\n VTokenUnderlyingPrice({\n vToken: address(vToken),\n underlyingPrice: priceOracle.getUnderlyingPrice(address(vToken))\n });\n }\n\n function _calculateNotDistributedAwards(\n address account,\n VToken[] memory markets,\n RewardsDistributor rewardsDistributor\n ) internal view returns (PendingReward[] memory) {\n PendingReward[] memory pendingRewards = new PendingReward[](markets.length);\n\n for (uint256 i; i < markets.length; ++i) {\n // Market borrow and supply state we will modify update in-memory, in order to not modify storage\n RewardTokenState memory borrowState;\n RewardTokenState memory supplyState;\n\n if (isTimeBased) {\n (\n borrowState.index,\n borrowState.blockOrTimestamp,\n borrowState.lastRewardingBlockOrTimestamp\n ) = rewardsDistributor.rewardTokenBorrowStateTimeBased(address(markets[i]));\n (\n supplyState.index,\n supplyState.blockOrTimestamp,\n supplyState.lastRewardingBlockOrTimestamp\n ) = rewardsDistributor.rewardTokenSupplyStateTimeBased(address(markets[i]));\n } else {\n (\n borrowState.index,\n borrowState.blockOrTimestamp,\n borrowState.lastRewardingBlockOrTimestamp\n ) = rewardsDistributor.rewardTokenBorrowState(address(markets[i]));\n (\n supplyState.index,\n supplyState.blockOrTimestamp,\n supplyState.lastRewardingBlockOrTimestamp\n ) = rewardsDistributor.rewardTokenSupplyState(address(markets[i]));\n }\n\n Exp memory marketBorrowIndex = Exp({ mantissa: markets[i].borrowIndex() });\n\n // Update market supply and borrow index in-memory\n updateMarketBorrowIndex(address(markets[i]), rewardsDistributor, borrowState, marketBorrowIndex);\n updateMarketSupplyIndex(address(markets[i]), rewardsDistributor, supplyState);\n\n // Calculate pending rewards\n uint256 borrowReward = calculateBorrowerReward(\n address(markets[i]),\n rewardsDistributor,\n account,\n borrowState,\n marketBorrowIndex\n );\n uint256 supplyReward = calculateSupplierReward(\n address(markets[i]),\n rewardsDistributor,\n account,\n supplyState\n );\n\n PendingReward memory pendingReward;\n pendingReward.vTokenAddress = address(markets[i]);\n pendingReward.amount = borrowReward + supplyReward;\n pendingRewards[i] = pendingReward;\n }\n return pendingRewards;\n }\n\n function updateMarketBorrowIndex(\n address vToken,\n RewardsDistributor rewardsDistributor,\n RewardTokenState memory borrowState,\n Exp memory marketBorrowIndex\n ) internal view {\n uint256 borrowSpeed = rewardsDistributor.rewardTokenBorrowSpeeds(vToken);\n uint256 blockNumberOrTimestamp = getBlockNumberOrTimestamp();\n\n if (\n borrowState.lastRewardingBlockOrTimestamp > 0 &&\n blockNumberOrTimestamp > borrowState.lastRewardingBlockOrTimestamp\n ) {\n blockNumberOrTimestamp = borrowState.lastRewardingBlockOrTimestamp;\n }\n\n uint256 deltaBlocksOrTimestamp = sub_(blockNumberOrTimestamp, borrowState.blockOrTimestamp);\n if (deltaBlocksOrTimestamp > 0 && borrowSpeed > 0) {\n // Remove the total earned interest rate since the opening of the market from total borrows\n uint256 borrowAmount = div_(VToken(vToken).totalBorrows(), marketBorrowIndex);\n uint256 tokensAccrued = mul_(deltaBlocksOrTimestamp, borrowSpeed);\n Double memory ratio = borrowAmount > 0 ? fraction(tokensAccrued, borrowAmount) : Double({ mantissa: 0 });\n Double memory index = add_(Double({ mantissa: borrowState.index }), ratio);\n borrowState.index = safe224(index.mantissa, \"new index overflows\");\n borrowState.blockOrTimestamp = blockNumberOrTimestamp;\n } else if (deltaBlocksOrTimestamp > 0) {\n borrowState.blockOrTimestamp = blockNumberOrTimestamp;\n }\n }\n\n function updateMarketSupplyIndex(\n address vToken,\n RewardsDistributor rewardsDistributor,\n RewardTokenState memory supplyState\n ) internal view {\n uint256 supplySpeed = rewardsDistributor.rewardTokenSupplySpeeds(vToken);\n uint256 blockNumberOrTimestamp = getBlockNumberOrTimestamp();\n\n if (\n supplyState.lastRewardingBlockOrTimestamp > 0 &&\n blockNumberOrTimestamp > supplyState.lastRewardingBlockOrTimestamp\n ) {\n blockNumberOrTimestamp = supplyState.lastRewardingBlockOrTimestamp;\n }\n\n uint256 deltaBlocksOrTimestamp = sub_(blockNumberOrTimestamp, supplyState.blockOrTimestamp);\n if (deltaBlocksOrTimestamp > 0 && supplySpeed > 0) {\n uint256 supplyTokens = VToken(vToken).totalSupply();\n uint256 tokensAccrued = mul_(deltaBlocksOrTimestamp, supplySpeed);\n Double memory ratio = supplyTokens > 0 ? fraction(tokensAccrued, supplyTokens) : Double({ mantissa: 0 });\n Double memory index = add_(Double({ mantissa: supplyState.index }), ratio);\n supplyState.index = safe224(index.mantissa, \"new index overflows\");\n supplyState.blockOrTimestamp = blockNumberOrTimestamp;\n } else if (deltaBlocksOrTimestamp > 0) {\n supplyState.blockOrTimestamp = blockNumberOrTimestamp;\n }\n }\n\n function calculateBorrowerReward(\n address vToken,\n RewardsDistributor rewardsDistributor,\n address borrower,\n RewardTokenState memory borrowState,\n Exp memory marketBorrowIndex\n ) internal view returns (uint256) {\n Double memory borrowIndex = Double({ mantissa: borrowState.index });\n Double memory borrowerIndex = Double({\n mantissa: rewardsDistributor.rewardTokenBorrowerIndex(vToken, borrower)\n });\n if (borrowerIndex.mantissa == 0 && borrowIndex.mantissa >= rewardsDistributor.INITIAL_INDEX()) {\n // Covers the case where users borrowed tokens before the market's borrow state index was set\n borrowerIndex.mantissa = rewardsDistributor.INITIAL_INDEX();\n }\n Double memory deltaIndex = sub_(borrowIndex, borrowerIndex);\n uint256 borrowerAmount = div_(VToken(vToken).borrowBalanceStored(borrower), marketBorrowIndex);\n uint256 borrowerDelta = mul_(borrowerAmount, deltaIndex);\n return borrowerDelta;\n }\n\n function calculateSupplierReward(\n address vToken,\n RewardsDistributor rewardsDistributor,\n address supplier,\n RewardTokenState memory supplyState\n ) internal view returns (uint256) {\n Double memory supplyIndex = Double({ mantissa: supplyState.index });\n Double memory supplierIndex = Double({\n mantissa: rewardsDistributor.rewardTokenSupplierIndex(vToken, supplier)\n });\n if (supplierIndex.mantissa == 0 && supplyIndex.mantissa >= rewardsDistributor.INITIAL_INDEX()) {\n // Covers the case where users supplied tokens before the market's supply state index was set\n supplierIndex.mantissa = rewardsDistributor.INITIAL_INDEX();\n }\n Double memory deltaIndex = sub_(supplyIndex, supplierIndex);\n uint256 supplierTokens = VToken(vToken).balanceOf(supplier);\n uint256 supplierDelta = mul_(supplierTokens, deltaIndex);\n return supplierDelta;\n }\n\n /**\n * @dev Encodes paused actions using the same bit positions as `PoolLens`.\n * @param comptroller The market's comptroller\n * @param vToken The market to read\n * @return A bitmask of the paused actions\n */\n function _pausedActions(address comptroller, address vToken) private view returns (uint256) {\n uint256 pausedActions;\n\n for (uint8 i; i <= uint8(type(Action).max); ++i) {\n uint256 paused = ComptrollerInterface(comptroller).actionPaused(vToken, Action(i)) ? 1 : 0;\n pausedActions |= paused << i;\n }\n\n return pausedActions;\n }\n}\n" + }, + "contracts/lib/ApproveOrRevert.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.25;\n\nimport { IERC20Upgradeable } from \"@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol\";\n\nlibrary ApproveOrRevert {\n /// @notice Thrown if a contract is unable to approve a transfer\n error ApproveFailed();\n\n /// @notice Approves a transfer, ensuring that it is successful. This function supports non-compliant\n /// tokens like the ones that don't return a boolean value on success. Thus, such approve call supports\n /// three different kinds of tokens:\n /// * Compliant tokens that revert on failure\n /// * Compliant tokens that return false on failure\n /// * Non-compliant tokens that don't return a value\n /// @param token The contract address of the token which will be transferred\n /// @param spender The spender contract address\n /// @param amount The value of the transfer\n function approveOrRevert(IERC20Upgradeable token, address spender, uint256 amount) internal {\n bytes memory callData = abi.encodeCall(token.approve, (spender, amount));\n\n // solhint-disable-next-line avoid-low-level-calls\n (bool success, bytes memory result) = address(token).call(callData);\n\n if (!success || (result.length != 0 && !abi.decode(result, (bool)))) {\n revert ApproveFailed();\n }\n }\n}\n" + }, + "contracts/lib/constants.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity ^0.8.25;\n\n/// @dev The approximate number of seconds per year\nuint256 constant SECONDS_PER_YEAR = 31_536_000;\n\n/// @dev Base unit for computations, usually used in scaling (multiplications, divisions)\nuint256 constant EXP_SCALE = 1e18;\n\n/// @dev A unit (literal one) in EXP_SCALE, usually used in additions/subtractions\nuint256 constant MANTISSA_ONE = EXP_SCALE;\n" + }, + "contracts/lib/imports.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\n\n// This file is needed to make hardhat and typechain generate artifacts for\n// contracts we depend on (e.g. in tests or deployments) but not use directly.\n// Another way to do this would be to use hardhat-dependency-compiler, but\n// since we only have a couple of dependencies, installing a separate package\n// seems an overhead.\n\nimport { UpgradeableBeacon } from \"@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol\";\nimport { BeaconProxy } from \"@openzeppelin/contracts/proxy/beacon/BeaconProxy.sol\";\n" + }, + "contracts/lib/TokenDebtTracker.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.25;\n\nimport { Initializable } from \"@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol\";\nimport { IERC20Upgradeable } from \"@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol\";\nimport { SafeERC20Upgradeable } from \"@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol\";\n\n/**\n * @title TokenDebtTracker\n * @author Venus\n * @notice TokenDebtTracker is an abstract contract that handles transfers _out_ of the inheriting contract.\n * If there is an error transferring out (due to any reason, e.g. the token contract restricted the user from\n * receiving incoming transfers), the amount is recorded as a debt that can be claimed later.\n * @dev Note that the inheriting contract keeps some amount of users' tokens on its balance, so be careful when\n * using balanceOf(address(this))!\n */\nabstract contract TokenDebtTracker is Initializable {\n using SafeERC20Upgradeable for IERC20Upgradeable;\n\n /**\n * @notice Mapping (IERC20Upgradeable token => (address user => uint256 amount)).\n * Tracks failed transfers: when a token transfer fails, we record the\n * amount of the transfer, so that the user can redeem this debt later.\n */\n mapping(IERC20Upgradeable => mapping(address => uint256)) public tokenDebt;\n\n /**\n * @notice Mapping (IERC20Upgradeable token => uint256 amount) shows how many\n * tokens the contract owes to all users. This is useful for accounting to\n * understand how much of balanceOf(address(this)) is already owed to users.\n */\n mapping(IERC20Upgradeable => uint256) public totalTokenDebt;\n\n /**\n * @dev This empty reserved space is put in place to allow future versions to add new\n * variables without shifting down storage in the inheritance chain.\n * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\n */\n uint256[48] private __gap;\n\n /**\n * @notice Emitted when the contract's debt to the user is increased due to a failed transfer\n * @param token Token address\n * @param user User address\n * @param amount The amount of debt added\n */\n event TokenDebtAdded(address indexed token, address indexed user, uint256 amount);\n\n /**\n * @notice Emitted when a user claims tokens that the contract owes them\n * @param token Token address\n * @param user User address\n * @param amount The amount transferred\n */\n event TokenDebtClaimed(address indexed token, address indexed user, uint256 amount);\n\n /**\n * @notice Thrown if the user tries to claim more tokens than they are owed\n * @param token The token the user is trying to claim\n * @param owedAmount The amount of tokens the contract owes to the user\n * @param amount The amount of tokens the user is trying to claim\n */\n error InsufficientDebt(address token, address user, uint256 owedAmount, uint256 amount);\n\n /**\n * @notice Thrown if trying to transfer more tokens than the contract currently has\n * @param token The token the contract is trying to transfer\n * @param recipient The recipient of the transfer\n * @param amount The amount of tokens the contract is trying to transfer\n * @param availableBalance The amount of tokens the contract currently has\n */\n error InsufficientBalance(address token, address recipient, uint256 amount, uint256 availableBalance);\n\n /**\n * @notice Transfers the tokens we owe to msg.sender, if any\n * @param token The token to claim\n * @param amount_ The amount of tokens to claim (or max uint256 to claim all)\n * @custom:error InsufficientDebt The contract doesn't have enough debt to the user\n */\n function claimTokenDebt(IERC20Upgradeable token, uint256 amount_) external {\n uint256 owedAmount = tokenDebt[token][msg.sender];\n uint256 amount = (amount_ == type(uint256).max ? owedAmount : amount_);\n if (amount > owedAmount) {\n revert InsufficientDebt(address(token), msg.sender, owedAmount, amount);\n }\n unchecked {\n // Safe because we revert if amount > owedAmount above\n tokenDebt[token][msg.sender] = owedAmount - amount;\n }\n totalTokenDebt[token] -= amount;\n emit TokenDebtClaimed(address(token), msg.sender, amount);\n token.safeTransfer(msg.sender, amount);\n }\n\n // solhint-disable-next-line func-name-mixedcase\n function __TokenDebtTracker_init() internal onlyInitializing {\n __TokenDebtTracker_init_unchained();\n }\n\n // solhint-disable-next-line func-name-mixedcase, no-empty-blocks\n function __TokenDebtTracker_init_unchained() internal onlyInitializing {}\n\n /**\n * @dev Transfers tokens to the recipient if the contract has enough balance, or\n * records the debt if the transfer fails due to reasons unrelated to the contract's\n * balance (e.g. if the token forbids transfers to the recipient).\n * @param token The token to transfer\n * @param to The recipient of the transfer\n * @param amount The amount to transfer\n * @custom:error InsufficientBalance The contract doesn't have enough balance to transfer\n */\n function _transferOutOrTrackDebt(IERC20Upgradeable token, address to, uint256 amount) internal {\n uint256 balance = token.balanceOf(address(this));\n if (balance < amount) {\n revert InsufficientBalance(address(token), address(this), amount, balance);\n }\n _transferOutOrTrackDebtSkippingBalanceCheck(token, to, amount);\n }\n\n /**\n * @dev Transfers tokens to the recipient, or records the debt if the transfer fails\n * due to any reason, including insufficient balance.\n * @param token The token to transfer\n * @param to The recipient of the transfer\n * @param amount The amount to transfer\n */\n function _transferOutOrTrackDebtSkippingBalanceCheck(IERC20Upgradeable token, address to, uint256 amount) internal {\n // We can't use safeTransfer here because we can't try-catch internal calls\n bool success = _tryTransferOut(token, to, amount);\n if (!success) {\n tokenDebt[token][to] += amount;\n totalTokenDebt[token] += amount;\n emit TokenDebtAdded(address(token), to, amount);\n }\n }\n\n /**\n * @dev Either transfers tokens to the recepient or returns false. Supports tokens\n * thet revert or return false to indicate failure, and the non-compliant ones\n * that do not return any value.\n * @param token The token to transfer\n * @param to The recipient of the transfer\n * @param amount The amount to transfer\n * @return true if the transfer succeeded, false otherwise\n */\n function _tryTransferOut(IERC20Upgradeable token, address to, uint256 amount) private returns (bool) {\n bytes memory callData = abi.encodeCall(token.transfer, (to, amount));\n\n // solhint-disable-next-line avoid-low-level-calls\n (bool success, bytes memory returndata) = address(token).call(callData);\n return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0;\n }\n}\n" + }, + "contracts/lib/validators.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\n\n/// @notice Thrown if the supplied address is a zero address where it is not allowed\nerror ZeroAddressNotAllowed();\n\n/// @notice Checks if the provided address is nonzero, reverts otherwise\n/// @param address_ Address to check\n/// @custom:error ZeroAddressNotAllowed is thrown if the provided address is a zero address\nfunction ensureNonzeroAddress(address address_) pure {\n if (address_ == address(0)) {\n revert ZeroAddressNotAllowed();\n }\n}\n" + }, + "contracts/MaxLoopsLimitHelper.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\n\n/**\n * @title MaxLoopsLimitHelper\n * @author Venus\n * @notice Abstract contract used to avoid collection with too many items that would generate gas errors and DoS.\n */\nabstract contract MaxLoopsLimitHelper {\n // Limit for the loops to avoid the DOS\n uint256 public maxLoopsLimit;\n\n /**\n * @dev This empty reserved space is put in place to allow future versions to add new\n * variables without shifting down storage in the inheritance chain.\n * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\n */\n uint256[49] private __gap;\n\n /// @notice Emitted when max loops limit is set\n event MaxLoopsLimitUpdated(uint256 oldMaxLoopsLimit, uint256 newmaxLoopsLimit);\n\n /// @notice Thrown an error on maxLoopsLimit exceeds for any loop\n error MaxLoopsLimitExceeded(uint256 loopsLimit, uint256 requiredLoops);\n\n /**\n * @notice Set the limit for the loops can iterate to avoid the DOS\n * @param limit Limit for the max loops can execute at a time\n */\n function _setMaxLoopsLimit(uint256 limit) internal {\n require(limit > maxLoopsLimit, \"Comptroller: Invalid maxLoopsLimit\");\n\n uint256 oldMaxLoopsLimit = maxLoopsLimit;\n maxLoopsLimit = limit;\n\n emit MaxLoopsLimitUpdated(oldMaxLoopsLimit, limit);\n }\n\n /**\n * @notice Compare the maxLoopsLimit with number of the times loop iterate\n * @param len Length of the loops iterate\n * @custom:error MaxLoopsLimitExceeded error is thrown when loops length exceeds maxLoopsLimit\n */\n function _ensureMaxLoops(uint256 len) internal view {\n if (len > maxLoopsLimit) {\n revert MaxLoopsLimitExceeded(maxLoopsLimit, len);\n }\n }\n}\n" + }, + "contracts/Pool/PoolRegistry.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\n\nimport { Ownable2StepUpgradeable } from \"@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol\";\nimport { SafeERC20Upgradeable } from \"@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol\";\nimport { IERC20Upgradeable } from \"@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol\";\nimport { AccessControlledV8 } from \"@venusprotocol/governance-contracts/contracts/Governance/AccessControlledV8.sol\";\n\nimport { PoolRegistryInterface } from \"./PoolRegistryInterface.sol\";\nimport { Comptroller } from \"../Comptroller.sol\";\nimport { VToken } from \"../VToken.sol\";\nimport { ensureNonzeroAddress } from \"../lib/validators.sol\";\n\n/**\n * @title PoolRegistry\n * @author Venus\n * @notice The Isolated Pools architecture centers around the `PoolRegistry` contract. The `PoolRegistry` maintains a directory of isolated lending\n * pools and can perform actions like creating and registering new pools, adding new markets to existing pools, setting and updating the pool's required\n * metadata, and providing the getter methods to get information on the pools.\n *\n * Isolated lending has three main components: PoolRegistry, pools, and markets. The PoolRegistry is responsible for managing pools.\n * It can create new pools, update pool metadata and manage markets within pools. PoolRegistry contains getter methods to get the details of\n * any existing pool like `getVTokenForAsset` and `getPoolsSupportedByAsset`. It also contains methods for updating pool metadata (`updatePoolMetadata`)\n * and setting pool name (`setPoolName`).\n *\n * The directory of pools is managed through two mappings: `_poolByComptroller` which is a hashmap with the comptroller address as the key and `VenusPool` as\n * the value and `_poolsByID` which is an array of comptroller addresses. Individual pools can be accessed by calling `getPoolByComptroller` with the pool's\n * comptroller address. `_poolsByID` is used to iterate through all of the pools.\n *\n * PoolRegistry also contains a map of asset addresses called `_supportedPools` that maps to an array of assets suppored by each pool. This array of pools by\n * asset is retrieved by calling `getPoolsSupportedByAsset`.\n *\n * PoolRegistry registers new isolated pools in the directory with the `createRegistryPool` method. Isolated pools are composed of independent markets with\n * specific assets and custom risk management configurations according to their markets.\n */\ncontract PoolRegistry is Ownable2StepUpgradeable, AccessControlledV8, PoolRegistryInterface {\n using SafeERC20Upgradeable for IERC20Upgradeable;\n\n struct AddMarketInput {\n VToken vToken;\n uint256 collateralFactor;\n uint256 liquidationThreshold;\n uint256 initialSupply;\n address vTokenReceiver;\n uint256 supplyCap;\n uint256 borrowCap;\n }\n\n uint256 internal constant MAX_POOL_NAME_LENGTH = 100;\n\n /**\n * @notice Maps pool's comptroller address to metadata.\n */\n mapping(address => VenusPoolMetaData) public metadata;\n\n /**\n * @dev Maps pool ID to pool's comptroller address\n */\n mapping(uint256 => address) private _poolsByID;\n\n /**\n * @dev Total number of pools created.\n */\n uint256 private _numberOfPools;\n\n /**\n * @dev Maps comptroller address to Venus pool Index.\n */\n mapping(address => VenusPool) private _poolByComptroller;\n\n /**\n * @dev Maps pool's comptroller address to asset to vToken.\n */\n mapping(address => mapping(address => address)) private _vTokens;\n\n /**\n * @dev Maps asset to list of supported pools.\n */\n mapping(address => address[]) private _supportedPools;\n\n /**\n * @notice Emitted when a new Venus pool is added to the directory.\n */\n event PoolRegistered(address indexed comptroller, VenusPool pool);\n\n /**\n * @notice Emitted when a pool name is set.\n */\n event PoolNameSet(address indexed comptroller, string oldName, string newName);\n\n /**\n * @notice Emitted when a pool metadata is updated.\n */\n event PoolMetadataUpdated(\n address indexed comptroller,\n VenusPoolMetaData oldMetadata,\n VenusPoolMetaData newMetadata\n );\n\n /**\n * @notice Emitted when a Market is added to the pool.\n */\n event MarketAdded(address indexed comptroller, address indexed vTokenAddress);\n\n /// @custom:oz-upgrades-unsafe-allow constructor\n constructor() {\n // Note that the contract is upgradeable. Use initialize() or reinitializers\n // to set the state variables.\n _disableInitializers();\n }\n\n /**\n * @notice Initializes the deployer to owner\n * @param accessControlManager_ AccessControlManager contract address\n */\n function initialize(address accessControlManager_) external initializer {\n __Ownable2Step_init();\n __AccessControlled_init_unchained(accessControlManager_);\n }\n\n /**\n * @notice Adds a new Venus pool to the directory\n * @dev Price oracle must be configured before adding a pool\n * @param name The name of the pool\n * @param comptroller Pool's Comptroller contract\n * @param closeFactor The pool's close factor (scaled by 1e18)\n * @param liquidationIncentive The pool's liquidation incentive (scaled by 1e18)\n * @param minLiquidatableCollateral Minimal collateral for regular (non-batch) liquidations flow\n * @return index The index of the registered Venus pool\n * @custom:error ZeroAddressNotAllowed is thrown when Comptroller address is zero\n * @custom:error ZeroAddressNotAllowed is thrown when price oracle address is zero\n */\n function addPool(\n string calldata name,\n Comptroller comptroller,\n uint256 closeFactor,\n uint256 liquidationIncentive,\n uint256 minLiquidatableCollateral\n ) external virtual returns (uint256 index) {\n _checkAccessAllowed(\"addPool(string,address,uint256,uint256,uint256)\");\n // Input validation\n ensureNonzeroAddress(address(comptroller));\n ensureNonzeroAddress(address(comptroller.oracle()));\n\n uint256 poolId = _registerPool(name, address(comptroller));\n\n // Set Venus pool parameters\n comptroller.setCloseFactor(closeFactor);\n comptroller.setLiquidationIncentive(liquidationIncentive);\n comptroller.setMinLiquidatableCollateral(minLiquidatableCollateral);\n\n return poolId;\n }\n\n /**\n * @notice Add a market to an existing pool and then mint to provide initial supply\n * @param input The structure describing the parameters for adding a market to a pool\n * @custom:error ZeroAddressNotAllowed is thrown when vToken address is zero\n * @custom:error ZeroAddressNotAllowed is thrown when vTokenReceiver address is zero\n */\n function addMarket(AddMarketInput memory input) external {\n _checkAccessAllowed(\"addMarket(AddMarketInput)\");\n ensureNonzeroAddress(address(input.vToken));\n ensureNonzeroAddress(input.vTokenReceiver);\n require(input.initialSupply > 0, \"PoolRegistry: initialSupply is zero\");\n\n VToken vToken = input.vToken;\n address vTokenAddress = address(vToken);\n address comptrollerAddress = address(vToken.comptroller());\n Comptroller comptroller = Comptroller(comptrollerAddress);\n address underlyingAddress = vToken.underlying();\n IERC20Upgradeable underlying = IERC20Upgradeable(underlyingAddress);\n\n require(_poolByComptroller[comptrollerAddress].creator != address(0), \"PoolRegistry: Pool not registered\");\n // solhint-disable-next-line reason-string\n require(\n _vTokens[comptrollerAddress][underlyingAddress] == address(0),\n \"PoolRegistry: Market already added for asset comptroller combination\"\n );\n\n comptroller.supportMarket(vToken);\n comptroller.setCollateralFactor(vToken, input.collateralFactor, input.liquidationThreshold);\n\n uint256[] memory newSupplyCaps = new uint256[](1);\n uint256[] memory newBorrowCaps = new uint256[](1);\n VToken[] memory vTokens = new VToken[](1);\n\n newSupplyCaps[0] = input.supplyCap;\n newBorrowCaps[0] = input.borrowCap;\n vTokens[0] = vToken;\n\n comptroller.setMarketSupplyCaps(vTokens, newSupplyCaps);\n comptroller.setMarketBorrowCaps(vTokens, newBorrowCaps);\n\n _vTokens[comptrollerAddress][underlyingAddress] = vTokenAddress;\n _supportedPools[underlyingAddress].push(comptrollerAddress);\n\n uint256 amountToSupply = _transferIn(underlying, msg.sender, input.initialSupply);\n underlying.forceApprove(vTokenAddress, 0);\n underlying.forceApprove(vTokenAddress, amountToSupply);\n vToken.mintBehalf(input.vTokenReceiver, amountToSupply);\n\n emit MarketAdded(comptrollerAddress, vTokenAddress);\n }\n\n /**\n * @notice Modify existing Venus pool name\n * @param comptroller Pool's Comptroller\n * @param name New pool name\n */\n function setPoolName(address comptroller, string calldata name) external {\n _checkAccessAllowed(\"setPoolName(address,string)\");\n _ensureValidName(name);\n VenusPool storage pool = _poolByComptroller[comptroller];\n string memory oldName = pool.name;\n pool.name = name;\n emit PoolNameSet(comptroller, oldName, name);\n }\n\n /**\n * @notice Update metadata of an existing pool\n * @param comptroller Pool's Comptroller\n * @param metadata_ New pool metadata\n */\n function updatePoolMetadata(address comptroller, VenusPoolMetaData calldata metadata_) external {\n _checkAccessAllowed(\"updatePoolMetadata(address,VenusPoolMetaData)\");\n VenusPoolMetaData memory oldMetadata = metadata[comptroller];\n metadata[comptroller] = metadata_;\n emit PoolMetadataUpdated(comptroller, oldMetadata, metadata_);\n }\n\n /**\n * @notice Returns arrays of all Venus pools' data\n * @dev This function is not designed to be called in a transaction: it is too gas-intensive\n * @return A list of all pools within PoolRegistry, with details for each pool\n */\n function getAllPools() external view override returns (VenusPool[] memory) {\n uint256 numberOfPools_ = _numberOfPools; // storage load to save gas\n VenusPool[] memory _pools = new VenusPool[](numberOfPools_);\n for (uint256 i = 1; i <= numberOfPools_; ++i) {\n address comptroller = _poolsByID[i];\n _pools[i - 1] = (_poolByComptroller[comptroller]);\n }\n return _pools;\n }\n\n /**\n * @param comptroller The comptroller proxy address associated to the pool\n * @return Returns Venus pool\n */\n function getPoolByComptroller(address comptroller) external view override returns (VenusPool memory) {\n return _poolByComptroller[comptroller];\n }\n\n /**\n * @param comptroller comptroller of Venus pool\n * @return Returns Metadata of Venus pool\n */\n function getVenusPoolMetadata(address comptroller) external view override returns (VenusPoolMetaData memory) {\n return metadata[comptroller];\n }\n\n function getVTokenForAsset(address comptroller, address asset) external view override returns (address) {\n return _vTokens[comptroller][asset];\n }\n\n function getPoolsSupportedByAsset(address asset) external view override returns (address[] memory) {\n return _supportedPools[asset];\n }\n\n /**\n * @dev Adds a new Venus pool to the directory (without checking msg.sender).\n * @param name The name of the pool\n * @param comptroller The pool's Comptroller proxy contract address\n * @return The index of the registered Venus pool\n */\n function _registerPool(string calldata name, address comptroller) internal returns (uint256) {\n VenusPool storage storedPool = _poolByComptroller[comptroller];\n\n require(storedPool.creator == address(0), \"PoolRegistry: Pool already exists in the directory.\");\n _ensureValidName(name);\n\n ++_numberOfPools;\n uint256 numberOfPools_ = _numberOfPools; // cache on stack to save storage read gas\n\n VenusPool memory pool = VenusPool(name, msg.sender, comptroller, block.number, block.timestamp);\n\n _poolsByID[numberOfPools_] = comptroller;\n _poolByComptroller[comptroller] = pool;\n\n emit PoolRegistered(comptroller, pool);\n return numberOfPools_;\n }\n\n function _transferIn(IERC20Upgradeable token, address from, uint256 amount) internal returns (uint256) {\n uint256 balanceBefore = token.balanceOf(address(this));\n token.safeTransferFrom(from, address(this), amount);\n uint256 balanceAfter = token.balanceOf(address(this));\n return balanceAfter - balanceBefore;\n }\n\n function _ensureValidName(string calldata name) internal pure {\n require(bytes(name).length <= MAX_POOL_NAME_LENGTH, \"Pool's name is too large\");\n }\n}\n" + }, + "contracts/Pool/PoolRegistryInterface.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity ^0.8.25;\n\n/**\n * @title PoolRegistryInterface\n * @author Venus\n * @notice Interface implemented by `PoolRegistry`.\n */\ninterface PoolRegistryInterface {\n /**\n * @notice Struct for a Venus interest rate pool.\n */\n struct VenusPool {\n string name;\n address creator;\n address comptroller;\n uint256 blockPosted;\n uint256 timestampPosted;\n }\n\n /**\n * @notice Struct for a Venus interest rate pool metadata.\n */\n struct VenusPoolMetaData {\n string category;\n string logoURL;\n string description;\n }\n\n /// @notice Get all pools in PoolRegistry\n function getAllPools() external view returns (VenusPool[] memory);\n\n /// @notice Get a pool by comptroller address\n function getPoolByComptroller(address comptroller) external view returns (VenusPool memory);\n\n /// @notice Get the address of the VToken contract in the Pool where the underlying token is the provided asset\n function getVTokenForAsset(address comptroller, address asset) external view returns (address);\n\n /// @notice Get the addresss of the Pools supported that include a market for the provided asset\n function getPoolsSupportedByAsset(address asset) external view returns (address[] memory);\n\n /// @notice Get the metadata of a Pool by comptroller address\n function getVenusPoolMetadata(address comptroller) external view returns (VenusPoolMetaData memory);\n}\n" + }, + "contracts/Rewards/RewardsDistributor.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\n\nimport { Ownable2StepUpgradeable } from \"@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol\";\nimport { IERC20Upgradeable } from \"@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol\";\nimport { SafeERC20Upgradeable } from \"@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol\";\nimport { AccessControlledV8 } from \"@venusprotocol/governance-contracts/contracts/Governance/AccessControlledV8.sol\";\nimport { TimeManagerV8 } from \"@venusprotocol/solidity-utilities/contracts/TimeManagerV8.sol\";\n\nimport { ExponentialNoError } from \"../ExponentialNoError.sol\";\nimport { VToken } from \"../VToken.sol\";\nimport { Comptroller } from \"../Comptroller.sol\";\nimport { MaxLoopsLimitHelper } from \"../MaxLoopsLimitHelper.sol\";\nimport { RewardsDistributorStorage } from \"./RewardsDistributorStorage.sol\";\n\n/**\n * @title `RewardsDistributor`\n * @author Venus\n * @notice Contract used to configure, track and distribute rewards to users based on their actions (borrows and supplies) in the protocol.\n * Users can receive additional rewards through a `RewardsDistributor`. Each `RewardsDistributor` proxy is initialized with a specific reward\n * token and `Comptroller`, which can then distribute the reward token to users that supply or borrow in the associated pool.\n * Authorized users can set the reward token borrow and supply speeds for each market in the pool. This sets a fixed amount of reward\n * token to be released each slot (block or second) for borrowers and suppliers, which is distributed based on a user’s percentage of the borrows or supplies\n * respectively. The owner can also set up reward distributions to contributor addresses (distinct from suppliers and borrowers) by setting\n * their contributor reward token speed, which similarly allocates a fixed amount of reward token per slot (block or second).\n *\n * The owner has the ability to transfer any amount of reward tokens held by the contract to any other address. Rewards are not distributed\n * automatically and must be claimed by a user calling `claimRewardToken()`. Users should be aware that it is up to the owner and other centralized\n * entities to ensure that the `RewardsDistributor` holds enough tokens to distribute the accumulated rewards of users and contributors.\n */\ncontract RewardsDistributor is\n ExponentialNoError,\n Ownable2StepUpgradeable,\n AccessControlledV8,\n MaxLoopsLimitHelper,\n RewardsDistributorStorage,\n TimeManagerV8\n{\n using SafeERC20Upgradeable for IERC20Upgradeable;\n\n /// @notice The initial REWARD TOKEN index for a market\n uint224 public constant INITIAL_INDEX = 1e36;\n\n /// @notice Emitted when REWARD TOKEN is distributed to a supplier\n event DistributedSupplierRewardToken(\n VToken indexed vToken,\n address indexed supplier,\n uint256 rewardTokenDelta,\n uint256 rewardTokenTotal,\n uint256 rewardTokenSupplyIndex\n );\n\n /// @notice Emitted when REWARD TOKEN is distributed to a borrower\n event DistributedBorrowerRewardToken(\n VToken indexed vToken,\n address indexed borrower,\n uint256 rewardTokenDelta,\n uint256 rewardTokenTotal,\n uint256 rewardTokenBorrowIndex\n );\n\n /// @notice Emitted when a new supply-side REWARD TOKEN speed is calculated for a market\n event RewardTokenSupplySpeedUpdated(VToken indexed vToken, uint256 newSpeed);\n\n /// @notice Emitted when a new borrow-side REWARD TOKEN speed is calculated for a market\n event RewardTokenBorrowSpeedUpdated(VToken indexed vToken, uint256 newSpeed);\n\n /// @notice Emitted when REWARD TOKEN is granted by admin\n event RewardTokenGranted(address indexed recipient, uint256 amount);\n\n /// @notice Emitted when a new REWARD TOKEN speed is set for a contributor\n event ContributorRewardTokenSpeedUpdated(address indexed contributor, uint256 newSpeed);\n\n /// @notice Emitted when a market is initialized\n event MarketInitialized(address indexed vToken);\n\n /// @notice Emitted when a reward token supply index is updated\n event RewardTokenSupplyIndexUpdated(address indexed vToken);\n\n /// @notice Emitted when a reward token borrow index is updated\n event RewardTokenBorrowIndexUpdated(address indexed vToken, Exp marketBorrowIndex);\n\n /// @notice Emitted when a reward for contributor is updated\n event ContributorRewardsUpdated(address indexed contributor, uint256 rewardAccrued);\n\n /// @notice Emitted when a reward token last rewarding block for supply is updated\n event SupplyLastRewardingBlockUpdated(address indexed vToken, uint32 newBlock);\n\n /// @notice Emitted when a reward token last rewarding block for borrow is updated\n event BorrowLastRewardingBlockUpdated(address indexed vToken, uint32 newBlock);\n\n /// @notice Emitted when a reward token last rewarding timestamp for supply is updated\n event SupplyLastRewardingBlockTimestampUpdated(address indexed vToken, uint256 newTimestamp);\n\n /// @notice Emitted when a reward token last rewarding timestamp for borrow is updated\n event BorrowLastRewardingBlockTimestampUpdated(address indexed vToken, uint256 newTimestamp);\n\n modifier onlyComptroller() {\n require(address(comptroller) == msg.sender, \"Only comptroller can call this function\");\n _;\n }\n\n /**\n * @param timeBased_ A boolean indicating whether the contract is based on time or block.\n * @param blocksPerYear_ The number of blocks per year\n * @custom:oz-upgrades-unsafe-allow constructor\n */\n constructor(bool timeBased_, uint256 blocksPerYear_) TimeManagerV8(timeBased_, blocksPerYear_) {\n // Note that the contract is upgradeable. Use initialize() or reinitializers\n // to set the state variables.\n _disableInitializers();\n }\n\n /**\n * @notice RewardsDistributor initializer\n * @dev Initializes the deployer to owner\n * @param comptroller_ Comptroller to attach the reward distributor to\n * @param rewardToken_ Reward token to distribute\n * @param loopsLimit_ Maximum number of iterations for the loops in this contract\n * @param accessControlManager_ AccessControlManager contract address\n */\n function initialize(\n Comptroller comptroller_,\n IERC20Upgradeable rewardToken_,\n uint256 loopsLimit_,\n address accessControlManager_\n ) external initializer {\n comptroller = comptroller_;\n rewardToken = rewardToken_;\n __Ownable2Step_init();\n __AccessControlled_init_unchained(accessControlManager_);\n\n _setMaxLoopsLimit(loopsLimit_);\n }\n\n /**\n * @notice Initializes the market state for a specific vToken\n * @param vToken The address of the vToken to be initialized\n * @custom:event MarketInitialized emits on success\n * @custom:access Only Comptroller\n */\n function initializeMarket(address vToken) external onlyComptroller {\n uint256 blockNumberOrTimestamp = getBlockNumberOrTimestamp();\n\n isTimeBased\n ? _initializeMarketTimestampBased(vToken, blockNumberOrTimestamp)\n : _initializeMarketBlockBased(vToken, safe32(blockNumberOrTimestamp, \"block number exceeds 32 bits\"));\n\n emit MarketInitialized(vToken);\n }\n\n /*** Reward Token Distribution ***/\n\n /**\n * @notice Calculate reward token accrued by a borrower and possibly transfer it to them\n * Borrowers will begin to accrue after the first interaction with the protocol.\n * @dev This function should only be called when the user has a borrow position in the market\n * (e.g. Comptroller.preBorrowHook, and Comptroller.preRepayHook)\n * We avoid an external call to check if they are in the market to save gas because this function is called in many places\n * @param vToken The market in which the borrower is interacting\n * @param borrower The address of the borrower to distribute REWARD TOKEN to\n * @param marketBorrowIndex The current global borrow index of vToken\n */\n function distributeBorrowerRewardToken(\n address vToken,\n address borrower,\n Exp memory marketBorrowIndex\n ) external onlyComptroller {\n _distributeBorrowerRewardToken(vToken, borrower, marketBorrowIndex);\n }\n\n function updateRewardTokenSupplyIndex(address vToken) external onlyComptroller {\n _updateRewardTokenSupplyIndex(vToken);\n }\n\n /**\n * @notice Transfer REWARD TOKEN to the recipient\n * @dev Note: If there is not enough REWARD TOKEN, we do not perform the transfer all\n * @param recipient The address of the recipient to transfer REWARD TOKEN to\n * @param amount The amount of REWARD TOKEN to (possibly) transfer\n */\n function grantRewardToken(address recipient, uint256 amount) external onlyOwner {\n uint256 amountLeft = _grantRewardToken(recipient, amount);\n require(amountLeft == 0, \"insufficient rewardToken for grant\");\n emit RewardTokenGranted(recipient, amount);\n }\n\n function updateRewardTokenBorrowIndex(address vToken, Exp memory marketBorrowIndex) external onlyComptroller {\n _updateRewardTokenBorrowIndex(vToken, marketBorrowIndex);\n }\n\n /**\n * @notice Set REWARD TOKEN borrow and supply speeds for the specified markets\n * @param vTokens The markets whose REWARD TOKEN speed to update\n * @param supplySpeeds New supply-side REWARD TOKEN speed for the corresponding market\n * @param borrowSpeeds New borrow-side REWARD TOKEN speed for the corresponding market\n */\n function setRewardTokenSpeeds(\n VToken[] memory vTokens,\n uint256[] memory supplySpeeds,\n uint256[] memory borrowSpeeds\n ) external {\n _checkAccessAllowed(\"setRewardTokenSpeeds(address[],uint256[],uint256[])\");\n uint256 numTokens = vTokens.length;\n require(numTokens == supplySpeeds.length && numTokens == borrowSpeeds.length, \"invalid setRewardTokenSpeeds\");\n\n for (uint256 i; i < numTokens; ++i) {\n _setRewardTokenSpeed(vTokens[i], supplySpeeds[i], borrowSpeeds[i]);\n }\n }\n\n /**\n * @notice Set REWARD TOKEN last rewarding block for the specified markets, used when contract is block based\n * @param vTokens The markets whose REWARD TOKEN last rewarding block to update\n * @param supplyLastRewardingBlocks New supply-side REWARD TOKEN last rewarding block for the corresponding market\n * @param borrowLastRewardingBlocks New borrow-side REWARD TOKEN last rewarding block for the corresponding market\n */\n function setLastRewardingBlocks(\n VToken[] calldata vTokens,\n uint32[] calldata supplyLastRewardingBlocks,\n uint32[] calldata borrowLastRewardingBlocks\n ) external {\n _checkAccessAllowed(\"setLastRewardingBlocks(address[],uint32[],uint32[])\");\n require(!isTimeBased, \"Block-based operation only\");\n\n uint256 numTokens = vTokens.length;\n require(\n numTokens == supplyLastRewardingBlocks.length && numTokens == borrowLastRewardingBlocks.length,\n \"RewardsDistributor::setLastRewardingBlocks invalid input\"\n );\n\n for (uint256 i; i < numTokens; ) {\n _setLastRewardingBlock(vTokens[i], supplyLastRewardingBlocks[i], borrowLastRewardingBlocks[i]);\n unchecked {\n ++i;\n }\n }\n }\n\n /**\n * @notice Set REWARD TOKEN last rewarding block timestamp for the specified markets, used when contract is time based\n * @param vTokens The markets whose REWARD TOKEN last rewarding block to update\n * @param supplyLastRewardingBlockTimestamps New supply-side REWARD TOKEN last rewarding block timestamp for the corresponding market\n * @param borrowLastRewardingBlockTimestamps New borrow-side REWARD TOKEN last rewarding block timestamp for the corresponding market\n */\n function setLastRewardingBlockTimestamps(\n VToken[] calldata vTokens,\n uint256[] calldata supplyLastRewardingBlockTimestamps,\n uint256[] calldata borrowLastRewardingBlockTimestamps\n ) external {\n _checkAccessAllowed(\"setLastRewardingBlockTimestamps(address[],uint256[],uint256[])\");\n require(isTimeBased, \"Time-based operation only\");\n\n uint256 numTokens = vTokens.length;\n require(\n numTokens == supplyLastRewardingBlockTimestamps.length &&\n numTokens == borrowLastRewardingBlockTimestamps.length,\n \"RewardsDistributor::setLastRewardingBlockTimestamps invalid input\"\n );\n\n for (uint256 i; i < numTokens; ) {\n _setLastRewardingBlockTimestamp(\n vTokens[i],\n supplyLastRewardingBlockTimestamps[i],\n borrowLastRewardingBlockTimestamps[i]\n );\n unchecked {\n ++i;\n }\n }\n }\n\n /**\n * @notice Set REWARD TOKEN speed for a single contributor\n * @param contributor The contributor whose REWARD TOKEN speed to update\n * @param rewardTokenSpeed New REWARD TOKEN speed for contributor\n */\n function setContributorRewardTokenSpeed(address contributor, uint256 rewardTokenSpeed) external onlyOwner {\n // note that REWARD TOKEN speed could be set to 0 to halt liquidity rewards for a contributor\n updateContributorRewards(contributor);\n if (rewardTokenSpeed == 0) {\n // release storage\n delete lastContributorBlock[contributor];\n } else {\n lastContributorBlock[contributor] = getBlockNumberOrTimestamp();\n }\n rewardTokenContributorSpeeds[contributor] = rewardTokenSpeed;\n\n emit ContributorRewardTokenSpeedUpdated(contributor, rewardTokenSpeed);\n }\n\n function distributeSupplierRewardToken(address vToken, address supplier) external onlyComptroller {\n _distributeSupplierRewardToken(vToken, supplier);\n }\n\n /**\n * @notice Claim all the rewardToken accrued by holder in all markets\n * @param holder The address to claim REWARD TOKEN for\n */\n function claimRewardToken(address holder) external {\n return claimRewardToken(holder, comptroller.getAllMarkets());\n }\n\n /**\n * @notice Set the limit for the loops can iterate to avoid the DOS\n * @param limit Limit for the max loops can execute at a time\n */\n function setMaxLoopsLimit(uint256 limit) external onlyOwner {\n _setMaxLoopsLimit(limit);\n }\n\n /**\n * @notice Calculate additional accrued REWARD TOKEN for a contributor since last accrual\n * @param contributor The address to calculate contributor rewards for\n */\n function updateContributorRewards(address contributor) public {\n uint256 rewardTokenSpeed = rewardTokenContributorSpeeds[contributor];\n uint256 blockNumberOrTimestamp = getBlockNumberOrTimestamp();\n uint256 deltaBlocksOrTimestamp = sub_(blockNumberOrTimestamp, lastContributorBlock[contributor]);\n if (deltaBlocksOrTimestamp > 0 && rewardTokenSpeed > 0) {\n uint256 newAccrued = mul_(deltaBlocksOrTimestamp, rewardTokenSpeed);\n uint256 contributorAccrued = add_(rewardTokenAccrued[contributor], newAccrued);\n\n rewardTokenAccrued[contributor] = contributorAccrued;\n lastContributorBlock[contributor] = blockNumberOrTimestamp;\n\n emit ContributorRewardsUpdated(contributor, rewardTokenAccrued[contributor]);\n }\n }\n\n /**\n * @notice Claim all the rewardToken accrued by holder in the specified markets\n * @param holder The address to claim REWARD TOKEN for\n * @param vTokens The list of markets to claim REWARD TOKEN in\n */\n function claimRewardToken(address holder, VToken[] memory vTokens) public {\n uint256 vTokensCount = vTokens.length;\n\n _ensureMaxLoops(vTokensCount);\n\n for (uint256 i; i < vTokensCount; ++i) {\n VToken vToken = vTokens[i];\n require(comptroller.isMarketListed(vToken), \"market must be listed\");\n Exp memory borrowIndex = Exp({ mantissa: vToken.borrowIndex() });\n _updateRewardTokenBorrowIndex(address(vToken), borrowIndex);\n _distributeBorrowerRewardToken(address(vToken), holder, borrowIndex);\n _updateRewardTokenSupplyIndex(address(vToken));\n _distributeSupplierRewardToken(address(vToken), holder);\n }\n rewardTokenAccrued[holder] = _grantRewardToken(holder, rewardTokenAccrued[holder]);\n }\n\n /**\n * @notice Set REWARD TOKEN last rewarding block for a single market.\n * @param vToken market's whose reward token last rewarding block to be updated\n * @param supplyLastRewardingBlock New supply-side REWARD TOKEN last rewarding block for market\n * @param borrowLastRewardingBlock New borrow-side REWARD TOKEN last rewarding block for market\n */\n function _setLastRewardingBlock(\n VToken vToken,\n uint32 supplyLastRewardingBlock,\n uint32 borrowLastRewardingBlock\n ) internal {\n require(comptroller.isMarketListed(vToken), \"rewardToken market is not listed\");\n\n uint256 blockNumber = getBlockNumberOrTimestamp();\n\n require(supplyLastRewardingBlock > blockNumber, \"setting last rewarding block in the past is not allowed\");\n require(borrowLastRewardingBlock > blockNumber, \"setting last rewarding block in the past is not allowed\");\n\n uint32 currentSupplyLastRewardingBlock = rewardTokenSupplyState[address(vToken)].lastRewardingBlock;\n uint32 currentBorrowLastRewardingBlock = rewardTokenBorrowState[address(vToken)].lastRewardingBlock;\n\n require(\n currentSupplyLastRewardingBlock == 0 || currentSupplyLastRewardingBlock > blockNumber,\n \"this RewardsDistributor is already locked\"\n );\n require(\n currentBorrowLastRewardingBlock == 0 || currentBorrowLastRewardingBlock > blockNumber,\n \"this RewardsDistributor is already locked\"\n );\n\n if (currentSupplyLastRewardingBlock != supplyLastRewardingBlock) {\n rewardTokenSupplyState[address(vToken)].lastRewardingBlock = supplyLastRewardingBlock;\n emit SupplyLastRewardingBlockUpdated(address(vToken), supplyLastRewardingBlock);\n }\n\n if (currentBorrowLastRewardingBlock != borrowLastRewardingBlock) {\n rewardTokenBorrowState[address(vToken)].lastRewardingBlock = borrowLastRewardingBlock;\n emit BorrowLastRewardingBlockUpdated(address(vToken), borrowLastRewardingBlock);\n }\n }\n\n /**\n * @notice Set REWARD TOKEN last rewarding timestamp for a single market.\n * @param vToken market's whose reward token last rewarding timestamp to be updated\n * @param supplyLastRewardingBlockTimestamp New supply-side REWARD TOKEN last rewarding timestamp for market\n * @param borrowLastRewardingBlockTimestamp New borrow-side REWARD TOKEN last rewarding timestamp for market\n */\n function _setLastRewardingBlockTimestamp(\n VToken vToken,\n uint256 supplyLastRewardingBlockTimestamp,\n uint256 borrowLastRewardingBlockTimestamp\n ) internal {\n require(comptroller.isMarketListed(vToken), \"rewardToken market is not listed\");\n\n uint256 blockTimestamp = getBlockNumberOrTimestamp();\n\n require(\n supplyLastRewardingBlockTimestamp > blockTimestamp,\n \"setting last rewarding timestamp in the past is not allowed\"\n );\n require(\n borrowLastRewardingBlockTimestamp > blockTimestamp,\n \"setting last rewarding timestamp in the past is not allowed\"\n );\n\n uint256 currentSupplyLastRewardingBlockTimestamp = rewardTokenSupplyStateTimeBased[address(vToken)]\n .lastRewardingTimestamp;\n uint256 currentBorrowLastRewardingBlockTimestamp = rewardTokenBorrowStateTimeBased[address(vToken)]\n .lastRewardingTimestamp;\n\n require(\n currentSupplyLastRewardingBlockTimestamp == 0 || currentSupplyLastRewardingBlockTimestamp > blockTimestamp,\n \"this RewardsDistributor is already locked\"\n );\n require(\n currentBorrowLastRewardingBlockTimestamp == 0 || currentBorrowLastRewardingBlockTimestamp > blockTimestamp,\n \"this RewardsDistributor is already locked\"\n );\n\n if (currentSupplyLastRewardingBlockTimestamp != supplyLastRewardingBlockTimestamp) {\n rewardTokenSupplyStateTimeBased[address(vToken)].lastRewardingTimestamp = supplyLastRewardingBlockTimestamp;\n emit SupplyLastRewardingBlockTimestampUpdated(address(vToken), supplyLastRewardingBlockTimestamp);\n }\n\n if (currentBorrowLastRewardingBlockTimestamp != borrowLastRewardingBlockTimestamp) {\n rewardTokenBorrowStateTimeBased[address(vToken)].lastRewardingTimestamp = borrowLastRewardingBlockTimestamp;\n emit BorrowLastRewardingBlockTimestampUpdated(address(vToken), borrowLastRewardingBlockTimestamp);\n }\n }\n\n /**\n * @notice Set REWARD TOKEN speed for a single market.\n * @param vToken market's whose reward token rate to be updated\n * @param supplySpeed New supply-side REWARD TOKEN speed for market\n * @param borrowSpeed New borrow-side REWARD TOKEN speed for market\n */\n function _setRewardTokenSpeed(VToken vToken, uint256 supplySpeed, uint256 borrowSpeed) internal {\n require(comptroller.isMarketListed(vToken), \"rewardToken market is not listed\");\n\n if (rewardTokenSupplySpeeds[address(vToken)] != supplySpeed) {\n // Supply speed updated so let's update supply state to ensure that\n // 1. REWARD TOKEN accrued properly for the old speed, and\n // 2. REWARD TOKEN accrued at the new speed starts after this block.\n _updateRewardTokenSupplyIndex(address(vToken));\n\n // Update speed and emit event\n rewardTokenSupplySpeeds[address(vToken)] = supplySpeed;\n emit RewardTokenSupplySpeedUpdated(vToken, supplySpeed);\n }\n\n if (rewardTokenBorrowSpeeds[address(vToken)] != borrowSpeed) {\n // Borrow speed updated so let's update borrow state to ensure that\n // 1. REWARD TOKEN accrued properly for the old speed, and\n // 2. REWARD TOKEN accrued at the new speed starts after this block.\n Exp memory borrowIndex = Exp({ mantissa: vToken.borrowIndex() });\n _updateRewardTokenBorrowIndex(address(vToken), borrowIndex);\n\n // Update speed and emit event\n rewardTokenBorrowSpeeds[address(vToken)] = borrowSpeed;\n emit RewardTokenBorrowSpeedUpdated(vToken, borrowSpeed);\n }\n }\n\n /**\n * @notice Calculate REWARD TOKEN accrued by a supplier and possibly transfer it to them.\n * @param vToken The market in which the supplier is interacting\n * @param supplier The address of the supplier to distribute REWARD TOKEN to\n */\n function _distributeSupplierRewardToken(address vToken, address supplier) internal {\n RewardToken storage supplyState = rewardTokenSupplyState[vToken];\n TimeBasedRewardToken storage supplyStateTimeBased = rewardTokenSupplyStateTimeBased[vToken];\n\n uint256 supplyIndex = isTimeBased ? supplyStateTimeBased.index : supplyState.index;\n uint256 supplierIndex = rewardTokenSupplierIndex[vToken][supplier];\n\n // Update supplier's index to the current index since we are distributing accrued REWARD TOKEN\n rewardTokenSupplierIndex[vToken][supplier] = supplyIndex;\n\n if (supplierIndex == 0 && supplyIndex >= INITIAL_INDEX) {\n // Covers the case where users supplied tokens before the market's supply state index was set.\n // Rewards the user with REWARD TOKEN accrued from the start of when supplier rewards were first\n // set for the market.\n supplierIndex = INITIAL_INDEX;\n }\n\n // Calculate change in the cumulative sum of the REWARD TOKEN per vToken accrued\n Double memory deltaIndex = Double({ mantissa: sub_(supplyIndex, supplierIndex) });\n\n uint256 supplierTokens = VToken(vToken).balanceOf(supplier);\n\n // Calculate REWARD TOKEN accrued: vTokenAmount * accruedPerVToken\n uint256 supplierDelta = mul_(supplierTokens, deltaIndex);\n\n uint256 supplierAccrued = add_(rewardTokenAccrued[supplier], supplierDelta);\n rewardTokenAccrued[supplier] = supplierAccrued;\n\n emit DistributedSupplierRewardToken(VToken(vToken), supplier, supplierDelta, supplierAccrued, supplyIndex);\n }\n\n /**\n * @notice Calculate reward token accrued by a borrower and possibly transfer it to them.\n * @param vToken The market in which the borrower is interacting\n * @param borrower The address of the borrower to distribute REWARD TOKEN to\n * @param marketBorrowIndex The current global borrow index of vToken\n */\n function _distributeBorrowerRewardToken(address vToken, address borrower, Exp memory marketBorrowIndex) internal {\n RewardToken storage borrowState = rewardTokenBorrowState[vToken];\n TimeBasedRewardToken storage borrowStateTimeBased = rewardTokenBorrowStateTimeBased[vToken];\n\n uint256 borrowIndex = isTimeBased ? borrowStateTimeBased.index : borrowState.index;\n uint256 borrowerIndex = rewardTokenBorrowerIndex[vToken][borrower];\n\n // Update borrowers's index to the current index since we are distributing accrued REWARD TOKEN\n rewardTokenBorrowerIndex[vToken][borrower] = borrowIndex;\n\n if (borrowerIndex == 0 && borrowIndex >= INITIAL_INDEX) {\n // Covers the case where users borrowed tokens before the market's borrow state index was set.\n // Rewards the user with REWARD TOKEN accrued from the start of when borrower rewards were first\n // set for the market.\n borrowerIndex = INITIAL_INDEX;\n }\n\n // Calculate change in the cumulative sum of the REWARD TOKEN per borrowed unit accrued\n Double memory deltaIndex = Double({ mantissa: sub_(borrowIndex, borrowerIndex) });\n\n uint256 borrowerAmount = div_(VToken(vToken).borrowBalanceStored(borrower), marketBorrowIndex);\n\n // Calculate REWARD TOKEN accrued: vTokenAmount * accruedPerBorrowedUnit\n if (borrowerAmount != 0) {\n uint256 borrowerDelta = mul_(borrowerAmount, deltaIndex);\n\n uint256 borrowerAccrued = add_(rewardTokenAccrued[borrower], borrowerDelta);\n rewardTokenAccrued[borrower] = borrowerAccrued;\n\n emit DistributedBorrowerRewardToken(VToken(vToken), borrower, borrowerDelta, borrowerAccrued, borrowIndex);\n }\n }\n\n /**\n * @notice Transfer REWARD TOKEN to the user.\n * @dev Note: If there is not enough REWARD TOKEN, we do not perform the transfer all.\n * @param user The address of the user to transfer REWARD TOKEN to\n * @param amount The amount of REWARD TOKEN to (possibly) transfer\n * @return The amount of REWARD TOKEN which was NOT transferred to the user\n */\n function _grantRewardToken(address user, uint256 amount) internal returns (uint256) {\n uint256 rewardTokenRemaining = rewardToken.balanceOf(address(this));\n if (amount > 0 && amount <= rewardTokenRemaining) {\n rewardToken.safeTransfer(user, amount);\n return 0;\n }\n return amount;\n }\n\n /**\n * @notice Accrue REWARD TOKEN to the market by updating the supply index\n * @param vToken The market whose supply index to update\n * @dev Index is a cumulative sum of the REWARD TOKEN per vToken accrued\n */\n function _updateRewardTokenSupplyIndex(address vToken) internal {\n RewardToken storage supplyState = rewardTokenSupplyState[vToken];\n TimeBasedRewardToken storage supplyStateTimeBased = rewardTokenSupplyStateTimeBased[vToken];\n\n uint256 supplySpeed = rewardTokenSupplySpeeds[vToken];\n uint256 blockNumberOrTimestamp = getBlockNumberOrTimestamp();\n\n if (!isTimeBased) {\n safe32(blockNumberOrTimestamp, \"block number exceeds 32 bits\");\n }\n\n uint256 lastRewardingBlockOrTimestamp = isTimeBased\n ? supplyStateTimeBased.lastRewardingTimestamp\n : uint256(supplyState.lastRewardingBlock);\n\n if (lastRewardingBlockOrTimestamp > 0 && blockNumberOrTimestamp > lastRewardingBlockOrTimestamp) {\n blockNumberOrTimestamp = lastRewardingBlockOrTimestamp;\n }\n\n uint256 deltaBlocksOrTimestamp = sub_(\n blockNumberOrTimestamp,\n (isTimeBased ? supplyStateTimeBased.timestamp : uint256(supplyState.block))\n );\n if (deltaBlocksOrTimestamp > 0 && supplySpeed > 0) {\n uint256 supplyTokens = VToken(vToken).totalSupply();\n uint256 accruedSinceUpdate = mul_(deltaBlocksOrTimestamp, supplySpeed);\n Double memory ratio = supplyTokens > 0\n ? fraction(accruedSinceUpdate, supplyTokens)\n : Double({ mantissa: 0 });\n uint224 supplyIndex = isTimeBased ? supplyStateTimeBased.index : supplyState.index;\n uint224 index = safe224(\n add_(Double({ mantissa: supplyIndex }), ratio).mantissa,\n \"new index exceeds 224 bits\"\n );\n\n if (isTimeBased) {\n supplyStateTimeBased.index = index;\n supplyStateTimeBased.timestamp = blockNumberOrTimestamp;\n } else {\n supplyState.index = index;\n supplyState.block = uint32(blockNumberOrTimestamp);\n }\n } else if (deltaBlocksOrTimestamp > 0) {\n isTimeBased ? supplyStateTimeBased.timestamp = blockNumberOrTimestamp : supplyState.block = uint32(\n blockNumberOrTimestamp\n );\n }\n\n emit RewardTokenSupplyIndexUpdated(vToken);\n }\n\n /**\n * @notice Accrue REWARD TOKEN to the market by updating the borrow index\n * @param vToken The market whose borrow index to update\n * @param marketBorrowIndex The current global borrow index of vToken\n * @dev Index is a cumulative sum of the REWARD TOKEN per vToken accrued\n */\n function _updateRewardTokenBorrowIndex(address vToken, Exp memory marketBorrowIndex) internal {\n RewardToken storage borrowState = rewardTokenBorrowState[vToken];\n TimeBasedRewardToken storage borrowStateTimeBased = rewardTokenBorrowStateTimeBased[vToken];\n\n uint256 borrowSpeed = rewardTokenBorrowSpeeds[vToken];\n uint256 blockNumberOrTimestamp = getBlockNumberOrTimestamp();\n\n if (!isTimeBased) {\n safe32(blockNumberOrTimestamp, \"block number exceeds 32 bits\");\n }\n\n uint256 lastRewardingBlockOrTimestamp = isTimeBased\n ? borrowStateTimeBased.lastRewardingTimestamp\n : uint256(borrowState.lastRewardingBlock);\n\n if (lastRewardingBlockOrTimestamp > 0 && blockNumberOrTimestamp > lastRewardingBlockOrTimestamp) {\n blockNumberOrTimestamp = lastRewardingBlockOrTimestamp;\n }\n\n uint256 deltaBlocksOrTimestamp = sub_(\n blockNumberOrTimestamp,\n (isTimeBased ? borrowStateTimeBased.timestamp : uint256(borrowState.block))\n );\n if (deltaBlocksOrTimestamp > 0 && borrowSpeed > 0) {\n uint256 borrowAmount = div_(VToken(vToken).totalBorrows(), marketBorrowIndex);\n uint256 accruedSinceUpdate = mul_(deltaBlocksOrTimestamp, borrowSpeed);\n Double memory ratio = borrowAmount > 0\n ? fraction(accruedSinceUpdate, borrowAmount)\n : Double({ mantissa: 0 });\n uint224 borrowIndex = isTimeBased ? borrowStateTimeBased.index : borrowState.index;\n uint224 index = safe224(\n add_(Double({ mantissa: borrowIndex }), ratio).mantissa,\n \"new index exceeds 224 bits\"\n );\n\n if (isTimeBased) {\n borrowStateTimeBased.index = index;\n borrowStateTimeBased.timestamp = blockNumberOrTimestamp;\n } else {\n borrowState.index = index;\n borrowState.block = uint32(blockNumberOrTimestamp);\n }\n } else if (deltaBlocksOrTimestamp > 0) {\n if (isTimeBased) {\n borrowStateTimeBased.timestamp = blockNumberOrTimestamp;\n } else {\n borrowState.block = uint32(blockNumberOrTimestamp);\n }\n }\n\n emit RewardTokenBorrowIndexUpdated(vToken, marketBorrowIndex);\n }\n\n /**\n * @notice Initializes the market state for a specific vToken called when contract is block-based\n * @param vToken The address of the vToken to be initialized\n * @param blockNumber current block number\n */\n function _initializeMarketBlockBased(address vToken, uint32 blockNumber) internal {\n RewardToken storage supplyState = rewardTokenSupplyState[vToken];\n RewardToken storage borrowState = rewardTokenBorrowState[vToken];\n\n /*\n * Update market state indices\n */\n if (supplyState.index == 0) {\n // Initialize supply state index with default value\n supplyState.index = INITIAL_INDEX;\n }\n\n if (borrowState.index == 0) {\n // Initialize borrow state index with default value\n borrowState.index = INITIAL_INDEX;\n }\n\n /*\n * Update market state block numbers\n */\n supplyState.block = borrowState.block = blockNumber;\n }\n\n /**\n * @notice Initializes the market state for a specific vToken called when contract is time-based\n * @param vToken The address of the vToken to be initialized\n * @param blockTimestamp current block timestamp\n */\n function _initializeMarketTimestampBased(address vToken, uint256 blockTimestamp) internal {\n TimeBasedRewardToken storage supplyState = rewardTokenSupplyStateTimeBased[vToken];\n TimeBasedRewardToken storage borrowState = rewardTokenBorrowStateTimeBased[vToken];\n\n /*\n * Update market state indices\n */\n if (supplyState.index == 0) {\n // Initialize supply state index with default value\n supplyState.index = INITIAL_INDEX;\n }\n\n if (borrowState.index == 0) {\n // Initialize borrow state index with default value\n borrowState.index = INITIAL_INDEX;\n }\n\n /*\n * Update market state block timestamp\n */\n supplyState.timestamp = borrowState.timestamp = blockTimestamp;\n }\n}\n" + }, + "contracts/Rewards/RewardsDistributorStorage.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\n\nimport { IERC20Upgradeable } from \"@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol\";\n\nimport { Comptroller } from \"../Comptroller.sol\";\n\n/**\n * @title RewardsDistributorStorage\n * @author Venus\n * @dev Storage for RewardsDistributor\n */\ncontract RewardsDistributorStorage {\n struct RewardToken {\n // The market's last updated rewardTokenBorrowIndex or rewardTokenSupplyIndex\n uint224 index;\n // The block number the index was last updated at\n uint32 block;\n // The block number at which to stop rewards\n uint32 lastRewardingBlock;\n }\n\n struct TimeBasedRewardToken {\n // The market's last updated rewardTokenBorrowIndex or rewardTokenSupplyIndex\n uint224 index;\n // The block timestamp the index was last updated at\n uint256 timestamp;\n // The block timestamp at which to stop rewards\n uint256 lastRewardingTimestamp;\n }\n\n /// @notice The REWARD TOKEN market supply state for each market\n mapping(address => RewardToken) public rewardTokenSupplyState;\n\n /// @notice The REWARD TOKEN borrow index for each market for each supplier as of the last time they accrued REWARD TOKEN\n mapping(address => mapping(address => uint256)) public rewardTokenSupplierIndex;\n\n /// @notice The REWARD TOKEN accrued but not yet transferred to each user\n mapping(address => uint256) public rewardTokenAccrued;\n\n /// @notice The rate at which rewardToken is distributed to the corresponding borrow market per slot (block or second)\n mapping(address => uint256) public rewardTokenBorrowSpeeds;\n\n /// @notice The rate at which rewardToken is distributed to the corresponding supply market per slot (block or second)\n mapping(address => uint256) public rewardTokenSupplySpeeds;\n\n /// @notice The REWARD TOKEN market borrow state for each market\n mapping(address => RewardToken) public rewardTokenBorrowState;\n\n /// @notice The portion of REWARD TOKEN that each contributor receives per slot (block or second)\n mapping(address => uint256) public rewardTokenContributorSpeeds;\n\n /// @notice Last slot (block or second) at which a contributor's REWARD TOKEN rewards have been allocated\n mapping(address => uint256) public lastContributorBlock;\n\n /// @notice The REWARD TOKEN borrow index for each market for each borrower as of the last time they accrued REWARD TOKEN\n mapping(address => mapping(address => uint256)) public rewardTokenBorrowerIndex;\n\n Comptroller internal comptroller;\n\n IERC20Upgradeable public rewardToken;\n\n /// @notice The REWARD TOKEN market supply state for each market\n mapping(address => TimeBasedRewardToken) public rewardTokenSupplyStateTimeBased;\n\n /// @notice The REWARD TOKEN market borrow state for each market\n mapping(address => TimeBasedRewardToken) public rewardTokenBorrowStateTimeBased;\n\n /**\n * @dev This empty reserved space is put in place to allow future versions to add new\n * variables without shifting down storage in the inheritance chain\n * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\n */\n uint256[37] private __gap;\n}\n" + }, + "contracts/Shortfall/IRiskFund.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity ^0.8.25;\n\n/**\n * @title IRiskFund\n * @author Venus\n * @notice Interface implemented by `RiskFund`.\n */\ninterface IRiskFund {\n function transferReserveForAuction(address comptroller, uint256 amount) external returns (uint256);\n\n function convertibleBaseAsset() external view returns (address);\n\n function getPoolsBaseAssetReserves(address comptroller) external view returns (uint256);\n}\n" + }, + "contracts/Shortfall/Shortfall.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\n\nimport { Ownable2StepUpgradeable } from \"@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol\";\nimport { IERC20Upgradeable } from \"@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol\";\nimport { SafeERC20Upgradeable } from \"@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol\";\nimport { ReentrancyGuardUpgradeable } from \"@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol\";\nimport { ResilientOracleInterface } from \"@venusprotocol/oracle/contracts/interfaces/OracleInterface.sol\";\nimport { AccessControlledV8 } from \"@venusprotocol/governance-contracts/contracts/Governance/AccessControlledV8.sol\";\nimport { ensureNonzeroAddress, ensureNonzeroValue } from \"@venusprotocol/solidity-utilities/contracts/validators.sol\";\nimport { TimeManagerV8 } from \"@venusprotocol/solidity-utilities/contracts/TimeManagerV8.sol\";\n\nimport { VToken } from \"../VToken.sol\";\nimport { ComptrollerInterface, ComptrollerViewInterface } from \"../ComptrollerInterface.sol\";\nimport { IRiskFund } from \"./IRiskFund.sol\";\nimport { PoolRegistry } from \"../Pool/PoolRegistry.sol\";\nimport { PoolRegistryInterface } from \"../Pool/PoolRegistryInterface.sol\";\nimport { TokenDebtTracker } from \"../lib/TokenDebtTracker.sol\";\nimport { ShortfallStorage } from \"./ShortfallStorage.sol\";\nimport { EXP_SCALE } from \"../lib/constants.sol\";\n\n/**\n * @title Shortfall\n * @author Venus\n * @notice Shortfall is an auction contract designed to auction off the `convertibleBaseAsset` accumulated in `RiskFund`. The `convertibleBaseAsset`\n * is auctioned in exchange for users paying off the pool's bad debt. An auction can be started by anyone once a pool's bad debt has reached a minimum value.\n * This value is set and can be changed by the authorized accounts. If the pool’s bad debt exceeds the risk fund plus a 10% incentive, then the auction winner\n * is determined by who will pay off the largest percentage of the pool's bad debt. The auction winner then exchanges for the entire risk fund. Otherwise,\n * if the risk fund covers the pool's bad debt plus the 10% incentive, then the auction winner is determined by who will take the smallest percentage of the\n * risk fund in exchange for paying off all the pool's bad debt.\n */\ncontract Shortfall is\n Ownable2StepUpgradeable,\n AccessControlledV8,\n ReentrancyGuardUpgradeable,\n TokenDebtTracker,\n ShortfallStorage,\n TimeManagerV8\n{\n using SafeERC20Upgradeable for IERC20Upgradeable;\n\n /// @dev Max basis points i.e., 100%\n uint256 private constant MAX_BPS = 10000;\n\n // @notice Default incentive basis points (BPS) for the auction participants, set to 10%\n uint256 private constant DEFAULT_INCENTIVE_BPS = 1000;\n\n // @notice Default block or timestamp limit for the next bidder to place a bid\n /// @custom:oz-upgrades-unsafe-allow state-variable-immutable\n uint256 private immutable DEFAULT_NEXT_BIDDER_BLOCK_OR_TIMESTAMP_LIMIT;\n\n // @notice Default number of blocks or seconds to wait for the first bidder before starting the auction\n /// @custom:oz-upgrades-unsafe-allow state-variable-immutable\n uint256 private immutable DEFAULT_WAIT_FOR_FIRST_BIDDER;\n\n /// @notice Emitted when a auction starts\n event AuctionStarted(\n address indexed comptroller,\n uint256 auctionStartBlockOrTimestamp,\n AuctionType auctionType,\n VToken[] markets,\n uint256[] marketsDebt,\n uint256 seizedRiskFund,\n uint256 startBidBps\n );\n\n /// @notice Emitted when a bid is placed\n event BidPlaced(\n address indexed comptroller,\n uint256 auctionStartBlockOrTimestamp,\n uint256 bidBps,\n address indexed bidder\n );\n\n /// @notice Emitted when a auction is completed\n event AuctionClosed(\n address indexed comptroller,\n uint256 auctionStartBlockOrTimestamp,\n address indexed highestBidder,\n uint256 highestBidBps,\n uint256 seizedRiskFind,\n VToken[] markets,\n uint256[] marketDebt\n );\n\n /// @notice Emitted when a auction is restarted\n event AuctionRestarted(address indexed comptroller, uint256 auctionStartBlockOrTimestamp);\n\n /// @notice Emitted when pool registry address is updated\n event PoolRegistryUpdated(address indexed oldPoolRegistry, address indexed newPoolRegistry);\n\n /// @notice Emitted when minimum pool bad debt is updated\n event MinimumPoolBadDebtUpdated(uint256 oldMinimumPoolBadDebt, uint256 newMinimumPoolBadDebt);\n\n /// @notice Emitted when wait for first bidder block or timestamp count is updated\n event WaitForFirstBidderUpdated(uint256 oldWaitForFirstBidder, uint256 newWaitForFirstBidder);\n\n /// @notice Emitted when next bidder block or timestamp limit is updated\n event NextBidderBlockLimitUpdated(\n uint256 oldNextBidderBlockOrTimestampLimit,\n uint256 newNextBidderBlockOrTimestampLimit\n );\n\n /// @notice Emitted when incentiveBps is updated\n event IncentiveBpsUpdated(uint256 oldIncentiveBps, uint256 newIncentiveBps);\n\n /// @notice Emitted when auctions are paused\n event AuctionsPaused(address sender);\n\n /// @notice Emitted when auctions are unpaused\n event AuctionsResumed(address sender);\n\n /**\n * @param timeBased_ A boolean indicating whether the contract is based on time or block.\n * @param blocksPerYear_ The number of blocks per year\n * @param nextBidderBlockOrTimestampLimit_ Default block or timestamp limit for the next bidder to place a bid\n * @param waitForFirstBidder_ Default number of blocks or seconds to wait for the first bidder before starting the auction\n * @custom:oz-upgrades-unsafe-allow constructor\n */\n constructor(\n bool timeBased_,\n uint256 blocksPerYear_,\n uint256 nextBidderBlockOrTimestampLimit_,\n uint256 waitForFirstBidder_\n ) TimeManagerV8(timeBased_, blocksPerYear_) {\n ensureNonzeroValue(nextBidderBlockOrTimestampLimit_);\n ensureNonzeroValue(waitForFirstBidder_);\n\n DEFAULT_NEXT_BIDDER_BLOCK_OR_TIMESTAMP_LIMIT = nextBidderBlockOrTimestampLimit_;\n DEFAULT_WAIT_FOR_FIRST_BIDDER = waitForFirstBidder_;\n\n // Note that the contract is upgradeable. Use initialize() or reinitializers\n // to set the state variables.\n _disableInitializers();\n }\n\n /**\n * @notice Initialize the shortfall contract\n * @param riskFund_ RiskFund contract address\n * @param minimumPoolBadDebt_ Minimum bad debt in base asset for a pool to start auction\n * @param accessControlManager_ AccessControlManager contract address\n * @custom:error ZeroAddressNotAllowed is thrown when convertible base asset address is zero\n * @custom:error ZeroAddressNotAllowed is thrown when risk fund address is zero\n */\n function initialize(\n IRiskFund riskFund_,\n uint256 minimumPoolBadDebt_,\n address accessControlManager_\n ) external initializer {\n ensureNonzeroAddress(address(riskFund_));\n require(minimumPoolBadDebt_ != 0, \"invalid minimum pool bad debt\");\n\n __Ownable2Step_init();\n __AccessControlled_init_unchained(accessControlManager_);\n __ReentrancyGuard_init();\n __TokenDebtTracker_init();\n minimumPoolBadDebt = minimumPoolBadDebt_;\n riskFund = riskFund_;\n incentiveBps = DEFAULT_INCENTIVE_BPS;\n auctionsPaused = false;\n\n waitForFirstBidder = DEFAULT_WAIT_FOR_FIRST_BIDDER;\n nextBidderBlockLimit = DEFAULT_NEXT_BIDDER_BLOCK_OR_TIMESTAMP_LIMIT;\n }\n\n /**\n * @notice Place a bid greater than the previous in an ongoing auction\n * @param comptroller Comptroller address of the pool\n * @param bidBps The bid percent of the risk fund or bad debt depending on auction type\n * @param auctionStartBlockOrTimestamp The block number or timestamp when auction started\n * @custom:event Emits BidPlaced event on success\n */\n function placeBid(address comptroller, uint256 bidBps, uint256 auctionStartBlockOrTimestamp) external nonReentrant {\n Auction storage auction = auctions[comptroller];\n\n require(auction.startBlockOrTimestamp == auctionStartBlockOrTimestamp, \"auction has been restarted\");\n require(_isStarted(auction), \"no on-going auction\");\n require(!_isStale(auction), \"auction is stale, restart it\");\n require(bidBps > 0, \"basis points cannot be zero\");\n require(bidBps <= MAX_BPS, \"basis points cannot be more than 10000\");\n require(\n (auction.auctionType == AuctionType.LARGE_POOL_DEBT &&\n ((auction.highestBidder != address(0) && bidBps > auction.highestBidBps) ||\n (auction.highestBidder == address(0) && bidBps >= auction.startBidBps))) ||\n (auction.auctionType == AuctionType.LARGE_RISK_FUND &&\n ((auction.highestBidder != address(0) && bidBps < auction.highestBidBps) ||\n (auction.highestBidder == address(0) && bidBps <= auction.startBidBps))),\n \"your bid is not the highest\"\n );\n\n uint256 marketsCount = auction.markets.length;\n for (uint256 i; i < marketsCount; ++i) {\n VToken vToken = VToken(address(auction.markets[i]));\n IERC20Upgradeable erc20 = IERC20Upgradeable(address(vToken.underlying()));\n\n if (auction.highestBidder != address(0)) {\n _transferOutOrTrackDebt(erc20, auction.highestBidder, auction.bidAmount[auction.markets[i]]);\n }\n uint256 balanceBefore = erc20.balanceOf(address(this));\n\n if (auction.auctionType == AuctionType.LARGE_POOL_DEBT) {\n uint256 currentBidAmount = ((auction.marketDebt[auction.markets[i]] * bidBps) / MAX_BPS);\n erc20.safeTransferFrom(msg.sender, address(this), currentBidAmount);\n } else {\n erc20.safeTransferFrom(msg.sender, address(this), auction.marketDebt[auction.markets[i]]);\n }\n\n uint256 balanceAfter = erc20.balanceOf(address(this));\n auction.bidAmount[auction.markets[i]] = balanceAfter - balanceBefore;\n }\n\n auction.highestBidder = msg.sender;\n auction.highestBidBps = bidBps;\n auction.highestBidBlockOrTimestamp = getBlockNumberOrTimestamp();\n\n emit BidPlaced(comptroller, auction.startBlockOrTimestamp, bidBps, msg.sender);\n }\n\n /**\n * @notice Close an auction\n * @param comptroller Comptroller address of the pool\n * @custom:event Emits AuctionClosed event on successful close\n */\n function closeAuction(address comptroller) external nonReentrant {\n Auction storage auction = auctions[comptroller];\n\n require(_isStarted(auction), \"no on-going auction\");\n require(\n getBlockNumberOrTimestamp() > auction.highestBidBlockOrTimestamp + nextBidderBlockLimit &&\n auction.highestBidder != address(0),\n \"waiting for next bidder. cannot close auction\"\n );\n\n uint256 marketsCount = auction.markets.length;\n uint256[] memory marketsDebt = new uint256[](marketsCount);\n\n auction.status = AuctionStatus.ENDED;\n\n for (uint256 i; i < marketsCount; ++i) {\n VToken vToken = VToken(address(auction.markets[i]));\n IERC20Upgradeable erc20 = IERC20Upgradeable(address(vToken.underlying()));\n\n uint256 balanceBefore = erc20.balanceOf(address(auction.markets[i]));\n erc20.safeTransfer(address(auction.markets[i]), auction.bidAmount[auction.markets[i]]);\n uint256 balanceAfter = erc20.balanceOf(address(auction.markets[i]));\n marketsDebt[i] = balanceAfter - balanceBefore;\n\n auction.markets[i].badDebtRecovered(marketsDebt[i]);\n }\n\n uint256 riskFundBidAmount;\n\n if (auction.auctionType == AuctionType.LARGE_POOL_DEBT) {\n riskFundBidAmount = auction.seizedRiskFund;\n } else {\n riskFundBidAmount = (auction.seizedRiskFund * auction.highestBidBps) / MAX_BPS;\n }\n\n address convertibleBaseAsset = riskFund.convertibleBaseAsset();\n\n uint256 transferredAmount = riskFund.transferReserveForAuction(comptroller, riskFundBidAmount);\n _transferOutOrTrackDebt(IERC20Upgradeable(convertibleBaseAsset), auction.highestBidder, riskFundBidAmount);\n\n emit AuctionClosed(\n comptroller,\n auction.startBlockOrTimestamp,\n auction.highestBidder,\n auction.highestBidBps,\n transferredAmount,\n auction.markets,\n marketsDebt\n );\n }\n\n /**\n * @notice Start a auction when there is not currently one active\n * @param comptroller Comptroller address of the pool\n * @custom:event Emits AuctionStarted event on success\n * @custom:event Errors if auctions are paused\n */\n function startAuction(address comptroller) external nonReentrant {\n require(!auctionsPaused, \"Auctions are paused\");\n _startAuction(comptroller);\n }\n\n /**\n * @notice Restart an auction\n * @param comptroller Address of the pool\n * @custom:event Emits AuctionRestarted event on successful restart\n */\n function restartAuction(address comptroller) external nonReentrant {\n Auction storage auction = auctions[comptroller];\n\n require(!auctionsPaused, \"auctions are paused\");\n require(_isStarted(auction), \"no on-going auction\");\n require(_isStale(auction), \"you need to wait for more time for first bidder\");\n\n auction.status = AuctionStatus.ENDED;\n\n emit AuctionRestarted(comptroller, auction.startBlockOrTimestamp);\n _startAuction(comptroller);\n }\n\n /**\n * @notice Update next bidder block or timestamp limit which is used determine when an auction can be closed\n * @param nextBidderBlockOrTimestampLimit_ New next bidder slot (block or second) limit\n * @custom:event Emits NextBidderBlockLimitUpdated on success\n * @custom:access Restricted by ACM\n */\n function updateNextBidderBlockLimit(uint256 nextBidderBlockOrTimestampLimit_) external {\n _checkAccessAllowed(\"updateNextBidderBlockLimit(uint256)\");\n require(nextBidderBlockOrTimestampLimit_ != 0, \"nextBidderBlockOrTimestampLimit_ must not be 0\");\n\n emit NextBidderBlockLimitUpdated(nextBidderBlockLimit, nextBidderBlockOrTimestampLimit_);\n nextBidderBlockLimit = nextBidderBlockOrTimestampLimit_;\n }\n\n /**\n * @notice Updates the incentive BPS\n * @param incentiveBps_ New incentive BPS\n * @custom:event Emits IncentiveBpsUpdated on success\n * @custom:access Restricted by ACM\n */\n function updateIncentiveBps(uint256 incentiveBps_) external {\n _checkAccessAllowed(\"updateIncentiveBps(uint256)\");\n require(incentiveBps_ != 0, \"incentiveBps must not be 0\");\n uint256 oldIncentiveBps = incentiveBps;\n incentiveBps = incentiveBps_;\n emit IncentiveBpsUpdated(oldIncentiveBps, incentiveBps_);\n }\n\n /**\n * @notice Update minimum pool bad debt to start auction\n * @param minimumPoolBadDebt_ Minimum bad debt in the base asset for a pool to start auction\n * @custom:event Emits MinimumPoolBadDebtUpdated on success\n * @custom:access Restricted by ACM\n */\n function updateMinimumPoolBadDebt(uint256 minimumPoolBadDebt_) external {\n _checkAccessAllowed(\"updateMinimumPoolBadDebt(uint256)\");\n uint256 oldMinimumPoolBadDebt = minimumPoolBadDebt;\n minimumPoolBadDebt = minimumPoolBadDebt_;\n emit MinimumPoolBadDebtUpdated(oldMinimumPoolBadDebt, minimumPoolBadDebt_);\n }\n\n /**\n * @notice Update wait for first bidder block or timestamp count. If the first bid is not made within this limit, the auction is closed and needs to be restarted\n * @param waitForFirstBidder_ New wait for first bidder block or timestamp count\n * @custom:event Emits WaitForFirstBidderUpdated on success\n * @custom:access Restricted by ACM\n */\n function updateWaitForFirstBidder(uint256 waitForFirstBidder_) external {\n _checkAccessAllowed(\"updateWaitForFirstBidder(uint256)\");\n uint256 oldWaitForFirstBidder = waitForFirstBidder;\n waitForFirstBidder = waitForFirstBidder_;\n emit WaitForFirstBidderUpdated(oldWaitForFirstBidder, waitForFirstBidder_);\n }\n\n /**\n * @notice Update the pool registry this shortfall supports\n * @dev After Pool Registry is deployed we need to set the pool registry address\n * @param poolRegistry_ Address of pool registry contract\n * @custom:event Emits PoolRegistryUpdated on success\n * @custom:access Restricted to owner\n * @custom:error ZeroAddressNotAllowed is thrown when pool registry address is zero\n */\n function updatePoolRegistry(address poolRegistry_) external onlyOwner {\n ensureNonzeroAddress(poolRegistry_);\n address oldPoolRegistry = poolRegistry;\n poolRegistry = poolRegistry_;\n emit PoolRegistryUpdated(oldPoolRegistry, poolRegistry_);\n }\n\n /**\n * @notice Pause auctions. This disables starting new auctions but lets the current auction finishes\n * @custom:event Emits AuctionsPaused on success\n * @custom:error Errors is auctions are paused\n * @custom:access Restricted by ACM\n */\n function pauseAuctions() external {\n _checkAccessAllowed(\"pauseAuctions()\");\n require(!auctionsPaused, \"Auctions are already paused\");\n auctionsPaused = true;\n emit AuctionsPaused(msg.sender);\n }\n\n /**\n * @notice Resume paused auctions.\n * @custom:event Emits AuctionsResumed on success\n * @custom:error Errors is auctions are active\n * @custom:access Restricted by ACM\n */\n function resumeAuctions() external {\n _checkAccessAllowed(\"resumeAuctions()\");\n require(auctionsPaused, \"Auctions are not paused\");\n auctionsPaused = false;\n emit AuctionsResumed(msg.sender);\n }\n\n /**\n * @notice Start a auction when there is not currently one active\n * @param comptroller Comptroller address of the pool\n */\n function _startAuction(address comptroller) internal {\n PoolRegistryInterface.VenusPool memory pool = PoolRegistry(poolRegistry).getPoolByComptroller(comptroller);\n require(pool.comptroller == comptroller, \"comptroller doesn't exist pool registry\");\n\n Auction storage auction = auctions[comptroller];\n require(\n auction.status == AuctionStatus.NOT_STARTED || auction.status == AuctionStatus.ENDED,\n \"auction is on-going\"\n );\n\n auction.highestBidBps = 0;\n auction.highestBidBlockOrTimestamp = 0;\n\n uint256 marketsCount = auction.markets.length;\n for (uint256 i; i < marketsCount; ++i) {\n VToken vToken = auction.markets[i];\n auction.marketDebt[vToken] = 0;\n }\n\n delete auction.markets;\n\n VToken[] memory vTokens = _getAllMarkets(comptroller);\n marketsCount = vTokens.length;\n ResilientOracleInterface priceOracle = _getPriceOracle(comptroller);\n uint256 poolBadDebt;\n\n uint256[] memory marketsDebt = new uint256[](marketsCount);\n auction.markets = new VToken[](marketsCount);\n\n for (uint256 i; i < marketsCount; ++i) {\n uint256 marketBadDebt = vTokens[i].badDebt();\n\n priceOracle.updatePrice(address(vTokens[i]));\n uint256 usdValue = (priceOracle.getUnderlyingPrice(address(vTokens[i])) * marketBadDebt) / EXP_SCALE;\n\n poolBadDebt = poolBadDebt + usdValue;\n auction.markets[i] = vTokens[i];\n auction.marketDebt[vTokens[i]] = marketBadDebt;\n marketsDebt[i] = marketBadDebt;\n }\n\n require(poolBadDebt >= minimumPoolBadDebt, \"pool bad debt is too low\");\n\n priceOracle.updateAssetPrice(riskFund.convertibleBaseAsset());\n uint256 riskFundBalance = (priceOracle.getPrice(riskFund.convertibleBaseAsset()) *\n riskFund.getPoolsBaseAssetReserves(comptroller)) / EXP_SCALE;\n uint256 remainingRiskFundBalance = riskFundBalance;\n uint256 badDebtPlusIncentive = poolBadDebt + ((poolBadDebt * incentiveBps) / MAX_BPS);\n if (badDebtPlusIncentive >= riskFundBalance) {\n auction.startBidBps =\n (MAX_BPS * MAX_BPS * remainingRiskFundBalance) /\n (poolBadDebt * (MAX_BPS + incentiveBps));\n remainingRiskFundBalance = 0;\n auction.auctionType = AuctionType.LARGE_POOL_DEBT;\n } else {\n uint256 maxSeizeableRiskFundBalance = badDebtPlusIncentive;\n\n remainingRiskFundBalance = remainingRiskFundBalance - maxSeizeableRiskFundBalance;\n auction.auctionType = AuctionType.LARGE_RISK_FUND;\n auction.startBidBps = MAX_BPS;\n }\n\n auction.seizedRiskFund = riskFundBalance - remainingRiskFundBalance;\n auction.startBlockOrTimestamp = getBlockNumberOrTimestamp();\n auction.status = AuctionStatus.STARTED;\n auction.highestBidder = address(0);\n\n emit AuctionStarted(\n comptroller,\n auction.startBlockOrTimestamp,\n auction.auctionType,\n auction.markets,\n marketsDebt,\n auction.seizedRiskFund,\n auction.startBidBps\n );\n }\n\n /**\n * @dev Returns the price oracle of the pool\n * @param comptroller Address of the pool's comptroller\n * @return oracle The pool's price oracle\n */\n function _getPriceOracle(address comptroller) internal view returns (ResilientOracleInterface) {\n return ResilientOracleInterface(ComptrollerViewInterface(comptroller).oracle());\n }\n\n /**\n * @dev Returns all markets of the pool\n * @param comptroller Address of the pool's comptroller\n * @return markets The pool's markets as VToken array\n */\n function _getAllMarkets(address comptroller) internal view returns (VToken[] memory) {\n return ComptrollerInterface(comptroller).getAllMarkets();\n }\n\n /**\n * @dev Checks if the auction has started\n * @param auction The auction to query the status for\n * @return True if the auction has started\n */\n function _isStarted(Auction storage auction) internal view returns (bool) {\n return auction.status == AuctionStatus.STARTED;\n }\n\n /**\n * @dev Checks if the auction is stale, i.e. there's no bidder and the auction\n * was started more than waitForFirstBidder blocks or seconds ago.\n * @param auction The auction to query the status for\n * @return True if the auction is stale\n */\n function _isStale(Auction storage auction) internal view returns (bool) {\n bool noBidder = auction.highestBidder == address(0);\n return noBidder && (getBlockNumberOrTimestamp() > auction.startBlockOrTimestamp + waitForFirstBidder);\n }\n}\n" + }, + "contracts/Shortfall/ShortfallStorage.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\n\nimport { VToken } from \"../VToken.sol\";\nimport { IRiskFund } from \"../Shortfall/IRiskFund.sol\";\n\n/**\n * @title ShortfallStorage\n * @author Venus\n * @dev Storage for Shortfall\n */\ncontract ShortfallStorage {\n /// @notice Type of auction\n enum AuctionType {\n LARGE_POOL_DEBT,\n LARGE_RISK_FUND\n }\n\n /// @notice Status of auction\n enum AuctionStatus {\n NOT_STARTED,\n STARTED,\n ENDED\n }\n\n /// @notice Auction metadata\n struct Auction {\n /// @notice It holds either the starting block number or timestamp\n uint256 startBlockOrTimestamp;\n AuctionType auctionType;\n AuctionStatus status;\n VToken[] markets;\n uint256 seizedRiskFund;\n address highestBidder;\n uint256 highestBidBps;\n /// @notice It holds either the highestBid block or timestamp\n uint256 highestBidBlockOrTimestamp;\n uint256 startBidBps;\n mapping(VToken => uint256) marketDebt;\n mapping(VToken => uint256) bidAmount;\n }\n\n /// @notice Pool registry address\n address public poolRegistry;\n\n /// @notice Risk fund address\n IRiskFund public riskFund;\n\n /// @notice Minimum USD debt in pool for shortfall to trigger\n uint256 public minimumPoolBadDebt;\n\n /// @notice Incentive to auction participants, initial value set to 1000 or 10%\n uint256 public incentiveBps;\n\n /// @notice Time to wait for next bidder. Initially waits for DEFAULT_NEXT_BIDDER_BLOCK_OR_TIMESTAMP_LIMIT\n uint256 public nextBidderBlockLimit;\n\n /// @notice Boolean of if auctions are paused\n bool public auctionsPaused;\n\n /// @notice Time to wait for first bidder. Initially waits for DEFAULT_WAIT_FOR_FIRST_BIDDER\n uint256 public waitForFirstBidder;\n\n /// @notice Auctions for each pool\n mapping(address => Auction) public auctions;\n\n /**\n * @dev This empty reserved space is put in place to allow future versions to add new\n * variables without shifting down storage in the inheritance chain\n * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\n */\n uint256[42] private __gap;\n}\n" + }, + "contracts/Spoke/SpokeComptroller.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\n\nimport { Ownable2StepUpgradeable } from \"@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol\";\nimport { ResilientOracleInterface } from \"@venusprotocol/oracle/contracts/interfaces/OracleInterface.sol\";\nimport { IDeviationBoundedOracle } from \"@venusprotocol/oracle/contracts/interfaces/IDeviationBoundedOracle.sol\";\nimport { AccessControlledV8 } from \"@venusprotocol/governance-contracts/contracts/Governance/AccessControlledV8.sol\";\n\nimport { Action } from \"../ComptrollerInterface.sol\";\nimport { SpokeComptrollerInterface } from \"./SpokeComptrollerInterface.sol\";\nimport { SpokeComptrollerStorage } from \"./SpokeComptrollerStorage.sol\";\nimport { ExponentialNoError } from \"../ExponentialNoError.sol\";\nimport { VToken } from \"../VToken.sol\";\nimport { RewardsDistributor } from \"../Rewards/RewardsDistributor.sol\";\nimport { MaxLoopsLimitHelper } from \"../MaxLoopsLimitHelper.sol\";\nimport { ensureNonzeroAddress } from \"../lib/validators.sol\";\n\n/// @notice Which per-market risk parameter weights an account's collateral in a liquidity snapshot. Internal to the\n/// implementation: no event, error or external function exposes it, so it is deliberately not part of\n/// `SpokeComptrollerInterface`.\nenum WeightFunction {\n USE_COLLATERAL_FACTOR,\n USE_LIQUIDATION_THRESHOLD\n}\n\n/**\n * @title SpokeComptroller\n * @author Venus\n * @notice The `SpokeComptroller` provides checks for all minting, redeeming, transferring, borrowing, repaying,\n * liquidating and seizing done by the `vToken` contract. It is the comptroller of a single pool and checks those\n * interactions across every market in it: when a user interacts with a market by one of these actions, the market\n * calls a corresponding hook here, which either allows or reverts the transaction. These hooks also update supply and\n * borrow rewards as they are called. The comptroller holds the logic for assessing liquidity snapshots of an account\n * via the collateral factor and liquidation threshold. This check determines the collateral needed for a borrow, as\n * well as how much of a borrow may be liquidated. A user may borrow a portion of their collateral with the maximum\n * amount determined by the market's collateral factor, applied to a collateral value the deviation-bounded oracle\n * caps against the asset's recent price window. However, if their borrowed amount exceeds an amount calculated using\n * the market's corresponding liquidation threshold, the borrow is eligible for liquidation. Liquidations themselves\n * are priced at spot.\n *\n * The `SpokeComptroller` also includes two functions `liquidateAccount()` and `healAccount()`, which are meant to\n * handle accounts that do not exceed the `minLiquidatableCollateral` for the `SpokeComptroller`:\n *\n * - `healAccount()`: This function is called to seize all of a given user's collateral, requiring the `msg.sender`\n * repay a certain percentage of the debt calculated by `maxClearableDebt/borrows`, where `maxClearableDebt` is the\n * sum over the user's collateral markets of `collateralValue/liquidationIncentive`, each market taken at its own\n * incentive. The function can only be called if the calculated percentage does not exceed 100%, because otherwise no\n * `badDebt` would be created and `liquidateAccount()` should be used instead. The difference in the actual amount of\n * debt and debt paid off is recorded as `badDebt` for each market, which can then be auctioned off for the risk\n * reserves of the pool.\n * - `liquidateAccount()`: This function can only be called if the collateral seized will cover all borrows of an\n * account, as well as the liquidation incentive of each collateral market, which is the same condition stated as\n * `borrows < maxClearableDebt`. Otherwise, the pool will incur bad debt, in which case the function `healAccount()`\n * should be used instead. This function skips the logic verifying that the repay amount does not exceed the close\n * factor.\n *\n * The two conditions are complements, so every account below `minLiquidatableCollateral` is served by exactly one of\n * them.\n *\n * @dev Fork of `Comptroller` (`contracts/Comptroller.sol`). It is a separate implementation because `Comptroller` is\n * the one every other pool in this repo shares, here and on other chains, so policy that applies only to a spoke pool\n * does not belong in it: bounded collateral pricing, the supply and liquidation allowlists, and per-market liquidation\n * incentives. Nothing keeps the two in sync automatically: a change to `Comptroller` has to be reviewed and\n * re-applied here by hand, and `diff contracts/Comptroller.sol contracts/Spoke/SpokeComptroller.sol` shows what this\n * fork currently changes.\n */\ncontract SpokeComptroller is\n Ownable2StepUpgradeable,\n AccessControlledV8,\n SpokeComptrollerStorage,\n SpokeComptrollerInterface,\n ExponentialNoError,\n MaxLoopsLimitHelper\n{\n // PoolRegistry, immutable to save on gas\n /// @custom:oz-upgrades-unsafe-allow state-variable-immutable\n address public immutable poolRegistry;\n\n /// @param poolRegistry_ Pool registry address\n /// @custom:oz-upgrades-unsafe-allow constructor\n /// @custom:error ZeroAddressNotAllowed is thrown when pool registry address is zero\n constructor(address poolRegistry_) {\n ensureNonzeroAddress(poolRegistry_);\n\n poolRegistry = poolRegistry_;\n _disableInitializers();\n }\n\n /**\n * @param loopLimit Limit for the loops can iterate to avoid the DOS\n * @param accessControlManager Access control manager contract address\n */\n function initialize(uint256 loopLimit, address accessControlManager) external initializer {\n __Ownable2Step_init();\n __AccessControlled_init_unchained(accessControlManager);\n\n _setMaxLoopsLimit(loopLimit);\n }\n\n /**\n * @notice Add assets to be included in account liquidity calculation; enabling them to be used as collateral\n * @param vTokens The list of addresses of the vToken markets to be enabled\n * @return errors An array of NO_ERROR for compatibility with Venus core tooling\n * @custom:event MarketEntered is emitted for each market on success\n * @custom:error ActionPaused error is thrown if entering any of the markets is paused\n * @custom:error MarketNotListed error is thrown if any of the markets is not listed\n * @custom:access Not restricted\n */\n function enterMarkets(address[] memory vTokens) external override returns (uint256[] memory) {\n uint256 len = vTokens.length;\n\n uint256[] memory results = new uint256[](len);\n for (uint256 i; i < len; ++i) {\n VToken vToken = VToken(vTokens[i]);\n\n _addToMarket(vToken, msg.sender);\n results[i] = NO_ERROR;\n }\n\n return results;\n }\n\n /**\n * @notice Add an asset to another account's liquidity calculation, enabling it as collateral for that account\n * @dev `enterMarkets` reads `msg.sender`, so a router supplying through `VToken.mintBehalf` would enter itself\n * rather than the supplier. This enters the supplier, so both steps fit in one user transaction. Grant the\n * permission only to a router that passes its own caller as `account`.\n * @param vToken The address of the vToken market to be enabled\n * @param account The account to enable the market for\n * @custom:event MarketEntered is emitted on success\n * @custom:error ActionPaused error is thrown if entering the market is paused\n * @custom:error MarketNotListed error is thrown if the market is not listed\n * @custom:error ZeroAddressNotAllowed is thrown when the account address is zero\n * @custom:access Controlled by AccessControlManager\n */\n function enterMarketBehalf(address vToken, address account) external {\n _checkAccessAllowed(\"enterMarketBehalf(address,address)\");\n ensureNonzeroAddress(account);\n\n _addToMarket(VToken(vToken), account);\n }\n\n /**\n * @notice Unlist a market by setting isListed to false\n * @dev Checks if all actions are paused, borrow/supply caps is set to 0 and collateral factor is to 0.\n * @param market The address of the market (token) to unlist\n * @return uint256 Always NO_ERROR for compatibility with Venus core tooling\n * @custom:event MarketUnlisted is emitted on success\n * @custom:error MarketNotListed error is thrown when the market is not listed\n * @custom:error BorrowActionNotPaused error is thrown if borrow action is not paused\n * @custom:error MintActionNotPaused error is thrown if mint action is not paused\n * @custom:error RedeemActionNotPaused error is thrown if redeem action is not paused\n * @custom:error RepayActionNotPaused error is thrown if repay action is not paused\n * @custom:error EnterMarketActionNotPaused error is thrown if enter market action is not paused\n * @custom:error LiquidateActionNotPaused error is thrown if liquidate action is not paused\n * @custom:error BorrowCapIsNotZero error is thrown if borrow cap is not zero\n * @custom:error SupplyCapIsNotZero error is thrown if supply cap is not zero\n * @custom:error CollateralFactorIsNotZero error is thrown if collateral factor is not zero\n */\n function unlistMarket(address market) external returns (uint256) {\n _checkAccessAllowed(\"unlistMarket(address)\");\n\n Market storage _market = markets[market];\n\n if (!_market.isListed) {\n revert MarketNotListed(market);\n }\n\n if (!actionPaused(market, Action.BORROW)) {\n revert BorrowActionNotPaused();\n }\n\n if (!actionPaused(market, Action.MINT)) {\n revert MintActionNotPaused();\n }\n\n if (!actionPaused(market, Action.REDEEM)) {\n revert RedeemActionNotPaused();\n }\n\n if (!actionPaused(market, Action.REPAY)) {\n revert RepayActionNotPaused();\n }\n\n if (!actionPaused(market, Action.SEIZE)) {\n revert SeizeActionNotPaused();\n }\n\n if (!actionPaused(market, Action.ENTER_MARKET)) {\n revert EnterMarketActionNotPaused();\n }\n\n if (!actionPaused(market, Action.LIQUIDATE)) {\n revert LiquidateActionNotPaused();\n }\n\n if (!actionPaused(market, Action.TRANSFER)) {\n revert TransferActionNotPaused();\n }\n\n if (!actionPaused(market, Action.EXIT_MARKET)) {\n revert ExitMarketActionNotPaused();\n }\n\n if (borrowCaps[market] != 0) {\n revert BorrowCapIsNotZero();\n }\n\n if (supplyCaps[market] != 0) {\n revert SupplyCapIsNotZero();\n }\n\n if (_market.collateralFactorMantissa != 0) {\n revert CollateralFactorIsNotZero();\n }\n\n _market.isListed = false;\n emit MarketUnlisted(market);\n\n return NO_ERROR;\n }\n\n /**\n * @notice Grants or revokes the borrowing or redeeming delegate rights to / from an account\n * If allowed, the delegate will be able to borrow funds on behalf of the sender\n * Upon a delegated borrow, the delegate will receive the funds, and the borrower\n * will see the debt on their account\n * Upon a delegated redeem, the delegate will receive the redeemed amount and the approver\n * will see a deduction in his vToken balance\n * @param delegate The address to update the rights for\n * @param approved Whether to grant (true) or revoke (false) the borrowing or redeeming rights\n * @custom:event DelegateUpdated emits on success\n * @custom:error ZeroAddressNotAllowed is thrown when delegate address is zero\n * @custom:error DelegationStatusUnchanged is thrown if approval status is already set to the requested value\n * @custom:access Not restricted\n */\n function updateDelegate(address delegate, bool approved) external {\n ensureNonzeroAddress(delegate);\n if (approvedDelegates[msg.sender][delegate] == approved) {\n revert DelegationStatusUnchanged();\n }\n\n approvedDelegates[msg.sender][delegate] = approved;\n emit DelegateUpdated(msg.sender, delegate, approved);\n }\n\n /**\n * @notice Removes asset from sender's account liquidity calculation; disabling them as collateral\n * @dev Sender must not have an outstanding borrow balance in the asset,\n * or be providing necessary collateral for an outstanding borrow.\n * @param vTokenAddress The address of the asset to be removed\n * @return error Always NO_ERROR for compatibility with Venus core tooling\n * @custom:event MarketExited is emitted on success\n * @custom:error ActionPaused error is thrown if exiting the market is paused\n * @custom:error NonzeroBorrowBalance error is thrown if the user has an outstanding borrow in this market\n * @custom:error MarketNotListed error is thrown when the market is not listed\n * @custom:error InsufficientLiquidity error is thrown if exiting the market would lead to user's insolvency\n * @custom:error SnapshotError is thrown if some vToken fails to return the account's supply and borrows\n * @custom:error PriceError is thrown if the oracle returns an incorrect price for some asset\n * @custom:access Not restricted\n */\n function exitMarket(address vTokenAddress) external override returns (uint256) {\n _checkActionPauseState(vTokenAddress, Action.EXIT_MARKET);\n VToken vToken = VToken(vTokenAddress);\n /* Get sender tokensHeld and amountOwed underlying from the vToken */\n (uint256 tokensHeld, uint256 amountOwed, ) = _safeGetAccountSnapshot(vToken, msg.sender);\n\n /* Fail if the sender has a borrow balance */\n if (amountOwed != 0) {\n revert NonzeroBorrowBalance();\n }\n\n /* Fail if the sender is not permitted to redeem all of their tokens */\n _checkRedeemAllowed(vTokenAddress, msg.sender, tokensHeld);\n\n Market storage marketToExit = markets[address(vToken)];\n\n /* Return true if the sender is not already ‘in’ the market */\n if (!marketToExit.accountMembership[msg.sender]) {\n return NO_ERROR;\n }\n\n /* Set vToken account membership to false */\n delete marketToExit.accountMembership[msg.sender];\n\n /* Delete vToken from the account’s list of assets */\n // load into memory for faster iteration\n VToken[] memory userAssetList = accountAssets[msg.sender];\n uint256 len = userAssetList.length;\n\n uint256 assetIndex = len;\n for (uint256 i; i < len; ++i) {\n if (userAssetList[i] == vToken) {\n assetIndex = i;\n break;\n }\n }\n\n // We *must* have found the asset in the list or our redundant data structure is broken\n assert(assetIndex < len);\n\n // copy last item in list to location of item to be removed, reduce length by 1\n VToken[] storage storedList = accountAssets[msg.sender];\n storedList[assetIndex] = storedList[storedList.length - 1];\n storedList.pop();\n\n emit MarketExited(vToken, msg.sender);\n\n return NO_ERROR;\n }\n\n /*** Policy Hooks ***/\n\n /**\n * @notice Checks if the account should be allowed to mint tokens in the given market\n * @param vToken The market to verify the mint against\n * @param minter The account which would get the minted tokens\n * @param mintAmount The amount of underlying being supplied to the market in exchange for tokens\n * @custom:error ActionPaused error is thrown if supplying to this market is paused\n * @custom:error MarketNotListed error is thrown when the market is not listed\n * @custom:error SupplyNotAllowed error is thrown if the market's supply allowlist is enabled and the minter is\n * not on it\n * @custom:error SupplyCapExceeded error is thrown if the total supply exceeds the cap after minting\n * @custom:access Not restricted\n */\n function preMintHook(address vToken, address minter, uint256 mintAmount) external override {\n _checkActionPauseState(vToken, Action.MINT);\n\n if (!markets[vToken].isListed) {\n revert MarketNotListed(address(vToken));\n }\n\n // `minter` is the account credited with the newly minted vTokens, not necessarily the account paying for\n // them: `mintBehalf` lets a third party fund a mint attributed to someone else. Metering the recipient is\n // what bounds the market's supply, so the payer is deliberately not checked.\n if (isSupplyAllowlistEnabled[vToken] && !isAllowedSupplier[vToken][minter]) {\n revert SupplyNotAllowed(vToken, minter);\n }\n\n uint256 supplyCap = supplyCaps[vToken];\n // Skipping the cap check for uncapped coins to save some gas\n if (supplyCap != type(uint256).max) {\n uint256 vTokenSupply = VToken(vToken).totalSupply();\n Exp memory exchangeRate = Exp({ mantissa: VToken(vToken).exchangeRateStored() });\n uint256 nextTotalSupply = mul_ScalarTruncateAddUInt(exchangeRate, vTokenSupply, mintAmount);\n if (nextTotalSupply > supplyCap) {\n revert SupplyCapExceeded(vToken, supplyCap);\n }\n }\n\n // Keep the flywheel moving\n uint256 rewardDistributorsCount = rewardsDistributors.length;\n\n for (uint256 i; i < rewardDistributorsCount; ++i) {\n RewardsDistributor rewardsDistributor = rewardsDistributors[i];\n rewardsDistributor.updateRewardTokenSupplyIndex(vToken);\n rewardsDistributor.distributeSupplierRewardToken(vToken, minter);\n }\n }\n\n /**\n * @notice Checks if the account should be allowed to redeem tokens in the given market\n * @param vToken The market to verify the redeem against\n * @param redeemer The account which would redeem the tokens\n * @param redeemTokens The number of vTokens to exchange for the underlying asset in the market\n * @custom:error ActionPaused error is thrown if withdrawals are paused in this market\n * @custom:error MarketNotListed error is thrown when the market is not listed\n * @custom:error InsufficientLiquidity error is thrown if the withdrawal would lead to user's insolvency\n * @custom:error SnapshotError is thrown if some vToken fails to return the account's supply and borrows\n * @custom:error PriceError is thrown if the oracle returns an incorrect price for some asset\n * @custom:access Not restricted\n */\n function preRedeemHook(address vToken, address redeemer, uint256 redeemTokens) external override {\n _checkActionPauseState(vToken, Action.REDEEM);\n\n _checkRedeemAllowed(vToken, redeemer, redeemTokens);\n\n // Keep the flywheel moving\n uint256 rewardDistributorsCount = rewardsDistributors.length;\n\n for (uint256 i; i < rewardDistributorsCount; ++i) {\n RewardsDistributor rewardsDistributor = rewardsDistributors[i];\n rewardsDistributor.updateRewardTokenSupplyIndex(vToken);\n rewardsDistributor.distributeSupplierRewardToken(vToken, redeemer);\n }\n }\n\n /**\n * @notice Checks if the account should be allowed to borrow the underlying asset of the given market\n * @param vToken The market to verify the borrow against\n * @param borrower The account which would borrow the asset\n * @param borrowAmount The amount of underlying the account would borrow\n * @custom:error ActionPaused error is thrown if borrowing is paused in this market\n * @custom:error MarketNotListed error is thrown when the market is not listed\n * @custom:error InsufficientLiquidity error is thrown if there is not enough collateral to borrow\n * @custom:error BorrowCapExceeded is thrown if the borrow cap will be exceeded should this borrow succeed\n * @custom:error SnapshotError is thrown if some vToken fails to return the account's supply and borrows\n * @custom:error PriceError is thrown if the oracle returns an incorrect price for some asset\n * @custom:access Not restricted if vToken is enabled as collateral, otherwise only vToken\n */\n /// disable-eslint\n function preBorrowHook(address vToken, address borrower, uint256 borrowAmount) external override {\n _checkActionPauseState(vToken, Action.BORROW);\n\n if (!markets[vToken].isListed) {\n revert MarketNotListed(address(vToken));\n }\n\n if (!markets[vToken].accountMembership[borrower]) {\n // only vTokens may call borrowAllowed if borrower not in market\n _checkSenderIs(vToken);\n\n // attempt to add borrower to the market or revert\n _addToMarket(VToken(msg.sender), borrower);\n }\n\n // Update the prices of tokens. Resolved once here, after the membership change above, and reused for both\n // updates.\n VToken[] memory borrowerAssets = getAssetsIn(borrower);\n _updatePrices(borrowerAssets);\n _updateProtectionStates(borrowerAssets);\n\n if (oracle.getUnderlyingPrice(vToken) == 0) {\n revert PriceError(address(vToken));\n }\n\n uint256 borrowCap = borrowCaps[vToken];\n // Skipping the cap check for uncapped coins to save some gas\n if (borrowCap != type(uint256).max) {\n uint256 totalBorrows = VToken(vToken).totalBorrows();\n uint256 badDebt = VToken(vToken).badDebt();\n uint256 nextTotalBorrows = totalBorrows + borrowAmount + badDebt;\n if (nextTotalBorrows > borrowCap) {\n revert BorrowCapExceeded(vToken, borrowCap);\n }\n }\n\n AccountLiquiditySnapshot memory snapshot = _getHypotheticalLiquiditySnapshot(\n borrower,\n VToken(vToken),\n 0,\n borrowAmount,\n WeightFunction.USE_COLLATERAL_FACTOR\n );\n\n if (snapshot.shortfall > 0) {\n revert InsufficientLiquidity();\n }\n\n Exp memory borrowIndex = Exp({ mantissa: VToken(vToken).borrowIndex() });\n\n // Keep the flywheel moving\n uint256 rewardDistributorsCount = rewardsDistributors.length;\n\n for (uint256 i; i < rewardDistributorsCount; ++i) {\n RewardsDistributor rewardsDistributor = rewardsDistributors[i];\n rewardsDistributor.updateRewardTokenBorrowIndex(vToken, borrowIndex);\n rewardsDistributor.distributeBorrowerRewardToken(vToken, borrower, borrowIndex);\n }\n }\n\n /**\n * @notice Checks if the account should be allowed to repay a borrow in the given market\n * @param vToken The market to verify the repay against\n * @param borrower The account which would borrowed the asset\n * @custom:error ActionPaused error is thrown if repayments are paused in this market\n * @custom:error MarketNotListed error is thrown when the market is not listed\n * @custom:access Not restricted\n */\n function preRepayHook(address vToken, address borrower) external override {\n _checkActionPauseState(vToken, Action.REPAY);\n\n oracle.updatePrice(vToken);\n\n if (!markets[vToken].isListed) {\n revert MarketNotListed(address(vToken));\n }\n\n // Keep the flywheel moving\n uint256 rewardDistributorsCount = rewardsDistributors.length;\n\n for (uint256 i; i < rewardDistributorsCount; ++i) {\n Exp memory borrowIndex = Exp({ mantissa: VToken(vToken).borrowIndex() });\n RewardsDistributor rewardsDistributor = rewardsDistributors[i];\n rewardsDistributor.updateRewardTokenBorrowIndex(vToken, borrowIndex);\n rewardsDistributor.distributeBorrowerRewardToken(vToken, borrower, borrowIndex);\n }\n }\n\n /**\n * @notice Checks if the liquidation should be allowed to occur\n * @param vTokenBorrowed Asset which was borrowed by the borrower\n * @param vTokenCollateral Asset which was used as collateral and will be seized\n * @param borrower The address of the borrower\n * @param repayAmount The amount of underlying being repaid\n * @param skipLiquidityCheck Allows the borrow to be liquidated regardless of the account liquidity\n * @custom:error ActionPaused error is thrown if liquidations are paused in this market\n * @custom:error MarketNotListed error is thrown if either collateral or borrowed token is not listed\n * @custom:error TooMuchRepay error is thrown if the liquidator is trying to repay more than allowed by close factor\n * @custom:error MinimalCollateralViolated is thrown if the users' total collateral is lower than the threshold for non-batch liquidations\n * @custom:error InsufficientShortfall is thrown when trying to liquidate a healthy account\n * @custom:error SnapshotError is thrown if some vToken fails to return the account's supply and borrows\n * @custom:error PriceError is thrown if the oracle returns an incorrect price for some asset\n */\n function preLiquidateHook(\n address vTokenBorrowed,\n address vTokenCollateral,\n address borrower,\n uint256 repayAmount,\n bool skipLiquidityCheck\n ) external override {\n // Pause Action.LIQUIDATE on BORROWED TOKEN to prevent liquidating it.\n // If we want to pause liquidating to vTokenCollateral, we should pause\n // Action.SEIZE on it\n _checkActionPauseState(vTokenBorrowed, Action.LIQUIDATE);\n\n // Update the prices of tokens\n updatePrices(borrower);\n\n if (!markets[vTokenBorrowed].isListed) {\n revert MarketNotListed(address(vTokenBorrowed));\n }\n if (!markets[vTokenCollateral].isListed) {\n revert MarketNotListed(address(vTokenCollateral));\n }\n\n uint256 borrowBalance = VToken(vTokenBorrowed).borrowBalanceStored(borrower);\n\n /* Allow accounts to be liquidated if it is a forced liquidation */\n if (skipLiquidityCheck || isForcedLiquidationEnabled[vTokenBorrowed]) {\n if (repayAmount > borrowBalance) {\n revert TooMuchRepay();\n }\n return;\n }\n\n /* The borrower must have shortfall and collateral > threshold in order to be liquidatable */\n AccountLiquiditySnapshot memory snapshot = _getCurrentLiquiditySnapshot(\n borrower,\n WeightFunction.USE_LIQUIDATION_THRESHOLD\n );\n\n if (snapshot.totalCollateral <= minLiquidatableCollateral) {\n /* The liquidator should use either liquidateAccount or healAccount */\n revert MinimalCollateralViolated(minLiquidatableCollateral, snapshot.totalCollateral);\n }\n\n if (snapshot.shortfall == 0) {\n revert InsufficientShortfall();\n }\n\n /* The liquidator may not repay more than what is allowed by the closeFactor */\n uint256 maxClose = mul_ScalarTruncate(Exp({ mantissa: closeFactorMantissa }), borrowBalance);\n if (repayAmount > maxClose) {\n revert TooMuchRepay();\n }\n }\n\n /**\n * @notice Checks if the seizing of assets should be allowed to occur\n * @param vTokenCollateral Asset which was used as collateral and will be seized\n * @param seizerContract Contract that tries to seize the asset (either borrowed vToken or Comptroller)\n * @param liquidator The address repaying the borrow and seizing the collateral\n * @param borrower The address of the borrower\n * @custom:error ActionPaused error is thrown if seizing this type of collateral is paused\n * @custom:error MarketNotListed error is thrown if either collateral or borrowed token is not listed\n * @custom:error ComptrollerMismatch error is when seizer contract or seized asset belong to different pools\n * @custom:error LiquidationNotAllowed is thrown if the liquidation allowlist is enabled and the liquidator is not\n * on it\n * @custom:access Not restricted while the liquidation allowlist is disabled, otherwise the liquidator has to be\n * on it\n */\n function preSeizeHook(\n address vTokenCollateral,\n address seizerContract,\n address liquidator,\n address borrower\n ) external override {\n // Pause Action.SEIZE on COLLATERAL to prevent seizing it.\n // If we want to pause liquidating vTokenBorrowed, we should pause\n // Action.LIQUIDATE on it\n _checkActionPauseState(vTokenCollateral, Action.SEIZE);\n\n Market storage market = markets[vTokenCollateral];\n\n if (!market.isListed) {\n revert MarketNotListed(vTokenCollateral);\n }\n\n if (seizerContract == address(this)) {\n // If Comptroller is the seizer, just check if collateral's comptroller\n // is equal to the current address\n if (address(VToken(vTokenCollateral).comptroller()) != address(this)) {\n revert ComptrollerMismatch();\n }\n } else {\n // If the seizer is not the Comptroller, check that the seizer is a\n // listed market, and that the markets' comptrollers match\n if (!markets[seizerContract].isListed) {\n revert MarketNotListed(seizerContract);\n }\n if (VToken(vTokenCollateral).comptroller() != VToken(seizerContract).comptroller()) {\n revert ComptrollerMismatch();\n }\n }\n\n if (!market.accountMembership[borrower]) {\n revert MarketNotCollateral(vTokenCollateral, borrower);\n }\n\n // Every seizure reaches this hook with the account that receives the collateral, whichever entry point it\n // came from, so this is the check that actually enforces the allowlist.\n _checkLiquidatorAllowlisted(liquidator);\n\n // Keep the flywheel moving\n uint256 rewardDistributorsCount = rewardsDistributors.length;\n\n for (uint256 i; i < rewardDistributorsCount; ++i) {\n RewardsDistributor rewardsDistributor = rewardsDistributors[i];\n rewardsDistributor.updateRewardTokenSupplyIndex(vTokenCollateral);\n rewardsDistributor.distributeSupplierRewardToken(vTokenCollateral, borrower);\n rewardsDistributor.distributeSupplierRewardToken(vTokenCollateral, liquidator);\n }\n }\n\n /**\n * @notice Checks if the account should be allowed to transfer tokens in the given market\n * @param vToken The market to verify the transfer against\n * @param src The account which sources the tokens\n * @param dst The account which receives the tokens\n * @param transferTokens The number of vTokens to transfer\n * @custom:error ActionPaused error is thrown if withdrawals are paused in this market\n * @custom:error MarketNotListed error is thrown when the market is not listed\n * @custom:error InsufficientLiquidity error is thrown if the withdrawal would lead to user's insolvency\n * @custom:error SnapshotError is thrown if some vToken fails to return the account's supply and borrows\n * @custom:error PriceError is thrown if the oracle returns an incorrect price for some asset\n * @custom:access Not restricted\n */\n function preTransferHook(address vToken, address src, address dst, uint256 transferTokens) external override {\n _checkActionPauseState(vToken, Action.TRANSFER);\n\n // Currently the only consideration is whether or not\n // the src is allowed to redeem this many tokens\n _checkRedeemAllowed(vToken, src, transferTokens);\n\n // Keep the flywheel moving\n uint256 rewardDistributorsCount = rewardsDistributors.length;\n\n for (uint256 i; i < rewardDistributorsCount; ++i) {\n RewardsDistributor rewardsDistributor = rewardsDistributors[i];\n rewardsDistributor.updateRewardTokenSupplyIndex(vToken);\n rewardsDistributor.distributeSupplierRewardToken(vToken, src);\n rewardsDistributor.distributeSupplierRewardToken(vToken, dst);\n }\n }\n\n /*** Post-action Hooks ***/\n\n // The vToken calls one of the seven functions below as the last step of every successful mint, redeem, borrow,\n // repayment, liquidation, seizure and transfer. All of them are no-ops here, and none of them can be dropped:\n // `ComptrollerInterface` declares all seven, so omitting one leaves this contract abstract, and the vToken calls\n // each of them with a plain external call against a comptroller that has no fallback, so a missing function would\n // revert the operation it belongs to. They are unrestricted, which is harmless because they do nothing, and their\n // parameters are unnamed because the bodies are empty.\n // solhint-disable no-empty-blocks\n\n /// @notice Called by the vToken once a mint has succeeded. No-op, see the note above.\n function mintVerify(address, address, uint256, uint256) external {}\n\n /// @notice Called by the vToken once a redeem has succeeded. No-op, see the note above.\n function redeemVerify(address, address, uint256, uint256) external {}\n\n /// @notice Called by the vToken once a borrow has succeeded. No-op, see the note above.\n function borrowVerify(address, address, uint256) external {}\n\n /// @notice Called by the vToken once a repayment has succeeded. No-op, see the note above.\n function repayBorrowVerify(address, address, address, uint256, uint256) external {}\n\n /// @notice Called by the vToken once a liquidation has succeeded. No-op, see the note above.\n function liquidateBorrowVerify(address, address, address, address, uint256, uint256) external {}\n\n /// @notice Called by the vToken once a seizure has succeeded. No-op, see the note above.\n function seizeVerify(address, address, address, address, uint256) external {}\n\n /// @notice Called by the vToken once a transfer has succeeded. No-op, see the note above.\n function transferVerify(address, address, address, uint256) external {}\n\n // solhint-enable no-empty-blocks\n\n /*** Pool-level operations ***/\n\n /**\n * @notice Seizes all the remaining collateral, makes msg.sender repay the existing\n * borrows, and treats the rest of the debt as bad debt (for each market).\n * The sender has to repay a certain percentage of the debt, computed as `maxClearableDebt / borrows`: see the\n * note on `AccountLiquiditySnapshot.maxClearableDebt`.\n * @param user account to heal\n * @custom:error LiquidationNotAllowed is thrown if the liquidation allowlist is enabled and the caller is not on it\n * @custom:error CollateralExceedsThreshold error is thrown when the collateral is too big for healing\n * @custom:error CollateralCoversDebt is thrown when the collateral can clear the whole debt, which leaves nothing\n * to heal\n * @custom:error SnapshotError is thrown if some vToken fails to return the account's supply and borrows\n * @custom:error PriceError is thrown if the oracle returns an incorrect price for some asset\n * @custom:access Not restricted while the liquidation allowlist is disabled, otherwise restricted to the accounts\n * on it\n */\n function healAccount(address user) external {\n // Checked here as well as in `preSeizeHook`, because a borrower holding no vTokens at all takes the branch\n // below that only calls `healBorrow`, which reaches no hook carrying the caller. Without this the whole\n // remaining principal could be moved into bad debt by anyone, at no cost.\n _checkLiquidatorAllowlisted(msg.sender);\n\n VToken[] memory userAssets = getAssetsIn(user);\n uint256 userAssetsCount = userAssets.length;\n\n address liquidator = msg.sender;\n\n // We need all user's markets to be fresh for the computations to be correct\n _refreshMarkets(userAssets);\n\n AccountLiquiditySnapshot memory snapshot = _getCurrentLiquiditySnapshot(\n user,\n WeightFunction.USE_LIQUIDATION_THRESHOLD\n );\n\n if (snapshot.totalCollateral > minLiquidatableCollateral) {\n revert CollateralExceedsThreshold(minLiquidatableCollateral, snapshot.totalCollateral);\n }\n\n if (snapshot.shortfall == 0) {\n revert InsufficientShortfall();\n }\n\n // The collateral covers the whole debt at every market's own incentive, so healing would forgive nothing and\n // the account should go through `liquidateAccount` instead.\n if (snapshot.maxClearableDebt > snapshot.borrows) {\n revert CollateralCoversDebt(snapshot.borrows, snapshot.maxClearableDebt);\n }\n\n // percentage = maxClearableDebt / borrows. One blended share applies to every borrow, so what the caller\n // pays in total is the sum over the collateral markets of each market's value at its own liquidation\n // incentive. The discount is exact in aggregate; it is not attributed per piece of collateral.\n Exp memory percentage = div_(Exp({ mantissa: snapshot.maxClearableDebt }), Exp({ mantissa: snapshot.borrows }));\n\n for (uint256 i; i < userAssetsCount; ++i) {\n VToken market = userAssets[i];\n\n (uint256 tokens, uint256 borrowBalance, ) = _safeGetAccountSnapshot(market, user);\n uint256 repaymentAmount = mul_ScalarTruncate(percentage, borrowBalance);\n\n // Seize the entire collateral\n if (tokens != 0) {\n market.seize(liquidator, user, tokens);\n }\n // Repay a certain percentage of the borrow, forgive the rest\n if (borrowBalance != 0) {\n market.healBorrow(liquidator, user, repaymentAmount);\n }\n }\n }\n\n /**\n * @notice Liquidates all borrows of the borrower. Callable only if the collateral is less than\n * a predefined threshold, and the account collateral can be seized to cover all borrows. If\n * the collateral is higher than the threshold, use regular liquidations. If the collateral is\n * below the threshold, and the account is insolvent, use healAccount.\n * @param borrower the borrower address\n * @param orders an array of liquidation orders\n * @custom:error LiquidationNotAllowed is thrown by `preSeizeHook`, while seizing for the first order, if the\n * liquidation allowlist is enabled and the caller is not on it\n * @custom:error CollateralExceedsThreshold error is thrown when the collateral is too big for a batch liquidation\n * @custom:error DebtExceedsClearableAmount is thrown when the collateral cannot clear the whole debt, which\n * means the account has to go through `healAccount`\n * @custom:error NonzeroBorrowBalanceAfterLiquidation is thrown if the orders do not clear every borrow\n * @custom:error SnapshotError is thrown if some vToken fails to return the account's supply and borrows\n * @custom:error PriceError is thrown if the oracle returns an incorrect price for some asset\n * @custom:access Not restricted while the liquidation allowlist is disabled, otherwise restricted to the accounts\n * on it\n */\n function liquidateAccount(address borrower, LiquidationOrder[] calldata orders) external {\n // No entry check on the liquidation allowlist here, unlike `healAccount`: every order ends in a seizure and\n // `preSeizeHook` carries the caller, so the allowlist is enforced there.\n\n VToken[] memory borrowerAssets = getAssetsIn(borrower);\n\n // We need all borrower's markets to be fresh for the computations to be correct. The orders below run with\n // the liquidity check skipped, so this snapshot is the only thing standing between a solvent account and a\n // full liquidation, and it decides on the same state the orders will execute against.\n _refreshMarkets(borrowerAssets);\n\n AccountLiquiditySnapshot memory snapshot = _getCurrentLiquiditySnapshot(\n borrower,\n WeightFunction.USE_LIQUIDATION_THRESHOLD\n );\n\n if (snapshot.totalCollateral > minLiquidatableCollateral) {\n // You should use the regular vToken.liquidateBorrow(...) call\n revert CollateralExceedsThreshold(minLiquidatableCollateral, snapshot.totalCollateral);\n }\n\n if (snapshot.borrows >= snapshot.maxClearableDebt) {\n // There is not enough collateral to seize. Use healAccount to repay some part of the borrow\n // and record bad debt.\n revert DebtExceedsClearableAmount(snapshot.borrows, snapshot.maxClearableDebt);\n }\n\n if (snapshot.shortfall == 0) {\n revert InsufficientShortfall();\n }\n\n uint256 ordersCount = orders.length;\n\n _ensureMaxLoops(ordersCount / 2);\n\n for (uint256 i; i < ordersCount; ++i) {\n if (!markets[address(orders[i].vTokenBorrowed)].isListed) {\n revert MarketNotListed(address(orders[i].vTokenBorrowed));\n }\n if (!markets[address(orders[i].vTokenCollateral)].isListed) {\n revert MarketNotListed(address(orders[i].vTokenCollateral));\n }\n\n LiquidationOrder calldata order = orders[i];\n order.vTokenBorrowed.forceLiquidateBorrow(\n msg.sender,\n borrower,\n order.repayAmount,\n order.vTokenCollateral,\n true\n );\n }\n\n uint256 marketsCount = borrowerAssets.length;\n\n for (uint256 i; i < marketsCount; ++i) {\n (, uint256 borrowBalance, ) = _safeGetAccountSnapshot(borrowerAssets[i], borrower);\n if (borrowBalance != 0) {\n revert NonzeroBorrowBalanceAfterLiquidation();\n }\n }\n }\n\n /**\n * @notice Sets the closeFactor to use when liquidating borrows\n * @param newCloseFactorMantissa New close factor, scaled by 1e18\n * @custom:event Emits NewCloseFactor on success\n * @custom:error InvalidCloseFactor is thrown if the new close factor is outside the allowed bounds\n * @custom:access Controlled by AccessControlManager\n */\n function setCloseFactor(uint256 newCloseFactorMantissa) external {\n _checkAccessAllowed(\"setCloseFactor(uint256)\");\n if (newCloseFactorMantissa > MAX_CLOSE_FACTOR_MANTISSA) {\n revert InvalidCloseFactor();\n }\n\n if (newCloseFactorMantissa < MIN_CLOSE_FACTOR_MANTISSA) {\n revert InvalidCloseFactor();\n }\n\n uint256 oldCloseFactorMantissa = closeFactorMantissa;\n closeFactorMantissa = newCloseFactorMantissa;\n emit NewCloseFactor(oldCloseFactorMantissa, newCloseFactorMantissa);\n }\n\n /**\n * @notice Sets the collateralFactor for a market\n * @dev This function is restricted by the AccessControlManager\n * @param vToken The market to set the factor on\n * @param newCollateralFactorMantissa The new collateral factor, scaled by 1e18\n * @param newLiquidationThresholdMantissa The new liquidation threshold, scaled by 1e18\n * @custom:event Emits NewCollateralFactor when collateral factor is updated\n * and NewLiquidationThreshold when liquidation threshold is updated\n * @custom:error MarketNotListed error is thrown when the market is not listed\n * @custom:error InvalidCollateralFactor error is thrown when collateral factor is too high\n * @custom:error InvalidLiquidationThreshold error is thrown when liquidation threshold is lower than collateral factor\n * @custom:error PriceError is thrown when the oracle returns an invalid price for the asset\n * @custom:access Controlled by AccessControlManager\n */\n function setCollateralFactor(\n VToken vToken,\n uint256 newCollateralFactorMantissa,\n uint256 newLiquidationThresholdMantissa\n ) external {\n _checkAccessAllowed(\"setCollateralFactor(address,uint256,uint256)\");\n\n // Verify market is listed\n Market storage market = markets[address(vToken)];\n if (!market.isListed) {\n revert MarketNotListed(address(vToken));\n }\n\n // Check collateral factor <= 0.9\n if (newCollateralFactorMantissa > MAX_COLLATERAL_FACTOR_MANTISSA) {\n revert InvalidCollateralFactor();\n }\n\n // Ensure that liquidation threshold <= 1\n if (newLiquidationThresholdMantissa > MANTISSA_ONE) {\n revert InvalidLiquidationThreshold();\n }\n\n // Ensure that liquidation threshold >= CF\n if (newLiquidationThresholdMantissa < newCollateralFactorMantissa) {\n revert InvalidLiquidationThreshold();\n }\n\n // If collateral factor != 0, fail if price == 0\n if (newCollateralFactorMantissa != 0 && oracle.getUnderlyingPrice(address(vToken)) == 0) {\n revert PriceError(address(vToken));\n }\n\n uint256 oldCollateralFactorMantissa = market.collateralFactorMantissa;\n if (newCollateralFactorMantissa != oldCollateralFactorMantissa) {\n market.collateralFactorMantissa = newCollateralFactorMantissa;\n emit NewCollateralFactor(vToken, oldCollateralFactorMantissa, newCollateralFactorMantissa);\n }\n\n uint256 oldLiquidationThresholdMantissa = market.liquidationThresholdMantissa;\n if (newLiquidationThresholdMantissa != oldLiquidationThresholdMantissa) {\n market.liquidationThresholdMantissa = newLiquidationThresholdMantissa;\n emit NewLiquidationThreshold(vToken, oldLiquidationThresholdMantissa, newLiquidationThresholdMantissa);\n }\n }\n\n /**\n * @notice Sets the liquidation incentive applied to any market that has no incentive of its own\n * @dev This function is restricted by the AccessControlManager\n * @dev `PoolRegistry.addPool` calls this while registering the pool, so the value clears the floor below by the\n * time any market can be listed.\n *\n * This value has to stay at or above `1e18 + protocolSeizeShareMantissa` of every market that has no incentive of\n * its own, or a liquidator of that market's collateral receives less than the debt it repaid. The shares of those\n * markets are only readable market by market, so what is enforced here is the floor for a market at the default\n * share: `MIN_POOL_LIQUIDATION_INCENTIVE_MANTISSA`. That covers a freshly listed market, which is the case\n * nothing else was watching, and it is where this fork parts with upstream `Comptroller` - which stops at 1e18\n * and would let a pool be registered one that pays every default-share market's liquidator less than it repaid.\n * A market whose share is raised above the default still needs an incentive of its own;\n * `setMarketLiquidationIncentive` bounds that from one side and `VToken.setProtocolSeizeShare` from the other.\n * @param newLiquidationIncentiveMantissa New liquidationIncentive scaled by 1e18\n * @custom:event Emits NewLiquidationIncentive on success\n * @custom:error InvalidLiquidationIncentive is thrown if the new incentive is below\n * `MIN_POOL_LIQUIDATION_INCENTIVE_MANTISSA`\n * @custom:access Controlled by AccessControlManager\n */\n function setLiquidationIncentive(uint256 newLiquidationIncentiveMantissa) external {\n _checkAccessAllowed(\"setLiquidationIncentive(uint256)\");\n\n // Upstream `Comptroller` stops at 1e18 and rejects with a revert string. Raised to the floor a default-share\n // market needs, and reduced to the custom error the per-market setter uses, so that the same condition\n // reports the same way from both.\n if (newLiquidationIncentiveMantissa < MIN_POOL_LIQUIDATION_INCENTIVE_MANTISSA) {\n revert InvalidLiquidationIncentive();\n }\n\n // Save current value for use in log\n uint256 oldLiquidationIncentiveMantissa = _poolLiquidationIncentiveMantissa;\n\n // Set liquidation incentive to new incentive\n _poolLiquidationIncentiveMantissa = newLiquidationIncentiveMantissa;\n\n // Emit event with old incentive, new incentive\n emit NewLiquidationIncentive(oldLiquidationIncentiveMantissa, newLiquidationIncentiveMantissa);\n }\n\n /**\n * @notice Add the market to the markets mapping and set it as listed\n * @dev Only callable by the PoolRegistry\n * @param vToken The address of the market (token) to list\n * @custom:error MarketAlreadyListed is thrown if the market is already listed in this pool\n * @custom:error InvalidVToken is thrown if the market does not identify itself as a VToken\n * @custom:access Only PoolRegistry\n */\n function supportMarket(VToken vToken) external {\n _checkSenderIs(poolRegistry);\n\n if (markets[address(vToken)].isListed) {\n revert MarketAlreadyListed(address(vToken));\n }\n\n // Sanity check to make sure its really a VToken\n if (!vToken.isVToken()) {\n revert InvalidVToken();\n }\n\n Market storage newMarket = markets[address(vToken)];\n newMarket.isListed = true;\n newMarket.collateralFactorMantissa = 0;\n newMarket.liquidationThresholdMantissa = 0;\n\n _addMarket(address(vToken));\n\n uint256 rewardDistributorsCount = rewardsDistributors.length;\n\n for (uint256 i; i < rewardDistributorsCount; ++i) {\n rewardsDistributors[i].initializeMarket(address(vToken));\n }\n\n emit MarketSupported(vToken);\n }\n\n /**\n * @notice Set the given borrow caps for the given vToken markets. Borrowing that brings total borrows to or above borrow cap will revert.\n * @dev This function is restricted by the AccessControlManager\n * @dev A borrow cap of type(uint256).max corresponds to unlimited borrowing.\n * @dev Borrow caps smaller than the current total borrows are accepted. This way, new borrows will not be allowed\n until the total borrows amount goes below the new borrow cap\n * @param vTokens The addresses of the markets (tokens) to change the borrow caps for\n * @param newBorrowCaps The new borrow cap values in underlying to be set. A value of type(uint256).max corresponds to unlimited borrowing.\n * @custom:error InvalidArrayLength is thrown if the arrays are empty or their lengths do not match\n * @custom:access Controlled by AccessControlManager\n */\n function setMarketBorrowCaps(VToken[] calldata vTokens, uint256[] calldata newBorrowCaps) external {\n _checkAccessAllowed(\"setMarketBorrowCaps(address[],uint256[])\");\n\n uint256 numMarkets = vTokens.length;\n uint256 numBorrowCaps = newBorrowCaps.length;\n\n if (numMarkets == 0 || numMarkets != numBorrowCaps) {\n revert InvalidArrayLength();\n }\n\n _ensureMaxLoops(numMarkets);\n\n for (uint256 i; i < numMarkets; ++i) {\n borrowCaps[address(vTokens[i])] = newBorrowCaps[i];\n emit NewBorrowCap(vTokens[i], newBorrowCaps[i]);\n }\n }\n\n /**\n * @notice Set the given supply caps for the given vToken markets. Supply that brings total Supply to or above supply cap will revert.\n * @dev This function is restricted by the AccessControlManager\n * @dev A supply cap of type(uint256).max corresponds to unlimited supply.\n * @dev Supply caps smaller than the current total supplies are accepted. This way, new supplies will not be allowed\n until the total supplies amount goes below the new supply cap\n * @param vTokens The addresses of the markets (tokens) to change the supply caps for\n * @param newSupplyCaps The new supply cap values in underlying to be set. A value of type(uint256).max corresponds to unlimited supply.\n * @custom:error InvalidArrayLength is thrown if the arrays are empty or their lengths do not match\n * @custom:access Controlled by AccessControlManager\n */\n function setMarketSupplyCaps(VToken[] calldata vTokens, uint256[] calldata newSupplyCaps) external {\n _checkAccessAllowed(\"setMarketSupplyCaps(address[],uint256[])\");\n uint256 vTokensCount = vTokens.length;\n\n if (vTokensCount == 0 || vTokensCount != newSupplyCaps.length) {\n revert InvalidArrayLength();\n }\n\n _ensureMaxLoops(vTokensCount);\n\n for (uint256 i; i < vTokensCount; ++i) {\n supplyCaps[address(vTokens[i])] = newSupplyCaps[i];\n emit NewSupplyCap(vTokens[i], newSupplyCaps[i]);\n }\n }\n\n /**\n * @notice Pause/unpause specified actions\n * @dev This function is restricted by the AccessControlManager\n * @param marketsList Markets to pause/unpause the actions on\n * @param actionsList List of action ids to pause/unpause\n * @param paused The new paused state (true=paused, false=unpaused)\n * @custom:error MarketNotListed is thrown if any of the markets is not listed\n * @custom:access Controlled by AccessControlManager\n */\n function setActionsPaused(VToken[] calldata marketsList, Action[] calldata actionsList, bool paused) external {\n _checkAccessAllowed(\"setActionsPaused(address[],uint256[],bool)\");\n\n uint256 marketsCount = marketsList.length;\n uint256 actionsCount = actionsList.length;\n\n _ensureMaxLoops(marketsCount * actionsCount);\n\n for (uint256 marketIdx; marketIdx < marketsCount; ++marketIdx) {\n for (uint256 actionIdx; actionIdx < actionsCount; ++actionIdx) {\n _setActionPaused(address(marketsList[marketIdx]), actionsList[actionIdx], paused);\n }\n }\n }\n\n /**\n * @notice Set the given collateral threshold for non-batch liquidations. Regular liquidations\n * will fail if the collateral amount is less than this threshold. Liquidators should use batch\n * operations like liquidateAccount or healAccount.\n * @dev This function is restricted by the AccessControlManager\n * @param newMinLiquidatableCollateral The new min liquidatable collateral (in USD).\n * @custom:access Controlled by AccessControlManager\n */\n function setMinLiquidatableCollateral(uint256 newMinLiquidatableCollateral) external {\n _checkAccessAllowed(\"setMinLiquidatableCollateral(uint256)\");\n\n uint256 oldMinLiquidatableCollateral = minLiquidatableCollateral;\n minLiquidatableCollateral = newMinLiquidatableCollateral;\n emit NewMinLiquidatableCollateral(oldMinLiquidatableCollateral, newMinLiquidatableCollateral);\n }\n\n /**\n * @notice Add a new RewardsDistributor and initialize it with all markets. We can add several RewardsDistributor\n * contracts with the same rewardToken, and there could be overlaping among them considering the last reward slot (block or second)\n * @dev Only callable by the admin\n * @param _rewardsDistributor Address of the RewardDistributor contract to add\n * @custom:access Only Governance\n * @custom:event Emits NewRewardsDistributor with distributor address\n * @custom:error RewardsDistributorAlreadyExists is thrown if this pool already has the given distributor\n */\n function addRewardsDistributor(RewardsDistributor _rewardsDistributor) external onlyOwner {\n if (rewardsDistributorExists[address(_rewardsDistributor)]) {\n revert RewardsDistributorAlreadyExists();\n }\n\n uint256 rewardsDistributorsLen = rewardsDistributors.length;\n _ensureMaxLoops(rewardsDistributorsLen + 1);\n\n rewardsDistributors.push(_rewardsDistributor);\n rewardsDistributorExists[address(_rewardsDistributor)] = true;\n\n uint256 marketsCount = allMarkets.length;\n\n for (uint256 i; i < marketsCount; ++i) {\n _rewardsDistributor.initializeMarket(address(allMarkets[i]));\n }\n\n emit NewRewardsDistributor(address(_rewardsDistributor), address(_rewardsDistributor.rewardToken()));\n }\n\n /**\n * @notice Sets a new price oracle for the Comptroller\n * @dev Only callable by the admin\n * @param newOracle Address of the new price oracle to set\n * @custom:event Emits NewPriceOracle on success\n * @custom:error ZeroAddressNotAllowed is thrown when the new oracle address is zero\n */\n function setPriceOracle(ResilientOracleInterface newOracle) external onlyOwner {\n ensureNonzeroAddress(address(newOracle));\n\n ResilientOracleInterface oldOracle = oracle;\n oracle = newOracle;\n emit NewPriceOracle(oldOracle, newOracle);\n }\n\n /**\n * @notice Sets a new deviation-bounded oracle for the Comptroller\n * @dev Only callable by the admin. Nothing falls back to spot when this is unset: both of the calls into the zero\n * address revert, so borrow and redeem fail closed rather than running unbounded, and this has to be set before\n * the pool serves either action. `_updateProtectionStates` is the one that fails first and it is the stronger of\n * the two guards, because solc emits an `extcodesize` existence check ahead of a call whose return data it does\n * not decode, which is exactly that call. `_safeGetUnderlyingPrices` gets no such check and instead relies on\n * the ABI decoder rejecting the empty return data.\n * @param newBoundedOracle Address of the new deviation-bounded oracle to set\n * @custom:event Emits NewDeviationBoundedOracle on success\n * @custom:error ZeroAddressNotAllowed is thrown when the new oracle address is zero\n */\n function setDeviationBoundedOracle(IDeviationBoundedOracle newBoundedOracle) external onlyOwner {\n ensureNonzeroAddress(address(newBoundedOracle));\n\n IDeviationBoundedOracle oldBoundedOracle = deviationBoundedOracle;\n deviationBoundedOracle = newBoundedOracle;\n emit NewDeviationBoundedOracle(oldBoundedOracle, newBoundedOracle);\n }\n\n /**\n * @notice Set the for loop iteration limit to avoid DOS\n * @param limit Limit for the max loops can execute at a time\n */\n function setMaxLoopsLimit(uint256 limit) external onlyOwner {\n _setMaxLoopsLimit(limit);\n }\n\n /**\n * @notice Enables forced liquidations for a market. If forced liquidation is enabled,\n * borrows in the market may be liquidated regardless of the account liquidity\n * @param vTokenBorrowed Borrowed vToken\n * @param enable Whether to enable forced liquidations\n */\n function setForcedLiquidation(address vTokenBorrowed, bool enable) external {\n _checkAccessAllowed(\"setForcedLiquidation(address,bool)\");\n ensureNonzeroAddress(vTokenBorrowed);\n\n if (!markets[vTokenBorrowed].isListed) {\n revert MarketNotListed(vTokenBorrowed);\n }\n\n isForcedLiquidationEnabled[vTokenBorrowed] = enable;\n emit IsForcedLiquidationEnabledUpdated(vTokenBorrowed, enable);\n }\n\n /**\n * @notice Sets the discount a liquidator receives on the collateral it seizes from a single market\n * @dev This function is restricted by the AccessControlManager\n * @dev Keyed on the collateral market, since that is what the discount prices. Setting it steers liquidators\n * toward one collateral over another, and it feeds the routing between `liquidateAccount` and `healAccount`\n * through `AccountLiquiditySnapshot.maxClearableDebt`.\n *\n * There is no way back to \"unset\" once a value is stored: `0` is the sentinel that means the pool-wide discount\n * applies, and it is rejected here so that a mistaken zero cannot silently move a market back onto the pool-wide\n * value. Pass that value explicitly to get the same effect.\n * @param vToken The collateral market to set the incentive for\n * @param newLiquidationIncentiveMantissa New incentive for this market, scaled by 1e18, at least\n * 1e18 + the market's `protocolSeizeShareMantissa`\n * @custom:event Emits NewMarketLiquidationIncentive on success\n * @custom:error MarketNotListed is thrown if the market is not listed\n * @custom:error InvalidLiquidationIncentive is thrown if the new incentive would leave the liquidator with less\n * collateral than the debt it repaid\n * @custom:access Controlled by AccessControlManager\n */\n function setMarketLiquidationIncentive(address vToken, uint256 newLiquidationIncentiveMantissa) external {\n _checkAccessAllowed(\"setMarketLiquidationIncentive(address,uint256)\");\n\n // Checked before reading from `vToken` below, so this never calls out to an arbitrary address\n if (!markets[vToken].isListed) {\n revert MarketNotListed(vToken);\n }\n\n // The incentive is a multiplier on the debt repaid, and `VToken._seize` hands the protocol\n // `protocolSeizeShareMantissa` of that debt out of the seized collateral, so an incentive below\n // `1e18 + protocolSeizeShareMantissa` pays the liquidator less collateral than it repaid and no one liquidates\n // this collateral. The 1e18 floor falls out of the same bound, since the seize share is never negative.\n // `VToken.setProtocolSeizeShare` holds the bound from the other side: it reads the incentive back through\n // `liquidationIncentiveMantissa()`, which answers for the calling market.\n if (newLiquidationIncentiveMantissa < MANTISSA_ONE + VToken(vToken).protocolSeizeShareMantissa()) {\n revert InvalidLiquidationIncentive();\n }\n\n uint256 oldLiquidationIncentiveMantissa = liquidationIncentives[vToken];\n liquidationIncentives[vToken] = newLiquidationIncentiveMantissa;\n emit NewMarketLiquidationIncentive(vToken, oldLiquidationIncentiveMantissa, newLiquidationIncentiveMantissa);\n }\n\n /**\n * @notice Restricts supplying to a market to the accounts on its supply allowlist, or lifts the restriction\n * @dev Enforced in `preMintHook`, so only supply is metered. Redeeming is never restricted, and an account\n * removed from the allowlist keeps the position it already holds and can still exit. Enabling it on a market that\n * is already serving supply cuts off every account that is not on the list, the seed supplier included.\n * @param vToken The market to change the setting for\n * @param enabled Whether the market should accept supply only from allowlisted accounts\n * @custom:event Emits SupplyAllowlistEnabledUpdated on success\n * @custom:error MarketNotListed is thrown if the market is not listed\n * @custom:access Controlled by AccessControlManager\n */\n function setSupplyAllowlistEnabled(address vToken, bool enabled) external {\n _checkAccessAllowed(\"setSupplyAllowlistEnabled(address,bool)\");\n\n if (!markets[vToken].isListed) {\n revert MarketNotListed(vToken);\n }\n\n isSupplyAllowlistEnabled[vToken] = enabled;\n emit SupplyAllowlistEnabledUpdated(vToken, enabled);\n }\n\n /**\n * @notice Adds an account to a market's supply allowlist or removes it\n * @dev Takes effect only while the market's supply allowlist is enabled. Setting an account to the value it\n * already holds is not an error, so a governance action that overlaps an earlier one still executes.\n * @param vToken The market whose allowlist to update\n * @param supplier The account to add or remove\n * @param allowed Whether the account should be allowed to supply\n * @custom:event Emits AllowedSupplierUpdated on success\n * @custom:error MarketNotListed is thrown if the market is not listed\n * @custom:error ZeroAddressNotAllowed is thrown if the account is the zero address\n * @custom:access Controlled by AccessControlManager\n */\n function setAllowedSupplier(address vToken, address supplier, bool allowed) external {\n _checkAccessAllowed(\"setAllowedSupplier(address,address,bool)\");\n ensureNonzeroAddress(supplier);\n\n if (!markets[vToken].isListed) {\n revert MarketNotListed(vToken);\n }\n\n isAllowedSupplier[vToken][supplier] = allowed;\n emit AllowedSupplierUpdated(vToken, supplier, allowed);\n }\n\n /**\n * @notice Restricts seizing collateral in this pool to the accounts on the liquidation allowlist, or lifts the\n * restriction\n * @dev Pool-wide rather than per market, for the reason given on `isLiquidationAllowlistEnabled`. Enabling this\n * also restricts `healAccount`, so any keeper relied on to record bad debt has to be allowlisted too.\n * @param enabled Whether seizing collateral should be restricted to allowlisted accounts\n * @custom:event Emits LiquidationAllowlistEnabledUpdated on success\n * @custom:access Controlled by AccessControlManager\n */\n function setLiquidationAllowlistEnabled(bool enabled) external {\n _checkAccessAllowed(\"setLiquidationAllowlistEnabled(bool)\");\n\n isLiquidationAllowlistEnabled = enabled;\n emit LiquidationAllowlistEnabledUpdated(enabled);\n }\n\n /**\n * @notice Adds an account to the pool's liquidation allowlist or removes it\n * @dev Takes effect only while the liquidation allowlist is enabled.\n * @param liquidator The account to add or remove\n * @param allowed Whether the account should be allowed to seize collateral\n * @custom:event Emits AllowedLiquidatorUpdated on success\n * @custom:error ZeroAddressNotAllowed is thrown if the account is the zero address\n * @custom:access Controlled by AccessControlManager\n */\n function setAllowedLiquidator(address liquidator, bool allowed) external {\n _checkAccessAllowed(\"setAllowedLiquidator(address,bool)\");\n ensureNonzeroAddress(liquidator);\n\n isAllowedLiquidator[liquidator] = allowed;\n emit AllowedLiquidatorUpdated(liquidator, allowed);\n }\n\n /**\n * @notice Determine the current account liquidity with respect to liquidation threshold requirements\n * @dev The interface of this function is intentionally kept compatible with Compound and Venus Core\n * @param account The account get liquidity for\n * @return error Always NO_ERROR for compatibility with Venus core tooling\n * @return liquidity Account liquidity in excess of liquidation threshold requirements,\n * @return shortfall Account shortfall below liquidation threshold requirements\n */\n function getAccountLiquidity(\n address account\n ) external view returns (uint256 error, uint256 liquidity, uint256 shortfall) {\n AccountLiquiditySnapshot memory snapshot = _getCurrentLiquiditySnapshot(\n account,\n WeightFunction.USE_LIQUIDATION_THRESHOLD\n );\n return (NO_ERROR, snapshot.liquidity, snapshot.shortfall);\n }\n\n /**\n * @notice Determine the current account liquidity with respect to collateral requirements\n * @dev The interface of this function is intentionally kept compatible with Compound and Venus Core\n * @param account The account get liquidity for\n * @return error Always NO_ERROR for compatibility with Venus core tooling\n * @return liquidity Account liquidity in excess of collateral requirements,\n * @return shortfall Account shortfall below collateral requirements\n */\n function getBorrowingPower(\n address account\n ) external view returns (uint256 error, uint256 liquidity, uint256 shortfall) {\n AccountLiquiditySnapshot memory snapshot = _getCurrentLiquiditySnapshot(\n account,\n WeightFunction.USE_COLLATERAL_FACTOR\n );\n return (NO_ERROR, snapshot.liquidity, snapshot.shortfall);\n }\n\n /**\n * @notice Determine what the account liquidity would be if the given amounts were redeemed/borrowed\n * @dev The interface of this function is intentionally kept compatible with Compound and Venus Core\n * @param vTokenModify The market to hypothetically redeem/borrow in\n * @param account The account to determine liquidity for\n * @param redeemTokens The number of tokens to hypothetically redeem\n * @param borrowAmount The amount of underlying to hypothetically borrow\n * @return error Always NO_ERROR for compatibility with Venus core tooling\n * @return liquidity Hypothetical account liquidity in excess of collateral requirements,\n * @return shortfall Hypothetical account shortfall below collateral requirements\n */\n function getHypotheticalAccountLiquidity(\n address account,\n address vTokenModify,\n uint256 redeemTokens,\n uint256 borrowAmount\n ) external view returns (uint256 error, uint256 liquidity, uint256 shortfall) {\n AccountLiquiditySnapshot memory snapshot = _getHypotheticalLiquiditySnapshot(\n account,\n VToken(vTokenModify),\n redeemTokens,\n borrowAmount,\n WeightFunction.USE_COLLATERAL_FACTOR\n );\n return (NO_ERROR, snapshot.liquidity, snapshot.shortfall);\n }\n\n /**\n * @notice Return all of the markets\n * @dev The automatic getter may be used to access an individual market.\n * @return markets The list of market addresses\n */\n function getAllMarkets() external view override returns (VToken[] memory) {\n return allMarkets;\n }\n\n /**\n * @notice Check if a market is marked as listed (active)\n * @param vToken vToken Address for the market to check\n * @return listed True if listed otherwise false\n */\n function isMarketListed(VToken vToken) external view returns (bool) {\n return markets[address(vToken)].isListed;\n }\n\n /**\n * @notice Returns the discount a liquidator receives on the collateral it seizes from the calling market\n * @dev Answers for `msg.sender` instead of taking the market as an argument, because this is the getter\n * `ComptrollerViewInterface` declares and `VToken` calls on itself: `_seize` divides the protocol seize share by\n * it, and `setProtocolSeizeShare` bounds that share against it. Both have to see the discount that prices the\n * calling market's own collateral, or the protocol takes more than its configured share of the repaid debt and the\n * liquidator is paid less than the market's configured discount.\n *\n * Any caller that is not a market of this pool, a lens or a liquidation bot, reads the pool-wide discount. Ask\n * about a specific market through `effectiveLiquidationIncentive`.\n * @return The discount that applies to the caller, scaled by 1e18\n */\n function liquidationIncentiveMantissa() external view returns (uint256) {\n return _liquidationIncentive(msg.sender);\n }\n\n /**\n * @notice Returns the discount a liquidator receives on the collateral it seizes from a market\n * @param vToken The collateral market to read the discount of\n * @return The market's own discount if it has one, otherwise the pool-wide discount, scaled by 1e18\n */\n function effectiveLiquidationIncentive(address vToken) external view returns (uint256) {\n return _liquidationIncentive(vToken);\n }\n\n /*** Assets You Are In ***/\n\n /**\n * @notice Returns whether the given account is entered in a given market\n * @param account The address of the account to check\n * @param vToken The vToken to check\n * @return True if the account is in the market specified, otherwise false.\n */\n function checkMembership(address account, VToken vToken) external view returns (bool) {\n return markets[address(vToken)].accountMembership[account];\n }\n\n /**\n * @notice Calculate number of tokens of collateral asset to seize given an underlying amount\n * @dev Used in liquidation (called in vToken.liquidateBorrowFresh)\n * @param vTokenBorrowed The address of the borrowed vToken\n * @param vTokenCollateral The address of the collateral vToken\n * @param actualRepayAmount The amount of vTokenBorrowed underlying to convert into vTokenCollateral tokens\n * @return error Always NO_ERROR for compatibility with Venus core tooling\n * @return tokensToSeize Number of vTokenCollateral tokens to be seized in a liquidation\n * @custom:error PriceError if the oracle returns an invalid price\n */\n function liquidateCalculateSeizeTokens(\n address vTokenBorrowed,\n address vTokenCollateral,\n uint256 actualRepayAmount\n ) external view override returns (uint256 error, uint256 tokensToSeize) {\n /* Read oracle prices for borrowed and collateral markets */\n uint256 priceBorrowedMantissa = _safeGetUnderlyingPrice(VToken(vTokenBorrowed));\n uint256 priceCollateralMantissa = _safeGetUnderlyingPrice(VToken(vTokenCollateral));\n\n /*\n * Get the exchange rate and calculate the number of collateral tokens to seize, where\n * `liquidationIncentive` is the collateral market's own discount if it has one and the pool-wide default\n * otherwise, the same value `VToken._seize` reads back through `liquidationIncentiveMantissa()`:\n * seizeAmount = actualRepayAmount * liquidationIncentive * priceBorrowed / priceCollateral\n * seizeTokens = seizeAmount / exchangeRate\n * = actualRepayAmount * (liquidationIncentive * priceBorrowed) / (priceCollateral * exchangeRate)\n */\n uint256 exchangeRateMantissa = VToken(vTokenCollateral).exchangeRateStored(); // Note: reverts on error\n uint256 seizeTokens;\n Exp memory numerator;\n Exp memory denominator;\n Exp memory ratio;\n\n numerator = mul_(\n Exp({ mantissa: _liquidationIncentive(vTokenCollateral) }),\n Exp({ mantissa: priceBorrowedMantissa })\n );\n denominator = mul_(Exp({ mantissa: priceCollateralMantissa }), Exp({ mantissa: exchangeRateMantissa }));\n ratio = div_(numerator, denominator);\n\n seizeTokens = mul_ScalarTruncate(ratio, actualRepayAmount);\n\n return (NO_ERROR, seizeTokens);\n }\n\n /**\n * @notice Returns reward speed given a vToken\n * @param vToken The vToken to get the reward speeds for\n * @return rewardSpeeds Array of total supply and borrow speeds and reward token for all reward distributors\n */\n function getRewardsByMarket(address vToken) external view returns (RewardSpeeds[] memory rewardSpeeds) {\n uint256 rewardsDistributorsLength = rewardsDistributors.length;\n rewardSpeeds = new RewardSpeeds[](rewardsDistributorsLength);\n for (uint256 i; i < rewardsDistributorsLength; ++i) {\n RewardsDistributor rewardsDistributor = rewardsDistributors[i];\n address rewardToken = address(rewardsDistributor.rewardToken());\n rewardSpeeds[i] = RewardSpeeds({\n rewardToken: rewardToken,\n supplySpeed: rewardsDistributor.rewardTokenSupplySpeeds(vToken),\n borrowSpeed: rewardsDistributor.rewardTokenBorrowSpeeds(vToken)\n });\n }\n return rewardSpeeds;\n }\n\n /**\n * @notice Return all reward distributors for this pool\n * @return Array of RewardDistributor addresses\n */\n function getRewardDistributors() external view returns (RewardsDistributor[] memory) {\n return rewardsDistributors;\n }\n\n /**\n * @notice A marker method that returns true for a valid Comptroller contract\n * @return Always true\n */\n function isComptroller() external pure override returns (bool) {\n return true;\n }\n\n /**\n * @notice Update the prices of all the tokens associated with the provided account\n * @param account Address of the account to get associated tokens with\n */\n function updatePrices(address account) public {\n _updatePrices(getAssetsIn(account));\n }\n\n /**\n * @notice Checks if a certain action is paused on a market\n * @param market vToken address\n * @param action Action to check\n * @return paused True if the action is paused otherwise false\n */\n function actionPaused(address market, Action action) public view returns (bool) {\n return _actionPaused[market][action];\n }\n\n /**\n * @notice Returns the assets an account has entered\n * @param account The address of the account to pull assets for\n * @return A list with the assets the account has entered\n */\n function getAssetsIn(address account) public view returns (VToken[] memory) {\n uint256 len;\n VToken[] memory _accountAssets = accountAssets[account];\n uint256 _accountAssetsLength = _accountAssets.length;\n\n VToken[] memory assetsIn = new VToken[](_accountAssetsLength);\n\n for (uint256 i; i < _accountAssetsLength; ++i) {\n Market storage market = markets[address(_accountAssets[i])];\n if (market.isListed) {\n assetsIn[len] = _accountAssets[i];\n ++len;\n }\n }\n\n assembly {\n mstore(assetsIn, len)\n }\n\n return assetsIn;\n }\n\n /**\n * @notice Add the market to the borrower's \"assets in\" for liquidity calculations\n * @param vToken The market to enter\n * @param borrower The address of the account to modify\n */\n function _addToMarket(VToken vToken, address borrower) internal {\n _checkActionPauseState(address(vToken), Action.ENTER_MARKET);\n Market storage marketToJoin = markets[address(vToken)];\n\n if (!marketToJoin.isListed) {\n revert MarketNotListed(address(vToken));\n }\n\n if (marketToJoin.accountMembership[borrower]) {\n // already joined\n return;\n }\n\n // survived the gauntlet, add to list\n // NOTE: we store these somewhat redundantly as a significant optimization\n // this avoids having to iterate through the list for the most common use cases\n // that is, only when we need to perform liquidity checks\n // and not whenever we want to check if an account is in a particular market\n marketToJoin.accountMembership[borrower] = true;\n accountAssets[borrower].push(vToken);\n\n emit MarketEntered(vToken, borrower);\n }\n\n /**\n * @notice Internal function to validate that a market hasn't already been added\n * and if it hasn't adds it\n * @param vToken The market to support\n */\n function _addMarket(address vToken) internal {\n uint256 marketsCount = allMarkets.length;\n\n for (uint256 i; i < marketsCount; ++i) {\n if (allMarkets[i] == VToken(vToken)) {\n revert MarketAlreadyListed(vToken);\n }\n }\n allMarkets.push(VToken(vToken));\n marketsCount = allMarkets.length;\n _ensureMaxLoops(marketsCount);\n }\n\n /**\n * @dev Pause/unpause an action on a market\n * @param market Market to pause/unpause the action on\n * @param action Action id to pause/unpause\n * @param paused The new paused state (true=paused, false=unpaused)\n */\n function _setActionPaused(address market, Action action, bool paused) internal {\n if (!markets[market].isListed) {\n revert MarketNotListed(market);\n }\n _actionPaused[market][action] = paused;\n emit ActionPausedMarket(VToken(market), action, paused);\n }\n\n /**\n * @dev Accrues interest and pushes an oracle update for each of the given markets, so that a snapshot taken\n * afterwards values the account on live balances and prices. Used by the two batch liquidation entry points,\n * which decide on their snapshot and then execute with the per-order liquidity check skipped.\n * @param vTokens The markets to refresh, as returned by `getAssetsIn`\n */\n function _refreshMarkets(VToken[] memory vTokens) internal {\n uint256 vTokensCount = vTokens.length;\n\n for (uint256 i; i < vTokensCount; ++i) {\n vTokens[i].accrueInterest();\n }\n\n _updatePrices(vTokens);\n }\n\n /**\n * @dev Pushes an oracle update for each of the given markets. Takes the resolved market list rather than an\n * account, because the two callers that need protection state as well would otherwise walk `getAssetsIn` twice\n * for the same account in the same call.\n * @param vTokens The markets to update, as returned by `getAssetsIn`\n */\n function _updatePrices(VToken[] memory vTokens) internal {\n uint256 vTokensCount = vTokens.length;\n\n ResilientOracleInterface oracle_ = oracle;\n\n for (uint256 i; i < vTokensCount; ++i) {\n oracle_.updatePrice(address(vTokens[i]));\n }\n }\n\n /**\n * @dev Persists the deviation-bounded oracle's price window and protection state for each of the given markets.\n * Runs before the borrow and redeem liquidity checks, the ones weighted by the collateral factor, so that a\n * deviating print latches protection and starts its cooldown instead of evaporating once the price returns to\n * the window. The liquidation-threshold paths never call this, matching how they read spot prices: see\n * `_safeGetUnderlyingPrices`.\n * @param vTokens The markets to update, as returned by `getAssetsIn`\n */\n function _updateProtectionStates(VToken[] memory vTokens) internal {\n uint256 vTokensCount = vTokens.length;\n\n IDeviationBoundedOracle boundedOracle = deviationBoundedOracle;\n\n for (uint256 i; i < vTokensCount; ++i) {\n boundedOracle.updateProtectionState(address(vTokens[i]));\n }\n }\n\n /**\n * @dev Internal function to check that vTokens can be safely redeemed for the underlying asset.\n * @param vToken Address of the vTokens to redeem\n * @param redeemer Account redeeming the tokens\n * @param redeemTokens The number of tokens to redeem\n */\n function _checkRedeemAllowed(address vToken, address redeemer, uint256 redeemTokens) internal {\n Market storage market = markets[vToken];\n\n if (!market.isListed) {\n revert MarketNotListed(address(vToken));\n }\n\n /* If the redeemer is not 'in' the market, then we can bypass the liquidity check */\n if (!market.accountMembership[redeemer]) {\n return;\n }\n\n // Update the prices of tokens\n VToken[] memory redeemerAssets = getAssetsIn(redeemer);\n _updatePrices(redeemerAssets);\n _updateProtectionStates(redeemerAssets);\n\n /* Otherwise, perform a hypothetical liquidity check to guard against shortfall */\n AccountLiquiditySnapshot memory snapshot = _getHypotheticalLiquiditySnapshot(\n redeemer,\n VToken(vToken),\n redeemTokens,\n 0,\n WeightFunction.USE_COLLATERAL_FACTOR\n );\n if (snapshot.shortfall > 0) {\n revert InsufficientLiquidity();\n }\n }\n\n /**\n * @notice Get the total collateral, weighted collateral, borrow balance, liquidity, shortfall\n * @param account The account to get the snapshot for\n * @param weighting Which per-market risk parameter weights the collateral, either the collateral factor or\n * the liquidation threshold\n * @dev Note that we calculate the exchangeRateStored for each collateral vToken using stored data,\n * without calculating accumulated interest.\n * @return snapshot Account liquidity snapshot\n */\n function _getCurrentLiquiditySnapshot(\n address account,\n WeightFunction weighting\n ) internal view returns (AccountLiquiditySnapshot memory snapshot) {\n return _getHypotheticalLiquiditySnapshot(account, VToken(address(0)), 0, 0, weighting);\n }\n\n /**\n * @notice Determine what the supply/borrow balances would be if the given amounts were redeemed/borrowed\n * @param vTokenModify The market to hypothetically redeem/borrow in\n * @param account The account to determine liquidity for\n * @param redeemTokens The number of tokens to hypothetically redeem\n * @param borrowAmount The amount of underlying to hypothetically borrow\n * @param weighting Which per-market risk parameter weights the collateral, either the collateral factor or\n * the liquidation threshold\n * @dev Note that we calculate the exchangeRateStored for each collateral vToken using stored data,\n * without calculating accumulated interest.\n * @return snapshot Account liquidity snapshot\n */\n function _getHypotheticalLiquiditySnapshot(\n address account,\n VToken vTokenModify,\n uint256 redeemTokens,\n uint256 borrowAmount,\n WeightFunction weighting\n ) internal view returns (AccountLiquiditySnapshot memory snapshot) {\n // For each asset the account is in\n VToken[] memory assets = getAssetsIn(account);\n uint256 assetsCount = assets.length;\n\n for (uint256 i; i < assetsCount; ++i) {\n VToken asset = assets[i];\n (Exp memory weightedVTokenPrice, Exp memory debtPrice) = _accumulateMarketPosition(\n snapshot,\n asset,\n account,\n weighting\n );\n\n // Calculate effects of interacting with vTokenModify\n if (asset == vTokenModify) {\n // redeem effect\n // effects += tokensToDenom * redeemTokens\n snapshot.effects = mul_ScalarTruncateAddUInt(weightedVTokenPrice, redeemTokens, snapshot.effects);\n\n // borrow effect\n // effects += debtPrice * borrowAmount\n snapshot.effects = mul_ScalarTruncateAddUInt(debtPrice, borrowAmount, snapshot.effects);\n }\n }\n\n uint256 borrowPlusEffects = snapshot.borrows + snapshot.effects;\n // These are safe, as the underflow condition is checked first\n unchecked {\n if (snapshot.weightedCollateral > borrowPlusEffects) {\n snapshot.liquidity = snapshot.weightedCollateral - borrowPlusEffects;\n snapshot.shortfall = 0;\n } else {\n snapshot.liquidity = 0;\n snapshot.shortfall = borrowPlusEffects - snapshot.weightedCollateral;\n }\n }\n\n return snapshot;\n }\n\n /**\n * @dev Adds one market's collateral and debt to an account's liquidity snapshot, and returns the two prices the\n * caller needs to apply a hypothetical redeem or borrow in that market. Split out of\n * `_getHypotheticalLiquiditySnapshot` because holding this market's prices and the loop's own variables in one\n * frame exceeds the stack the legacy code generator can address, and this repo does not enable `viaIR`.\n * `snapshot` is a memory reference, so the accumulated fields are updated in place.\n * @param snapshot The snapshot to accumulate into\n * @param asset The market to value\n * @param account The account whose position in the market is being valued\n * @param weighting Which per-market risk parameter weights the collateral\n * @return weightedVTokenPrice Value of one vToken after the risk weight, which prices a hypothetical redeem\n * @return debtPrice Price valuing the market's debt, which prices a hypothetical borrow\n */\n function _accumulateMarketPosition(\n AccountLiquiditySnapshot memory snapshot,\n VToken asset,\n address account,\n WeightFunction weighting\n ) internal view returns (Exp memory weightedVTokenPrice, Exp memory debtPrice) {\n // Read the balances and exchange rate from the vToken\n (uint256 vTokenBalance, uint256 borrowBalance, uint256 exchangeRateMantissa) = _safeGetAccountSnapshot(\n asset,\n account\n );\n\n // Get the normalized prices that value this market's collateral and debt\n Exp memory collateralPrice;\n (collateralPrice, debtPrice) = _safeGetUnderlyingPrices(asset, weighting);\n\n // Pre-compute conversion factors from vTokens -> usd\n Exp memory vTokenPrice = mul_(Exp({ mantissa: exchangeRateMantissa }), collateralPrice);\n weightedVTokenPrice = mul_(_getWeightingFactor(asset, weighting), vTokenPrice);\n\n // weightedCollateral += weightedVTokenPrice * vTokenBalance\n snapshot.weightedCollateral = mul_ScalarTruncateAddUInt(\n weightedVTokenPrice,\n vTokenBalance,\n snapshot.weightedCollateral\n );\n\n // totalCollateral += vTokenPrice * vTokenBalance\n snapshot.totalCollateral = mul_ScalarTruncateAddUInt(vTokenPrice, vTokenBalance, snapshot.totalCollateral);\n\n // maxClearableDebt += (vTokenPrice * vTokenBalance) / liquidationIncentive, at this market's own incentive.\n // Only the liquidation-threshold weighting gives this field a meaning and only that weighting's callers read\n // it, so a collateral-factor snapshot would read the incentive and divide only to discard the result: see the\n // note on `AccountLiquiditySnapshot.maxClearableDebt`. Also skipped when the account holds none of this\n // market, since the term would be zero and a borrower is a member of every market it borrows from, including\n // ones it holds no collateral in. The repeated product costs nothing, the optimizer shares it with the line\n // above.\n if (weighting == WeightFunction.USE_LIQUIDATION_THRESHOLD && vTokenBalance != 0) {\n snapshot.maxClearableDebt += div_(\n mul_ScalarTruncate(vTokenPrice, vTokenBalance),\n Exp({ mantissa: _liquidationIncentive(address(asset)) })\n );\n }\n\n // borrows += debtPrice * borrowBalance\n snapshot.borrows = mul_ScalarTruncateAddUInt(debtPrice, borrowBalance, snapshot.borrows);\n }\n\n /**\n * @dev Retrieves price from oracle for an asset and checks it is nonzero\n * @param asset Address for asset to query price\n * @return Underlying price\n */\n function _safeGetUnderlyingPrice(VToken asset) internal view returns (uint256) {\n uint256 oraclePriceMantissa = oracle.getUnderlyingPrice(address(asset));\n if (oraclePriceMantissa == 0) {\n revert PriceError(address(asset));\n }\n return oraclePriceMantissa;\n }\n\n /**\n * @dev Retrieves the two prices that value a market's collateral and its debt, and checks they are nonzero.\n * Under the collateral factor both come from `deviationBoundedOracle`: while protection is active for the asset\n * it values collateral at the low end of the asset's recent price window and debt at the high end, and it returns\n * spot on both legs otherwise, including for an asset it holds no configuration for. A deviating print can\n * therefore only ever shrink an account's borrowing capacity, never inflate it. Under the liquidation threshold\n * both legs are spot, because those snapshots route an unhealthy account between `liquidateAccount` and\n * `healAccount` and set how much of its debt healing repays, which has to track the live price.\n * @param asset Address for asset to query prices for\n * @param weighting Which risk parameter weights the position being valued\n * @return collateralPrice Price valuing the collateral held in the market\n * @return debtPrice Price valuing the debt owed to the market\n */\n function _safeGetUnderlyingPrices(\n VToken asset,\n WeightFunction weighting\n ) internal view returns (Exp memory collateralPrice, Exp memory debtPrice) {\n if (weighting == WeightFunction.USE_LIQUIDATION_THRESHOLD) {\n uint256 spotPriceMantissa = _safeGetUnderlyingPrice(asset);\n return (Exp({ mantissa: spotPriceMantissa }), Exp({ mantissa: spotPriceMantissa }));\n }\n\n (uint256 collateralPriceMantissa, uint256 debtPriceMantissa) = deviationBoundedOracle.getBoundedPricesView(\n address(asset)\n );\n if (collateralPriceMantissa == 0 || debtPriceMantissa == 0) {\n revert PriceError(address(asset));\n }\n return (Exp({ mantissa: collateralPriceMantissa }), Exp({ mantissa: debtPriceMantissa }));\n }\n\n /**\n * @dev Returns the risk parameter that weights a market's collateral in a liquidity snapshot\n * @param asset Address for asset whose parameter to read\n * @param weighting Which of the two parameters to read\n * @return The market's collateral factor or liquidation threshold, as an exponential\n */\n function _getWeightingFactor(VToken asset, WeightFunction weighting) internal view returns (Exp memory) {\n Market storage market = markets[address(asset)];\n return\n Exp({\n mantissa: weighting == WeightFunction.USE_COLLATERAL_FACTOR\n ? market.collateralFactorMantissa\n : market.liquidationThresholdMantissa\n });\n }\n\n /**\n * @dev Returns the liquidation incentive that applies when a market's collateral is seized\n * @param vTokenCollateral Market whose collateral would be seized\n * @return The market's own incentive, or the pool-wide one if the market has none. Never zero for a listed\n * market, so callers may divide by it: see the note on `liquidationIncentives`. An unlisted market still reads\n * back whatever incentive it was last given, which no seizure can reach, since every path that divides by this\n * is gated on the market being listed.\n */\n function _liquidationIncentive(address vTokenCollateral) internal view returns (uint256) {\n uint256 incentive = liquidationIncentives[vTokenCollateral];\n return incentive != 0 ? incentive : _poolLiquidationIncentiveMantissa;\n }\n\n /**\n * @dev Returns supply and borrow balances of user in vToken, reverts on failure\n * @param vToken Market to query\n * @param user Account address\n * @return vTokenBalance Balance of vTokens, the same as vToken.balanceOf(user)\n * @return borrowBalance Borrowed amount, including the interest\n * @return exchangeRateMantissa Stored exchange rate\n */\n function _safeGetAccountSnapshot(\n VToken vToken,\n address user\n ) internal view returns (uint256 vTokenBalance, uint256 borrowBalance, uint256 exchangeRateMantissa) {\n uint256 err;\n (err, vTokenBalance, borrowBalance, exchangeRateMantissa) = vToken.getAccountSnapshot(user);\n if (err != 0) {\n revert SnapshotError(address(vToken), user);\n }\n return (vTokenBalance, borrowBalance, exchangeRateMantissa);\n }\n\n /// @notice Reverts if the call is not from expectedSender\n /// @param expectedSender Expected transaction sender\n function _checkSenderIs(address expectedSender) internal view {\n if (msg.sender != expectedSender) {\n revert UnexpectedSender(expectedSender, msg.sender);\n }\n }\n\n /// @notice Reverts if a certain action is paused on a market\n /// @param market Market to check\n /// @param action Action to check\n function _checkActionPauseState(address market, Action action) private view {\n if (actionPaused(market, action)) {\n revert ActionPaused(market, action);\n }\n }\n\n /// @notice Reverts if the liquidation allowlist is enabled and the given account is not on it\n /// @param liquidator Account that would receive the seized collateral\n function _checkLiquidatorAllowlisted(address liquidator) private view {\n if (isLiquidationAllowlistEnabled && !isAllowedLiquidator[liquidator]) {\n revert LiquidationNotAllowed(liquidator);\n }\n }\n}\n" + }, + "contracts/Spoke/SpokeComptrollerInterface.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity ^0.8.25;\n\nimport { ResilientOracleInterface } from \"@venusprotocol/oracle/contracts/interfaces/OracleInterface.sol\";\nimport { IDeviationBoundedOracle } from \"@venusprotocol/oracle/contracts/interfaces/IDeviationBoundedOracle.sol\";\n\nimport { ComptrollerInterface, Action } from \"../ComptrollerInterface.sol\";\nimport { VToken } from \"../VToken.sol\";\n\n/**\n * @title SpokeComptrollerInterface\n * @author Venus\n * @notice Interface implemented by the `SpokeComptroller` contract. It declares the events and errors that make up the\n * contract's observable surface, so integrators and off-chain consumers can decode them without depending on the\n * implementation.\n * @dev The getters this fork adds are declared separately, in `SpokeComptrollerViewInterface` below. They cannot live\n * here: `SpokeComptroller` implements this interface, and it serves those getters from public variables declared in\n * `SpokeComptrollerStorage`, which is a sibling base rather than a derived one. Solidity will not let a public\n * variable in one base satisfy a function declared in another, so declaring them here would force\n * `SpokeComptrollerStorage` to inherit this interface and mark six variables `override` - noise in the file that has\n * to be diffed by hand against `ComptrollerStorage`, for no gain over a standalone view interface.\n */\ninterface SpokeComptrollerInterface is ComptrollerInterface {\n /// @notice Emitted when an account enters a market\n event MarketEntered(VToken indexed vToken, address indexed account);\n\n /// @notice Emitted when an account exits a market\n event MarketExited(VToken indexed vToken, address indexed account);\n\n /// @notice Emitted when close factor is changed by admin\n event NewCloseFactor(uint256 oldCloseFactorMantissa, uint256 newCloseFactorMantissa);\n\n /// @notice Emitted when a collateral factor is changed by admin\n event NewCollateralFactor(VToken vToken, uint256 oldCollateralFactorMantissa, uint256 newCollateralFactorMantissa);\n\n /// @notice Emitted when liquidation threshold is changed by admin\n event NewLiquidationThreshold(\n VToken vToken,\n uint256 oldLiquidationThresholdMantissa,\n uint256 newLiquidationThresholdMantissa\n );\n\n /// @notice Emitted when liquidation incentive is changed by admin\n event NewLiquidationIncentive(uint256 oldLiquidationIncentiveMantissa, uint256 newLiquidationIncentiveMantissa);\n\n /// @notice Emitted when the liquidation incentive of a single market is changed by admin\n event NewMarketLiquidationIncentive(\n address indexed vToken,\n uint256 oldLiquidationIncentiveMantissa,\n uint256 newLiquidationIncentiveMantissa\n );\n\n /// @notice Emitted when price oracle is changed\n event NewPriceOracle(ResilientOracleInterface oldPriceOracle, ResilientOracleInterface newPriceOracle);\n\n /// @notice Emitted when the deviation-bounded oracle is changed\n event NewDeviationBoundedOracle(IDeviationBoundedOracle oldBoundedOracle, IDeviationBoundedOracle newBoundedOracle);\n\n /// @notice Emitted when an action is paused on a market\n event ActionPausedMarket(VToken vToken, Action action, bool pauseState);\n\n /// @notice Emitted when borrow cap for a vToken is changed\n event NewBorrowCap(VToken indexed vToken, uint256 newBorrowCap);\n\n /// @notice Emitted when the collateral threshold (in USD) for non-batch liquidations is changed\n event NewMinLiquidatableCollateral(uint256 oldMinLiquidatableCollateral, uint256 newMinLiquidatableCollateral);\n\n /// @notice Emitted when supply cap for a vToken is changed\n event NewSupplyCap(VToken indexed vToken, uint256 newSupplyCap);\n\n /// @notice Emitted when a rewards distributor is added\n event NewRewardsDistributor(address indexed rewardsDistributor, address indexed rewardToken);\n\n /// @notice Emitted when a market is supported\n event MarketSupported(VToken vToken);\n\n /// @notice Emitted when forced liquidation is enabled or disabled for a market\n event IsForcedLiquidationEnabledUpdated(address indexed vToken, bool enable);\n\n /// @notice Emitted when a market is unlisted\n event MarketUnlisted(address indexed vToken);\n\n /// @notice Emitted when the borrowing or redeeming delegate rights are updated for an account\n event DelegateUpdated(address indexed approver, address indexed delegate, bool approved);\n\n /// @notice Emitted when a market's supply allowlist is enabled or disabled\n event SupplyAllowlistEnabledUpdated(address indexed vToken, bool enabled);\n\n /// @notice Emitted when an account is added to or removed from a market's supply allowlist\n event AllowedSupplierUpdated(address indexed vToken, address indexed supplier, bool allowed);\n\n /// @notice Emitted when the pool's liquidation allowlist is enabled or disabled\n event LiquidationAllowlistEnabledUpdated(bool enabled);\n\n /// @notice Emitted when an account is added to or removed from the pool's liquidation allowlist\n event AllowedLiquidatorUpdated(address indexed liquidator, bool allowed);\n\n /// @notice Thrown when the close factor is outside the bounds set by `MIN_CLOSE_FACTOR_MANTISSA` and\n /// `MAX_CLOSE_FACTOR_MANTISSA`\n error InvalidCloseFactor();\n\n /// @notice Thrown when collateral factor exceeds the upper bound\n error InvalidCollateralFactor();\n\n /// @notice Thrown when liquidation threshold exceeds the collateral factor\n error InvalidLiquidationThreshold();\n\n /// @notice Thrown when a liquidation incentive is low enough that a liquidator would seize less value than it\n /// repaid: below `1e18 + protocolSeizeShareMantissa` for a single market, below\n /// `MIN_POOL_LIQUIDATION_INCENTIVE_MANTISSA` for the pool-wide fallback\n error InvalidLiquidationIncentive();\n\n /// @notice Thrown when the action is only available to specific sender, but the real sender was different\n error UnexpectedSender(address expectedSender, address actualSender);\n\n /// @notice Thrown when the oracle returns an invalid price for some asset\n error PriceError(address vToken);\n\n /// @notice Thrown if VToken unexpectedly returned a nonzero error code while trying to get account snapshot\n error SnapshotError(address vToken, address user);\n\n /// @notice Thrown when the market is not listed\n error MarketNotListed(address market);\n\n /// @notice Thrown when a market has an unexpected comptroller\n error ComptrollerMismatch();\n\n /// @notice Thrown when user is not member of market\n error MarketNotCollateral(address vToken, address user);\n\n /// @notice Thrown when borrow action is not paused\n error BorrowActionNotPaused();\n\n /// @notice Thrown when mint action is not paused\n error MintActionNotPaused();\n\n /// @notice Thrown when redeem action is not paused\n error RedeemActionNotPaused();\n\n /// @notice Thrown when repay action is not paused\n error RepayActionNotPaused();\n\n /// @notice Thrown when seize action is not paused\n error SeizeActionNotPaused();\n\n /// @notice Thrown when exit market action is not paused\n error ExitMarketActionNotPaused();\n\n /// @notice Thrown when transfer action is not paused\n error TransferActionNotPaused();\n\n /// @notice Thrown when enter market action is not paused\n error EnterMarketActionNotPaused();\n\n /// @notice Thrown when liquidate action is not paused\n error LiquidateActionNotPaused();\n\n /// @notice Thrown when borrow cap is not zero\n error BorrowCapIsNotZero();\n\n /// @notice Thrown when supply cap is not zero\n error SupplyCapIsNotZero();\n\n /// @notice Thrown when collateral factor is not zero\n error CollateralFactorIsNotZero();\n\n /**\n * @notice Thrown during the liquidation if user's total collateral amount is lower than\n * a predefined threshold. In this case only batch liquidations (either liquidateAccount\n * or healAccount) are available.\n */\n error MinimalCollateralViolated(uint256 expectedGreaterThan, uint256 actual);\n\n /**\n * @notice Thrown by the batch operations when the account's total collateral is above\n * `minLiquidatableCollateral`, which means it is large enough for a regular `VToken.liquidateBorrow`\n * @dev Same name, arguments and meaning as the shared `Comptroller`, so a decoder written against either one\n * reads this correctly.\n */\n error CollateralExceedsThreshold(uint256 expectedLessThanOrEqualTo, uint256 actual);\n\n /// @notice Thrown by `healAccount` when the collateral can clear the whole debt at each market's own liquidation\n /// incentive, so healing would forgive nothing and `liquidateAccount` has to be used instead\n error CollateralCoversDebt(uint256 borrows, uint256 maxClearableDebt);\n\n /// @notice Thrown by `liquidateAccount` when the debt is too large for the collateral to clear, so `healAccount`\n /// has to be used instead. Reports the debt and the largest debt the collateral could have cleared.\n /// @dev Deliberately not the shared `Comptroller`'s `InsufficientCollateral`: both arguments here are debt values\n /// rather than collateral values, and reusing that name would leave the two versions sharing one selector.\n error DebtExceedsClearableAmount(uint256 borrows, uint256 maxClearableDebt);\n\n /// @notice Thrown when the account doesn't have enough liquidity to redeem or borrow\n error InsufficientLiquidity();\n\n /// @notice Thrown when trying to liquidate a healthy account\n error InsufficientShortfall();\n\n /// @notice Thrown if the liquidation allowlist is enabled and the account liquidating is not on it\n error LiquidationNotAllowed(address liquidator);\n\n /// @notice Thrown when trying to repay more than allowed by close factor\n error TooMuchRepay();\n\n /// @notice Thrown if the user is trying to exit a market in which they have an outstanding debt\n error NonzeroBorrowBalance();\n\n /// @notice Thrown if a debt remains in any of the borrower's markets once every liquidation order has been\n /// executed, which means the orders passed to `liquidateAccount` did not cover the whole position\n error NonzeroBorrowBalanceAfterLiquidation();\n\n /// @notice Thrown when trying to perform an action that is paused\n error ActionPaused(address market, Action action);\n\n /// @notice Thrown when trying to add a market that is already listed\n error MarketAlreadyListed(address market);\n\n /// @notice Thrown when the market being listed does not identify itself as a VToken\n error InvalidVToken();\n\n /// @notice Thrown when an array argument is empty, or when two array arguments have different lengths\n error InvalidArrayLength();\n\n /// @notice Thrown if the supply cap is exceeded\n error SupplyCapExceeded(address market, uint256 cap);\n\n /// @notice Thrown if the account being credited with the minted vTokens is not on the market's supply allowlist\n error SupplyNotAllowed(address market, address supplier);\n\n /// @notice Thrown if the borrow cap is exceeded\n error BorrowCapExceeded(address market, uint256 cap);\n\n /// @notice Thrown if delegate approval status is already set to the requested value\n error DelegationStatusUnchanged();\n\n /// @notice Thrown when adding a rewards distributor that this pool already has\n error RewardsDistributorAlreadyExists();\n}\n\n/**\n * @title SpokeComptrollerViewInterface\n * @author Venus\n * @notice The getters `SpokeComptroller` adds on top of the pooled `Comptroller`, for integrators that read this pool\n * rather than implement it - the Liquidity Hub's spoke adapter, lenses, keepers and VIP tooling.\n * @dev Standalone and not implemented by `SpokeComptroller`, matching how `ComptrollerViewInterface` exposes the\n * shared pool's getters.\n *\n * Scope is the supply side: what an integrator that funds this pool has to read before and while it supplies, plus\n * the two allowlists and the per-market discount that only exist on this fork. `supplyCaps` and `actionPaused` are\n * repeated from `ComptrollerViewInterface` and `ComptrollerInterface` rather than inherited, so that a consumer of a\n * spoke pool needs one import and not three. `actionPaused` also has to be repeated on its own terms: the shared\n * declaration types its second argument as the `Action` enum, and a consumer that does not want this repo's enum in\n * its build needs the ABI-equivalent `uint8` form. The two share a selector, so they cannot both be declared in one\n * inheritance chain - which is the reason this interface inherits nothing.\n *\n * `markets` is repeated for a different reason: the shared declaration returns two of the three words the getter\n * actually produces, so a consumer reading it there silently loses `liquidationThresholdMantissa`. The form declared\n * here returns all three.\n *\n * Anything else the fork shares with the pooled `Comptroller` - `borrowCaps`, `oracle`, `closeFactorMantissa`,\n * `minLiquidatableCollateral` - is read through `ComptrollerViewInterface` as usual.\n */\ninterface SpokeComptrollerViewInterface {\n /**\n * @notice The listing state and both collateral weights of a market\n * @dev The auto-generated getter over the `markets` mapping. It returns three words, one per non-mapping member\n * of `Market`. `ComptrollerViewInterface` declares only the first two, so a caller reading the getter through\n * that declaration drops `liquidationThresholdMantissa` without any error: the extra word is simply trailing\n * return data the decoder ignores. Read it here to get all three.\n * @param vToken The market to query\n * @return isListed True once the market has been added to the pool\n * @return collateralFactorMantissa The most an account may borrow against this collateral, scaled by 1e18\n * @return liquidationThresholdMantissa The weighting past which the position becomes liquidatable, scaled by 1e18\n */\n function markets(\n address vToken\n ) external view returns (bool isListed, uint256 collateralFactorMantissa, uint256 liquidationThresholdMantissa);\n\n /**\n * @notice Whether a market may be liquidated while the borrower is still above the liquidation threshold\n * @dev Read in `preLiquidateHook`. While this is set the close factor no longer bounds the repayment, so a\n * position in the market can be closed in full in one call.\n * @param vToken The market to read the setting of\n * @return enabled True if this market allows liquidation without a shortfall\n */\n function isForcedLiquidationEnabled(address vToken) external view returns (bool enabled);\n\n /**\n * @notice The most underlying a market will hold before it stops accepting supply\n * @dev `type(uint256).max` disables the check. Zero is a real cap of zero rather than an \"unset\" sentinel:\n * `preMintHook` compares `nextTotalSupply > supplyCap`, so a market left at zero rejects every mint.\n * @param vToken The market to query\n * @return cap The market's supply cap, in underlying units\n */\n function supplyCaps(address vToken) external view returns (uint256 cap);\n\n /**\n * @notice Whether a market accepts supply only from the accounts on its supply allowlist\n * @dev Enforced in `preMintHook` alone. Redeeming is never restricted, and a market that has never had this\n * enabled accepts supply from anyone.\n * @param vToken The market to read the setting of\n * @return enabled True if the market's supply allowlist is armed\n */\n function isSupplyAllowlistEnabled(address vToken) external view returns (bool enabled);\n\n /**\n * @notice Whether an account may supply to a market while that market's supply allowlist is enabled\n * @dev The account this meters is the one CREDITED with the newly minted vTokens, not the one paying for them:\n * `mintBehalf` lets a third party fund a mint attributed to someone else, and metering the recipient is what\n * bounds the market's supply.\n * @param vToken The market whose allowlist to read\n * @param supplier The account to test\n * @return allowed True if `supplier` may be credited with this market's vTokens\n */\n function isAllowedSupplier(address vToken, address supplier) external view returns (bool allowed);\n\n /**\n * @notice Whether seizing collateral in this pool is restricted to the accounts on the liquidation allowlist\n * @dev Pool-wide rather than per market, because `healAccount` seizes across every market the borrower is in and\n * so cannot attribute a seizure to a single one of them.\n * @return enabled True if the pool's liquidation allowlist is armed\n */\n function isLiquidationAllowlistEnabled() external view returns (bool enabled);\n\n /**\n * @notice Whether an account may seize collateral in this pool while the liquidation allowlist is enabled\n * @param liquidator The account to test\n * @return allowed True if `liquidator` may seize collateral in this pool\n */\n function isAllowedLiquidator(address liquidator) external view returns (bool allowed);\n\n /**\n * @notice The discount a market has of its own, or zero when it takes the pool-wide one\n * @dev Zero is the \"unset\" sentinel rather than a real discount. Read `effectiveLiquidationIncentive` to get the\n * value that actually applies to a market.\n * @param vToken The collateral market to read the discount of\n * @return incentiveMantissa The market's own discount scaled by 1e18, or zero if it has none\n */\n function liquidationIncentives(address vToken) external view returns (uint256 incentiveMantissa);\n\n /**\n * @notice The discount that applies to a market: its own if it has one, otherwise the pool-wide value\n * @param vToken The collateral market to read the discount of\n * @return incentiveMantissa The discount that prices this market's collateral, scaled by 1e18\n */\n function effectiveLiquidationIncentive(address vToken) external view returns (uint256 incentiveMantissa);\n\n /**\n * @notice The oracle that bounds an asset's price against a recent window\n * @dev Read only where the collateral factor weights a position. The liquidation-threshold paths stay on the\n * `oracle`, because they route liquidations. Zero until governance sets it, and while it is zero borrowing and\n * redeeming fail closed.\n * @return boundedOracle The deviation-bounded oracle this pool prices borrowing capacity through\n */\n function deviationBoundedOracle() external view returns (IDeviationBoundedOracle boundedOracle);\n\n /**\n * @notice Whether an action is paused on a market\n * @dev Typed as `uint8` rather than `Action` so that a consumer can read this without importing the enum. The\n * encoding is identical; the deployed ordering is\n * `{ MINT, REDEEM, BORROW, REPAY, SEIZE, LIQUIDATE, TRANSFER, ENTER_MARKET, EXIT_MARKET }`.\n * @param market The market to query\n * @param action The action, as its position in `Action`\n * @return paused True if the action is paused on this market\n */\n function actionPaused(address market, uint8 action) external view returns (bool paused);\n}\n" + }, + "contracts/Spoke/SpokeComptrollerStorage.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\n\nimport { ResilientOracleInterface } from \"@venusprotocol/oracle/contracts/interfaces/OracleInterface.sol\";\nimport { IDeviationBoundedOracle } from \"@venusprotocol/oracle/contracts/interfaces/IDeviationBoundedOracle.sol\";\n\nimport { VToken } from \"../VToken.sol\";\nimport { RewardsDistributor } from \"../Rewards/RewardsDistributor.sol\";\nimport { Action } from \"../ComptrollerInterface.sol\";\n\n/**\n * @title SpokeComptrollerStorage\n * @author Venus\n * @notice Storage layout for the `SpokeComptroller` contract.\n * @dev Fork of `ComptrollerStorage` (`contracts/ComptrollerStorage.sol`), kept separate so the spoke layout and the\n * `AccountLiquiditySnapshot` struct can change without touching the shared implementation. Re-synced by hand when\n * `ComptrollerStorage` changes, like the implementation it accompanies.\n */\ncontract SpokeComptrollerStorage {\n struct LiquidationOrder {\n VToken vTokenCollateral;\n VToken vTokenBorrowed;\n uint256 repayAmount;\n }\n\n /// @dev `totalCollateral` and `maxClearableDebt` are only meaningful under\n /// `WeightFunction.USE_LIQUIDATION_THRESHOLD`, which is the weighting every caller that reads them passes. Under\n /// the collateral factor `totalCollateral` is derived from the deviation-bounded collateral price rather than spot,\n /// and `maxClearableDebt` is not accumulated at all, so it stays at zero. A new caller on that weighting must not\n /// start reading either one without deciding what it wants first: a zero `maxClearableDebt` puts `healAccount` on a\n /// repayment percentage of zero, which forgives the whole position as bad debt.\n struct AccountLiquiditySnapshot {\n uint256 totalCollateral;\n uint256 weightedCollateral;\n uint256 borrows;\n uint256 effects;\n uint256 liquidity;\n uint256 shortfall;\n // The largest borrow value the account's collateral can clear without leaving bad debt behind, computed as\n // the sum over the account's collateral markets of `collateralValue / liquidationIncentive`, each market at\n // its own incentive. Used to route an under-threshold account between `liquidateAccount` and `healAccount`.\n uint256 maxClearableDebt;\n }\n\n struct RewardSpeeds {\n address rewardToken;\n uint256 supplySpeed;\n uint256 borrowSpeed;\n }\n\n struct Market {\n // Whether or not this market is listed\n bool isListed;\n // Multiplier representing the most one can borrow against their collateral in this market.\n // For instance, 0.9 to allow borrowing 90% of collateral value.\n // Must be between 0 and 1, and stored as a mantissa.\n uint256 collateralFactorMantissa;\n // Multiplier representing the collateralization after which the borrow is eligible\n // for liquidation. For instance, 0.8 liquidate when the borrow is 80% of collateral\n // value. Must be between 0 and collateral factor, stored as a mantissa.\n uint256 liquidationThresholdMantissa;\n // Per-market mapping of \"accounts in this asset\"\n mapping(address => bool) accountMembership;\n }\n\n /**\n * @notice Oracle which gives the price of any given asset\n */\n ResilientOracleInterface public oracle;\n\n /**\n * @notice Multiplier used to calculate the maximum repayAmount when liquidating a borrow\n */\n uint256 public closeFactorMantissa;\n\n /**\n * @notice Multiplier representing the discount on collateral that a liquidator receives, applied to every market\n * that has no discount of its own\n * @dev Internal rather than public, because the `liquidationIncentiveMantissa()` getter has to resolve the\n * caller's market before answering: `VToken` reads that getter on itself and needs the discount that prices its\n * own collateral, not the pool-wide default. See `SpokeComptroller.liquidationIncentiveMantissa`.\n */\n uint256 internal _poolLiquidationIncentiveMantissa;\n\n /**\n * @notice Per-account mapping of \"assets you are in\"\n */\n mapping(address => VToken[]) public accountAssets;\n\n /**\n * @notice Official mapping of vTokens -> Market metadata\n * @dev Used e.g. to determine if a market is supported\n */\n mapping(address => Market) public markets;\n\n /// @notice A list of all markets\n VToken[] public allMarkets;\n\n /// @notice Borrow caps enforced by borrowAllowed for each vToken address. Defaults to zero which restricts borrowing.\n mapping(address => uint256) public borrowCaps;\n\n /// @notice Minimal collateral required for regular (non-batch) liquidations\n uint256 public minLiquidatableCollateral;\n\n /// @notice Supply caps enforced by mintAllowed for each vToken address. Defaults to zero which corresponds to minting not allowed\n mapping(address => uint256) public supplyCaps;\n\n /// @notice True if a certain action is paused on a certain market\n mapping(address => mapping(Action => bool)) internal _actionPaused;\n\n // List of Reward Distributors added\n RewardsDistributor[] internal rewardsDistributors;\n\n // Used to check if rewards distributor is added\n mapping(address => bool) internal rewardsDistributorExists;\n\n /// @notice Flag indicating whether forced liquidation enabled for a market\n mapping(address => bool) public isForcedLiquidationEnabled;\n\n uint256 internal constant NO_ERROR = 0;\n\n // closeFactorMantissa must be strictly greater than this value\n uint256 internal constant MIN_CLOSE_FACTOR_MANTISSA = 0.05e18; // 0.05\n\n // closeFactorMantissa must not exceed this value\n uint256 internal constant MAX_CLOSE_FACTOR_MANTISSA = 0.9e18; // 0.9\n\n // No collateralFactorMantissa may exceed this value\n uint256 internal constant MAX_COLLATERAL_FACTOR_MANTISSA = 0.95e18; // 0.95\n\n // No pool-wide liquidation incentive may fall below this value. It is `1e18 + VToken`'s\n // `DEFAULT_PROTOCOL_SEIZE_SHARE_MANTISSA`, restated here because that constant is internal to `VToken` and a\n // market's share is only readable once the market exists. A newly listed market carries the default share and no\n // incentive of its own, so this floor is what keeps its first liquidation from paying the liquidator less than it\n // repaid. It is a backstop for that case, not a full guarantee: a market whose share is later raised above the\n // default needs an incentive of its own, which `setMarketLiquidationIncentive` and `VToken.setProtocolSeizeShare`\n // bound from both sides.\n uint256 internal constant MIN_POOL_LIQUIDATION_INCENTIVE_MANTISSA = 1.05e18; // 1e18 + 0.05e18\n\n /// @notice Whether the delegate is allowed to borrow or redeem on behalf of the user\n //mapping(address user => mapping (address delegate => bool approved)) public approvedDelegates;\n mapping(address => mapping(address => bool)) public approvedDelegates;\n\n /// @notice Whether a market accepts supply only from the accounts on its supply allowlist. Keyed by market, and\n /// disabled by default, so a newly listed market accepts supply from anyone.\n mapping(address => bool) public isSupplyAllowlistEnabled;\n\n /// @notice The accounts a market accepts supply from while its supply allowlist is enabled. Keyed by market,\n /// then by account.\n mapping(address => mapping(address => bool)) public isAllowedSupplier;\n\n /// @notice Whether seizing collateral in this pool is restricted to the accounts on the liquidation allowlist.\n /// Disabled by default.\n /// @dev Pool-wide rather than per market, because `healAccount` seizes across every market the borrower is in and\n /// so cannot attribute a seizure to a single one of them.\n bool public isLiquidationAllowlistEnabled;\n\n /// @notice The accounts allowed to seize collateral in this pool while the liquidation allowlist is enabled\n mapping(address => bool) public isAllowedLiquidator;\n\n /// @notice Per-market discount a liquidator receives on the collateral it seizes, scaled by 1e18. Keyed by the\n /// collateral market, since that is what the discount prices.\n /// @dev Zero means no market value has been set, in which case `_poolLiquidationIncentiveMantissa` applies. That\n /// pool-wide value is always at least 1e18 for a listed market: `PoolRegistry.addMarket` refuses to add one to an\n /// unregistered pool, and registering a pool goes through `setLiquidationIncentive`, which enforces the floor.\n mapping(address => uint256) public liquidationIncentives;\n\n /// @notice Oracle that bounds an asset's price against a recent window, so a deviating print cannot inflate\n /// borrowing capacity. Read only where the collateral factor weights the position; the liquidation-threshold\n /// paths stay on `oracle`, because they route liquidations.\n IDeviationBoundedOracle public deviationBoundedOracle;\n\n /**\n * @dev This empty reserved space is put in place to allow future versions to add new\n * variables without shifting down storage in the inheritance chain.\n * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\n * The size is derived from the 47 slots `ComptrollerStorage` reserves: plus one for the Prime token slot, which\n * is unused here and is returned to the gap rather than left as a hole, minus the six slots the allowlists, the\n * per-market liquidation incentives and the deviation-bounded oracle above take. This contract therefore occupies\n * the same number of slots as the one it was forked from.\n *\n * That is not layout compatibility. Reclaiming the Prime slot moved every variable declared after it up by one,\n * so `approvedDelegates` here sits in the slot `ComptrollerStorage` gives to `prime`, and the two layouts cannot\n * be swapped under a live pool in either direction. They do not have to be: a spoke pool upgrades through its own\n * beacon. `tests/hardhat/Spoke/storageLayout.ts` pins the slot list, the divergence and this gap size.\n */\n uint256[42] private __gap;\n}\n" + }, + "contracts/test/ComptrollerHarness.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity ^0.8.10;\n\nimport { ResilientOracleInterface } from \"@venusprotocol/oracle/contracts/interfaces/OracleInterface.sol\";\n\nimport { Comptroller } from \"../Comptroller.sol\";\n\ncontract ComptrollerHarness is Comptroller {\n uint256 public blockNumber;\n\n // solhint-disable-next-line no-empty-blocks\n constructor(address _poolRegistry) Comptroller(_poolRegistry) {}\n\n function harnessFastForward(uint256 blocks) public returns (uint256) {\n blockNumber += blocks;\n return blockNumber;\n }\n\n function setBlockNumber(uint256 number) public {\n blockNumber = number;\n }\n}\n\ncontract EchoTypesComptroller {\n function stringy(string memory s) public pure returns (string memory) {\n return s;\n }\n\n function addresses(address a) public pure returns (address) {\n return a;\n }\n\n function booly(bool b) public pure returns (bool) {\n return b;\n }\n\n function listOInts(uint256[] memory u) public pure returns (uint256[] memory) {\n return u;\n }\n\n function reverty() public pure {\n require(false, \"gotcha sucka\");\n }\n}\n" + }, + "contracts/test/ComptrollerScenario.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity ^0.8.10;\n\nimport { Comptroller } from \"../Comptroller.sol\";\nimport { VToken } from \"../VToken.sol\";\n\ncontract ComptrollerScenario is Comptroller {\n uint256 public blockNumber;\n\n // solhint-disable-next-line no-empty-blocks\n constructor(address _poolRegistry) Comptroller(_poolRegistry) {}\n\n function fastForward(uint256 blocks) public returns (uint256) {\n blockNumber += blocks;\n return blockNumber;\n }\n\n function setBlockNumber(uint256 number) public {\n blockNumber = number;\n }\n\n function unlist(VToken vToken) public {\n markets[address(vToken)].isListed = false;\n }\n\n function membershipLength(VToken vToken) public view returns (uint256) {\n return accountAssets[address(vToken)].length;\n }\n}\n" + }, + "contracts/test/ERC20.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity ^0.8.10;\n\nimport { SafeMath } from \"./SafeMath.sol\";\n\ninterface ERC20Base {\n event Approval(address indexed owner, address indexed spender, uint256 value);\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n function approve(address spender, uint256 value) external returns (bool);\n\n function totalSupply() external view returns (uint256);\n\n function allowance(address owner, address spender) external view returns (uint256);\n\n function balanceOf(address who) external view returns (uint256);\n}\n\nabstract contract ERC20 is ERC20Base {\n function transfer(address to, uint256 value) external virtual returns (bool);\n\n function transferFrom(address from, address to, uint256 value) external virtual returns (bool);\n}\n\nabstract contract ERC20NS is ERC20Base {\n function transfer(address to, uint256 value) external virtual;\n\n function transferFrom(address from, address to, uint256 value) external virtual;\n}\n\n/**\n * @title Standard ERC20 token\n * @dev Implementation of the basic standard token.\n * See https://github.com/ethereum/EIPs/issues/20\n */\ncontract StandardToken is ERC20 {\n using SafeMath for uint256;\n\n string public name;\n string public symbol;\n uint8 public decimals;\n uint256 public override totalSupply;\n mapping(address => mapping(address => uint256)) public override allowance;\n mapping(address => uint256) public override balanceOf;\n\n constructor(uint256 _initialAmount, string memory _tokenName, uint8 _decimalUnits, string memory _tokenSymbol) {\n totalSupply = _initialAmount;\n balanceOf[msg.sender] = _initialAmount;\n name = _tokenName;\n symbol = _tokenSymbol;\n decimals = _decimalUnits;\n }\n\n function transfer(address dst, uint256 amount) external virtual override returns (bool) {\n balanceOf[msg.sender] = balanceOf[msg.sender].sub(amount, \"Insufficient balance\");\n balanceOf[dst] = balanceOf[dst].add(amount, \"Balance overflow\");\n emit Transfer(msg.sender, dst, amount);\n return true;\n }\n\n function transferFrom(address src, address dst, uint256 amount) external virtual override returns (bool) {\n allowance[src][msg.sender] = allowance[src][msg.sender].sub(amount, \"Insufficient allowance\");\n balanceOf[src] = balanceOf[src].sub(amount, \"Insufficient balance\");\n balanceOf[dst] = balanceOf[dst].add(amount, \"Balance overflow\");\n emit Transfer(src, dst, amount);\n return true;\n }\n\n function approve(address _spender, uint256 amount) external virtual override returns (bool) {\n allowance[msg.sender][_spender] = amount;\n emit Approval(msg.sender, _spender, amount);\n return true;\n }\n}\n\n/**\n * @title Non-Standard ERC20 token\n * @dev Version of ERC20 with no return values for `transfer` and `transferFrom`\n * See https://medium.com/coinmonks/missing-return-value-bug-at-least-130-tokens-affected-d67bf08521ca\n */\ncontract NonStandardToken is ERC20NS {\n using SafeMath for uint256;\n\n string public name;\n uint8 public decimals;\n string public symbol;\n uint256 public override totalSupply;\n mapping(address => mapping(address => uint256)) public override allowance;\n mapping(address => uint256) public override balanceOf;\n\n constructor(uint256 _initialAmount, string memory _tokenName, uint8 _decimalUnits, string memory _tokenSymbol) {\n totalSupply = _initialAmount;\n balanceOf[msg.sender] = _initialAmount;\n name = _tokenName;\n symbol = _tokenSymbol;\n decimals = _decimalUnits;\n }\n\n function transfer(address dst, uint256 amount) external override {\n balanceOf[msg.sender] = balanceOf[msg.sender].sub(amount, \"Insufficient balance\");\n balanceOf[dst] = balanceOf[dst].add(amount, \"Balance overflow\");\n emit Transfer(msg.sender, dst, amount);\n }\n\n function transferFrom(address src, address dst, uint256 amount) external override {\n allowance[src][msg.sender] = allowance[src][msg.sender].sub(amount, \"Insufficient allowance\");\n balanceOf[src] = balanceOf[src].sub(amount, \"Insufficient balance\");\n balanceOf[dst] = balanceOf[dst].add(amount, \"Balance overflow\");\n emit Transfer(src, dst, amount);\n }\n\n function approve(address _spender, uint256 amount) external override returns (bool) {\n allowance[msg.sender][_spender] = amount;\n emit Approval(msg.sender, _spender, amount);\n return true;\n }\n}\n\ncontract ERC20Harness is StandardToken {\n using SafeMath for uint256;\n // To support testing, we can specify addresses for which transferFrom should fail and return false\n mapping(address => bool) public failTransferFromAddresses;\n\n // To support testing, we allow the contract to always fail `transfer`.\n mapping(address => bool) public failTransferToAddresses;\n\n constructor(\n uint256 _initialAmount,\n string memory _tokenName,\n uint8 _decimalUnits,\n string memory _tokenSymbol\n )\n StandardToken(_initialAmount, _tokenName, _decimalUnits, _tokenSymbol)\n /* solhint-disable-next-line no-empty-blocks */\n {\n\n }\n\n function transfer(address dst, uint256 amount) external override returns (bool success) {\n // Added for testing purposes\n if (failTransferToAddresses[dst]) {\n return false;\n }\n balanceOf[msg.sender] = balanceOf[msg.sender].sub(amount, \"Insufficient balance\");\n balanceOf[dst] = balanceOf[dst].add(amount, \"Balance overflow\");\n emit Transfer(msg.sender, dst, amount);\n return true;\n }\n\n function transferFrom(address src, address dst, uint256 amount) external override returns (bool success) {\n // Added for testing purposes\n if (failTransferFromAddresses[src]) {\n return false;\n }\n allowance[src][msg.sender] = allowance[src][msg.sender].sub(amount, \"Insufficient allowance\");\n balanceOf[src] = balanceOf[src].sub(amount, \"Insufficient balance\");\n balanceOf[dst] = balanceOf[dst].add(amount, \"Balance overflow\");\n emit Transfer(src, dst, amount);\n return true;\n }\n\n function harnessSetFailTransferFromAddress(address src, bool _fail) public {\n failTransferFromAddresses[src] = _fail;\n }\n\n function harnessSetFailTransferToAddress(address dst, bool _fail) public {\n failTransferToAddresses[dst] = _fail;\n }\n\n function harnessSetBalance(address _account, uint256 _amount) public {\n balanceOf[_account] = _amount;\n }\n}\n" + }, + "contracts/test/EvilToken.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity ^0.8.10;\n\nimport { FaucetToken } from \"./FaucetToken.sol\";\nimport { SafeMath } from \"./SafeMath.sol\";\n\n/**\n * @title The Compound Evil Test Token\n * @author Compound\n * @notice A simple test token that fails certain operations\n */\ncontract EvilToken is FaucetToken {\n using SafeMath for uint256;\n\n bool public fail;\n\n constructor(\n uint256 _initialAmount,\n string memory _tokenName,\n uint8 _decimalUnits,\n string memory _tokenSymbol\n ) FaucetToken(_initialAmount, _tokenName, _decimalUnits, _tokenSymbol) {\n fail = true;\n }\n\n function setFail(bool _fail) external {\n fail = _fail;\n }\n\n function transfer(address dst, uint256 amount) external override returns (bool) {\n if (fail) {\n return false;\n }\n balanceOf[msg.sender] = balanceOf[msg.sender].sub(amount);\n balanceOf[dst] = balanceOf[dst].add(amount);\n emit Transfer(msg.sender, dst, amount);\n return true;\n }\n\n function transferFrom(address src, address dst, uint256 amount) external override returns (bool) {\n if (fail) {\n return false;\n }\n balanceOf[src] = balanceOf[src].sub(amount);\n balanceOf[dst] = balanceOf[dst].add(amount);\n allowance[src][msg.sender] = allowance[src][msg.sender].sub(amount);\n emit Transfer(src, dst, amount);\n return true;\n }\n}\n" + }, + "contracts/test/FaucetToken.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity ^0.8.10;\n\nimport { StandardToken, NonStandardToken } from \"./ERC20.sol\";\nimport { SafeMath } from \"./SafeMath.sol\";\n\n/**\n * @title The Compound Faucet Test Token\n * @author Compound\n * @notice A simple test token that lets anyone get more of it.\n */\ncontract FaucetToken is StandardToken {\n constructor(\n uint256 _initialAmount,\n string memory _tokenName,\n uint8 _decimalUnits,\n string memory _tokenSymbol\n )\n StandardToken(_initialAmount, _tokenName, _decimalUnits, _tokenSymbol)\n /* solhint-disable-next-line no-empty-blocks */\n {\n\n }\n\n function allocateTo(address _owner, uint256 value) public {\n balanceOf[_owner] += value;\n totalSupply += value;\n emit Transfer(address(this), _owner, value);\n }\n}\n\n/**\n * @title The Compound Faucet Test Token (non-standard)\n * @author Compound\n * @notice A simple test token that lets anyone get more of it.\n */\ncontract FaucetNonStandardToken is NonStandardToken {\n constructor(\n uint256 _initialAmount,\n string memory _tokenName,\n uint8 _decimalUnits,\n string memory _tokenSymbol\n )\n NonStandardToken(_initialAmount, _tokenName, _decimalUnits, _tokenSymbol)\n /* solhint-disable-next-line no-empty-blocks */\n {\n\n }\n\n function allocateTo(address _owner, uint256 value) public {\n balanceOf[_owner] += value;\n totalSupply += value;\n emit Transfer(address(this), _owner, value);\n }\n}\n\n/**\n * @title The Compound Faucet Re-Entrant Test Token\n * @author Compound\n * @notice A test token that is malicious and tries to re-enter callers\n */\ncontract FaucetTokenReEntrantHarness {\n using SafeMath for uint256;\n\n string public name;\n string public symbol;\n uint8 public decimals;\n uint256 private totalSupply_;\n mapping(address => mapping(address => uint256)) private allowance_;\n mapping(address => uint256) private balanceOf_;\n\n bytes public reEntryCallData;\n string public reEntryFun;\n\n event Transfer(address indexed from, address indexed to, uint256 value);\n event Approval(address indexed owner, address indexed spender, uint256 value);\n\n modifier reEnter(string memory funName) {\n string memory _reEntryFun = reEntryFun;\n if (compareStrings(_reEntryFun, funName)) {\n reEntryFun = \"\"; // Clear re-entry fun\n // solhint-disable-next-line avoid-low-level-calls\n (bool success, bytes memory returndata) = msg.sender.call(reEntryCallData);\n // solhint-disable-next-line no-inline-assembly\n assembly {\n if eq(success, 0) {\n revert(add(returndata, 0x20), returndatasize())\n }\n }\n }\n\n _;\n }\n\n constructor(\n uint256 _initialAmount,\n string memory _tokenName,\n uint8 _decimalUnits,\n string memory _tokenSymbol,\n bytes memory _reEntryCallData,\n string memory _reEntryFun\n ) {\n totalSupply_ = _initialAmount;\n balanceOf_[msg.sender] = _initialAmount;\n name = _tokenName;\n symbol = _tokenSymbol;\n decimals = _decimalUnits;\n reEntryCallData = _reEntryCallData;\n reEntryFun = _reEntryFun;\n }\n\n function allocateTo(address _owner, uint256 value) public {\n balanceOf_[_owner] += value;\n totalSupply_ += value;\n emit Transfer(address(this), _owner, value);\n }\n\n function totalSupply() public reEnter(\"totalSupply\") returns (uint256) {\n return totalSupply_;\n }\n\n function allowance(address owner, address spender) public reEnter(\"allowance\") returns (uint256 remaining) {\n return allowance_[owner][spender];\n }\n\n function approve(address spender, uint256 amount) public reEnter(\"approve\") returns (bool success) {\n _approve(msg.sender, spender, amount);\n return true;\n }\n\n function balanceOf(address owner) public reEnter(\"balanceOf\") returns (uint256 balance) {\n return balanceOf_[owner];\n }\n\n function transfer(address dst, uint256 amount) public reEnter(\"transfer\") returns (bool success) {\n _transfer(msg.sender, dst, amount);\n return true;\n }\n\n function transferFrom(\n address src,\n address dst,\n uint256 amount\n ) public reEnter(\"transferFrom\") returns (bool success) {\n _transfer(src, dst, amount);\n _approve(src, msg.sender, allowance_[src][msg.sender].sub(amount));\n return true;\n }\n\n function _approve(address owner, address spender, uint256 amount) internal {\n require(spender != address(0), \"FaucetToken: approve to the zero address\");\n require(owner != address(0), \"FaucetToken: approve from the zero address\");\n allowance_[owner][spender] = amount;\n emit Approval(owner, spender, amount);\n }\n\n function _transfer(address src, address dst, uint256 amount) internal {\n require(dst != address(0), \"FaucetToken: transfer to the zero address\");\n balanceOf_[src] = balanceOf_[src].sub(amount);\n balanceOf_[dst] = balanceOf_[dst].add(amount);\n emit Transfer(src, dst, amount);\n }\n\n function compareStrings(string memory a, string memory b) internal pure returns (bool) {\n return keccak256(abi.encodePacked((a))) == keccak256(abi.encodePacked((b)));\n }\n}\n" + }, + "contracts/test/FeeToken.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity ^0.8.10;\n\nimport { FaucetToken } from \"./FaucetToken.sol\";\nimport { SafeMath } from \"./SafeMath.sol\";\n\n/**\n * @title Fee Token\n * @author Compound\n * @notice A simple test token that charges fees on transfer. Used to mock USDT.\n */\ncontract FeeToken is FaucetToken {\n using SafeMath for uint256;\n\n uint256 public basisPointFee;\n address public owner;\n\n constructor(\n uint256 _initialAmount,\n string memory _tokenName,\n uint8 _decimalUnits,\n string memory _tokenSymbol,\n uint256 _basisPointFee,\n address _owner\n ) FaucetToken(_initialAmount, _tokenName, _decimalUnits, _tokenSymbol) {\n basisPointFee = _basisPointFee;\n owner = _owner;\n }\n\n function transfer(address dst, uint256 amount) public override returns (bool) {\n uint256 fee = amount.mul(basisPointFee).div(10000);\n uint256 net = amount.sub(fee);\n balanceOf[owner] = balanceOf[owner].add(fee);\n balanceOf[msg.sender] = balanceOf[msg.sender].sub(amount);\n balanceOf[dst] = balanceOf[dst].add(net);\n emit Transfer(msg.sender, dst, amount);\n return true;\n }\n\n function transferFrom(address src, address dst, uint256 amount) public override returns (bool) {\n uint256 fee = amount.mul(basisPointFee).div(10000);\n uint256 net = amount.sub(fee);\n balanceOf[owner] = balanceOf[owner].add(fee);\n balanceOf[src] = balanceOf[src].sub(amount);\n balanceOf[dst] = balanceOf[dst].add(net);\n allowance[src][msg.sender] = allowance[src][msg.sender].sub(amount);\n emit Transfer(src, dst, amount);\n return true;\n }\n}\n" + }, + "contracts/test/HarnessMaxLoopsLimitHelper.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\n\nimport { MaxLoopsLimitHelper } from \"../MaxLoopsLimitHelper.sol\";\n\ncontract HarnessMaxLoopsLimitHelper is MaxLoopsLimitHelper {\n function setMaxLoopsLimit(uint256 limit) external {\n _setMaxLoopsLimit(limit);\n }\n\n function ensureMaxLoops(uint256 limit) external view {\n _ensureMaxLoops(limit);\n }\n}\n" + }, + "contracts/test/HubSpoke/AdapterSpokeV1.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { SafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport { IResourceAdapter } from \"./interfaces/IResourceAdapter.sol\";\nimport { IVTokenIsolated } from \"./interfaces/external/IVTokenIsolated.sol\";\nimport { ISpokeComptroller } from \"./interfaces/external/ISpokeComptroller.sol\";\n\n/**\n * @title AdapterSpokeV1\n * @author Venus\n * @notice Stateless adapter for the liquidity side of a hub-funded spoke pool — an isolated-pools\n * `VToken` governed by a `SpokeComptroller`. ONE deployment serves every YieldGroup that\n * registers a spoke market; there is no per-(YieldGroup, market) instance, no proxy, no\n * clone factory.\n * @dev Dispatch model: mutating functions (`deposit`, `withdraw`) MUST be invoked via DELEGATECALL\n * from a YieldGroup. They execute in the YieldGroup's storage context, so vTokens are credited\n * to / burned from the YieldGroup, not this adapter. View functions are invoked via normal\n * CALL / STATICCALL with an explicit `holder` argument.\n *\n * Storage-safety invariants enforced by this contract:\n * 1. Zero `internal`/`private`/`public` state variables are declared. Only `immutable` values\n * are used. Immutables live in bytecode, not storage slots, so they're delegatecall-safe.\n * 2. No inline assembly performs `sstore`.\n * 3. All external calls go to the supplied `resource` (vToken), its `underlying()`, or its\n * `comptroller()`. No arbitrary user-supplied call target.\n * 4. {onlyDelegateCall} reverts when a mutating function is invoked directly on the adapter,\n * preventing accidents that would orphan vTokens here.\n *\n * **Why this is not `AdapterCoreV1`.** The two speak nearly the same selectors and disagree on\n * every number that matters. Four differences, each verified against `isolated-pools`:\n *\n * - **NAV excludes `badDebt`.** The market's exchange rate keeps `badDebt` in its numerator, so\n * it does not fall when `healAccount` writes a loss off; the loss surfaces only as redemptions\n * failing for want of cash. Valuing at that rate would report unrecoverable value, and inside\n * the Hub the loss would then land by exit order — early LPs out at the pre-loss share price,\n * the last one absorbing everything. {totalAssets} therefore values the position off the\n * components with `badDebt` excluded, which marks every LP down at the same instant. The mark\n * is pro-rata (`balance / totalSupply`), so it stays correct whether or not the Hub is the\n * market's only supplier. A Shortfall auction that later recovers the debt raises cash and\n * lowers `badDebt` by the same amount, so the mark recovers on its own.\n * - **Liquidity is cash NET of reserves**, floored to what a whole number of vTokens is worth\n * and then checked against the market's own redeem arithmetic, so a certified amount is\n * always one the market will actually settle.\n * - **No exit fee to gross up.** Isolated pools have no `treasuryPercent`; their cut is the\n * reserve factor, already netted out of the exchange rate. There is nothing unmodeled to\n * reject at registration, so {validateRegistration} guards the supply allowlist instead.\n * - **The supply-cap sentinels are inverted** (see {ISpokeComptroller-supplyCaps}).\n *\n * **The supply allowlist and caller identity.** A spoke market can restrict minting to\n * allowlisted accounts, and the account it checks is the one CREDITED with the vTokens. Under\n * delegatecall that is the YieldGroup. {maxDeposit} and {validateRegistration} are the two\n * `IResourceAdapter` members that take no `holder`, and both are invoked by the YieldGroup as a\n * plain call — so `msg.sender` IS the prospective supplier, and reading the allowlist against it\n * is exact rather than a convention. This matters operationally: a YieldGroup whose grant is\n * revoked reports zero room and is routed around, instead of advertising capacity that every\n * deposit then reverts on.\n *\n * Fee-on-transfer underlyings are unsupported, matching the Hub. The market itself tolerates\n * them (it mints against the measured delta) but {deposit} reports the requested amount, so the\n * YieldGroup's accounting would drift.\n */\ncontract AdapterSpokeV1 is IResourceAdapter {\n using SafeERC20 for IERC20;\n\n // ============================== State (immutable only) ====================\n\n /// @notice This adapter's own address, captured at deployment. Used by {onlyDelegateCall} to\n /// detect direct invocation. Immutables live in bytecode, not storage.\n address private immutable _ADAPTER_SELF;\n\n // ============================== State (constants) =========================\n\n /// @notice Fixed-point scale matching Compound's mantissa (`1e18`).\n uint256 public constant EXP_SCALE = 1e18;\n\n /// @notice Deposit room reported for a market whose supply cap is the isolated-pools \"uncapped\"\n /// sentinel (`type(uint256).max`).\n /// @dev Finite on purpose. `YieldGroupBase.maxDeposit()` SUMS the room of every queued resource\n /// in checked arithmetic, so reporting `type(uint256).max` would overflow that sum the\n /// moment a second uncapped market joined the queue — the YieldGroup's whole capacity view\n /// would revert and the Hub would read it as zero capacity with an immovable balance. At\n /// `2^128 - 1` the sum cannot overflow for any realistic queue, while the value is still\n /// unreachable for any real token (~3.4e20 whole units at 18 decimals). Mirrors the Flux\n /// family, where Fluid reports `int128.max` for the same reason. The binding controls on an\n /// uncapped market are the YieldGroup's per-resource cap and the Hub's dual cap, not this.\n uint256 public constant UNCAPPED_DEPOSIT_ROOM = type(uint128).max;\n\n /// @notice Scaling factor between mantissa-rate (`1e18`) and BPS (`1e4`).\n uint256 internal constant MANTISSA_TO_BPS = 1e14;\n\n /// @notice `Action.MINT` in the isolated-pools Comptroller enum.\n uint8 internal constant ACTION_MINT = 0;\n\n /// @notice `Action.REDEEM` in the isolated-pools Comptroller enum.\n uint8 internal constant ACTION_REDEEM = 1;\n\n /// @notice Fraction of raw supply-cap headroom withheld to absorb interest accrued between a\n /// `maxDeposit` read and the mint it sizes (`1/1000` = 0.1%).\n uint256 internal constant CAP_TRIM_DIVISOR = 1000;\n\n // ============================== Errors ===================================\n\n /// @notice A mutating function was invoked directly (not via delegatecall).\n error NotDelegateCall();\n\n /// @notice A vToken `mint` call returned a non-zero error code.\n /// @param resource Market that rejected the mint.\n /// @param errorCode Compound-style error code returned by the market.\n error VTokenMintFailed(address resource, uint256 errorCode);\n\n /// @notice A vToken `redeemUnderlying` call returned a non-zero error code.\n /// @param resource Market that rejected the redeem.\n /// @param errorCode Compound-style error code returned by the market.\n error VTokenRedeemFailed(address resource, uint256 errorCode);\n\n /// @notice A vToken `accrueInterest` call returned a non-zero error code.\n /// @param resource Market that failed to accrue.\n /// @param errorCode Compound-style error code returned by the market.\n error VTokenAccrueFailed(address resource, uint256 errorCode);\n\n /**\n * @notice A redeem succeeded but delivered less than the requested amount. Indicates a\n * fee-on-transfer underlying or a market bug — an isolated-pools redeem otherwise\n * delivers at least what was asked for.\n * @param resource Resource that under-delivered.\n * @param requested Underlying units expected.\n * @param actual Underlying units actually received.\n */\n error VTokenUnderfilled(address resource, uint256 requested, uint256 actual);\n\n /**\n * @notice The deposit is too small to mint a single vToken, so the market would keep the\n * underlying and issue nothing for it.\n * @dev Reverting hands the leg back to the YieldGroup's deposit cascade, which routes around it;\n * minting zero would strand the amount silently. Only reachable for a sub-one-vToken-unit\n * remainder, which {maxDeposit} already declines to advertise.\n * @param resource Market that would have minted zero.\n * @param amount Underlying units offered.\n * @param minimum Underlying units needed to mint one vToken.\n */\n error DepositBelowOneVToken(address resource, uint256 amount, uint256 minimum);\n\n /**\n * @notice The market's supply allowlist is enabled and the prospective supplier is not on it.\n * @param resource Market whose allowlist rejected the supplier.\n * @param supplier Account that would be credited with the vTokens (the YieldGroup).\n */\n error SupplyNotAllowed(address resource, address supplier);\n\n // ============================== Modifiers ================================\n\n /// @notice Reverts {NotDelegateCall} when the call is not arriving via delegatecall.\n /// @dev During a delegatecall, `address(this)` is the caller's address; during a direct call it\n /// equals `_ADAPTER_SELF` (the address captured at construction).\n modifier onlyDelegateCall() {\n if (address(this) == _ADAPTER_SELF) revert NotDelegateCall();\n _;\n }\n\n // ============================== Constructor ==============================\n\n /// @notice Captures the adapter's own address into an immutable for {onlyDelegateCall}.\n constructor() {\n _ADAPTER_SELF = address(this);\n }\n\n // ============================== External — mutating =======================\n\n /// @inheritdoc IResourceAdapter\n /// @dev `address(this)` here is the YieldGroup (delegatecall context), which already holds\n /// `amount` of underlying. We approve the market and mint; delegatecall preserves the caller\n /// context, so the market sees the YieldGroup as `msg.sender` and credits the vTokens there.\n /// That is also the account its supply allowlist checks.\n function deposit(address resource, uint256 amount) external override onlyDelegateCall returns (uint256 deposited) {\n uint256 minimum = _oneVTokenUnit(IVTokenIsolated(resource).exchangeRateStored());\n if (amount < minimum) revert DepositBelowOneVToken(resource, amount, minimum);\n\n IERC20 assetToken = IERC20(IVTokenIsolated(resource).underlying());\n assetToken.forceApprove(resource, amount);\n uint256 errCode = IVTokenIsolated(resource).mint(amount);\n if (errCode != 0) revert VTokenMintFailed(resource, errCode);\n return amount;\n }\n\n /// @inheritdoc IResourceAdapter\n /// @dev `address(this)` here is the YieldGroup, so `redeemUnderlying` burns its vTokens and\n /// credits the underlying back to it. The market over-delivers: it burns\n /// `ceil(request / exchangeRate)` tokens and pays out `truncate(exchangeRate x tokens)`,\n /// which exceeds the request by up to one vToken unit. We forward exactly `amount` and leave\n /// the surplus as idle on the YieldGroup, where `totalAssets` counts it and the next\n /// withdrawal consumes it idle-first — the same treatment `AdapterCoreV1` gives its\n /// gross-up surplus, and what keeps the YieldGroup's `received == requested` invariant true.\n function withdraw(address resource, uint256 amount, address to) external override onlyDelegateCall {\n IERC20 assetToken = IERC20(IVTokenIsolated(resource).underlying());\n\n uint256 exchangeRate = IVTokenIsolated(resource).exchangeRateStored();\n uint256 preBal = assetToken.balanceOf(address(this));\n uint256 errCode = IVTokenIsolated(resource).redeemUnderlying(_bumpToSettleable(exchangeRate, amount));\n if (errCode != 0) revert VTokenRedeemFailed(resource, errCode);\n uint256 received = assetToken.balanceOf(address(this)) - preBal;\n if (received < amount) revert VTokenUnderfilled(resource, amount, received);\n\n assetToken.safeTransfer(to, amount);\n }\n\n /// @inheritdoc IResourceAdapter\n /// @dev Plain CALL, no `onlyDelegateCall`: `accrueInterest` mutates the market's own global\n /// index, not this caller's position, so it is safe (and cheaper) to run in the adapter's\n /// context. After it returns, every view below is fresh to the current block or timestamp.\n function accrue(address resource) external override {\n uint256 errCode = IVTokenIsolated(resource).accrueInterest();\n if (errCode != 0) revert VTokenAccrueFailed(resource, errCode);\n }\n\n // ============================== External — view ==========================\n\n /// @inheritdoc IResourceAdapter\n function asset(address resource) external view override returns (address) {\n return IVTokenIsolated(resource).underlying();\n }\n\n /// @inheritdoc IResourceAdapter\n /// @dev The position's RECOVERABLE value: its pro-rata share of `cash + totalBorrows -\n /// totalReserves`, i.e. the market's exchange rate with `badDebt` excluded from the\n /// numerator. See the contract NatSpec for why the market's own rate is unusable here.\n /// Deliberately conservative in one direction: because a redeem still burns tokens at the\n /// market's un-haircut rate, an exit costs fewer tokens than this mark implies, so the mark\n /// can never overstate what the position delivers.\n function totalAssets(address resource, address holder) external view override returns (uint256) {\n uint256 vBal = IVTokenIsolated(resource).balanceOf(holder);\n if (vBal == 0) return 0;\n return _recoverableValue(resource, vBal);\n }\n\n /// @inheritdoc IResourceAdapter\n /// @dev Honors, in order: the market's supply allowlist (against `msg.sender`, the prospective\n /// supplier — see the contract NatSpec), its MINT pause, and its supply cap. The headroom\n /// math mirrors `preMintHook`'s `nextTotalSupply = totalSupply x exchangeRate + mintAmount`\n /// check and so uses the market's own (badDebt-inclusive) exchange rate, NOT the valuation\n /// basis {totalAssets} uses. Room below one vToken unit is reported as zero, because a\n /// deposit of it would mint nothing.\n function maxDeposit(address resource) external view override returns (uint256) {\n ISpokeComptroller comptrollerContract = ISpokeComptroller(IVTokenIsolated(resource).comptroller());\n if (\n comptrollerContract.isSupplyAllowlistEnabled(resource) &&\n !comptrollerContract.isAllowedSupplier(resource, msg.sender)\n ) {\n return 0;\n }\n if (comptrollerContract.actionPaused(resource, ACTION_MINT)) return 0;\n\n uint256 supplyCap = comptrollerContract.supplyCaps(resource);\n // A cap of zero is a real cap here, not an \"unset\" sentinel: `preMintHook` rejects every\n // non-zero mint against it. The uncapped sentinel is `type(uint256).max`.\n if (supplyCap == 0) return 0;\n if (supplyCap == type(uint256).max) return UNCAPPED_DEPOSIT_ROOM;\n\n uint256 exchangeRate = IVTokenIsolated(resource).exchangeRateStored();\n uint256 supplied = (IVTokenIsolated(resource).totalSupply() * exchangeRate) / EXP_SCALE;\n if (supplied >= supplyCap) return 0;\n\n uint256 room = supplyCap - supplied;\n // `exchangeRateStored` is pre-accrual, but the market re-checks the cap at mint AFTER\n // `accrueInterest` raises the rate, so the raw headroom is slightly optimistic on an\n // interest-accruing near-cap market. Withhold a small conservative margin so a normal accrual\n // between this view and execution cannot push `deposit(maxDeposit())` over the cap. The\n // routing cascade is the backstop for larger staleness (an over-cap leg is skipped, not\n // bubbled), so this only needs to absorb a typical inter-slot accrual.\n room -= room / CAP_TRIM_DIVISOR;\n return room < _oneVTokenUnit(exchangeRate) ? 0 : room;\n }\n\n /// @inheritdoc IResourceAdapter\n /// @dev Two constraints: the position's recoverable value, and the market's payable cash\n /// (`getCash - totalReserves` — reserves sit inside cash but are not redeemable, and\n /// `_redeemFresh` gates on the difference). Subtracting reserves also makes this figure\n /// invariant across the reserve sweep `accrueInterest` performs, which lowers cash and\n /// reserves by the same amount.\n ///\n /// The cash side is floored to what a WHOLE number of vTokens is worth, because a redeem\n /// rounds its burn up: asking for an amount that is not a whole-token multiple pays out the\n /// next token up, and asking for exactly the payable cash would therefore overshoot it and\n /// revert `RedeemTransferOutNotPossible`. Flooring is exact rather than conservative — the\n /// round-up can never exceed the token count this floor is taken at — so unlike a flat\n /// margin it still lets the last vToken out, which is what lets a market be drained to zero\n /// and deregistered.\n ///\n /// The floor is necessary but not sufficient, so the bound is finally checked against the\n /// market's own redeem arithmetic: whatever request {withdraw} would issue for it has to\n /// produce a non-zero payout AND settle within the payable cash, or this reports zero. That\n /// check is exact rather than a safety margin, so a market holding real liquidity still\n /// reports all of it.\n ///\n /// Reads stored state, so the figure is exact only for a caller that has already settled\n /// this market's interest in the same transaction — the Hub's routing paths all do, via\n /// {accrue}. Against an unaccrued market it is optimistic by the reserves the next accrual\n /// will book, since those come out of the payable cash.\n function maxWithdraw(address resource, address holder) external view override returns (uint256) {\n ISpokeComptroller comptrollerContract = ISpokeComptroller(IVTokenIsolated(resource).comptroller());\n if (comptrollerContract.actionPaused(resource, ACTION_REDEEM)) return 0;\n\n uint256 vBal = IVTokenIsolated(resource).balanceOf(holder);\n if (vBal == 0) return 0;\n uint256 ourValue = _recoverableValue(resource, vBal);\n if (ourValue == 0) return 0;\n\n uint256 cash = IVTokenIsolated(resource).getCash();\n uint256 reserves = IVTokenIsolated(resource).totalReserves();\n if (cash <= reserves) return 0;\n\n uint256 exchangeRate = IVTokenIsolated(resource).exchangeRateStored();\n if (exchangeRate == 0) return 0;\n uint256 payableTokens = ((cash - reserves) * EXP_SCALE) / exchangeRate;\n uint256 cashBound = (payableTokens * exchangeRate) / EXP_SCALE;\n\n uint256 liquid = ourValue < cashBound ? ourValue : cashBound;\n if (liquid == 0) return 0;\n\n // Certify the bound only if the redeem it implies actually settles. Re-run the market's own\n // arithmetic on the request {withdraw} would issue for `liquid`, because the whole-vToken\n // floor above is not sufficient on its own in two cases. A bound worth less than one vToken\n // is raised by {_bumpToSettleable} to a request whose round-up burns a SECOND token,\n // doubling the payout past the cash this bound was sized for. And at an exchange rate below\n // `1e18` — normal for an underlying with fewer decimals — the payout can truncate to zero,\n // which the market rejects outright. Mirroring beats a blanket safety margin here: a market\n // holding real liquidity still reports all of it, so a position stays fully drainable.\n uint256 payout = _redeemPayout(exchangeRate, _bumpToSettleable(exchangeRate, liquid));\n if (payout == 0 || payout > cash - reserves) return 0;\n return liquid;\n }\n\n /// @inheritdoc IResourceAdapter\n /// @dev The `blocksPerYear` parameter is IGNORED. An isolated-pools market carries its own\n /// annualiser as an immutable and can be block-based or time-based, so a YieldGroup-level\n /// constant would misprice whichever kind it was not configured for; the market's own value\n /// always matches the unit of its own rate. Deploy the spoke YieldGroup with\n /// `blocksPerYear = 0`, as the Flux family already does.\n function spotAPYBps(address resource, uint256 /* blocksPerYear */) external view override returns (uint64) {\n uint256 annualised = (IVTokenIsolated(resource).supplyRatePerBlock() *\n IVTokenIsolated(resource).blocksOrSecondsPerYear()) / MANTISSA_TO_BPS;\n // forge-lint: disable-next-line(unsafe-typecast)\n return annualised > type(uint64).max ? type(uint64).max : uint64(annualised);\n }\n\n /// @inheritdoc IResourceAdapter\n function receiptBalance(address resource, address holder) external view override returns (uint256) {\n return IVTokenIsolated(resource).balanceOf(holder);\n }\n\n /// @inheritdoc IResourceAdapter\n /// @dev Rejects a market whose supply allowlist is enabled without the registering YieldGroup on\n /// it: minting would revert on every deposit, so the resource would occupy a queue slot it\n /// can never fill. `msg.sender` is the YieldGroup (see the contract NatSpec), which is the\n /// account the market's allowlist gates.\n ///\n /// This doubles as the check that `resource` really belongs to a spoke pool: the allowlist\n /// accessors exist only on `SpokeComptroller`, so the call itself reverts against any other\n /// Comptroller. Nothing else is asserted. In particular an unset `deviationBoundedOracle`\n /// is NOT grounds for rejection: it blocks borrowing, and so the market's yield, but leaves\n /// the Hub's own paths intact — `preMintHook` never reads a price, and `preRedeemHook`\n /// returns before the priced liquidity check for an account that is not a market member,\n /// which a supply-only YieldGroup never becomes.\n function validateRegistration(address resource) external view override {\n ISpokeComptroller comptrollerContract = ISpokeComptroller(IVTokenIsolated(resource).comptroller());\n if (\n comptrollerContract.isSupplyAllowlistEnabled(resource) &&\n !comptrollerContract.isAllowedSupplier(resource, msg.sender)\n ) {\n revert SupplyNotAllowed(resource, msg.sender);\n }\n }\n\n // ============================== Private — view ===========================\n\n /**\n * @notice Value `vBal` vTokens at the market's backing EXCLUDING written-off debt.\n * @dev `vBal x (cash + totalBorrows - totalReserves) / totalSupply`, in one rounding step. This\n * is `exchangeRateStored` with `badDebt` dropped from the numerator.\n * @param resource Market holding the position.\n * @param vBal vToken units held (caller has already established this is non-zero).\n * @return value Recoverable underlying value of the position.\n */\n function _recoverableValue(address resource, uint256 vBal) private view returns (uint256 value) {\n uint256 supply = IVTokenIsolated(resource).totalSupply();\n // Unreachable while `vBal` is non-zero, which every caller has already established. Kept as\n // a division guard rather than an assumption about a contract this one does not own.\n if (supply == 0) return 0;\n\n uint256 backing = IVTokenIsolated(resource).getCash() + IVTokenIsolated(resource).totalBorrows();\n uint256 reserves = IVTokenIsolated(resource).totalReserves();\n // The market keeps `cash >= totalReserves` (both the redeem and borrow paths check cash net\n // of reserves), so this only guards a pathological state rather than an expected one.\n if (backing <= reserves) return 0;\n\n return (vBal * (backing - reserves)) / supply;\n }\n\n /**\n * @notice Underlying value of exactly one vToken, rounded up.\n * @param exchangeRate Market's stored exchange rate, scaled by `1e18`.\n * @return unit `ceil(exchangeRate / 1e18)`.\n */\n function _oneVTokenUnit(uint256 exchangeRate) private pure returns (uint256 unit) {\n return (exchangeRate + EXP_SCALE - 1) / EXP_SCALE;\n }\n\n /**\n * @notice Raise a redeem the market would settle for nothing up to the smallest one it settles.\n * @dev The market burns `floor(amount x 1e18 / exchangeRate)` vTokens, rounds that up when those\n * tokens are not worth exactly `amount`, then truncates the payout back to underlying — and\n * reverts `\"redeemAmount is zero\"` if the payout truncates away. A withdraw cascade can\n * legitimately hand this adapter a dust `amount` (e.g. a 1-wei remainder left after an\n * upstream ERC-4626 resource rounds its own redeem down), which would otherwise revert an\n * entirely valid, within-{maxWithdraw} withdrawal. Redeem the smallest settleable amount\n * instead; {withdraw} still forwards only `amount` and leaves the surplus idle on the\n * YieldGroup. A no-op for normal-sized redeems.\n *\n * The trigger is the PAYOUT truncating to zero, not the burn. Those coincide only while the\n * rate is at or above `1e18`. Below it — the normal state for an underlying with fewer\n * decimals than the vToken, e.g. a 6-decimal stablecoin listed at a `1e16` initial rate — a\n * dust request burns plenty of tokens and still pays out nothing, so a burn-based test\n * would never fire on exactly the markets that need it.\n * @param exchangeRate Market's stored exchange rate, scaled by `1e18`.\n * @param amount Underlying units the caller intends to redeem.\n * @return bumped `amount`, or the value of the fewest vTokens worth a non-zero payout.\n */\n function _bumpToSettleable(uint256 exchangeRate, uint256 amount) private pure returns (uint256 bumped) {\n if (exchangeRate == 0) return amount;\n if (_redeemPayout(exchangeRate, amount) != 0) return amount;\n\n // Fewest vTokens worth at least one unit of underlying, then the smallest request that\n // redeems that many. Reduces to `ceil(exchangeRate / 1e18)` — one whole vToken — whenever the\n // rate is at or above `1e18`.\n uint256 minTokens = (EXP_SCALE + exchangeRate - 1) / exchangeRate;\n return (minTokens * exchangeRate + EXP_SCALE - 1) / EXP_SCALE;\n }\n\n /**\n * @notice Underlying the market would pay out for a `redeemUnderlying(request)` call.\n * @dev A line-for-line mirror of `_redeemFresh`: floor the request into vTokens, round the burn\n * up when that many tokens are not worth exactly the request, then truncate the payout back\n * to underlying. The payout is therefore `>= request` (the over-delivery {withdraw} retains\n * as idle) except when it truncates to `0`, which is the market's own reject condition.\n * Used by {maxWithdraw} to avoid certifying a bound the market will not settle.\n * @param exchangeRate Market's stored exchange rate, scaled by `1e18`. Caller has established\n * this is non-zero.\n * @param request Underlying units that would be passed to `redeemUnderlying`.\n * @return payout Underlying units the market would transfer out.\n */\n function _redeemPayout(uint256 exchangeRate, uint256 request) private pure returns (uint256 payout) {\n uint256 tokens = (request * EXP_SCALE) / exchangeRate;\n uint256 probe = (tokens * exchangeRate) / EXP_SCALE;\n if (probe != 0 && probe != request) ++tokens;\n return (tokens * exchangeRate) / EXP_SCALE;\n }\n}\n" + }, + "contracts/test/HubSpoke/interfaces/external/ISpokeComptroller.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\n\nimport { SpokeComptrollerViewInterface } from \"../../../../Spoke/SpokeComptrollerInterface.sol\";\n\n/**\n * @title ISpokeComptroller\n * @author Venus\n * @notice The name `AdapterSpokeV1` imports for its view of a spoke pool. In `venus-liquidity-hub` this is a\n * hand-written copy of the same four getters, because that repo does not depend on this one. Here the name is\n * bound to this repo's own `SpokeComptrollerViewInterface`, so the adapter compiles against the declarations\n * the pool actually ships rather than against a second copy of them that nothing keeps in step.\n * @dev The hub's copy still exists on its side of the boundary and still has to match. `tests/hardhat/Fork/HubSpoke/\n * interfaces.ts` asserts that it does, selector for selector and return type for return type, against both this\n * interface and the deployed `SpokeComptroller`.\n */\n// solhint-disable-next-line no-empty-blocks\ninterface ISpokeComptroller is SpokeComptrollerViewInterface {\n\n}\n" + }, + "contracts/test/HubSpoke/interfaces/external/IVTokenIsolated.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\n\n/**\n * @title IVTokenIsolated\n * @author Venus\n * @notice Minimal subset of the Venus isolated-pools `VToken` interface needed by the Spoke Yield\n * Group adapter. Kept narrow on purpose, and kept SEPARATE from the Core `IVToken`: the two\n * share most selectors but differ in semantics on every line that matters to an adapter, so\n * one interface serving both would attach the wrong contract to the wrong reader.\n * @dev The four semantic differences from Core's `VBep20`, each verified against\n * `isolated-pools/contracts/VToken.sol`:\n *\n * 1. **`badDebt` is inside the exchange rate.** `_exchangeRateStored` computes\n * `(totalCash + totalBorrows + badDebt - totalReserves) / totalSupply`. `healAccount` moves\n * an unrecoverable shortfall out of `totalBorrows` and into `badDebt`, leaving that\n * numerator unchanged — so the rate does NOT fall when a loss is recorded and no supplier is\n * marked down. Valuing a position at `balance x exchangeRateStored` therefore reports value\n * that can never be redeemed. `AdapterSpokeV1` values the position off the components\n * instead, excluding `badDebt`; that is why `totalBorrows`, `totalReserves` and `badDebt`\n * are declared here at all.\n * 2. **Redeem is bounded by cash NET of reserves.** `_redeemFresh` reverts\n * `RedeemTransferOutNotPossible` when `getCash() - totalReserves < redeemAmount`, not when\n * `getCash() < redeemAmount`. Reserves are not payable liquidity.\n * 3. **`redeemUnderlying` OVER-delivers.** It burns `ceil(redeemAmount / exchangeRate)` tokens\n * and then transfers `truncate(exchangeRate x tokens)`, which is `>= redeemAmount` by up to\n * one vToken unit of underlying. Callers must transfer the exact requested amount onward and\n * retain the surplus; they must NOT assume an exact delivery.\n * 4. **No per-redeem pool fee.** Isolated pools take their cut through `reserveFactorMantissa`\n * (already netted out of the exchange rate) and route it to the ProtocolShareReserve. There\n * is no `treasuryPercent` equivalent, so there is nothing to gross up on redeem.\n *\n * Shared with Core: `mint` pulls `mintAmount` from `msg.sender` and credits vTokens to\n * `msg.sender`; both mutating calls return a Compound-style error code that isolated pools\n * always sets to `NO_ERROR` (failures revert instead); `mintBehalf` /\n * `redeemUnderlyingBehalf` exist but are on no path here, because the adapter is delegatecalled\n * and so is already `msg.sender` from the vToken's perspective.\n */\ninterface IVTokenIsolated {\n // -------------------------------- Mutating -------------------------------\n\n /**\n * @notice Supply `mintAmount` of underlying and receive vTokens.\n * @dev Accrues interest first, so the supply-cap check in `preMintHook` runs against a\n * POST-accrual exchange rate — one notch tighter than the rate a `maxDeposit` view reads.\n * @param mintAmount Underlying units to deposit.\n * @return errorCode Compound-style error code; always `0` (failures revert).\n */\n function mint(uint256 mintAmount) external returns (uint256 errorCode);\n\n /**\n * @notice Burn vTokens and receive AT LEAST `redeemAmount` of underlying.\n * @dev Over-delivers by up to one vToken unit of underlying (see note 3 in the contract NatSpec).\n * Reverts `\"redeemAmount is zero\"` when `redeemAmount x 1e18 < exchangeRateStored()`, i.e.\n * when the burn would round to zero tokens.\n * @param redeemAmount Underlying units to withdraw.\n * @return errorCode Compound-style error code; always `0` (failures revert).\n */\n function redeemUnderlying(uint256 redeemAmount) external returns (uint256 errorCode);\n\n /**\n * @notice Settle accrued interest into the stored exchange rate up to the current block or\n * timestamp.\n * @dev Also sweeps accrued reserves to the ProtocolShareReserve once\n * `reduceReservesBlockDelta` has elapsed. That lowers `getCash()` and `totalReserves()` by\n * the same amount, so any quantity derived from `getCash() - totalReserves()` is invariant\n * across the sweep.\n * @return errorCode Compound-style error code; always `0` (failures revert).\n */\n function accrueInterest() external returns (uint256 errorCode);\n\n // ---------------------------------- Views --------------------------------\n\n /**\n * @notice vToken balance of `account`, in vToken units (NOT underlying).\n * @param account Holder to query.\n * @return balance vToken units held by `account`.\n */\n function balanceOf(address account) external view returns (uint256 balance);\n\n /// @notice Total vToken supply, in vToken units.\n /// @return supply Total outstanding vTokens.\n function totalSupply() external view returns (uint256 supply);\n\n /**\n * @notice Last-settled underlying-per-vToken exchange rate, scaled by `1e18`.\n * @dev INCLUDES `badDebt` in its numerator, so it is the rate the market redeems and prices caps\n * at, but NOT a sound basis for valuing a position. Use it for supply-cap math and for\n * sizing a redeem; use the components for NAV.\n * @return rate Exchange rate scaled by `1e18`.\n */\n function exchangeRateStored() external view returns (uint256 rate);\n\n /**\n * @notice Underlying cash the market tracks internally (`internalCash`, not the token balance).\n * @dev Reserves sit inside this figure but are not redeemable; subtract `totalReserves()` before\n * treating it as payable liquidity.\n * @return cash Underlying units tracked by the market.\n */\n function getCash() external view returns (uint256 cash);\n\n /// @notice Outstanding borrows, in underlying units. Excludes anything already written off to\n /// `badDebt`.\n /// @return borrows Underlying units currently borrowed.\n function totalBorrows() external view returns (uint256 borrows);\n\n /// @notice Accrued protocol reserves, in underlying units. Not payable to suppliers.\n /// @return reserves Underlying units reserved for the protocol.\n function totalReserves() external view returns (uint256 reserves);\n\n /**\n * @notice Debt written off as unrecoverable by `healAccount`, in underlying units.\n * @dev Recovered by the Shortfall auction, which transfers the winning bid into this market and\n * then calls `badDebtRecovered` — raising `getCash()` and lowering this figure by the same\n * amount, so a recovery lifts a `badDebt`-excluding valuation back up on its own.\n * @return debt Underlying units of unrecoverable debt.\n */\n function badDebt() external view returns (uint256 debt);\n\n /// @notice The ERC-20 underlying this market is backed by.\n /// @return token Address of the underlying ERC-20.\n function underlying() external view returns (address token);\n\n /// @notice Comptroller of the pool this market belongs to.\n /// @return comptrollerAddress Address of the pool's Comptroller.\n function comptroller() external view returns (address comptrollerAddress);\n\n /**\n * @notice Current supply interest rate per slot, scaled by `1e18`.\n * @dev A \"slot\" is a block on a block-based market and a second on a time-based one; annualise\n * with {blocksOrSecondsPerYear}, never with a chain-level constant.\n * @return rate Per-slot supply rate, scaled by `1e18`.\n */\n function supplyRatePerBlock() external view returns (uint256 rate);\n\n /**\n * @notice Slots per year for this market: blocks per year on a block-based market, seconds per\n * year on a time-based one.\n * @dev An immutable set at construction (`TimeManagerV8`), so it is the only annualiser that\n * matches this market's rate unit. A YieldGroup-level `blocksPerYear` cannot, because one\n * YieldGroup can hold markets of both kinds.\n * @return periods Slots per year.\n */\n function blocksOrSecondsPerYear() external view returns (uint256 periods);\n}\n" + }, + "contracts/test/HubSpoke/interfaces/IResourceAdapter.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\n\n/**\n * @title IResourceAdapter\n * @author Venus\n * @notice Boundary between a `YieldGroup` and a specific yield-protocol ABI (e.g. Compound-\n * style vTokens for Core V1, a different shape for Core V2, Fluid for Flux, etc.).\n * A single deployment of each implementation is shared across every YieldGroup that\n * registers a resource of the matching protocol family.\n * @dev Dispatch model — load-bearing for security review:\n * - **Mutating functions (`deposit`, `withdraw`) MUST be invoked via DELEGATECALL** from\n * the YieldGroup. They execute in the YieldGroup's storage context so that\n * receipt-token credits and debits land on the YieldGroup, not the adapter. Adapter\n * implementations enforce this with an `address(this) != _ADAPTER_SELF` guard.\n * - **View functions are invoked via normal CALL / STATICCALL.** They take an explicit\n * `holder` parameter (the YieldGroup) where their answer depends on whose position is\n * being queried.\n * - **`accrue` is invoked via normal CALL** (not delegatecall). It settles the resource's\n * own global interest state and credits nothing to the holder, so it needs neither the\n * YieldGroup's storage context nor an `onlyDelegateCall` guard.\n *\n * Adapter implementation invariants (required of every adapter; enforced in-code):\n * 1. The contract declares zero storage variables; only `immutable` values are\n * permitted (immutables live in bytecode, not storage slots, so they're safe across\n * delegatecall).\n * 2. No inline assembly performs `sstore`.\n * 3. External calls go only to (a) the supplied `resource`, (b) addresses the resource\n * itself reports — its asset (`underlying()` on a vToken, `asset()` on an ERC-4626\n * resource) and, where applicable, its `comptroller()` — and (c) trusted, chain-fixed\n * addresses captured as immutables at construction (e.g. AdapterFlux's `LENDING_RESOLVER`,\n * read-only). Never an arbitrary user-supplied address. The exact target set is\n * per-adapter; see each implementation's NatSpec for its list.\n * 4. Mutating functions revert when called outside a delegatecall context, preventing\n * accidental direct invocation that would orphan receipt tokens on the adapter.\n */\ninterface IResourceAdapter {\n // -------------------------------- Mutating -------------------------------\n\n /**\n * @notice Deposit `amount` of the resource's underlying into `resource`.\n * @dev MUST be delegatecalled from the YieldGroup. Pre-conditions:\n * - The YieldGroup already holds `amount` of `underlying` (transferred in by the\n * caller of `YieldGroup.deposit`).\n * Post-conditions:\n * - Receipt tokens (e.g. vTokens) are credited to the YieldGroup.\n * - The full `amount` of `underlying` leaves the YieldGroup into `resource` (under\n * delegatecall the code runs in the YieldGroup's context, so the adapter contract\n * itself never custodies funds).\n * @param resource Underlying yield protocol address (e.g. a Venus vToken).\n * @param amount Underlying units to deposit.\n * @return deposited Actual amount placed; equals `amount` on success.\n */\n function deposit(address resource, uint256 amount) external returns (uint256 deposited);\n\n /**\n * @notice Redeem exactly `amount` of underlying from `resource` and deliver it to `to`.\n * @dev MUST be delegatecalled from the YieldGroup. Pre-conditions:\n * - The YieldGroup holds enough receipt tokens to back the redeem.\n * Post-conditions:\n * - `to` receives exactly `amount` of underlying.\n * - Some adapters redeem slightly more than `amount` and leave the excess as idle on\n * the YieldGroup (AdapterCoreV1's treasury-fee gross-up and its sub-one-vToken dust\n * bump; AdapterSpokeV1 on every redeem, because an isolated-pools market rounds its\n * burn up and then recomputes the payout from the rounded-up token count). Such\n * surplus is counted in `totalAssets` and consumed idle-first on the next call.\n * Fee-free adapters (Flux, FRV) leave no surplus.\n * @param resource Underlying yield protocol address.\n * @param amount Net underlying units to deliver to `to`.\n * @param to Recipient of the underlying.\n */\n function withdraw(address resource, uint256 amount, address to) external;\n\n /**\n * @notice Settle `resource`'s accrued interest up to the current block, so a subsequent\n * {totalAssets} read values the position at a fresh exchange rate rather than a\n * one-cycle-stale one.\n * @dev Invoked via normal CALL (not delegatecall): it mutates the resource's own global\n * interest state, not the holder's position, so receipt tokens never move. Only\n * block-lazy adapters do real work here — Core and Spoke poke their vToken's\n * `accrueInterest`; Flux and FRV implement it as a no-op (their NAV is not a block-lazy\n * exchange rate).\n * @param resource Underlying yield protocol address to settle interest on.\n */\n function accrue(address resource) external;\n\n // ---------------------------------- Views --------------------------------\n\n /**\n * @notice The ERC-20 underlying accepted by `resource`. YieldGroup uses this to validate\n * that a resource's asset matches its own at registration time.\n * @param resource Underlying yield protocol address.\n * @return underlying Address of the asset token.\n */\n function asset(address resource) external view returns (address underlying);\n\n /**\n * @notice Underlying value held by `holder` via `resource`, using stale (non-accruing)\n * exchange-rate state. Cheap; safe to call from Hub-level totalAssets aggregation.\n * @param resource Underlying yield protocol address.\n * @param holder Address whose position to value (the YieldGroup).\n * @return value Underlying units `holder` holds via this resource.\n */\n function totalAssets(address resource, address holder) external view returns (uint256 value);\n\n /**\n * @notice Spare deposit headroom on `resource` right now (capacity remaining before the\n * resource rejects mint via its own caps or pause).\n * @param resource Underlying yield protocol address.\n * @return capacity Underlying units a holder can still place into this resource.\n */\n function maxDeposit(address resource) external view returns (uint256 capacity);\n\n /**\n * @notice Underlying that `holder` can withdraw from `resource` right now, NET of any\n * protocol fees that will be taken on redeem. Bounded by the lesser of `holder`'s\n * position value and the resource's available cash.\n * @param resource Underlying yield protocol address.\n * @param holder Address whose position to query (the YieldGroup).\n * @return liquid Underlying units deliverable to a recipient on a redeem call.\n */\n function maxWithdraw(address resource, address holder) external view returns (uint256 liquid);\n\n /**\n * @notice Spot supply-side APY for `resource` in BPS.\n * @dev `blocksPerYear` is consumed only by block-rate adapters (e.g. Core annualises a\n * per-block supply rate with it). Adapters whose yieldGroup is already annualised — Flux\n * (Fluid APR) and FRV (time-based fixed APY) — ignore the parameter, as does Spoke, whose\n * markets each carry their own annualiser and may be block- or time-based.\n * @param resource Underlying yield protocol address.\n * @param blocksPerYear Per-chain block cadence (chain-dependent; ~10_512_000 on BNB Chain);\n * used only by block-rate adapters, ignored by the rest.\n * @return apyBps Spot APY in basis points (capped at `uint64.max`).\n */\n function spotAPYBps(address resource, uint256 blocksPerYear) external view returns (uint64 apyBps);\n\n /**\n * @notice Raw receipt-token balance `holder` owns via `resource` (vToken / fToken / FRV\n * shares), in receipt-token units — NOT underlying value.\n * @dev Used by `YieldGroup.removeResource` as a share-based emptiness gate. A value-based\n * check (`totalAssets`) rounds a small-but-non-zero receipt balance down to 0, which\n * would let removal orphan those receipt tokens; reading the raw balance closes that gap.\n * @param resource Underlying yield protocol address.\n * @param holder Address whose receipt balance to read (the YieldGroup).\n * @return shares Receipt-token units `holder` holds in this resource.\n */\n function receiptBalance(address resource, address holder) external view returns (uint256 shares);\n\n /**\n * @notice Revert if `resource` fails a protocol-specific precondition for registration.\n * @dev Called by `YieldGroup.addResource`. AdapterCoreV1 rejects a vToken whose Comptroller\n * charges a non-zero `treasuryPercent` — that exit fee leaves the pool on every redeem\n * without burning shares, so it is unmodeled in the Hub's gross NAV and must not be\n * registered. AdapterSpokeV1 rejects a market whose supply allowlist is enabled without\n * the registering YieldGroup on it, since every deposit would revert; that call also\n * proves the market belongs to a spoke pool, because no other Comptroller exposes the\n * allowlist. FRV and Flux have no per-redeem pool fee and implement this as a no-op.\n * @param resource Underlying yield protocol address being registered.\n */\n function validateRegistration(address resource) external view;\n}\n" + }, + "contracts/test/lib/ApproveOrRevertHarness.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.25;\n\nimport { IERC20Upgradeable } from \"@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol\";\nimport { ApproveOrRevert } from \"../../lib/ApproveOrRevert.sol\";\n\ncontract ApproveOrRevertHarness {\n using ApproveOrRevert for IERC20Upgradeable;\n\n function approve(IERC20Upgradeable token, address spender, uint256 amount) external {\n token.approveOrRevert(spender, amount);\n }\n}\n" + }, + "contracts/test/lib/ProtocolShareReserve.sol": { + "content": "pragma solidity 0.8.25;\nimport { ProtocolShareReserve } from \"@venusprotocol/protocol-reserve/contracts/ProtocolReserve/ProtocolShareReserve.sol\";\n" + }, + "contracts/test/lib/TokenDebtTrackerHarness.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.25;\n\nimport { IERC20Upgradeable } from \"@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol\";\nimport { TokenDebtTracker } from \"../../lib/TokenDebtTracker.sol\";\n\ncontract TokenDebtTrackerHarness is TokenDebtTracker {\n function initialize() external initializer {\n __TokenDebtTracker_init();\n }\n\n function addTokenDebt(IERC20Upgradeable token, address user, uint256 amount) external {\n tokenDebt[token][user] += amount;\n totalTokenDebt[token] += amount;\n }\n\n function transferOutOrTrackDebt(IERC20Upgradeable token, address user, uint256 amount) external {\n _transferOutOrTrackDebt(token, user, amount);\n }\n\n function transferOutOrTrackDebtSkippingBalanceCheck(\n IERC20Upgradeable token,\n address user,\n uint256 amount\n ) external {\n _transferOutOrTrackDebtSkippingBalanceCheck(token, user, amount);\n }\n}\n" + }, + "contracts/test/MockDeflationaryToken.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\n\ncontract MockDeflatingToken {\n string public constant NAME = \"Deflating Test Token\";\n string public constant SYMBOL = \"DTT\";\n uint8 public constant DECIMALS = 18;\n uint256 public totalSupply;\n mapping(address => uint256) public balanceOf;\n mapping(address => mapping(address => uint256)) public allowance;\n\n bytes32 public DOMAIN_SEPARATOR;\n // keccak256(\"Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)\");\n bytes32 public constant PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;\n mapping(address => uint256) public nonces;\n\n event Approval(address indexed owner, address indexed spender, uint256 value);\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n constructor(uint256 _totalSupply) {\n uint256 chainId;\n assembly {\n chainId := chainid()\n }\n DOMAIN_SEPARATOR = keccak256(\n abi.encode(\n keccak256(\"EIP712Domain(string NAME,string version,uint256 chainId,address verifyingContract)\"),\n keccak256(bytes(NAME)),\n keccak256(bytes(\"1\")),\n chainId,\n address(this)\n )\n );\n _mint(msg.sender, _totalSupply);\n }\n\n function approve(address spender, uint256 value) external returns (bool) {\n _approve(msg.sender, spender, value);\n return true;\n }\n\n function transfer(address to, uint256 value) external returns (bool) {\n _transfer(msg.sender, to, value);\n return true;\n }\n\n function transferFrom(address from, address to, uint256 value) external returns (bool) {\n if (allowance[from][msg.sender] != type(uint256).max) {\n allowance[from][msg.sender] = allowance[from][msg.sender] - value;\n }\n _transfer(from, to, value);\n return true;\n }\n\n function permit(\n address owner,\n address spender,\n uint256 value,\n uint256 deadline,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) external {\n require(deadline >= block.timestamp, \"EXPIRED\");\n bytes32 digest = keccak256(\n abi.encodePacked(\n \"\\x19\\x01\",\n DOMAIN_SEPARATOR,\n keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, nonces[owner]++, deadline))\n )\n );\n address recoveredAddress = ecrecover(digest, v, r, s);\n require(recoveredAddress != address(0) && recoveredAddress == owner, \"INVALID_SIGNATURE\");\n _approve(owner, spender, value);\n }\n\n function _mint(address to, uint256 value) internal {\n totalSupply = totalSupply + value;\n balanceOf[to] = balanceOf[to] + value;\n emit Transfer(address(0), to, value);\n }\n\n function _burn(address from, uint256 value) internal {\n balanceOf[from] = balanceOf[from] - value;\n totalSupply = totalSupply - value;\n emit Transfer(from, address(0), value);\n }\n\n function _approve(address owner, address spender, uint256 value) private {\n allowance[owner][spender] = value;\n emit Approval(owner, spender, value);\n }\n\n function _transfer(address from, address to, uint256 value) private {\n uint256 burnAmount = value / 100;\n _burn(from, burnAmount);\n uint256 transferAmount = value - burnAmount;\n balanceOf[from] = balanceOf[from] - transferAmount;\n balanceOf[to] = balanceOf[to] + transferAmount;\n emit Transfer(from, to, transferAmount);\n }\n}\n" + }, + "contracts/test/Mocks/MockERC4626Token.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\nimport { MockToken } from \"./MockToken.sol\";\n\ncontract MockERC4626Token is MockToken {\n constructor(string memory name_, string memory symbol_, uint8 decimals_) MockToken(name_, symbol_, decimals_) {}\n\n function convertToAssets(uint256 shares) external pure returns (uint256) {\n return shares;\n }\n}\n" + }, + "contracts/test/Mocks/MockPriceOracle.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity ^0.8.10;\n\nimport { ResilientOracleInterface } from \"@venusprotocol/oracle/contracts/interfaces/OracleInterface.sol\";\nimport { BinanceOracle } from \"@venusprotocol/oracle/contracts/oracles/BinanceOracle.sol\";\nimport { ChainlinkOracle } from \"@venusprotocol/oracle/contracts/oracles/ChainlinkOracle.sol\";\nimport { ProtocolShareReserve } from \"@venusprotocol/protocol-reserve/contracts/ProtocolReserve/ProtocolShareReserve.sol\";\nimport { VToken } from \"../../VToken.sol\";\n\ncontract MockPriceOracle is ResilientOracleInterface {\n struct TokenConfig {\n /// @notice asset address\n address asset;\n /// @notice `oracles` stores the oracles based on their role in the following order:\n /// [main, pivot, fallback],\n /// It can be indexed with the corresponding enum OracleRole value\n address[3] oracles;\n /// @notice `enableFlagsForOracles` stores the enabled state\n /// for each oracle in the same order as `oracles`\n bool[3] enableFlagsForOracles;\n }\n\n mapping(address => uint256) public assetPrices;\n\n //set price in 6 decimal precision\n // solhint-disable-next-line no-empty-blocks\n constructor() {}\n\n function setPrice(address asset, uint256 price) external {\n assetPrices[asset] = price;\n }\n\n // solhint-disable-next-line no-empty-blocks\n function updatePrice(address vToken) external override {}\n\n // solhint-disable-next-line no-empty-blocks\n function updateAssetPrice(address asset) external override {}\n\n function getPrice(address asset) external view returns (uint256) {\n return assetPrices[asset];\n }\n\n function getTokenConfig(address asset) external view returns (TokenConfig memory) {}\n\n function setTokenConfig(TokenConfig memory tokenConfig) public {}\n\n //https://compound.finance/docs/prices\n function getUnderlyingPrice(address vToken) public view override returns (uint256) {\n return assetPrices[VToken(vToken).underlying()];\n }\n}\n" + }, + "contracts/test/Mocks/MockToken.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/ERC20.sol)\n\npragma solidity ^0.8.0;\n\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\ncontract MockToken is ERC20 {\n uint8 private immutable _decimals;\n\n constructor(string memory name_, string memory symbol_, uint8 decimals_) ERC20(name_, symbol_) {\n _decimals = decimals_;\n }\n\n function faucet(uint256 amount) external {\n _mint(msg.sender, amount);\n }\n\n function decimals() public view virtual override returns (uint8) {\n return _decimals;\n }\n}\n" + }, + "contracts/test/Mocks/MockZkETHToken.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\nimport { MockToken } from \"./MockToken.sol\";\n\ncontract MockZkETHToken is MockToken {\n constructor(string memory name_, string memory symbol_, uint8 decimals_) MockToken(name_, symbol_, decimals_) {}\n\n function LSTPerToken() external pure returns (uint256) {\n return 1005000000000000000; // 1.005e18\n }\n}\n" + }, + "contracts/test/PrimeLiquidityProviderScenario.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\n\nimport { PrimeLiquidityProvider } from \"@venusprotocol/venus-protocol/contracts/Tokens/Prime/PrimeLiquidityProvider.sol\";\n\ncontract PrimeLiquidityProviderScenario is PrimeLiquidityProvider {\n /// @custom:oz-upgrades-unsafe-allow constructor\n constructor(bool _timeBased, uint256 _blocksPerYear) PrimeLiquidityProvider(_timeBased, _blocksPerYear) {}\n}\n" + }, + "contracts/test/PrimeScenario.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\n\nimport { Prime } from \"@venusprotocol/venus-protocol/contracts/Tokens/Prime/Prime.sol\";\nimport { IPrimeLiquidityProvider } from \"@venusprotocol/venus-protocol/contracts/Tokens/Prime/Interfaces/IPrimeLiquidityProvider.sol\";\nimport { Scores } from \"@venusprotocol/venus-protocol/contracts/Tokens/Prime/libs/Scores.sol\";\n\ncontract PrimeScenario is Prime {\n constructor(\n address _wbnb,\n address _vbnb,\n uint256 _blocksPerYear,\n uint256 _stakingPeriod,\n uint256 _minimumStakedXVS,\n uint256 _maximumXVSCap,\n bool _timeBased\n ) Prime(_wbnb, _vbnb, _blocksPerYear, _stakingPeriod, _minimumStakedXVS, _maximumXVSCap, _timeBased) {}\n\n function setPLP(address plp) external {\n primeLiquidityProvider = plp;\n }\n\n function calculateScore(uint256 xvs, uint256 capital) external view returns (uint256) {\n return Scores._calculateScore(xvs, capital, alphaNumerator, alphaDenominator);\n }\n}\n" + }, + "contracts/test/SafeMath.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity ^0.8.10;\n\n// From https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/math/Math.sol\n// Subject to the MIT license.\n\n/**\n * @dev Wrappers over Solidity's arithmetic operations with added overflow\n * checks.\n *\n * Arithmetic operations in Solidity wrap on overflow. This can easily result\n * in bugs, because programmers usually assume that an overflow raises an\n * error, which is the standard behavior in high level programming languages.\n * `SafeMath` restores this intuition by reverting the transaction when an\n * operation overflows.\n *\n * Using this library instead of the unchecked operations eliminates an entire\n * class of bugs, so it's recommended to use it always.\n */\nlibrary SafeMath {\n /**\n * @dev Returns the addition of two unsigned integers, reverting on overflow.\n *\n * Counterpart to Solidity's `+` operator.\n *\n * Requirements:\n * - Addition cannot overflow.\n */\n function add(uint256 a, uint256 b) internal pure returns (uint256) {\n uint256 c;\n unchecked {\n c = a + b;\n }\n require(c >= a, \"SafeMath: addition overflow\");\n\n return c;\n }\n\n /**\n * @dev Returns the addition of two unsigned integers, reverting with custom message on overflow.\n *\n * Counterpart to Solidity's `+` operator.\n *\n * Requirements:\n * - Addition cannot overflow.\n */\n function add(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {\n uint256 c;\n unchecked {\n c = a + b;\n }\n require(c >= a, errorMessage);\n\n return c;\n }\n\n /**\n * @dev Returns the subtraction of two unsigned integers, reverting on underflow (when the result is negative).\n *\n * Counterpart to Solidity's `-` operator.\n *\n * Requirements:\n * - Subtraction cannot underflow.\n */\n function sub(uint256 a, uint256 b) internal pure returns (uint256) {\n return sub(a, b, \"SafeMath: subtraction underflow\");\n }\n\n /**\n * @dev Returns the subtraction of two unsigned integers, reverting with custom message on underflow (when the result is negative).\n *\n * Counterpart to Solidity's `-` operator.\n *\n * Requirements:\n * - Subtraction cannot underflow.\n */\n function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {\n require(b <= a, errorMessage);\n uint256 c = a - b;\n\n return c;\n }\n\n /**\n * @dev Returns the multiplication of two unsigned integers, reverting on overflow.\n *\n * Counterpart to Solidity's `*` operator.\n *\n * Requirements:\n * - Multiplication cannot overflow.\n */\n function mul(uint256 a, uint256 b) internal pure returns (uint256) {\n // Gas optimization: this is cheaper than requiring 'a' not being zero, but the\n // benefit is lost if 'b' is also tested.\n // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522\n if (a == 0) {\n return 0;\n }\n\n uint256 c;\n unchecked {\n c = a * b;\n }\n require(c / a == b, \"SafeMath: multiplication overflow\");\n\n return c;\n }\n\n /**\n * @dev Returns the multiplication of two unsigned integers, reverting on overflow.\n *\n * Counterpart to Solidity's `*` operator.\n *\n * Requirements:\n * - Multiplication cannot overflow.\n */\n function mul(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {\n // Gas optimization: this is cheaper than requiring 'a' not being zero, but the\n // benefit is lost if 'b' is also tested.\n // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522\n if (a == 0) {\n return 0;\n }\n\n uint256 c;\n unchecked {\n c = a * b;\n }\n require(c / a == b, errorMessage);\n\n return c;\n }\n\n /**\n * @dev Returns the integer division of two unsigned integers.\n * Reverts on division by zero. The result is rounded towards zero.\n *\n * Counterpart to Solidity's `/` operator. Note: this function uses a\n * `revert` opcode (which leaves remaining gas untouched) while Solidity\n * uses an invalid opcode to revert (consuming all remaining gas).\n *\n * Requirements:\n * - The divisor cannot be zero.\n */\n function div(uint256 a, uint256 b) internal pure returns (uint256) {\n return div(a, b, \"SafeMath: division by zero\");\n }\n\n /**\n * @dev Returns the integer division of two unsigned integers.\n * Reverts with custom message on division by zero. The result is rounded towards zero.\n *\n * Counterpart to Solidity's `/` operator. Note: this function uses a\n * `revert` opcode (which leaves remaining gas untouched) while Solidity\n * uses an invalid opcode to revert (consuming all remaining gas).\n *\n * Requirements:\n * - The divisor cannot be zero.\n */\n function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {\n // Solidity only automatically asserts when dividing by 0\n require(b > 0, errorMessage);\n uint256 c = a / b;\n // assert(a == b * c + a % b); // There is no case in which this doesn't hold\n\n return c;\n }\n\n /**\n * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),\n * Reverts when dividing by zero.\n *\n * Counterpart to Solidity's `%` operator. This function uses a `revert`\n * opcode (which leaves remaining gas untouched) while Solidity uses an\n * invalid opcode to revert (consuming all remaining gas).\n *\n * Requirements:\n * - The divisor cannot be zero.\n */\n function mod(uint256 a, uint256 b) internal pure returns (uint256) {\n return mod(a, b, \"SafeMath: modulo by zero\");\n }\n\n /**\n * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),\n * Reverts with custom message when dividing by zero.\n *\n * Counterpart to Solidity's `%` operator. This function uses a `revert`\n * opcode (which leaves remaining gas untouched) while Solidity uses an\n * invalid opcode to revert (consuming all remaining gas).\n *\n * Requirements:\n * - The divisor cannot be zero.\n */\n function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {\n require(b != 0, errorMessage);\n return a % b;\n }\n}\n" + }, + "contracts/test/UpgradedSpokeComptroller.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\n\nimport { SpokeComptroller } from \"../Spoke/SpokeComptroller.sol\";\n\n/**\n * @title UpgradedSpokeComptroller\n * @notice Test-only successor implementation for the spoke beacon.\n * @dev Appends one variable and one function, which is the shape a real upgrade takes. Lets the tests prove that the\n * existing storage survives a beacon upgrade and that the appended slot starts empty. Never deployed to a network.\n */\ncontract UpgradedSpokeComptroller is SpokeComptroller {\n /// @notice Value only the upgraded implementation knows about\n uint256 public addedAfterUpgrade;\n\n /// @custom:oz-upgrades-unsafe-allow constructor\n constructor(address poolRegistry_) SpokeComptroller(poolRegistry_) {}\n\n /// @notice Writes the appended variable, so a test can tell a live upgrade from a no-op\n function setAddedAfterUpgrade(uint256 value) external {\n addedAfterUpgrade = value;\n }\n}\n" + }, + "contracts/test/UpgradedVToken.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity ^0.8.10;\n\nimport { AccessControlManager } from \"@venusprotocol/governance-contracts/contracts/Governance/AccessControlManager.sol\";\n\nimport { VToken } from \"../VToken.sol\";\nimport { ComptrollerInterface } from \"../ComptrollerInterface.sol\";\nimport { InterestRateModel } from \"../InterestRateModel.sol\";\n\n/**\n * @title Venus's VToken Contract\n * @notice VTokens which wrap an EIP-20 underlying and are immutable\n * @author Venus\n */\ncontract UpgradedVToken is VToken {\n /**\n * @param timeBased_ A boolean indicating whether the contract is based on time or block.\n * @param blocksPerYear_ The number of blocks per year\n * @custom:oz-upgrades-unsafe-allow constructor\n */\n constructor(\n bool timeBased_,\n uint256 blocksPerYear_,\n uint256 maxBorrowRateMantissa_\n ) VToken(timeBased_, blocksPerYear_, maxBorrowRateMantissa_) {\n // Note that the contract is upgradeable. Use initialize() or reinitializers\n // to set the state variables.\n _disableInitializers();\n }\n\n /**\n * @notice Construct a new money market\n * @param underlying_ The address of the underlying asset\n * @param comptroller_ The address of the Comptroller\n * @param interestRateModel_ The address of the interest rate model\n * @param initialExchangeRateMantissa_ The initial exchange rate, scaled by 1e18\n * @param name_ ERC-20 name of this token\n * @param symbol_ ERC-20 symbol of this token\n * @param decimals_ ERC-20 decimal precision of this token\n * @param admin_ Address of the administrator of this token\n * @param riskManagement Addresses of risk fund contracts\n */\n\n /// @notice We added this new function to test contract upgrade\n function version() external pure returns (uint256) {\n return 2;\n }\n\n function initializeV2(\n address underlying_,\n ComptrollerInterface comptroller_,\n InterestRateModel interestRateModel_,\n uint256 initialExchangeRateMantissa_,\n string memory name_,\n string memory symbol_,\n uint8 decimals_,\n address payable admin_,\n address accessControlManager_,\n RiskManagementInit memory riskManagement,\n uint256 reserveFactorMantissa_\n ) public reinitializer(2) {\n super._initialize(\n underlying_,\n comptroller_,\n interestRateModel_,\n initialExchangeRateMantissa_,\n name_,\n symbol_,\n decimals_,\n admin_,\n accessControlManager_,\n riskManagement,\n reserveFactorMantissa_\n );\n }\n\n function getTokenUnderlying() public view returns (address) {\n return underlying;\n }\n}\n" + }, + "contracts/test/VTokenHarness.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity ^0.8.10;\n\nimport { AccessControlManager } from \"@venusprotocol/governance-contracts/contracts/Governance/AccessControlManager.sol\";\n\nimport { VToken } from \"../VToken.sol\";\nimport { InterestRateModel } from \"../InterestRateModel.sol\";\n\ncontract VTokenHarness is VToken {\n uint256 public blockNumber;\n uint256 public harnessExchangeRate;\n bool public harnessExchangeRateStored;\n\n mapping(address => bool) public failTransferToAddresses;\n\n /**\n * @param timeBased_ A boolean indicating whether the contract is based on time or block.\n * @param blocksPerYear_ The number of blocks per year\n * @custom:oz-upgrades-unsafe-allow constructor\n */\n constructor(\n bool timeBased_,\n uint256 blocksPerYear_,\n uint256 maxBorrowRateMantissa_\n ) VToken(timeBased_, blocksPerYear_, maxBorrowRateMantissa_) {\n // Note that the contract is upgradeable. Use initialize() or reinitializers\n // to set the state variables.\n _disableInitializers();\n }\n\n function harnessSetAccrualBlockNumber(uint256 accrualBlockNumber_) external {\n accrualBlockNumber = accrualBlockNumber_;\n }\n\n function harnessSetBlockNumber(uint256 newBlockNumber) external {\n blockNumber = newBlockNumber;\n }\n\n function harnessFastForward(uint256 blocks) external {\n blockNumber += blocks;\n }\n\n function harnessSetBalance(address account, uint256 amount) external {\n accountTokens[account] = amount;\n }\n\n function harnessSetTotalSupply(uint256 totalSupply_) external {\n totalSupply = totalSupply_;\n }\n\n function harnessSetTotalBorrows(uint256 totalBorrows_) external {\n totalBorrows = totalBorrows_;\n }\n\n function harnessSetTotalReserves(uint256 totalReserves_) external {\n totalReserves = totalReserves_;\n }\n\n function harnessSetInternalCash(uint256 cash_) external {\n internalCash = cash_;\n }\n\n function harnessExchangeRateDetails(uint256 totalSupply_, uint256 totalBorrows_, uint256 totalReserves_) external {\n totalSupply = totalSupply_;\n totalBorrows = totalBorrows_;\n totalReserves = totalReserves_;\n }\n\n function harnessSetExchangeRate(uint256 exchangeRate) external {\n harnessExchangeRate = exchangeRate;\n harnessExchangeRateStored = true;\n }\n\n function harnessSetFailTransferToAddress(address to_, bool fail_) external {\n failTransferToAddresses[to_] = fail_;\n }\n\n function harnessMintFresh(address account, uint256 mintAmount) external {\n super._mintFresh(account, account, mintAmount);\n }\n\n function harnessRedeemFresh(address payable account, uint256 vTokenAmount, uint256 underlyingAmount) external {\n super._redeemFresh(account, account, vTokenAmount, underlyingAmount);\n }\n\n function harnessSetAccountBorrows(address account, uint256 principal, uint256 interestIndex) external {\n accountBorrows[account] = BorrowSnapshot({ principal: principal, interestIndex: interestIndex });\n }\n\n function harnessSetBorrowIndex(uint256 borrowIndex_) external {\n borrowIndex = borrowIndex_;\n }\n\n function harnessBorrowFresh(address payable account, uint256 borrowAmount) external {\n _borrowFresh(account, account, borrowAmount);\n }\n\n function harnessRepayBorrowFresh(address payer, address account, uint256 repayAmount) external {\n _repayBorrowFresh(payer, account, repayAmount);\n }\n\n function harnessLiquidateBorrowFresh(\n address liquidator,\n address borrower,\n uint256 repayAmount,\n VToken vTokenCollateral,\n bool skipLiquidityCheck\n ) external {\n _liquidateBorrowFresh(liquidator, borrower, repayAmount, vTokenCollateral, skipLiquidityCheck);\n }\n\n function harnessReduceReservesFresh(uint256 spreadAmount) external {\n return _reduceReservesFresh(spreadAmount);\n }\n\n function harnessSetReserveFactorFresh(uint256 newReserveFactorMantissa) external {\n _setReserveFactorFresh(newReserveFactorMantissa);\n }\n\n function harnessSetInterestRateModelFresh(InterestRateModel newInterestRateModel) external {\n _setInterestRateModelFresh(newInterestRateModel);\n }\n\n function harnessAccountBorrows(address account) external view returns (uint256 principal, uint256 interestIndex) {\n BorrowSnapshot memory snapshot = accountBorrows[account];\n return (snapshot.principal, snapshot.interestIndex);\n }\n\n function getBorrowRateMaxMantissa() external view returns (uint256) {\n return MAX_BORROW_RATE_MANTISSA;\n }\n\n function harnessSetInterestRateModel(address newInterestRateModelAddress) public {\n interestRateModel = InterestRateModel(newInterestRateModelAddress);\n }\n\n function harnessCallPreBorrowHook(uint256 amount) public {\n comptroller.preBorrowHook(address(this), msg.sender, amount);\n }\n\n function getBlockNumberOrTimestamp() public view override returns (uint256) {\n return blockNumber;\n }\n\n function _doTransferOut(address to, uint256 amount) internal override {\n require(failTransferToAddresses[to] == false, \"HARNESS_TOKEN_TRANSFER_OUT_FAILED\");\n return super._doTransferOut(to, amount);\n }\n\n function _exchangeRateStored() internal view override returns (uint256) {\n if (harnessExchangeRateStored) {\n return harnessExchangeRate;\n }\n return super._exchangeRateStored();\n }\n}\n" + }, + "contracts/test/WrappedNative.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\n\ncontract WrappedNative {\n string public name = \"Wrapped Native\";\n string public symbol = \"WNATIVE\";\n uint8 public decimals = 18;\n\n mapping(address => uint256) public balanceOf;\n mapping(address => mapping(address => uint256)) public allowance;\n\n event Approval(address indexed src, address indexed guy, uint256 wad);\n event Transfer(address indexed src, address indexed dst, uint256 wad);\n event Deposit(address indexed dst, uint256 wad);\n event Withdrawal(address indexed src, uint256 wad);\n\n receive() external payable {\n deposit();\n }\n\n function deposit() public payable {\n balanceOf[msg.sender] += msg.value;\n emit Deposit(msg.sender, msg.value);\n }\n\n function withdraw(uint256 wad) public {\n require(balanceOf[msg.sender] >= wad);\n balanceOf[msg.sender] -= wad;\n payable(msg.sender).transfer(wad);\n emit Withdrawal(msg.sender, wad);\n }\n\n function approve(address guy, uint256 wad) public returns (bool) {\n allowance[msg.sender][guy] = wad;\n emit Approval(msg.sender, guy, wad);\n return true;\n }\n\n function transfer(address dst, uint256 wad) public returns (bool) {\n return transferFrom(msg.sender, dst, wad);\n }\n\n function transferFrom(address src, address dst, uint256 wad) public returns (bool) {\n require(balanceOf[src] >= wad);\n\n if (src != msg.sender && allowance[src][msg.sender] != type(uint256).max) {\n require(allowance[src][msg.sender] >= wad);\n allowance[src][msg.sender] -= wad;\n }\n\n balanceOf[src] -= wad;\n balanceOf[dst] += wad;\n\n emit Transfer(src, dst, wad);\n\n return true;\n }\n\n function mint(uint256 value) public returns (bool) {\n balanceOf[msg.sender] += value;\n emit Transfer(address(0), msg.sender, value);\n }\n\n function totalSupply() public view returns (uint256) {\n return address(this).balance;\n }\n}\n" + }, + "contracts/TwoKinksInterestRateModel.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\n\nimport { TimeManagerV8 } from \"@venusprotocol/solidity-utilities/contracts/TimeManagerV8.sol\";\nimport { InterestRateModel } from \"./InterestRateModel.sol\";\nimport { EXP_SCALE, MANTISSA_ONE } from \"./lib/constants.sol\";\n\n/**\n * @title TwoKinksInterestRateModel\n * @author Venus\n * @notice An interest rate model with two different slope increase or decrease each after a certain utilization threshold called **kink** is reached.\n */\ncontract TwoKinksInterestRateModel is InterestRateModel, TimeManagerV8 {\n ////////////////////// SLOPE 1 //////////////////////\n\n /**\n * @notice The multiplier of utilization rate per block or second that gives the slope 1 of the interest rate scaled by EXP_SCALE\n */\n int256 public immutable MULTIPLIER_PER_BLOCK_OR_SECOND;\n\n /**\n * @notice The base interest rate per block or second which is the y-intercept when utilization rate is 0 scaled by EXP_SCALE\n */\n int256 public immutable BASE_RATE_PER_BLOCK_OR_SECOND;\n\n ////////////////////// SLOPE 2 //////////////////////\n\n /**\n * @notice The utilization point at which the multiplier2 is applied\n */\n int256 public immutable KINK_1;\n\n /**\n * @notice The multiplier of utilization rate per block or second that gives the slope 2 of the interest rate scaled by EXP_SCALE\n */\n int256 public immutable MULTIPLIER_2_PER_BLOCK_OR_SECOND;\n\n /**\n * @notice The base interest rate per block or second which is the y-intercept when utilization rate hits KINK_1 scaled by EXP_SCALE\n */\n int256 public immutable BASE_RATE_2_PER_BLOCK_OR_SECOND;\n\n /**\n * @notice The maximum kink interest rate scaled by EXP_SCALE\n */\n int256 public immutable RATE_1;\n\n ////////////////////// SLOPE 3 //////////////////////\n\n /**\n * @notice The utilization point at which the jump multiplier is applied\n */\n int256 public immutable KINK_2;\n\n /**\n * @notice The multiplier of utilization rate per block or second that gives the slope 3 of interest rate scaled by EXP_SCALE\n */\n int256 public immutable JUMP_MULTIPLIER_PER_BLOCK_OR_SECOND;\n\n /**\n * @notice The maximum kink interest rate scaled by EXP_SCALE\n */\n int256 public immutable RATE_2;\n\n /**\n * @notice Thrown when a negative value is not allowed\n */\n error NegativeValueNotAllowed();\n\n /**\n * @notice Thrown when the kink points are not in the correct order\n */\n error InvalidKink();\n\n /**\n * @notice Construct an interest rate model\n * @param baseRatePerYear_ The approximate target base APR, as a mantissa (scaled by EXP_SCALE)\n * @param multiplierPerYear_ The rate of increase or decrease in interest rate wrt utilization (scaled by EXP_SCALE)\n * @param kink1_ The utilization point at which the multiplier2 is applied\n * @param multiplier2PerYear_ The rate of increase or decrease in interest rate wrt utilization after hitting KINK_1 (scaled by EXP_SCALE)\n * @param baseRate2PerYear_ The additonal base APR after hitting KINK_1, as a mantissa (scaled by EXP_SCALE)\n * @param kink2_ The utilization point at which the jump multiplier is applied\n * @param jumpMultiplierPerYear_ The multiplier after hitting KINK_2\n * @param timeBased_ A boolean indicating whether the contract is based on time or block.\n * @param blocksPerYear_ The number of blocks per year\n */\n constructor(\n int256 baseRatePerYear_,\n int256 multiplierPerYear_,\n int256 kink1_,\n int256 multiplier2PerYear_,\n int256 baseRate2PerYear_,\n int256 kink2_,\n int256 jumpMultiplierPerYear_,\n bool timeBased_,\n uint256 blocksPerYear_\n ) TimeManagerV8(timeBased_, blocksPerYear_) {\n if (baseRatePerYear_ < 0 || baseRate2PerYear_ < 0) {\n revert NegativeValueNotAllowed();\n }\n\n if (kink2_ <= kink1_ || kink1_ <= 0) {\n revert InvalidKink();\n }\n\n int256 blocksOrSecondsPerYear_ = int256(blocksOrSecondsPerYear);\n BASE_RATE_PER_BLOCK_OR_SECOND = baseRatePerYear_ / blocksOrSecondsPerYear_;\n MULTIPLIER_PER_BLOCK_OR_SECOND = multiplierPerYear_ / blocksOrSecondsPerYear_;\n KINK_1 = kink1_;\n MULTIPLIER_2_PER_BLOCK_OR_SECOND = multiplier2PerYear_ / blocksOrSecondsPerYear_;\n BASE_RATE_2_PER_BLOCK_OR_SECOND = baseRate2PerYear_ / blocksOrSecondsPerYear_;\n KINK_2 = kink2_;\n JUMP_MULTIPLIER_PER_BLOCK_OR_SECOND = jumpMultiplierPerYear_ / blocksOrSecondsPerYear_;\n\n int256 expScale = int256(EXP_SCALE);\n RATE_1 = (((KINK_1 * MULTIPLIER_PER_BLOCK_OR_SECOND) / expScale) + BASE_RATE_PER_BLOCK_OR_SECOND);\n\n int256 slope2Util;\n unchecked {\n slope2Util = KINK_2 - KINK_1;\n }\n RATE_2 = ((slope2Util * MULTIPLIER_2_PER_BLOCK_OR_SECOND) / expScale) + BASE_RATE_2_PER_BLOCK_OR_SECOND;\n }\n\n /**\n * @notice Calculates the current borrow rate per slot (block or second)\n * @param cash The amount of cash in the market\n * @param borrows The amount of borrows in the market\n * @param reserves The amount of reserves in the market\n * @param badDebt The amount of badDebt in the market\n * @return The borrow rate percentage per slot (block or second) as a mantissa (scaled by EXP_SCALE)\n */\n function getBorrowRate(\n uint256 cash,\n uint256 borrows,\n uint256 reserves,\n uint256 badDebt\n ) external view override returns (uint256) {\n return _getBorrowRate(cash, borrows, reserves, badDebt);\n }\n\n /**\n * @notice Calculates the current supply rate per slot (block or second)\n * @param cash The amount of cash in the market\n * @param borrows The amount of borrows in the market\n * @param reserves The amount of reserves in the market\n * @param reserveFactorMantissa The current reserve factor for the market\n * @param badDebt The amount of badDebt in the market\n * @return The supply rate percentage per slot (block or second) as a mantissa (scaled by EXP_SCALE)\n */\n function getSupplyRate(\n uint256 cash,\n uint256 borrows,\n uint256 reserves,\n uint256 reserveFactorMantissa,\n uint256 badDebt\n ) public view virtual override returns (uint256) {\n uint256 oneMinusReserveFactor = MANTISSA_ONE - reserveFactorMantissa;\n uint256 borrowRate = _getBorrowRate(cash, borrows, reserves, badDebt);\n uint256 rateToPool = (borrowRate * oneMinusReserveFactor) / EXP_SCALE;\n uint256 incomeToDistribute = borrows * rateToPool;\n uint256 supply = cash + borrows + badDebt - reserves;\n return incomeToDistribute / supply;\n }\n\n /**\n * @notice Calculates the utilization rate of the market: `(borrows + badDebt) / (cash + borrows + badDebt - reserves)`\n * @param cash The amount of cash in the market\n * @param borrows The amount of borrows in the market\n * @param reserves The amount of reserves in the market (currently unused)\n * @param badDebt The amount of badDebt in the market\n * @return The utilization rate as a mantissa between [0, MANTISSA_ONE]\n */\n function utilizationRate(\n uint256 cash,\n uint256 borrows,\n uint256 reserves,\n uint256 badDebt\n ) public pure returns (uint256) {\n // Utilization rate is 0 when there are no borrows and badDebt\n if ((borrows + badDebt) == 0) {\n return 0;\n }\n\n uint256 rate = ((borrows + badDebt) * EXP_SCALE) / (cash + borrows + badDebt - reserves);\n\n if (rate > EXP_SCALE) {\n rate = EXP_SCALE;\n }\n\n return rate;\n }\n\n /**\n * @notice Calculates the current borrow rate per slot (block or second), with the error code expected by the market\n * @param cash The amount of cash in the market\n * @param borrows The amount of borrows in the market\n * @param reserves The amount of reserves in the market\n * @param badDebt The amount of badDebt in the market\n * @return The borrow rate percentage per slot (block or second) as a mantissa (scaled by EXP_SCALE)\n */\n function _getBorrowRate(\n uint256 cash,\n uint256 borrows,\n uint256 reserves,\n uint256 badDebt\n ) internal view returns (uint256) {\n int256 util = int256(utilizationRate(cash, borrows, reserves, badDebt));\n int256 expScale = int256(EXP_SCALE);\n\n if (util < KINK_1) {\n return _minCap(((util * MULTIPLIER_PER_BLOCK_OR_SECOND) / expScale) + BASE_RATE_PER_BLOCK_OR_SECOND);\n } else if (util < KINK_2) {\n int256 slope2Util;\n unchecked {\n slope2Util = util - KINK_1;\n }\n int256 rate2 = ((slope2Util * MULTIPLIER_2_PER_BLOCK_OR_SECOND) / expScale) +\n BASE_RATE_2_PER_BLOCK_OR_SECOND;\n\n return _minCap(RATE_1 + rate2);\n } else {\n int256 slope3Util;\n unchecked {\n slope3Util = util - KINK_2;\n }\n int256 rate3 = ((slope3Util * JUMP_MULTIPLIER_PER_BLOCK_OR_SECOND) / expScale);\n\n return _minCap(RATE_1 + RATE_2 + rate3);\n }\n }\n\n /**\n * @notice Returns 0 if number is less than 0, otherwise returns the input\n * @param number The first number\n * @return The maximum of 0 and input number\n */\n function _minCap(int256 number) internal pure returns (uint256) {\n int256 zero;\n return uint256(number > zero ? number : zero);\n }\n}\n" + }, + "contracts/VToken.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\n\nimport { Ownable2StepUpgradeable } from \"@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol\";\nimport { IERC20Upgradeable } from \"@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol\";\nimport { SafeERC20Upgradeable } from \"@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol\";\nimport { AccessControlledV8 } from \"@venusprotocol/governance-contracts/contracts/Governance/AccessControlledV8.sol\";\nimport { IProtocolShareReserve } from \"@venusprotocol/protocol-reserve/contracts/Interfaces/IProtocolShareReserve.sol\";\n\nimport { VTokenInterface } from \"./VTokenInterfaces.sol\";\nimport { ComptrollerInterface, ComptrollerViewInterface } from \"./ComptrollerInterface.sol\";\nimport { TokenErrorReporter } from \"./ErrorReporter.sol\";\nimport { InterestRateModel } from \"./InterestRateModel.sol\";\nimport { ExponentialNoError } from \"./ExponentialNoError.sol\";\nimport { TimeManagerV8 } from \"@venusprotocol/solidity-utilities/contracts/TimeManagerV8.sol\";\nimport { ensureNonzeroAddress } from \"./lib/validators.sol\";\n\n/**\n * @title VToken\n * @author Venus\n * @notice Each asset that is supported by a pool is integrated through an instance of the `VToken` contract. As outlined in the protocol overview,\n * each isolated pool creates its own `vToken` corresponding to an asset. Within a given pool, each included `vToken` is referred to as a market of\n * the pool. The main actions a user regularly interacts with in a market are:\n\n- mint/redeem of vTokens;\n- transfer of vTokens;\n- borrow/repay a loan on an underlying asset;\n- liquidate a borrow or liquidate/heal an account.\n\n * A user supplies the underlying asset to a pool by minting `vTokens`, where the corresponding `vToken` amount is determined by the `exchangeRate`.\n * The `exchangeRate` will change over time, dependent on a number of factors, some of which accrue interest. Additionally, once users have minted\n * `vToken` in a pool, they can borrow any asset in the isolated pool by using their `vToken` as collateral. In order to borrow an asset or use a `vToken`\n * as collateral, the user must be entered into each corresponding market (else, the `vToken` will not be considered collateral for a borrow). Note that\n * a user may borrow up to a portion of their collateral determined by the market’s collateral factor. However, if their borrowed amount exceeds an amount\n * calculated using the market’s corresponding liquidation threshold, the borrow is eligible for liquidation. When a user repays a borrow, they must also\n * pay off interest accrued on the borrow.\n * \n * The Venus protocol includes unique mechanisms for healing an account and liquidating an account. These actions are performed in the `Comptroller`\n * and consider all borrows and collateral for which a given account is entered within a market. These functions may only be called on an account with a\n * total collateral amount that is no larger than a universal `minLiquidatableCollateral` value, which is used for all markets within a `Comptroller`.\n * Both functions settle all of an account’s borrows, but `healAccount()` may add `badDebt` to a vToken. For more detail, see the description of\n * `healAccount()` and `liquidateAccount()` in the `Comptroller` summary section below.\n */\ncontract VToken is\n Ownable2StepUpgradeable,\n AccessControlledV8,\n VTokenInterface,\n ExponentialNoError,\n TokenErrorReporter,\n TimeManagerV8\n{\n using SafeERC20Upgradeable for IERC20Upgradeable;\n\n uint256 internal constant DEFAULT_PROTOCOL_SEIZE_SHARE_MANTISSA = 5e16; // 5%\n\n // Maximum fraction of interest that can be set aside for reserves\n uint256 internal constant MAX_RESERVE_FACTOR_MANTISSA = 1e18;\n\n // Maximum borrow rate that can ever be applied per slot(block or second)\n /// @custom:oz-upgrades-unsafe-allow state-variable-immutable\n uint256 internal immutable MAX_BORROW_RATE_MANTISSA;\n\n /**\n * Reentrancy Guard **\n */\n\n /**\n * @dev Prevents a contract from calling itself, directly or indirectly.\n */\n modifier nonReentrant() {\n require(_notEntered, \"re-entered\");\n _notEntered = false;\n _;\n _notEntered = true; // get a gas-refund post-Istanbul\n }\n\n /**\n * @param timeBased_ A boolean indicating whether the contract is based on time or block.\n * @param blocksPerYear_ The number of blocks per year\n * @param maxBorrowRateMantissa_ The maximum value of borrowing rate mantissa\n * @custom:oz-upgrades-unsafe-allow constructor\n */\n constructor(\n bool timeBased_,\n uint256 blocksPerYear_,\n uint256 maxBorrowRateMantissa_\n ) TimeManagerV8(timeBased_, blocksPerYear_) {\n // Note that the contract is upgradeable. Use initialize() or reinitializers\n // to set the state variables.\n require(maxBorrowRateMantissa_ <= 1e18, \"Max borrow rate must be <= 1e18\");\n\n MAX_BORROW_RATE_MANTISSA = maxBorrowRateMantissa_;\n _disableInitializers();\n }\n\n /**\n * @notice Construct a new money market\n * @param underlying_ The address of the underlying asset\n * @param comptroller_ The address of the Comptroller\n * @param interestRateModel_ The address of the interest rate model\n * @param initialExchangeRateMantissa_ The initial exchange rate, scaled by 1e18\n * @param name_ ERC-20 name of this token\n * @param symbol_ ERC-20 symbol of this token\n * @param decimals_ ERC-20 decimal precision of this token\n * @param admin_ Address of the administrator of this token\n * @param accessControlManager_ AccessControlManager contract address\n * @param riskManagement Addresses of risk & income related contracts\n * @param reserveFactorMantissa_ Percentage of borrow interest that goes to reserves (from 0 to 1e18)\n * @custom:error ZeroAddressNotAllowed is thrown when admin address is zero\n * @custom:error ZeroAddressNotAllowed is thrown when shortfall contract address is zero\n * @custom:error ZeroAddressNotAllowed is thrown when protocol share reserve address is zero\n */\n function initialize(\n address underlying_,\n ComptrollerInterface comptroller_,\n InterestRateModel interestRateModel_,\n uint256 initialExchangeRateMantissa_,\n string memory name_,\n string memory symbol_,\n uint8 decimals_,\n address admin_,\n address accessControlManager_,\n RiskManagementInit memory riskManagement,\n uint256 reserveFactorMantissa_\n ) external initializer {\n ensureNonzeroAddress(admin_);\n\n // Initialize the market\n _initialize(\n underlying_,\n comptroller_,\n interestRateModel_,\n initialExchangeRateMantissa_,\n name_,\n symbol_,\n decimals_,\n admin_,\n accessControlManager_,\n riskManagement,\n reserveFactorMantissa_\n );\n }\n\n /**\n * @notice Transfer `amount` tokens from `msg.sender` to `dst`\n * @param dst The address of the destination account\n * @param amount The number of tokens to transfer\n * @return success True if the transfer succeeded, reverts otherwise\n * @custom:event Emits Transfer event on success\n * @custom:error TransferNotAllowed is thrown if trying to transfer to self\n * @custom:access Not restricted\n */\n function transfer(address dst, uint256 amount) external override nonReentrant returns (bool) {\n _transferTokens(msg.sender, msg.sender, dst, amount);\n return true;\n }\n\n /**\n * @notice Transfer `amount` tokens from `src` to `dst`\n * @param src The address of the source account\n * @param dst The address of the destination account\n * @param amount The number of tokens to transfer\n * @return success True if the transfer succeeded, reverts otherwise\n * @custom:event Emits Transfer event on success\n * @custom:error TransferNotAllowed is thrown if trying to transfer to self\n * @custom:access Not restricted\n */\n function transferFrom(address src, address dst, uint256 amount) external override nonReentrant returns (bool) {\n _transferTokens(msg.sender, src, dst, amount);\n return true;\n }\n\n /**\n * @notice Approve `spender` to transfer up to `amount` from `src`\n * @dev This will overwrite the approval amount for `spender`\n * and is subject to issues noted [here](https://eips.ethereum.org/EIPS/eip-20#approve)\n * @param spender The address of the account which may transfer tokens\n * @param amount The number of tokens that are approved (uint256.max means infinite)\n * @return success Whether or not the approval succeeded\n * @custom:event Emits Approval event\n * @custom:access Not restricted\n * @custom:error ZeroAddressNotAllowed is thrown when spender address is zero\n */\n function approve(address spender, uint256 amount) external override returns (bool) {\n ensureNonzeroAddress(spender);\n\n address src = msg.sender;\n transferAllowances[src][spender] = amount;\n emit Approval(src, spender, amount);\n return true;\n }\n\n /**\n * @notice Increase approval for `spender`\n * @param spender The address of the account which may transfer tokens\n * @param addedValue The number of additional tokens spender can transfer\n * @return success Whether or not the approval succeeded\n * @custom:event Emits Approval event\n * @custom:access Not restricted\n * @custom:error ZeroAddressNotAllowed is thrown when spender address is zero\n */\n function increaseAllowance(address spender, uint256 addedValue) external override returns (bool) {\n ensureNonzeroAddress(spender);\n\n address src = msg.sender;\n uint256 newAllowance = transferAllowances[src][spender];\n newAllowance += addedValue;\n transferAllowances[src][spender] = newAllowance;\n\n emit Approval(src, spender, newAllowance);\n return true;\n }\n\n /**\n * @notice Decreases approval for `spender`\n * @param spender The address of the account which may transfer tokens\n * @param subtractedValue The number of tokens to remove from total approval\n * @return success Whether or not the approval succeeded\n * @custom:event Emits Approval event\n * @custom:access Not restricted\n * @custom:error ZeroAddressNotAllowed is thrown when spender address is zero\n */\n function decreaseAllowance(address spender, uint256 subtractedValue) external override returns (bool) {\n ensureNonzeroAddress(spender);\n\n address src = msg.sender;\n uint256 currentAllowance = transferAllowances[src][spender];\n require(currentAllowance >= subtractedValue, \"decreased allowance below zero\");\n unchecked {\n currentAllowance -= subtractedValue;\n }\n\n transferAllowances[src][spender] = currentAllowance;\n\n emit Approval(src, spender, currentAllowance);\n return true;\n }\n\n /**\n * @notice Get the underlying balance of the `owner`\n * @dev This also accrues interest in a transaction\n * @param owner The address of the account to query\n * @return amount The amount of underlying owned by `owner`\n */\n function balanceOfUnderlying(address owner) external override returns (uint256) {\n Exp memory exchangeRate = Exp({ mantissa: exchangeRateCurrent() });\n return mul_ScalarTruncate(exchangeRate, accountTokens[owner]);\n }\n\n /**\n * @notice Returns the current total borrows plus accrued interest\n * @return totalBorrows The total borrows with interest\n */\n function totalBorrowsCurrent() external override nonReentrant returns (uint256) {\n accrueInterest();\n return totalBorrows;\n }\n\n /**\n * @notice Accrue interest to updated borrowIndex and then calculate account's borrow balance using the updated borrowIndex\n * @param account The address whose balance should be calculated after updating borrowIndex\n * @return borrowBalance The calculated balance\n */\n function borrowBalanceCurrent(address account) external override nonReentrant returns (uint256) {\n accrueInterest();\n return _borrowBalanceStored(account);\n }\n\n /**\n * @notice Sender supplies assets into the market and receives vTokens in exchange\n * @dev Accrues interest whether or not the operation succeeds, unless reverted\n * @param mintAmount The amount of the underlying asset to supply\n * @return error Always NO_ERROR for compatibility with Venus core tooling\n * @custom:event Emits Mint and Transfer events; may emit AccrueInterest\n * @custom:access Not restricted\n */\n function mint(uint256 mintAmount) external override nonReentrant returns (uint256) {\n accrueInterest();\n\n _mintFresh(msg.sender, msg.sender, mintAmount);\n return NO_ERROR;\n }\n\n /**\n * @notice Sender calls on-behalf of minter. minter supplies assets into the market and receives vTokens in exchange\n * @dev Accrues interest whether or not the operation succeeds, unless reverted\n * @param minter User whom the supply will be attributed to\n * @param mintAmount The amount of the underlying asset to supply\n * @return error Always NO_ERROR for compatibility with Venus core tooling\n * @custom:event Emits Mint and Transfer events; may emit AccrueInterest\n * @custom:access Not restricted\n * @custom:error ZeroAddressNotAllowed is thrown when minter address is zero\n */\n function mintBehalf(address minter, uint256 mintAmount) external override nonReentrant returns (uint256) {\n ensureNonzeroAddress(minter);\n\n accrueInterest();\n\n _mintFresh(msg.sender, minter, mintAmount);\n return NO_ERROR;\n }\n\n /**\n * @notice Sender redeems vTokens in exchange for the underlying asset\n * @dev Accrues interest whether or not the operation succeeds, unless reverted\n * @param redeemTokens The number of vTokens to redeem into underlying\n * @return error Always NO_ERROR for compatibility with Venus core tooling\n * @custom:event Emits Redeem and Transfer events; may emit AccrueInterest\n * @custom:error RedeemTransferOutNotPossible is thrown when the protocol has insufficient cash\n * @custom:access Not restricted\n */\n function redeem(uint256 redeemTokens) external override nonReentrant returns (uint256) {\n accrueInterest();\n\n _redeemFresh(msg.sender, msg.sender, redeemTokens, 0);\n return NO_ERROR;\n }\n\n /**\n * @notice Sender redeems assets on behalf of some other address. This function is only available\n * for senders, explicitly marked as delegates of the supplier using `comptroller.updateDelegate`\n * @dev Accrues interest whether or not the operation succeeds, unless reverted\n * @param redeemer The user on behalf of whom to redeem\n * @param redeemTokens The number of vTokens to redeem into underlying\n * @return error Always NO_ERROR for compatibility with Venus core tooling\n * @custom:error InsufficientRedeemApproval is thrown when sender is not approved by the redeemer for the given amount\n * @custom:error RedeemTransferOutNotPossible is thrown when the protocol has insufficient cash\n * @custom:event Emits Redeem and Transfer events; may emit AccrueInterest\n * @custom:access Not restricted\n */\n function redeemBehalf(address redeemer, uint256 redeemTokens) external override nonReentrant returns (uint256) {\n _ensureSenderIsDelegateOf(redeemer);\n\n accrueInterest();\n\n _redeemFresh(redeemer, msg.sender, redeemTokens, 0);\n return NO_ERROR;\n }\n\n /**\n * @notice Sender redeems vTokens in exchange for a specified amount of underlying asset\n * @dev Accrues interest whether or not the operation succeeds, unless reverted\n * @param redeemAmount The amount of underlying to receive from redeeming vTokens\n * @return error Always NO_ERROR for compatibility with Venus core tooling\n */\n function redeemUnderlying(uint256 redeemAmount) external override nonReentrant returns (uint256) {\n accrueInterest();\n\n _redeemFresh(msg.sender, msg.sender, 0, redeemAmount);\n return NO_ERROR;\n }\n\n /**\n * @notice Sender redeems underlying assets on behalf of some other address. This function is only available\n * for senders, explicitly marked as delegates of the supplier using `comptroller.updateDelegate`\n * @dev Accrues interest whether or not the operation succeeds, unless reverted\n * @param redeemer, on behalf of whom to redeem\n * @param redeemAmount The amount of underlying to receive from redeeming vTokens\n * @return error Always NO_ERROR for compatibility with Venus core tooling\n * @custom:error InsufficientRedeemApproval is thrown when sender is not approved by the redeemer for the given amount\n * @custom:event Emits Redeem and Transfer events; may emit AccrueInterest\n * @custom:access Not restricted\n */\n function redeemUnderlyingBehalf(\n address redeemer,\n uint256 redeemAmount\n ) external override nonReentrant returns (uint256) {\n _ensureSenderIsDelegateOf(redeemer);\n\n accrueInterest();\n\n _redeemFresh(redeemer, msg.sender, 0, redeemAmount);\n return NO_ERROR;\n }\n\n /**\n * @notice Sender borrows assets from the protocol to their own address\n * @param borrowAmount The amount of the underlying asset to borrow\n * @return error Always NO_ERROR for compatibility with Venus core tooling\n * @custom:event Emits Borrow event; may emit AccrueInterest\n * @custom:error BorrowCashNotAvailable is thrown when the protocol has insufficient cash\n * @custom:access Not restricted\n */\n function borrow(uint256 borrowAmount) external override nonReentrant returns (uint256) {\n accrueInterest();\n\n _borrowFresh(msg.sender, msg.sender, borrowAmount);\n return NO_ERROR;\n }\n\n /**\n * @notice Sender borrows assets on behalf of some other address. This function is only available\n * for senders, explicitly marked as delegates of the borrower using `comptroller.updateDelegate`\n * @param borrower The borrower, on behalf of whom to borrow\n * @param borrowAmount The amount of the underlying asset to borrow\n * @return error Always NO_ERROR for compatibility with Venus core tooling\n * @custom:error DelegateNotApproved is thrown if caller is not approved delegate\n * @custom:error BorrowCashNotAvailable is thrown when the protocol has insufficient cash\n * @custom:event Emits Borrow event; may emit AccrueInterest\n * @custom:access Not restricted\n */\n function borrowBehalf(address borrower, uint256 borrowAmount) external override returns (uint256) {\n _ensureSenderIsDelegateOf(borrower);\n accrueInterest();\n\n _borrowFresh(borrower, msg.sender, borrowAmount);\n return NO_ERROR;\n }\n\n /**\n * @notice Sender repays their own borrow\n * @param repayAmount The amount to repay, or type(uint256).max for the full outstanding amount\n * @return error Always NO_ERROR for compatibility with Venus core tooling\n * @custom:event Emits RepayBorrow event; may emit AccrueInterest\n * @custom:access Not restricted\n */\n function repayBorrow(uint256 repayAmount) external override nonReentrant returns (uint256) {\n accrueInterest();\n\n _repayBorrowFresh(msg.sender, msg.sender, repayAmount);\n return NO_ERROR;\n }\n\n /**\n * @notice Sender repays a borrow belonging to borrower\n * @param borrower the account with the debt being payed off\n * @param repayAmount The amount to repay, or type(uint256).max for the full outstanding amount\n * @return error Always NO_ERROR for compatibility with Venus core tooling\n * @custom:event Emits RepayBorrow event; may emit AccrueInterest\n * @custom:access Not restricted\n */\n function repayBorrowBehalf(address borrower, uint256 repayAmount) external override nonReentrant returns (uint256) {\n accrueInterest();\n\n _repayBorrowFresh(msg.sender, borrower, repayAmount);\n return NO_ERROR;\n }\n\n /**\n * @notice The sender liquidates the borrowers collateral.\n * The collateral seized is transferred to the liquidator.\n * @param borrower The borrower of this vToken to be liquidated\n * @param repayAmount The amount of the underlying borrowed asset to repay\n * @param vTokenCollateral The market in which to seize collateral from the borrower\n * @return error Always NO_ERROR for compatibility with Venus core tooling\n * @custom:event Emits LiquidateBorrow event; may emit AccrueInterest\n * @custom:error LiquidateAccrueCollateralInterestFailed is thrown when it is not possible to accrue interest on the collateral vToken\n * @custom:error LiquidateCollateralFreshnessCheck is thrown when interest has not been accrued on the collateral vToken\n * @custom:error LiquidateLiquidatorIsBorrower is thrown when trying to liquidate self\n * @custom:error LiquidateCloseAmountIsZero is thrown when repayment amount is zero\n * @custom:error LiquidateCloseAmountIsUintMax is thrown when repayment amount is UINT_MAX\n * @custom:access Not restricted\n */\n function liquidateBorrow(\n address borrower,\n uint256 repayAmount,\n VTokenInterface vTokenCollateral\n ) external override returns (uint256) {\n _liquidateBorrow(msg.sender, borrower, repayAmount, vTokenCollateral, false);\n return NO_ERROR;\n }\n\n /**\n * @notice sets protocol share accumulated from liquidations\n * @dev must be equal or less than liquidation incentive - 1\n * @param newProtocolSeizeShareMantissa_ new protocol share mantissa\n * @custom:event Emits NewProtocolSeizeShare event on success\n * @custom:error Unauthorized error is thrown when the call is not authorized by AccessControlManager\n * @custom:error ProtocolSeizeShareTooBig is thrown when the new seize share is too high\n * @custom:access Controlled by AccessControlManager\n */\n function setProtocolSeizeShare(uint256 newProtocolSeizeShareMantissa_) external {\n _checkAccessAllowed(\"setProtocolSeizeShare(uint256)\");\n uint256 liquidationIncentive = ComptrollerViewInterface(address(comptroller)).liquidationIncentiveMantissa();\n if (newProtocolSeizeShareMantissa_ + MANTISSA_ONE > liquidationIncentive) {\n revert ProtocolSeizeShareTooBig();\n }\n\n uint256 oldProtocolSeizeShareMantissa = protocolSeizeShareMantissa;\n protocolSeizeShareMantissa = newProtocolSeizeShareMantissa_;\n emit NewProtocolSeizeShare(oldProtocolSeizeShareMantissa, newProtocolSeizeShareMantissa_);\n }\n\n /**\n * @notice accrues interest and sets a new reserve factor for the protocol using _setReserveFactorFresh\n * @dev Admin function to accrue interest and set a new reserve factor\n * @param newReserveFactorMantissa New reserve factor (from 0 to 1e18)\n * @custom:event Emits NewReserveFactor event; may emit AccrueInterest\n * @custom:error Unauthorized error is thrown when the call is not authorized by AccessControlManager\n * @custom:error SetReserveFactorBoundsCheck is thrown when the new reserve factor is too high\n * @custom:access Controlled by AccessControlManager\n */\n function setReserveFactor(uint256 newReserveFactorMantissa) external override nonReentrant {\n _checkAccessAllowed(\"setReserveFactor(uint256)\");\n\n accrueInterest();\n _setReserveFactorFresh(newReserveFactorMantissa);\n }\n\n /**\n * @notice Accrues interest and reduces reserves by transferring to the protocol reserve contract\n * @dev Gracefully return if reserves already reduced in accrueInterest\n * @param reduceAmount Amount of reduction to reserves\n * @custom:event Emits ReservesReduced event; may emit AccrueInterest\n * @custom:error ReduceReservesCashNotAvailable is thrown when the vToken does not have sufficient cash\n * @custom:error ReduceReservesCashValidation is thrown when trying to withdraw more cash than the reserves have\n * @custom:access Not restricted\n */\n function reduceReserves(uint256 reduceAmount) external override nonReentrant {\n accrueInterest();\n if (reduceReservesBlockNumber == getBlockNumberOrTimestamp()) return;\n _reduceReservesFresh(reduceAmount);\n }\n\n /**\n * @notice The sender adds to reserves.\n * @param addAmount The amount of underlying token to add as reserves\n * @custom:event Emits ReservesAdded event; may emit AccrueInterest\n * @custom:access Not restricted\n */\n function addReserves(uint256 addAmount) external override nonReentrant {\n accrueInterest();\n _addReservesFresh(addAmount);\n }\n\n /**\n * @notice accrues interest and updates the interest rate model using _setInterestRateModelFresh\n * @dev Admin function to accrue interest and update the interest rate model\n * @param newInterestRateModel the new interest rate model to use\n * @custom:event Emits NewMarketInterestRateModel event; may emit AccrueInterest\n * @custom:error Unauthorized error is thrown when the call is not authorized by AccessControlManager\n * @custom:access Controlled by AccessControlManager\n */\n function setInterestRateModel(InterestRateModel newInterestRateModel) external override {\n _checkAccessAllowed(\"setInterestRateModel(address)\");\n\n accrueInterest();\n _setInterestRateModelFresh(newInterestRateModel);\n }\n\n /**\n * @notice Repays a certain amount of debt, treats the rest of the borrow as bad debt, essentially\n * \"forgiving\" the borrower. Healing is a situation that should rarely happen. However, some pools\n * may list risky assets or be configured improperly – we want to still handle such cases gracefully.\n * We assume that Comptroller does the seizing, so this function is only available to Comptroller.\n * @dev This function does not call any Comptroller hooks (like \"healAllowed\"), because we assume\n * the Comptroller does all the necessary checks before calling this function.\n * @param payer account who repays the debt\n * @param borrower account to heal\n * @param repayAmount amount to repay\n * @custom:event Emits RepayBorrow, BadDebtIncreased events; may emit AccrueInterest\n * @custom:error HealBorrowUnauthorized is thrown when the request does not come from Comptroller\n * @custom:access Only Comptroller\n */\n function healBorrow(address payer, address borrower, uint256 repayAmount) external override nonReentrant {\n if (repayAmount != 0) {\n comptroller.preRepayHook(address(this), borrower);\n }\n\n if (msg.sender != address(comptroller)) {\n revert HealBorrowUnauthorized();\n }\n\n uint256 accountBorrowsPrev = _borrowBalanceStored(borrower);\n uint256 totalBorrowsNew = totalBorrows;\n\n uint256 actualRepayAmount;\n if (repayAmount != 0) {\n // _doTransferIn reverts if anything goes wrong, since we can't be sure if side effects occurred.\n // We violate checks-effects-interactions here to account for tokens that take transfer fees\n actualRepayAmount = _doTransferIn(payer, repayAmount);\n totalBorrowsNew = totalBorrowsNew - actualRepayAmount;\n emit RepayBorrow(\n payer,\n borrower,\n actualRepayAmount,\n accountBorrowsPrev - actualRepayAmount,\n totalBorrowsNew\n );\n }\n\n // The transaction will fail if trying to repay too much\n uint256 badDebtDelta = accountBorrowsPrev - actualRepayAmount;\n if (badDebtDelta != 0) {\n uint256 badDebtOld = badDebt;\n uint256 badDebtNew = badDebtOld + badDebtDelta;\n totalBorrowsNew = totalBorrowsNew - badDebtDelta;\n badDebt = badDebtNew;\n\n // We treat healing as \"repayment\", where vToken is the payer\n emit RepayBorrow(address(this), borrower, badDebtDelta, 0, totalBorrowsNew);\n emit BadDebtIncreased(borrower, badDebtDelta, badDebtOld, badDebtNew);\n }\n\n accountBorrows[borrower].principal = 0;\n accountBorrows[borrower].interestIndex = borrowIndex;\n totalBorrows = totalBorrowsNew;\n\n emit HealBorrow(payer, borrower, repayAmount);\n }\n\n /**\n * @notice The extended version of liquidations, callable only by Comptroller. May skip\n * the close factor check. The collateral seized is transferred to the liquidator.\n * @param liquidator The address repaying the borrow and seizing collateral\n * @param borrower The borrower of this vToken to be liquidated\n * @param repayAmount The amount of the underlying borrowed asset to repay\n * @param vTokenCollateral The market in which to seize collateral from the borrower\n * @param skipLiquidityCheck If set to true, allows to liquidate up to 100% of the borrow\n * regardless of the account liquidity\n * @custom:event Emits LiquidateBorrow event; may emit AccrueInterest\n * @custom:error ForceLiquidateBorrowUnauthorized is thrown when the request does not come from Comptroller\n * @custom:error LiquidateAccrueCollateralInterestFailed is thrown when it is not possible to accrue interest on the collateral vToken\n * @custom:error LiquidateCollateralFreshnessCheck is thrown when interest has not been accrued on the collateral vToken\n * @custom:error LiquidateLiquidatorIsBorrower is thrown when trying to liquidate self\n * @custom:error LiquidateCloseAmountIsZero is thrown when repayment amount is zero\n * @custom:error LiquidateCloseAmountIsUintMax is thrown when repayment amount is UINT_MAX\n * @custom:access Only Comptroller\n */\n function forceLiquidateBorrow(\n address liquidator,\n address borrower,\n uint256 repayAmount,\n VTokenInterface vTokenCollateral,\n bool skipLiquidityCheck\n ) external override {\n if (msg.sender != address(comptroller)) {\n revert ForceLiquidateBorrowUnauthorized();\n }\n _liquidateBorrow(liquidator, borrower, repayAmount, vTokenCollateral, skipLiquidityCheck);\n }\n\n /**\n * @notice Transfers collateral tokens (this market) to the liquidator.\n * @dev Will fail unless called by another vToken during the process of liquidation.\n * It's absolutely critical to use msg.sender as the borrowed vToken and not a parameter.\n * @param liquidator The account receiving seized collateral\n * @param borrower The account having collateral seized\n * @param seizeTokens The number of vTokens to seize\n * @custom:event Emits Transfer, ReservesAdded events\n * @custom:error LiquidateSeizeLiquidatorIsBorrower is thrown when trying to liquidate self\n * @custom:access Not restricted\n */\n function seize(address liquidator, address borrower, uint256 seizeTokens) external override nonReentrant {\n _seize(msg.sender, liquidator, borrower, seizeTokens);\n }\n\n /**\n * @notice Updates bad debt\n * @dev Called only when bad debt is recovered from auction. Updates internal cash balance.\n * @param recoveredAmount_ The amount of bad debt recovered\n * @custom:event Emits BadDebtRecovered event\n * @custom:access Only Shortfall contract\n */\n function badDebtRecovered(uint256 recoveredAmount_) external {\n require(msg.sender == shortfall, \"only shortfall contract can update bad debt\");\n require(recoveredAmount_ <= badDebt, \"more than bad debt recovered from auction\");\n\n uint256 badDebtOld = badDebt;\n uint256 badDebtNew = badDebtOld - recoveredAmount_;\n badDebt = badDebtNew;\n internalCash += recoveredAmount_;\n\n emit BadDebtRecovered(badDebtOld, badDebtNew);\n }\n\n /**\n * @notice Sets protocol share reserve contract address\n * @param protocolShareReserve_ The address of the protocol share reserve contract\n * @custom:error ZeroAddressNotAllowed is thrown when protocol share reserve address is zero\n * @custom:access Only Governance\n */\n function setProtocolShareReserve(address payable protocolShareReserve_) external onlyOwner {\n _setProtocolShareReserve(protocolShareReserve_);\n }\n\n /**\n * @notice Sets shortfall contract address\n * @param shortfall_ The address of the shortfall contract\n * @custom:error ZeroAddressNotAllowed is thrown when shortfall contract address is zero\n * @custom:access Only Governance\n */\n function setShortfallContract(address shortfall_) external onlyOwner {\n _setShortfallContract(shortfall_);\n }\n\n /**\n * @notice A public function to sweep accidental ERC-20 transfers to this contract. Tokens are sent to admin (timelock)\n * @param token The address of the ERC-20 token to sweep\n * @custom:access Only Governance\n */\n function sweepToken(IERC20Upgradeable token) external override {\n require(msg.sender == owner(), \"VToken::sweepToken: only admin can sweep tokens\");\n require(address(token) != underlying, \"VToken::sweepToken: can not sweep underlying token\");\n uint256 balance = token.balanceOf(address(this));\n token.safeTransfer(owner(), balance);\n\n emit SweepToken(address(token));\n }\n\n /**\n * @notice Sync internalCash with the actual underlying token balance\n * @dev Used for one-time migration after upgrade to initialize internalCash\n * @custom:event Emits CashSynced\n * @custom:access Controlled by AccessControlManager\n */\n function syncCash() external override nonReentrant {\n _checkAccessAllowed(\"syncCash()\");\n uint256 oldInternalCash = internalCash;\n uint256 actualCash = IERC20Upgradeable(underlying).balanceOf(address(this));\n internalCash = actualCash;\n emit CashSynced(oldInternalCash, actualCash);\n }\n\n /**\n * @notice A public function to set new threshold of slot(block or second) difference after which funds will be sent to the protocol share reserve\n * @param _newReduceReservesBlockOrTimestampDelta slot(block or second) difference value\n * @custom:access Only Governance\n */\n function setReduceReservesBlockDelta(uint256 _newReduceReservesBlockOrTimestampDelta) external {\n _checkAccessAllowed(\"setReduceReservesBlockDelta(uint256)\");\n require(_newReduceReservesBlockOrTimestampDelta > 0, \"Invalid Input\");\n emit NewReduceReservesBlockDelta(reduceReservesBlockDelta, _newReduceReservesBlockOrTimestampDelta);\n reduceReservesBlockDelta = _newReduceReservesBlockOrTimestampDelta;\n }\n\n /**\n * @notice Get the current allowance from `owner` for `spender`\n * @param owner The address of the account which owns the tokens to be spent\n * @param spender The address of the account which may transfer tokens\n * @return amount The number of tokens allowed to be spent (type(uint256).max means infinite)\n */\n function allowance(address owner, address spender) external view override returns (uint256) {\n return transferAllowances[owner][spender];\n }\n\n /**\n * @notice Get the token balance of the `owner`\n * @param owner The address of the account to query\n * @return amount The number of tokens owned by `owner`\n */\n function balanceOf(address owner) external view override returns (uint256) {\n return accountTokens[owner];\n }\n\n /**\n * @notice Get a snapshot of the account's balances, and the cached exchange rate\n * @dev This is used by comptroller to more efficiently perform liquidity checks.\n * @param account Address of the account to snapshot\n * @return error Always NO_ERROR for compatibility with Venus core tooling\n * @return vTokenBalance User's balance of vTokens\n * @return borrowBalance Amount owed in terms of underlying\n * @return exchangeRate Stored exchange rate\n */\n function getAccountSnapshot(\n address account\n )\n external\n view\n override\n returns (uint256 error, uint256 vTokenBalance, uint256 borrowBalance, uint256 exchangeRate)\n {\n return (NO_ERROR, accountTokens[account], _borrowBalanceStored(account), _exchangeRateStored());\n }\n\n /**\n * @notice Get the internally tracked cash balance of this vToken in the underlying asset\n * @return cash The quantity of underlying asset tracked internally by this contract\n */\n function getCash() external view override returns (uint256) {\n return _getCashPrior();\n }\n\n /**\n * @notice Returns the current per slot(block or second) borrow interest rate for this vToken\n * @return rate The borrow interest rate per slot(block or second), scaled by 1e18\n */\n function borrowRatePerBlock() external view override returns (uint256) {\n return interestRateModel.getBorrowRate(_getCashPrior(), totalBorrows, totalReserves, badDebt);\n }\n\n /**\n * @notice Returns the current per-slot(block or second) supply interest rate for this v\n * @return rate The supply interest rate per slot(block or second), scaled by 1e18\n */\n function supplyRatePerBlock() external view override returns (uint256) {\n return\n interestRateModel.getSupplyRate(\n _getCashPrior(),\n totalBorrows,\n totalReserves,\n reserveFactorMantissa,\n badDebt\n );\n }\n\n /**\n * @notice Return the borrow balance of account based on stored data\n * @param account The address whose balance should be calculated\n * @return borrowBalance The calculated balance\n */\n function borrowBalanceStored(address account) external view override returns (uint256) {\n return _borrowBalanceStored(account);\n }\n\n /**\n * @notice Calculates the exchange rate from the underlying to the VToken\n * @dev This function does not accrue interest before calculating the exchange rate\n * @return exchangeRate Calculated exchange rate scaled by 1e18\n */\n function exchangeRateStored() external view override returns (uint256) {\n return _exchangeRateStored();\n }\n\n /**\n * @notice Accrue interest then return the up-to-date exchange rate\n * @return exchangeRate Calculated exchange rate scaled by 1e18\n */\n function exchangeRateCurrent() public override nonReentrant returns (uint256) {\n accrueInterest();\n return _exchangeRateStored();\n }\n\n /**\n * @notice Applies accrued interest to total borrows and reserves\n * @dev This calculates interest accrued from the last checkpointed slot(block or second)\n * up to the current slot(block or second) and writes new checkpoint to storage and\n * reduce spread reserves to protocol share reserve\n * if currentSlot - reduceReservesBlockNumber >= slotDelta\n * @return Always NO_ERROR\n * @custom:event Emits AccrueInterest event on success\n * @custom:access Not restricted\n */\n function accrueInterest() public virtual override returns (uint256) {\n /* Remember the initial block number or timestamp */\n uint256 currentSlotNumber = getBlockNumberOrTimestamp();\n uint256 accrualSlotNumberPrior = accrualBlockNumber;\n\n /* Short-circuit accumulating 0 interest */\n if (accrualSlotNumberPrior == currentSlotNumber) {\n return NO_ERROR;\n }\n\n /* Read the previous values out of storage */\n uint256 cashPrior = _getCashPrior();\n uint256 borrowsPrior = totalBorrows;\n uint256 reservesPrior = totalReserves;\n uint256 borrowIndexPrior = borrowIndex;\n\n /* Calculate the current borrow interest rate */\n uint256 borrowRateMantissa = interestRateModel.getBorrowRate(cashPrior, borrowsPrior, reservesPrior, badDebt);\n require(borrowRateMantissa <= MAX_BORROW_RATE_MANTISSA, \"borrow rate is absurdly high\");\n\n /* Calculate the number of slots elapsed since the last accrual */\n uint256 slotDelta = currentSlotNumber - accrualSlotNumberPrior;\n\n /*\n * Calculate the interest accumulated into borrows and reserves and the new index:\n * simpleInterestFactor = borrowRate * slotDelta\n * interestAccumulated = simpleInterestFactor * totalBorrows\n * totalBorrowsNew = interestAccumulated + totalBorrows\n * totalReservesNew = interestAccumulated * reserveFactor + totalReserves\n * borrowIndexNew = simpleInterestFactor * borrowIndex + borrowIndex\n */\n\n Exp memory simpleInterestFactor = mul_(Exp({ mantissa: borrowRateMantissa }), slotDelta);\n uint256 interestAccumulated = mul_ScalarTruncate(simpleInterestFactor, borrowsPrior);\n uint256 totalBorrowsNew = interestAccumulated + borrowsPrior;\n uint256 totalReservesNew = mul_ScalarTruncateAddUInt(\n Exp({ mantissa: reserveFactorMantissa }),\n interestAccumulated,\n reservesPrior\n );\n uint256 borrowIndexNew = mul_ScalarTruncateAddUInt(simpleInterestFactor, borrowIndexPrior, borrowIndexPrior);\n\n /////////////////////////\n // EFFECTS & INTERACTIONS\n // (No safe failures beyond this point)\n\n /* We write the previously calculated values into storage */\n accrualBlockNumber = currentSlotNumber;\n borrowIndex = borrowIndexNew;\n totalBorrows = totalBorrowsNew;\n totalReserves = totalReservesNew;\n\n if (currentSlotNumber - reduceReservesBlockNumber >= reduceReservesBlockDelta) {\n reduceReservesBlockNumber = currentSlotNumber;\n if (cashPrior < totalReservesNew) {\n _reduceReservesFresh(cashPrior);\n } else {\n _reduceReservesFresh(totalReservesNew);\n }\n }\n\n /* We emit an AccrueInterest event */\n emit AccrueInterest(cashPrior, interestAccumulated, borrowIndexNew, totalBorrowsNew);\n\n return NO_ERROR;\n }\n\n /**\n * @notice User supplies assets into the market and receives vTokens in exchange\n * @dev Assumes interest has already been accrued up to the current block or timestamp\n * @param payer The address of the account which is sending the assets for supply\n * @param minter The address of the account which is supplying the assets\n * @param mintAmount The amount of the underlying asset to supply\n */\n function _mintFresh(address payer, address minter, uint256 mintAmount) internal {\n /* Fail if mint not allowed */\n comptroller.preMintHook(address(this), minter, mintAmount);\n\n /* Verify market's slot(block or second) number equals current slot(block or second) number */\n if (accrualBlockNumber != getBlockNumberOrTimestamp()) {\n revert MintFreshnessCheck();\n }\n\n Exp memory exchangeRate = Exp({ mantissa: _exchangeRateStored() });\n\n /////////////////////////\n // EFFECTS & INTERACTIONS\n // (No safe failures beyond this point)\n\n /*\n * We call `_doTransferIn` for the minter and the mintAmount.\n * `_doTransferIn` reverts if anything goes wrong, since we can't be sure if\n * side-effects occurred. The function returns the amount actually transferred,\n * in case of a fee. On success, the vToken holds an additional `actualMintAmount`\n * of cash.\n */\n uint256 actualMintAmount = _doTransferIn(payer, mintAmount);\n\n /*\n * We get the current exchange rate and calculate the number of vTokens to be minted:\n * mintTokens = actualMintAmount / exchangeRate\n */\n\n uint256 mintTokens = div_(actualMintAmount, exchangeRate);\n\n /*\n * We calculate the new total supply of vTokens and minter token balance, checking for overflow:\n * totalSupplyNew = totalSupply + mintTokens\n * accountTokensNew = accountTokens[minter] + mintTokens\n * And write them into storage\n */\n totalSupply = totalSupply + mintTokens;\n uint256 balanceAfter = accountTokens[minter] + mintTokens;\n accountTokens[minter] = balanceAfter;\n\n /* We emit a Mint event, and a Transfer event */\n emit Mint(minter, actualMintAmount, mintTokens, balanceAfter);\n emit Transfer(address(0), minter, mintTokens);\n\n /* We call the defense and prime accrue interest hook */\n comptroller.mintVerify(address(this), minter, actualMintAmount, mintTokens);\n }\n\n /**\n * @notice Redeemer redeems vTokens in exchange for the underlying assets, transferred to the receiver. Redeemer and receiver can be the same\n * address, or different addresses if the receiver was previously approved by the redeemer as a valid delegate (see Comptroller.updateDelegate)\n * @dev Assumes interest has already been accrued up to the current slot(block or second)\n * @param redeemer The address of the account which is redeeming the tokens\n * @param receiver The receiver of the underlying tokens\n * @param redeemTokensIn The number of vTokens to redeem into underlying (only one of redeemTokensIn or redeemAmountIn may be non-zero)\n * @param redeemAmountIn The number of underlying tokens to receive from redeeming vTokens (only one of redeemTokensIn or redeemAmountIn may be non-zero)\n */\n function _redeemFresh(address redeemer, address receiver, uint256 redeemTokensIn, uint256 redeemAmountIn) internal {\n require(redeemTokensIn == 0 || redeemAmountIn == 0, \"one of redeemTokensIn or redeemAmountIn must be zero\");\n\n /* Verify market's slot(block or second) number equals current slot(block or second) number */\n if (accrualBlockNumber != getBlockNumberOrTimestamp()) {\n revert RedeemFreshnessCheck();\n }\n\n /* exchangeRate = invoke Exchange Rate Stored() */\n Exp memory exchangeRate = Exp({ mantissa: _exchangeRateStored() });\n\n uint256 redeemTokens;\n uint256 redeemAmount;\n\n /* If redeemTokensIn > 0: */\n if (redeemTokensIn > 0) {\n /*\n * We calculate the exchange rate and the amount of underlying to be redeemed:\n * redeemTokens = redeemTokensIn\n */\n redeemTokens = redeemTokensIn;\n } else {\n /*\n * We get the current exchange rate and calculate the amount to be redeemed:\n * redeemTokens = redeemAmountIn / exchangeRate\n */\n redeemTokens = div_(redeemAmountIn, exchangeRate);\n\n uint256 _redeemAmount = mul_(redeemTokens, exchangeRate);\n if (_redeemAmount != 0 && _redeemAmount != redeemAmountIn) redeemTokens++; // round up\n }\n\n // redeemAmount = exchangeRate * redeemTokens\n redeemAmount = mul_ScalarTruncate(exchangeRate, redeemTokens);\n\n // Revert if amount is zero\n if (redeemAmount == 0) {\n revert(\"redeemAmount is zero\");\n }\n\n /* Fail if redeem not allowed */\n comptroller.preRedeemHook(address(this), redeemer, redeemTokens);\n\n /* Fail gracefully if protocol has insufficient cash */\n if (_getCashPrior() - totalReserves < redeemAmount) {\n revert RedeemTransferOutNotPossible();\n }\n\n /////////////////////////\n // EFFECTS & INTERACTIONS\n // (No safe failures beyond this point)\n\n /*\n * We write the previously calculated values into storage.\n * Note: Avoid token reentrancy attacks by writing reduced supply before external transfer.\n */\n totalSupply = totalSupply - redeemTokens;\n uint256 balanceAfter = accountTokens[redeemer] - redeemTokens;\n accountTokens[redeemer] = balanceAfter;\n\n /*\n * We invoke _doTransferOut for the receiver and the redeemAmount.\n * On success, the vToken has redeemAmount less of cash.\n * _doTransferOut reverts if anything goes wrong, since we can't be sure if side effects occurred.\n */\n _doTransferOut(receiver, redeemAmount);\n\n /* We emit a Transfer event, and a Redeem event */\n emit Transfer(redeemer, address(this), redeemTokens);\n emit Redeem(redeemer, redeemAmount, redeemTokens, balanceAfter);\n\n /* We call the defense and prime accrue interest hook */\n comptroller.redeemVerify(address(this), redeemer, redeemAmount, redeemTokens);\n }\n\n /**\n * @notice Users or their delegates borrow assets from the protocol\n * @param borrower User who borrows the assets\n * @param receiver The receiver of the tokens, if called by a delegate\n * @param borrowAmount The amount of the underlying asset to borrow\n */\n function _borrowFresh(address borrower, address receiver, uint256 borrowAmount) internal {\n /* Fail if borrow not allowed */\n comptroller.preBorrowHook(address(this), borrower, borrowAmount);\n\n /* Verify market's slot(block or second) number equals current slot(block or second) number */\n if (accrualBlockNumber != getBlockNumberOrTimestamp()) {\n revert BorrowFreshnessCheck();\n }\n\n /* Fail gracefully if protocol has insufficient underlying cash */\n if (_getCashPrior() - totalReserves < borrowAmount) {\n revert BorrowCashNotAvailable();\n }\n\n /*\n * We calculate the new borrower and total borrow balances, failing on overflow:\n * accountBorrowNew = accountBorrow + borrowAmount\n * totalBorrowsNew = totalBorrows + borrowAmount\n */\n uint256 accountBorrowsPrev = _borrowBalanceStored(borrower);\n uint256 accountBorrowsNew = accountBorrowsPrev + borrowAmount;\n uint256 totalBorrowsNew = totalBorrows + borrowAmount;\n\n /////////////////////////\n // EFFECTS & INTERACTIONS\n // (No safe failures beyond this point)\n\n /*\n * We write the previously calculated values into storage.\n * Note: Avoid token reentrancy attacks by writing increased borrow before external transfer.\n `*/\n accountBorrows[borrower].principal = accountBorrowsNew;\n accountBorrows[borrower].interestIndex = borrowIndex;\n totalBorrows = totalBorrowsNew;\n\n /*\n * We invoke _doTransferOut for the receiver and the borrowAmount.\n * On success, the vToken borrowAmount less of cash.\n * _doTransferOut reverts if anything goes wrong, since we can't be sure if side effects occurred.\n */\n _doTransferOut(receiver, borrowAmount);\n\n /* We emit a Borrow event */\n emit Borrow(borrower, borrowAmount, accountBorrowsNew, totalBorrowsNew);\n\n /* We call the defense and prime accrue interest hook */\n comptroller.borrowVerify(address(this), borrower, borrowAmount);\n }\n\n /**\n * @notice Borrows are repaid by another user (possibly the borrower).\n * @param payer the account paying off the borrow\n * @param borrower the account with the debt being payed off\n * @param repayAmount the amount of underlying tokens being returned, or type(uint256).max for the full outstanding amount\n * @return (uint) the actual repayment amount.\n */\n function _repayBorrowFresh(address payer, address borrower, uint256 repayAmount) internal returns (uint256) {\n /* Fail if repayBorrow not allowed */\n comptroller.preRepayHook(address(this), borrower);\n\n /* Verify market's slot(block or second) number equals current slot(block or second) number */\n if (accrualBlockNumber != getBlockNumberOrTimestamp()) {\n revert RepayBorrowFreshnessCheck();\n }\n\n /* We fetch the amount the borrower owes, with accumulated interest */\n uint256 accountBorrowsPrev = _borrowBalanceStored(borrower);\n\n uint256 repayAmountFinal = repayAmount >= accountBorrowsPrev ? accountBorrowsPrev : repayAmount;\n\n /////////////////////////\n // EFFECTS & INTERACTIONS\n // (No safe failures beyond this point)\n\n /*\n * We call _doTransferIn for the payer and the repayAmount\n * On success, the vToken holds an additional repayAmount of cash.\n * _doTransferIn reverts if anything goes wrong, since we can't be sure if side effects occurred.\n * it returns the amount actually transferred, in case of a fee.\n */\n uint256 actualRepayAmount = _doTransferIn(payer, repayAmountFinal);\n\n /*\n * We calculate the new borrower and total borrow balances, failing on underflow:\n * accountBorrowsNew = accountBorrows - actualRepayAmount\n * totalBorrowsNew = totalBorrows - actualRepayAmount\n */\n uint256 accountBorrowsNew = accountBorrowsPrev - actualRepayAmount;\n uint256 totalBorrowsNew = totalBorrows - actualRepayAmount;\n\n /* We write the previously calculated values into storage */\n accountBorrows[borrower].principal = accountBorrowsNew;\n accountBorrows[borrower].interestIndex = borrowIndex;\n totalBorrows = totalBorrowsNew;\n\n /* We emit a RepayBorrow event */\n emit RepayBorrow(payer, borrower, actualRepayAmount, accountBorrowsNew, totalBorrowsNew);\n\n /* We call the defense and prime accrue interest hook */\n comptroller.repayBorrowVerify(address(this), payer, borrower, actualRepayAmount, borrowIndex);\n\n return actualRepayAmount;\n }\n\n /**\n * @notice The sender liquidates the borrowers collateral.\n * The collateral seized is transferred to the liquidator.\n * @param liquidator The address repaying the borrow and seizing collateral\n * @param borrower The borrower of this vToken to be liquidated\n * @param repayAmount The amount of the underlying borrowed asset to repay\n * @param vTokenCollateral The market in which to seize collateral from the borrower\n * @param skipLiquidityCheck If set to true, allows to liquidate up to 100% of the borrow\n * regardless of the account liquidity\n */\n function _liquidateBorrow(\n address liquidator,\n address borrower,\n uint256 repayAmount,\n VTokenInterface vTokenCollateral,\n bool skipLiquidityCheck\n ) internal nonReentrant {\n accrueInterest();\n\n uint256 error = vTokenCollateral.accrueInterest();\n if (error != NO_ERROR) {\n // accrueInterest emits logs on errors, but we still want to log the fact that an attempted liquidation failed\n revert LiquidateAccrueCollateralInterestFailed(error);\n }\n\n _liquidateBorrowFresh(liquidator, borrower, repayAmount, vTokenCollateral, skipLiquidityCheck);\n }\n\n /**\n * @notice The liquidator liquidates the borrowers collateral.\n * The collateral seized is transferred to the liquidator.\n * @param liquidator The address repaying the borrow and seizing collateral\n * @param borrower The borrower of this vToken to be liquidated\n * @param repayAmount The amount of the underlying borrowed asset to repay\n * @param vTokenCollateral The market in which to seize collateral from the borrower\n * @param skipLiquidityCheck If set to true, allows to liquidate up to 100% of the borrow\n * regardless of the account liquidity\n */\n function _liquidateBorrowFresh(\n address liquidator,\n address borrower,\n uint256 repayAmount,\n VTokenInterface vTokenCollateral,\n bool skipLiquidityCheck\n ) internal {\n /* Fail if liquidate not allowed */\n comptroller.preLiquidateHook(\n address(this),\n address(vTokenCollateral),\n borrower,\n repayAmount,\n skipLiquidityCheck\n );\n\n /* Verify market's slot(block or second) number equals current slot(block or second) number */\n if (accrualBlockNumber != getBlockNumberOrTimestamp()) {\n revert LiquidateFreshnessCheck();\n }\n\n /* Verify vTokenCollateral market's slot(block or second) number equals current slot(block or second) number */\n if (vTokenCollateral.accrualBlockNumber() != getBlockNumberOrTimestamp()) {\n revert LiquidateCollateralFreshnessCheck();\n }\n\n /* Fail if borrower = liquidator */\n if (borrower == liquidator) {\n revert LiquidateLiquidatorIsBorrower();\n }\n\n /* Fail if repayAmount = 0 */\n if (repayAmount == 0) {\n revert LiquidateCloseAmountIsZero();\n }\n\n /* Fail if repayAmount = type(uint256).max */\n if (repayAmount == type(uint256).max) {\n revert LiquidateCloseAmountIsUintMax();\n }\n\n /* Fail if repayBorrow fails */\n uint256 actualRepayAmount = _repayBorrowFresh(liquidator, borrower, repayAmount);\n\n /////////////////////////\n // EFFECTS & INTERACTIONS\n // (No safe failures beyond this point)\n\n /* We calculate the number of collateral tokens that will be seized */\n (uint256 amountSeizeError, uint256 seizeTokens) = comptroller.liquidateCalculateSeizeTokens(\n address(this),\n address(vTokenCollateral),\n actualRepayAmount\n );\n require(amountSeizeError == NO_ERROR, \"LIQUIDATE_COMPTROLLER_CALCULATE_AMOUNT_SEIZE_FAILED\");\n\n /* Revert if borrower collateral token balance < seizeTokens */\n require(vTokenCollateral.balanceOf(borrower) >= seizeTokens, \"LIQUIDATE_SEIZE_TOO_MUCH\");\n\n // If this is also the collateral, call _seize internally to avoid re-entrancy, otherwise make an external call\n if (address(vTokenCollateral) == address(this)) {\n _seize(address(this), liquidator, borrower, seizeTokens);\n } else {\n vTokenCollateral.seize(liquidator, borrower, seizeTokens);\n }\n\n /* We emit a LiquidateBorrow event */\n emit LiquidateBorrow(liquidator, borrower, actualRepayAmount, address(vTokenCollateral), seizeTokens);\n\n /* We call the defense and prime accrue interest hook */\n comptroller.liquidateBorrowVerify(\n address(this),\n address(vTokenCollateral),\n liquidator,\n borrower,\n actualRepayAmount,\n seizeTokens\n );\n }\n\n /**\n * @notice Transfers collateral tokens (this market) to the liquidator.\n * @dev Called only during an in-kind liquidation, or by liquidateBorrow during the liquidation of another VToken.\n * It's absolutely critical to use msg.sender as the seizer vToken and not a parameter.\n * @param seizerContract The contract seizing the collateral (either borrowed vToken or Comptroller)\n * @param liquidator The account receiving seized collateral\n * @param borrower The account having collateral seized\n * @param seizeTokens The number of vTokens to seize\n */\n function _seize(address seizerContract, address liquidator, address borrower, uint256 seizeTokens) internal {\n /* Fail if seize not allowed */\n comptroller.preSeizeHook(address(this), seizerContract, liquidator, borrower);\n\n /* Fail if borrower = liquidator */\n if (borrower == liquidator) {\n revert LiquidateSeizeLiquidatorIsBorrower();\n }\n\n /*\n * We calculate the new borrower and liquidator token balances, failing on underflow/overflow:\n * borrowerTokensNew = accountTokens[borrower] - seizeTokens\n * liquidatorTokensNew = accountTokens[liquidator] + seizeTokens\n */\n uint256 liquidationIncentiveMantissa = ComptrollerViewInterface(address(comptroller))\n .liquidationIncentiveMantissa();\n uint256 numerator = mul_(seizeTokens, Exp({ mantissa: protocolSeizeShareMantissa }));\n uint256 protocolSeizeTokens = div_(numerator, Exp({ mantissa: liquidationIncentiveMantissa }));\n uint256 liquidatorSeizeTokens = seizeTokens - protocolSeizeTokens;\n Exp memory exchangeRate = Exp({ mantissa: _exchangeRateStored() });\n uint256 protocolSeizeAmount = mul_ScalarTruncate(exchangeRate, protocolSeizeTokens);\n\n /////////////////////////\n // EFFECTS & INTERACTIONS\n // (No safe failures beyond this point)\n\n /* We write the calculated values into storage */\n totalSupply = totalSupply - protocolSeizeTokens;\n accountTokens[borrower] = accountTokens[borrower] - seizeTokens;\n accountTokens[liquidator] = accountTokens[liquidator] + liquidatorSeizeTokens;\n\n // _doTransferOut reverts if anything goes wrong, since we can't be sure if side effects occurred.\n // Transferring an underlying asset to the protocolShareReserve contract to channel the funds for different use.\n _doTransferOut(protocolShareReserve, protocolSeizeAmount);\n\n // Update the pool asset's state in the protocol share reserve for the above transfer.\n IProtocolShareReserve(protocolShareReserve).updateAssetsState(\n address(comptroller),\n underlying,\n IProtocolShareReserve.IncomeType.LIQUIDATION\n );\n\n /* Emit a Transfer event */\n emit Transfer(borrower, liquidator, liquidatorSeizeTokens);\n emit ProtocolSeize(borrower, protocolShareReserve, protocolSeizeAmount);\n\n /* We call the defense and prime accrue interest hook */\n comptroller.seizeVerify(address(this), seizerContract, liquidator, borrower, seizeTokens);\n }\n\n function _setComptroller(ComptrollerInterface newComptroller) internal {\n ComptrollerInterface oldComptroller = comptroller;\n // Ensure invoke comptroller.isComptroller() returns true\n require(newComptroller.isComptroller(), \"marker method returned false\");\n\n // Set market's comptroller to newComptroller\n comptroller = newComptroller;\n\n // Emit NewComptroller(oldComptroller, newComptroller)\n emit NewComptroller(oldComptroller, newComptroller);\n }\n\n /**\n * @notice Sets a new reserve factor for the protocol (*requires fresh interest accrual)\n * @dev Admin function to set a new reserve factor\n * @param newReserveFactorMantissa New reserve factor (from 0 to 1e18)\n */\n function _setReserveFactorFresh(uint256 newReserveFactorMantissa) internal {\n // Verify market's slot(block or second) number equals current slot(block or second) number\n if (accrualBlockNumber != getBlockNumberOrTimestamp()) {\n revert SetReserveFactorFreshCheck();\n }\n\n // Check newReserveFactor ≤ maxReserveFactor\n if (newReserveFactorMantissa > MAX_RESERVE_FACTOR_MANTISSA) {\n revert SetReserveFactorBoundsCheck();\n }\n\n uint256 oldReserveFactorMantissa = reserveFactorMantissa;\n reserveFactorMantissa = newReserveFactorMantissa;\n\n emit NewReserveFactor(oldReserveFactorMantissa, newReserveFactorMantissa);\n }\n\n /**\n * @notice Add reserves by transferring from caller\n * @dev Requires fresh interest accrual\n * @param addAmount Amount of addition to reserves\n * @return actualAddAmount The actual amount added, excluding the potential token fees\n */\n function _addReservesFresh(uint256 addAmount) internal returns (uint256) {\n // totalReserves + actualAddAmount\n uint256 totalReservesNew;\n uint256 actualAddAmount;\n\n // We fail gracefully unless market's slot(block or second) number equals current slot(block or second) number\n if (accrualBlockNumber != getBlockNumberOrTimestamp()) {\n revert AddReservesFactorFreshCheck(actualAddAmount);\n }\n\n actualAddAmount = _doTransferIn(msg.sender, addAmount);\n totalReservesNew = totalReserves + actualAddAmount;\n totalReserves = totalReservesNew;\n emit ReservesAdded(msg.sender, actualAddAmount, totalReservesNew);\n\n return actualAddAmount;\n }\n\n /**\n * @notice Reduces reserves by transferring to the protocol reserve contract\n * @dev Requires fresh interest accrual\n * @param reduceAmount Amount of reduction to reserves\n */\n function _reduceReservesFresh(uint256 reduceAmount) internal {\n if (reduceAmount == 0) {\n return;\n }\n // totalReserves - reduceAmount\n uint256 totalReservesNew;\n\n // We fail gracefully unless market's slot(block or second) number equals current slot(block or second) number\n if (accrualBlockNumber != getBlockNumberOrTimestamp()) {\n revert ReduceReservesFreshCheck();\n }\n\n // Fail gracefully if protocol has insufficient underlying cash\n if (_getCashPrior() < reduceAmount) {\n revert ReduceReservesCashNotAvailable();\n }\n\n // Check reduceAmount ≤ reserves[n] (totalReserves)\n if (reduceAmount > totalReserves) {\n revert ReduceReservesCashValidation();\n }\n\n /////////////////////////\n // EFFECTS & INTERACTIONS\n // (No safe failures beyond this point)\n\n totalReservesNew = totalReserves - reduceAmount;\n\n // Store reserves[n+1] = reserves[n] - reduceAmount\n totalReserves = totalReservesNew;\n\n // _doTransferOut reverts if anything goes wrong, since we can't be sure if side effects occurred.\n // Transferring an underlying asset to the protocolShareReserve contract to channel the funds for different use.\n _doTransferOut(protocolShareReserve, reduceAmount);\n\n // Update the pool asset's state in the protocol share reserve for the above transfer.\n IProtocolShareReserve(protocolShareReserve).updateAssetsState(\n address(comptroller),\n underlying,\n IProtocolShareReserve.IncomeType.SPREAD\n );\n\n emit SpreadReservesReduced(protocolShareReserve, reduceAmount, totalReservesNew);\n }\n\n /**\n * @notice updates the interest rate model (*requires fresh interest accrual)\n * @dev Admin function to update the interest rate model\n * @param newInterestRateModel the new interest rate model to use\n */\n function _setInterestRateModelFresh(InterestRateModel newInterestRateModel) internal {\n // Used to store old model for use in the event that is emitted on success\n InterestRateModel oldInterestRateModel;\n\n // We fail gracefully unless market's slot(block or second) number equals current slot(block or second) number\n if (accrualBlockNumber != getBlockNumberOrTimestamp()) {\n revert SetInterestRateModelFreshCheck();\n }\n\n // Track the market's current interest rate model\n oldInterestRateModel = interestRateModel;\n\n // Ensure invoke newInterestRateModel.isInterestRateModel() returns true\n require(newInterestRateModel.isInterestRateModel(), \"marker method returned false\");\n\n // Set the interest rate model to newInterestRateModel\n interestRateModel = newInterestRateModel;\n\n // Emit NewMarketInterestRateModel(oldInterestRateModel, newInterestRateModel)\n emit NewMarketInterestRateModel(oldInterestRateModel, newInterestRateModel);\n }\n\n /**\n * Safe Token **\n */\n\n /**\n * @dev Similar to ERC-20 transfer, but handles tokens that have transfer fees.\n * This function returns the actual amount received,\n * which may be less than `amount` if there is a fee attached to the transfer.\n * @param from Sender of the underlying tokens\n * @param amount Amount of underlying to transfer\n * @return Actual amount received\n */\n function _doTransferIn(address from, uint256 amount) internal virtual returns (uint256) {\n IERC20Upgradeable token = IERC20Upgradeable(underlying);\n uint256 balanceBefore = token.balanceOf(address(this));\n token.safeTransferFrom(from, address(this), amount);\n uint256 balanceAfter = token.balanceOf(address(this));\n uint256 actualAmount = balanceAfter - balanceBefore;\n internalCash += actualAmount;\n // Return the amount that was *actually* transferred\n return actualAmount;\n }\n\n /**\n * @dev Just a regular ERC-20 transfer, reverts on failure\n * @param to Receiver of the underlying tokens\n * @param amount Amount of underlying to transfer\n */\n function _doTransferOut(address to, uint256 amount) internal virtual {\n IERC20Upgradeable token = IERC20Upgradeable(underlying);\n internalCash -= amount;\n token.safeTransfer(to, amount);\n }\n\n /**\n * @notice Transfer `tokens` tokens from `src` to `dst` by `spender`\n * @dev Called by both `transfer` and `transferFrom` internally\n * @param spender The address of the account performing the transfer\n * @param src The address of the source account\n * @param dst The address of the destination account\n * @param tokens The number of tokens to transfer\n */\n function _transferTokens(address spender, address src, address dst, uint256 tokens) internal {\n /* Fail if transfer not allowed */\n comptroller.preTransferHook(address(this), src, dst, tokens);\n\n /* Do not allow self-transfers */\n if (src == dst) {\n revert TransferNotAllowed();\n }\n\n /* Get the allowance, infinite for the account owner */\n uint256 startingAllowance;\n if (spender == src) {\n startingAllowance = type(uint256).max;\n } else {\n startingAllowance = transferAllowances[src][spender];\n }\n\n /* Do the calculations, checking for {under,over}flow */\n uint256 allowanceNew = startingAllowance - tokens;\n uint256 srcTokensNew = accountTokens[src] - tokens;\n uint256 dstTokensNew = accountTokens[dst] + tokens;\n\n /////////////////////////\n // EFFECTS & INTERACTIONS\n\n accountTokens[src] = srcTokensNew;\n accountTokens[dst] = dstTokensNew;\n\n /* Eat some of the allowance (if necessary) */\n if (startingAllowance != type(uint256).max) {\n transferAllowances[src][spender] = allowanceNew;\n }\n\n /* We emit a Transfer event */\n emit Transfer(src, dst, tokens);\n\n comptroller.transferVerify(address(this), src, dst, tokens);\n }\n\n /**\n * @notice Initialize the money market\n * @param underlying_ The address of the underlying asset\n * @param comptroller_ The address of the Comptroller\n * @param interestRateModel_ The address of the interest rate model\n * @param initialExchangeRateMantissa_ The initial exchange rate, scaled by 1e18\n * @param name_ ERC-20 name of this token\n * @param symbol_ ERC-20 symbol of this token\n * @param decimals_ ERC-20 decimal precision of this token\n * @param admin_ Address of the administrator of this token\n * @param accessControlManager_ AccessControlManager contract address\n * @param riskManagement Addresses of risk & income related contracts\n * @param reserveFactorMantissa_ Percentage of borrow interest that goes to reserves (from 0 to 1e18)\n */\n function _initialize(\n address underlying_,\n ComptrollerInterface comptroller_,\n InterestRateModel interestRateModel_,\n uint256 initialExchangeRateMantissa_,\n string memory name_,\n string memory symbol_,\n uint8 decimals_,\n address admin_,\n address accessControlManager_,\n RiskManagementInit memory riskManagement,\n uint256 reserveFactorMantissa_\n ) internal onlyInitializing {\n __Ownable2Step_init();\n __AccessControlled_init_unchained(accessControlManager_);\n require(accrualBlockNumber == 0 && borrowIndex == 0, \"market may only be initialized once\");\n\n // Set initial exchange rate\n initialExchangeRateMantissa = initialExchangeRateMantissa_;\n require(initialExchangeRateMantissa > 0, \"initial exchange rate must be greater than zero.\");\n\n _setComptroller(comptroller_);\n\n // Initialize slot(block or second) number and borrow index (slot(block or second) number mocks depend on comptroller being set)\n accrualBlockNumber = getBlockNumberOrTimestamp();\n borrowIndex = MANTISSA_ONE;\n\n // Set the interest rate model (depends on slot(block or second) number / borrow index)\n _setInterestRateModelFresh(interestRateModel_);\n\n _setReserveFactorFresh(reserveFactorMantissa_);\n\n name = name_;\n symbol = symbol_;\n decimals = decimals_;\n _setShortfallContract(riskManagement.shortfall);\n _setProtocolShareReserve(riskManagement.protocolShareReserve);\n protocolSeizeShareMantissa = DEFAULT_PROTOCOL_SEIZE_SHARE_MANTISSA;\n\n // Set underlying and sanity check it\n underlying = underlying_;\n IERC20Upgradeable(underlying).totalSupply();\n\n // The counter starts true to prevent changing it from zero to non-zero (i.e. smaller cost/refund)\n _notEntered = true;\n _transferOwnership(admin_);\n }\n\n function _setShortfallContract(address shortfall_) internal {\n ensureNonzeroAddress(shortfall_);\n address oldShortfall = shortfall;\n shortfall = shortfall_;\n emit NewShortfallContract(oldShortfall, shortfall_);\n }\n\n function _setProtocolShareReserve(address payable protocolShareReserve_) internal {\n ensureNonzeroAddress(protocolShareReserve_);\n address oldProtocolShareReserve = address(protocolShareReserve);\n protocolShareReserve = protocolShareReserve_;\n emit NewProtocolShareReserve(oldProtocolShareReserve, address(protocolShareReserve_));\n }\n\n function _ensureSenderIsDelegateOf(address user) internal view {\n if (!ComptrollerViewInterface(address(comptroller)).approvedDelegates(user, msg.sender)) {\n revert DelegateNotApproved();\n }\n }\n\n /**\n * @notice Gets the internally tracked cash balance of this market\n * @dev Returns the internalCash state variable, which is updated on transfers in/out\n * @return The quantity of underlying tokens tracked internally by this contract\n */\n function _getCashPrior() internal view virtual returns (uint256) {\n return internalCash;\n }\n\n /**\n * @notice Return the borrow balance of account based on stored data\n * @param account The address whose balance should be calculated\n * @return borrowBalance the calculated balance\n */\n function _borrowBalanceStored(address account) internal view returns (uint256) {\n /* Get borrowBalance and borrowIndex */\n BorrowSnapshot memory borrowSnapshot = accountBorrows[account];\n\n /* If borrowBalance = 0 then borrowIndex is likely also 0.\n * Rather than failing the calculation with a division by 0, we immediately return 0 in this case.\n */\n if (borrowSnapshot.principal == 0) {\n return 0;\n }\n\n /* Calculate new borrow balance using the interest index:\n * recentBorrowBalance = borrower.borrowBalance * market.borrowIndex / borrower.borrowIndex\n */\n uint256 principalTimesIndex = borrowSnapshot.principal * borrowIndex;\n\n return principalTimesIndex / borrowSnapshot.interestIndex;\n }\n\n /**\n * @notice Calculates the exchange rate from the underlying to the VToken\n * @dev This function does not accrue interest before calculating the exchange rate\n * @return exchangeRate Calculated exchange rate scaled by 1e18\n */\n function _exchangeRateStored() internal view virtual returns (uint256) {\n uint256 _totalSupply = totalSupply;\n if (_totalSupply == 0) {\n /*\n * If there are no tokens minted:\n * exchangeRate = initialExchangeRate\n */\n return initialExchangeRateMantissa;\n }\n /*\n * Otherwise:\n * exchangeRate = (totalCash + totalBorrows + badDebt - totalReserves) / totalSupply\n */\n uint256 totalCash = _getCashPrior();\n uint256 cashPlusBorrowsMinusReserves = totalCash + totalBorrows + badDebt - totalReserves;\n uint256 exchangeRate = (cashPlusBorrowsMinusReserves * EXP_SCALE) / _totalSupply;\n\n return exchangeRate;\n }\n}\n" + }, + "contracts/VTokenInterfaces.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\n\nimport { IERC20Upgradeable } from \"@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol\";\nimport { ResilientOracleInterface } from \"@venusprotocol/oracle/contracts/interfaces/OracleInterface.sol\";\n\nimport { ComptrollerInterface } from \"./ComptrollerInterface.sol\";\nimport { InterestRateModel } from \"./InterestRateModel.sol\";\n\n/**\n * @title VTokenStorage\n * @author Venus\n * @notice Storage layout used by the `VToken` contract\n */\n// solhint-disable-next-line max-states-count\ncontract VTokenStorage {\n /**\n * @notice Container for borrow balance information\n * @member principal Total balance (with accrued interest), after applying the most recent balance-changing action\n * @member interestIndex Global borrowIndex as of the most recent balance-changing action\n */\n struct BorrowSnapshot {\n uint256 principal;\n uint256 interestIndex;\n }\n\n /**\n * @dev Guard variable for re-entrancy checks\n */\n bool internal _notEntered;\n\n /**\n * @notice Underlying asset for this VToken\n */\n address public underlying;\n\n /**\n * @notice EIP-20 token name for this token\n */\n string public name;\n\n /**\n * @notice EIP-20 token symbol for this token\n */\n string public symbol;\n\n /**\n * @notice EIP-20 token decimals for this token\n */\n uint8 public decimals;\n\n /**\n * @notice Protocol share Reserve contract address\n */\n address payable public protocolShareReserve;\n\n /**\n * @notice Contract which oversees inter-vToken operations\n */\n ComptrollerInterface public comptroller;\n\n /**\n * @notice Model which tells what the current interest rate should be\n */\n InterestRateModel public interestRateModel;\n\n // Initial exchange rate used when minting the first VTokens (used when totalSupply = 0)\n uint256 internal initialExchangeRateMantissa;\n\n /**\n * @notice Fraction of interest currently set aside for reserves\n */\n uint256 public reserveFactorMantissa;\n\n /**\n * @notice Slot(block or second) number that interest was last accrued at\n */\n uint256 public accrualBlockNumber;\n\n /**\n * @notice Accumulator of the total earned interest rate since the opening of the market\n */\n uint256 public borrowIndex;\n\n /**\n * @notice Total amount of outstanding borrows of the underlying in this market\n */\n uint256 public totalBorrows;\n\n /**\n * @notice Total amount of reserves of the underlying held in this market\n */\n uint256 public totalReserves;\n\n /**\n * @notice Total number of tokens in circulation\n */\n uint256 public totalSupply;\n\n /**\n * @notice Total bad debt of the market\n */\n uint256 public badDebt;\n\n // Official record of token balances for each account\n mapping(address => uint256) internal accountTokens;\n\n // Approved token transfer amounts on behalf of others\n mapping(address => mapping(address => uint256)) internal transferAllowances;\n\n // Mapping of account addresses to outstanding borrow balances\n mapping(address => BorrowSnapshot) internal accountBorrows;\n\n /**\n * @notice Share of seized collateral that is added to reserves\n */\n uint256 public protocolSeizeShareMantissa;\n\n /**\n * @notice Storage of Shortfall contract address\n */\n address public shortfall;\n\n /**\n * @notice delta slot (block or second) after which reserves will be reduced\n */\n uint256 public reduceReservesBlockDelta;\n\n /**\n * @notice last slot (block or second) number at which reserves were reduced\n */\n uint256 public reduceReservesBlockNumber;\n\n /**\n * @notice Tracked internal cash balance, immune to direct token transfers (donation attacks)\n * @dev Updated via _doTransferIn(), _doTransferOut(), syncCash(), and badDebtRecovered(). Must be initialized via syncCash() after upgrade.\n */\n uint256 public internalCash;\n\n /**\n * @dev This empty reserved space is put in place to allow future versions to add new\n * variables without shifting down storage in the inheritance chain.\n * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\n */\n uint256[47] private __gap;\n}\n\n/**\n * @title VTokenInterface\n * @author Venus\n * @notice Interface implemented by the `VToken` contract\n */\nabstract contract VTokenInterface is VTokenStorage {\n struct RiskManagementInit {\n address shortfall;\n address payable protocolShareReserve;\n }\n\n /*** Market Events ***/\n\n /**\n * @notice Event emitted when interest is accrued\n */\n event AccrueInterest(uint256 cashPrior, uint256 interestAccumulated, uint256 borrowIndex, uint256 totalBorrows);\n\n /**\n * @notice Event emitted when tokens are minted\n */\n event Mint(address indexed minter, uint256 mintAmount, uint256 mintTokens, uint256 accountBalance);\n\n /**\n * @notice Event emitted when tokens are redeemed\n */\n event Redeem(address indexed redeemer, uint256 redeemAmount, uint256 redeemTokens, uint256 accountBalance);\n\n /**\n * @notice Event emitted when underlying is borrowed\n */\n event Borrow(address indexed borrower, uint256 borrowAmount, uint256 accountBorrows, uint256 totalBorrows);\n\n /**\n * @notice Event emitted when a borrow is repaid\n */\n event RepayBorrow(\n address indexed payer,\n address indexed borrower,\n uint256 repayAmount,\n uint256 accountBorrows,\n uint256 totalBorrows\n );\n\n /**\n * @notice Event emitted when bad debt is accumulated on a market\n * @param borrower borrower to \"forgive\"\n * @param badDebtDelta amount of new bad debt recorded\n * @param badDebtOld previous bad debt value\n * @param badDebtNew new bad debt value\n */\n event BadDebtIncreased(address indexed borrower, uint256 badDebtDelta, uint256 badDebtOld, uint256 badDebtNew);\n\n /**\n * @notice Event emitted when bad debt is recovered via an auction\n * @param badDebtOld previous bad debt value\n * @param badDebtNew new bad debt value\n */\n event BadDebtRecovered(uint256 badDebtOld, uint256 badDebtNew);\n\n /**\n * @notice Event emitted when a borrow is liquidated\n */\n event LiquidateBorrow(\n address indexed liquidator,\n address indexed borrower,\n uint256 repayAmount,\n address indexed vTokenCollateral,\n uint256 seizeTokens\n );\n\n /*** Admin Events ***/\n\n /**\n * @notice Event emitted when comptroller is changed\n */\n event NewComptroller(ComptrollerInterface indexed oldComptroller, ComptrollerInterface indexed newComptroller);\n\n /**\n * @notice Event emitted when shortfall contract address is changed\n */\n event NewShortfallContract(address indexed oldShortfall, address indexed newShortfall);\n\n /**\n * @notice Event emitted when protocol share reserve contract address is changed\n */\n event NewProtocolShareReserve(address indexed oldProtocolShareReserve, address indexed newProtocolShareReserve);\n\n /**\n * @notice Event emitted when interestRateModel is changed\n */\n event NewMarketInterestRateModel(\n InterestRateModel indexed oldInterestRateModel,\n InterestRateModel indexed newInterestRateModel\n );\n\n /**\n * @notice Event emitted when protocol seize share is changed\n */\n event NewProtocolSeizeShare(uint256 oldProtocolSeizeShareMantissa, uint256 newProtocolSeizeShareMantissa);\n\n /**\n * @notice Event emitted when the reserve factor is changed\n */\n event NewReserveFactor(uint256 oldReserveFactorMantissa, uint256 newReserveFactorMantissa);\n\n /**\n * @notice Event emitted when the reserves are added\n */\n event ReservesAdded(address indexed benefactor, uint256 addAmount, uint256 newTotalReserves);\n\n /**\n * @notice Event emitted when the spread reserves are reduced\n */\n event SpreadReservesReduced(address indexed protocolShareReserve, uint256 reduceAmount, uint256 newTotalReserves);\n\n /**\n * @notice EIP20 Transfer event\n */\n event Transfer(address indexed from, address indexed to, uint256 amount);\n\n /**\n * @notice EIP20 Approval event\n */\n event Approval(address indexed owner, address indexed spender, uint256 amount);\n\n /**\n * @notice Event emitted when healing the borrow\n */\n event HealBorrow(address indexed payer, address indexed borrower, uint256 repayAmount);\n\n /**\n * @notice Event emitted when tokens are swept\n */\n event SweepToken(address indexed token);\n\n /**\n * @notice Event emitted when reduce reserves slot (block or second) delta is changed\n */\n event NewReduceReservesBlockDelta(\n uint256 oldReduceReservesBlockOrTimestampDelta,\n uint256 newReduceReservesBlockOrTimestampDelta\n );\n\n /**\n * @notice Event emitted when liquidation reserves are reduced\n */\n event ProtocolSeize(address indexed from, address indexed to, uint256 amount);\n\n /**\n * @notice Event emitted when internalCash is synced with actual token balance\n */\n event CashSynced(uint256 oldInternalCash, uint256 newInternalCash);\n\n /*** User Interface ***/\n\n function mint(uint256 mintAmount) external virtual returns (uint256);\n\n function mintBehalf(address minter, uint256 mintAllowed) external virtual returns (uint256);\n\n function redeem(uint256 redeemTokens) external virtual returns (uint256);\n\n function redeemBehalf(address redeemer, uint256 redeemTokens) external virtual returns (uint256);\n\n function redeemUnderlying(uint256 redeemAmount) external virtual returns (uint256);\n\n function redeemUnderlyingBehalf(address redeemer, uint256 redeemAmount) external virtual returns (uint256);\n\n function borrow(uint256 borrowAmount) external virtual returns (uint256);\n\n function borrowBehalf(address borrwwer, uint256 borrowAmount) external virtual returns (uint256);\n\n function repayBorrow(uint256 repayAmount) external virtual returns (uint256);\n\n function repayBorrowBehalf(address borrower, uint256 repayAmount) external virtual returns (uint256);\n\n function liquidateBorrow(\n address borrower,\n uint256 repayAmount,\n VTokenInterface vTokenCollateral\n ) external virtual returns (uint256);\n\n function healBorrow(address payer, address borrower, uint256 repayAmount) external virtual;\n\n function forceLiquidateBorrow(\n address liquidator,\n address borrower,\n uint256 repayAmount,\n VTokenInterface vTokenCollateral,\n bool skipCloseFactorCheck\n ) external virtual;\n\n function seize(address liquidator, address borrower, uint256 seizeTokens) external virtual;\n\n function transfer(address dst, uint256 amount) external virtual returns (bool);\n\n function transferFrom(address src, address dst, uint256 amount) external virtual returns (bool);\n\n function accrueInterest() external virtual returns (uint256);\n\n function sweepToken(IERC20Upgradeable token) external virtual;\n\n /*** Admin Functions ***/\n\n function setReserveFactor(uint256 newReserveFactorMantissa) external virtual;\n\n function reduceReserves(uint256 reduceAmount) external virtual;\n\n function exchangeRateCurrent() external virtual returns (uint256);\n\n function borrowBalanceCurrent(address account) external virtual returns (uint256);\n\n function setInterestRateModel(InterestRateModel newInterestRateModel) external virtual;\n\n function addReserves(uint256 addAmount) external virtual;\n\n function syncCash() external virtual;\n\n function totalBorrowsCurrent() external virtual returns (uint256);\n\n function balanceOfUnderlying(address owner) external virtual returns (uint256);\n\n function approve(address spender, uint256 amount) external virtual returns (bool);\n\n function increaseAllowance(address spender, uint256 addedValue) external virtual returns (bool);\n\n function decreaseAllowance(address spender, uint256 subtractedValue) external virtual returns (bool);\n\n function allowance(address owner, address spender) external view virtual returns (uint256);\n\n function balanceOf(address owner) external view virtual returns (uint256);\n\n function getAccountSnapshot(address account) external view virtual returns (uint256, uint256, uint256, uint256);\n\n function borrowRatePerBlock() external view virtual returns (uint256);\n\n function supplyRatePerBlock() external view virtual returns (uint256);\n\n function borrowBalanceStored(address account) external view virtual returns (uint256);\n\n function exchangeRateStored() external view virtual returns (uint256);\n\n function getCash() external view virtual returns (uint256);\n\n /**\n * @notice Indicator that this is a VToken contract (for inspection)\n * @return Always true\n */\n function isVToken() external pure virtual returns (bool) {\n return true;\n }\n}\n" + }, + "contracts/WhitePaperInterestRateModel.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\n\nimport { InterestRateModel } from \"./InterestRateModel.sol\";\nimport { TimeManagerV8 } from \"@venusprotocol/solidity-utilities/contracts/TimeManagerV8.sol\";\nimport { EXP_SCALE, MANTISSA_ONE } from \"./lib/constants.sol\";\n\n/**\n * @title Compound's WhitePaperInterestRateModel Contract\n * @author Compound\n * @notice The parameterized model described in section 2.4 of the original Compound Protocol whitepaper\n */\ncontract WhitePaperInterestRateModel is InterestRateModel, TimeManagerV8 {\n /**\n * @notice The multiplier of utilization rate per block or second that gives the slope of the interest rate\n */\n uint256 public immutable multiplierPerBlock;\n\n /**\n * @notice The base interest rate per block or second which is the y-intercept when utilization rate is 0\n */\n uint256 public immutable baseRatePerBlock;\n\n event NewInterestParams(uint256 baseRatePerBlockOrTimestamp, uint256 multiplierPerBlockOrTimestamp);\n\n /**\n * @notice Construct an interest rate model\n * @param baseRatePerYear_ The approximate target base APR, as a mantissa (scaled by EXP_SCALE)\n * @param multiplierPerYear_ The rate of increase in interest rate wrt utilization (scaled by EXP_SCALE)\n * @param timeBased_ A boolean indicating whether the contract is based on time or block.\n * @param blocksPerYear_ The number of blocks per year\n */\n constructor(\n uint256 baseRatePerYear_,\n uint256 multiplierPerYear_,\n bool timeBased_,\n uint256 blocksPerYear_\n ) TimeManagerV8(timeBased_, blocksPerYear_) {\n baseRatePerBlock = baseRatePerYear_ / blocksOrSecondsPerYear;\n multiplierPerBlock = multiplierPerYear_ / blocksOrSecondsPerYear;\n\n emit NewInterestParams(baseRatePerBlock, multiplierPerBlock);\n }\n\n /**\n * @notice Calculates the current borrow rate per slot(block/second), with the error code expected by the market\n * @param cash The amount of cash in the market\n * @param borrows The amount of borrows in the market\n * @param reserves The amount of reserves in the market\n * @param badDebt The amount of badDebt in the market\n * @return The borrow rate percentage per slot(block/second) as a mantissa (scaled by EXP_SCALE)\n */\n function getBorrowRate(\n uint256 cash,\n uint256 borrows,\n uint256 reserves,\n uint256 badDebt\n ) public view override returns (uint256) {\n uint256 ur = utilizationRate(cash, borrows, reserves, badDebt);\n return ((ur * multiplierPerBlock) / EXP_SCALE) + baseRatePerBlock;\n }\n\n /**\n * @notice Calculates the current supply rate per slot(block/second)\n * @param cash The amount of cash in the market\n * @param borrows The amount of borrows in the market\n * @param reserves The amount of reserves in the market\n * @param reserveFactorMantissa The current reserve factor for the market\n * @param badDebt The amount of badDebt in the market\n * @return The supply rate percentage per slot(block/second) as a mantissa (scaled by EXP_SCALE)\n */\n function getSupplyRate(\n uint256 cash,\n uint256 borrows,\n uint256 reserves,\n uint256 reserveFactorMantissa,\n uint256 badDebt\n ) public view override returns (uint256) {\n uint256 oneMinusReserveFactor = MANTISSA_ONE - reserveFactorMantissa;\n uint256 borrowRate = getBorrowRate(cash, borrows, reserves, badDebt);\n uint256 rateToPool = (borrowRate * oneMinusReserveFactor) / EXP_SCALE;\n uint256 incomeToDistribute = borrows * rateToPool;\n uint256 supply = cash + borrows + badDebt - reserves;\n return incomeToDistribute / supply;\n }\n\n /**\n * @notice Calculates the utilization rate of the market: `(borrows + badDebt) / (cash + borrows + badDebt - reserves)`\n * @param cash The amount of cash in the market\n * @param borrows The amount of borrows in the market\n * @param reserves The amount of reserves in the market (currently unused)\n * @param badDebt The amount of badDebt in the market\n * @return The utilization rate as a mantissa between [0, MANTISSA_ONE]\n */\n function utilizationRate(\n uint256 cash,\n uint256 borrows,\n uint256 reserves,\n uint256 badDebt\n ) public pure returns (uint256) {\n // Utilization rate is 0 when there are no borrows and badDebt\n if ((borrows + badDebt) == 0) {\n return 0;\n }\n\n uint256 rate = ((borrows + badDebt) * EXP_SCALE) / (cash + borrows + badDebt - reserves);\n\n if (rate > EXP_SCALE) {\n rate = EXP_SCALE;\n }\n\n return rate;\n }\n}\n" + }, + "contracts/WUSDMLiquidator.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\n\nimport { IERC20Upgradeable } from \"@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol\";\nimport { SafeERC20Upgradeable } from \"@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol\";\nimport { ResilientOracleInterface } from \"@venusprotocol/oracle/contracts/interfaces/OracleInterface.sol\";\nimport { Ownable2StepUpgradeable } from \"@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol\";\n\nimport { ApproveOrRevert } from \"./lib/ApproveOrRevert.sol\";\nimport { Comptroller } from \"./Comptroller.sol\";\nimport { Action } from \"./ComptrollerInterface.sol\";\nimport { VToken } from \"./VToken.sol\";\n\ncontract WUSDMLiquidator is Ownable2StepUpgradeable {\n using SafeERC20Upgradeable for IERC20Upgradeable;\n using ApproveOrRevert for IERC20Upgradeable;\n\n struct OriginalConfig {\n uint256 minLiquidatableCollateral;\n uint256 closeFactor;\n uint256 wUSDMCollateralFactor;\n uint256 wUSDMLiquidationThreshold;\n uint256 vWUSDMProtocolSeizeShare;\n uint256 vWETHProtocolSeizeShare;\n uint256 vUSDCEProtocolSeizeShare;\n uint256 vUSDTProtocolSeizeShare;\n }\n\n OriginalConfig private _originalConfig;\n\n ResilientOracleInterface public constant ORACLE =\n ResilientOracleInterface(0xDe564a4C887d5ad315a19a96DC81991c98b12182);\n Comptroller public constant COMPTROLLER = Comptroller(0xddE4D098D9995B659724ae6d5E3FB9681Ac941B1);\n VToken public constant VWUSDM = VToken(0x183dE3C349fCf546aAe925E1c7F364EA6FB4033c);\n VToken public constant VWETH = VToken(0x1Fa916C27c7C2c4602124A14C77Dbb40a5FF1BE8);\n VToken public constant VUSDCE = VToken(0x1aF23bD57c62A99C59aD48236553D0Dd11e49D2D);\n VToken public constant VUSDT = VToken(0x69cDA960E3b20DFD480866fFfd377Ebe40bd0A46);\n\n uint256 public constant LIQUIDATION_INCENTIVE = 1.1e18;\n\n address public constant A2 = 0x4C0e4B3e6c5756fb31886a0A01079701ffEC0561;\n address public constant A3 = 0x924EDEd3D010b3F20009b872183eec48D0111265;\n address public constant A4 = 0x2B379d8c90e02016658aD00ba2566F55E814C369;\n address public constant A5 = 0xfffAB9120d9Df39EEa07063F6465a0aA45a80C52;\n\n IERC20Upgradeable public immutable WUSDM;\n IERC20Upgradeable public immutable WETH;\n IERC20Upgradeable public immutable USDCE;\n IERC20Upgradeable public immutable USDT;\n\n /// @notice Emitted when token is swept from the contract\n event SweepToken(address indexed token, address indexed receiver, uint256 amount);\n\n /**\n * @notice Constructor to initialize immutable token references and disable initializers\n */\n constructor() {\n WUSDM = IERC20Upgradeable(VWUSDM.underlying());\n WETH = IERC20Upgradeable(VWETH.underlying());\n USDCE = IERC20Upgradeable(VUSDCE.underlying());\n USDT = IERC20Upgradeable(VUSDT.underlying());\n _disableInitializers();\n }\n\n /**\n * @notice Initializes the contract and sets up ownership\n * @dev This function can only be called once\n */\n function initialize() external initializer {\n __Ownable2Step_init();\n }\n\n /**\n * @notice Executes the liquidation and repayment process\n * @dev This function performs the following steps:\n * 1. Configures the markets by setting collateral factors, close factors, and enabling actions.\n * 2. Supplies WUSDM as collateral.\n * 3. Borrows WETH and liquidates borrowers with WETH debt.\n * 4. Borrows USDCE and liquidates borrowers with USDCE debt.\n * 5. Borrows USDT and liquidates borrowers with USDT debt.\n * 6. Restores the original market configuration.\n */\n function run() external onlyOwner {\n uint256 wusdmPrice = ORACLE.getPrice(address(WUSDM));\n uint256 vwUSDMExchangeRate = VWUSDM.exchangeRateCurrent();\n\n _configureMarkets();\n _supplyCollateral();\n _borrowWETHAndLiquidateBorrowers(wusdmPrice, vwUSDMExchangeRate);\n _borrowUSDCeAndLiquidateBorrowers(wusdmPrice, vwUSDMExchangeRate);\n _borrowUSDTAndLiquidateBorrowers(wusdmPrice, vwUSDMExchangeRate);\n _restoreOriginalConfiguration();\n }\n\n /**\n * @notice Sweeps the input token address tokens from the contract and sends them to the owner\n * @param token Address of the token\n * @custom:event SweepToken emits on success\n * @custom:access Controlled by Governance\n */\n function sweepToken(IERC20Upgradeable token) external onlyOwner {\n uint256 balance = token.balanceOf(address(this));\n\n if (balance > 0) {\n address owner_ = owner();\n token.safeTransfer(owner_, balance);\n emit SweepToken(address(token), owner_, balance);\n }\n }\n\n function _configureMarkets() internal {\n (, uint256 wUSDMCollateralFactor, uint256 wUSDMLiquidationThreshold) = COMPTROLLER.markets(address(VWUSDM));\n _originalConfig = OriginalConfig({\n minLiquidatableCollateral: COMPTROLLER.minLiquidatableCollateral(),\n closeFactor: COMPTROLLER.closeFactorMantissa(),\n wUSDMCollateralFactor: wUSDMCollateralFactor,\n wUSDMLiquidationThreshold: wUSDMLiquidationThreshold,\n vWUSDMProtocolSeizeShare: VWUSDM.protocolSeizeShareMantissa(),\n vWETHProtocolSeizeShare: VWETH.protocolSeizeShareMantissa(),\n vUSDCEProtocolSeizeShare: VUSDCE.protocolSeizeShareMantissa(),\n vUSDTProtocolSeizeShare: VUSDT.protocolSeizeShareMantissa()\n });\n COMPTROLLER.setMinLiquidatableCollateral(0);\n COMPTROLLER.setCloseFactor(1e18);\n COMPTROLLER.setCollateralFactor(VWUSDM, 0.78e18, 0.78e18);\n VToken[] memory markets = new VToken[](1);\n Action[] memory actions = new Action[](2);\n markets[0] = VWUSDM;\n actions[0] = Action.MINT;\n actions[1] = Action.ENTER_MARKET;\n COMPTROLLER.setActionsPaused(markets, actions, false);\n VWUSDM.setProtocolSeizeShare(0);\n VWETH.setProtocolSeizeShare(0);\n VUSDCE.setProtocolSeizeShare(0);\n VUSDT.setProtocolSeizeShare(0);\n }\n\n function _restoreOriginalConfiguration() internal {\n COMPTROLLER.setMinLiquidatableCollateral(_originalConfig.minLiquidatableCollateral);\n COMPTROLLER.setCloseFactor(_originalConfig.closeFactor);\n COMPTROLLER.setCollateralFactor(\n VWUSDM,\n _originalConfig.wUSDMCollateralFactor,\n _originalConfig.wUSDMLiquidationThreshold\n );\n VToken[] memory markets = new VToken[](1);\n Action[] memory actions = new Action[](2);\n markets[0] = VWUSDM;\n actions[0] = Action.MINT;\n actions[1] = Action.ENTER_MARKET;\n COMPTROLLER.setActionsPaused(markets, actions, true);\n VWUSDM.setProtocolSeizeShare(_originalConfig.vWUSDMProtocolSeizeShare);\n VWETH.setProtocolSeizeShare(_originalConfig.vWETHProtocolSeizeShare);\n VUSDCE.setProtocolSeizeShare(_originalConfig.vUSDCEProtocolSeizeShare);\n VUSDT.setProtocolSeizeShare(_originalConfig.vUSDTProtocolSeizeShare);\n // Get some gas refunds for zeroing out storage\n _originalConfig = OriginalConfig(0, 0, 0, 0, 0, 0, 0, 0);\n }\n\n function _supplyCollateral() internal {\n uint256 amount = WUSDM.balanceOf(address(this));\n WUSDM.approveOrRevert(address(VWUSDM), amount);\n VWUSDM.mint(amount);\n WUSDM.approveOrRevert(address(VWUSDM), 0);\n address[] memory markets = new address[](1);\n markets[0] = address(VWUSDM);\n COMPTROLLER.enterMarkets(markets);\n }\n\n function _borrowWETHAndLiquidateBorrowers(uint256 wusdmPrice, uint256 vwUSDMExchangeRate) internal {\n uint256 a2Debt = _getDebt(VWETH, A2);\n uint256 ratio = _getBorrowedTokensToCollateralVTokensRatio(VWETH, wusdmPrice, vwUSDMExchangeRate);\n uint256 amountToRepay = _getAmountToRepayDuringLiquidation(A2, a2Debt, VWUSDM, ratio);\n if (amountToRepay > 0) {\n _borrow(VWETH, amountToRepay);\n WETH.approveOrRevert(address(VWETH), amountToRepay);\n VWETH.liquidateBorrow(A2, amountToRepay, VWUSDM);\n WETH.approveOrRevert(address(VWETH), 0);\n }\n }\n\n function _borrowUSDCeAndLiquidateBorrowers(uint256 wusdmPrice, uint256 vwUSDMExchangeRate) internal {\n uint256 a3Debt = _getDebt(VUSDCE, A3);\n uint256 a4Debt = _getDebt(VUSDCE, A4);\n uint256 a5Debt = _getDebt(VUSDCE, A5);\n uint256 totalRepayment = a3Debt + a4Debt + a5Debt;\n\n _borrow(VUSDCE, totalRepayment);\n USDCE.approveOrRevert(address(VUSDCE), totalRepayment);\n uint256 ratio = _getBorrowedTokensToCollateralVTokensRatio(VUSDCE, wusdmPrice, vwUSDMExchangeRate);\n a3Debt = _liquidateAsMuchAsPossible(A3, a3Debt, VUSDCE, VWUSDM, ratio);\n a4Debt = _liquidateAsMuchAsPossible(A4, a4Debt, VUSDCE, VWUSDM, ratio);\n a5Debt = _liquidateAsMuchAsPossible(A5, a5Debt, VUSDCE, VWUSDM, ratio);\n _repay(A3, VUSDCE, a3Debt > 1 ? a3Debt - 1 : 0);\n _repay(A4, VUSDCE, a4Debt > 1 ? a4Debt - 1 : 0);\n _repay(A5, VUSDCE, a5Debt > 1 ? a5Debt - 1 : 0);\n USDCE.approveOrRevert(address(VUSDCE), 0);\n }\n\n function _borrowUSDTAndLiquidateBorrowers(uint256 wusdmPrice, uint256 vwUSDMExchangeRate) internal {\n uint256 a3Debt = _getDebt(VUSDT, A3);\n uint256 a4Debt = _getDebt(VUSDT, A4);\n uint256 a5Debt = _getDebt(VUSDT, A5);\n uint256 totalRepayment = a3Debt + a4Debt + a5Debt;\n\n _borrow(VUSDT, totalRepayment);\n USDT.approveOrRevert(address(VUSDT), totalRepayment);\n uint256 ratio = _getBorrowedTokensToCollateralVTokensRatio(VUSDT, wusdmPrice, vwUSDMExchangeRate);\n a3Debt = _liquidateAsMuchAsPossible(A3, a3Debt, VUSDT, VWUSDM, ratio);\n a4Debt = _liquidateAsMuchAsPossible(A4, a4Debt, VUSDT, VWUSDM, ratio);\n a5Debt = _liquidateAsMuchAsPossible(A5, a5Debt, VUSDT, VWUSDM, ratio);\n _repay(A3, VUSDT, a3Debt > 1 ? a3Debt - 1 : 0);\n _repay(A4, VUSDT, a4Debt > 1 ? a4Debt - 1 : 0);\n _repay(A5, VUSDT, a5Debt > 1 ? a5Debt - 1 : 0);\n USDT.approveOrRevert(address(VUSDT), 0);\n }\n\n function _getDebt(VToken market, address account) internal returns (uint256) {\n if (!_isUnderwater(account)) {\n return 0; // no debt if not underwater\n }\n return market.borrowBalanceCurrent(account);\n }\n\n function _borrow(VToken market, uint256 amount) internal {\n market.borrow(amount);\n }\n\n function _repay(address account, VToken market, uint256 amount) internal {\n if (amount == 0) {\n return;\n }\n market.repayBorrowBehalf(account, amount);\n }\n\n function _liquidateAsMuchAsPossible(\n address account,\n uint256 debt,\n VToken market,\n VToken collateral,\n uint256 ratio\n ) internal returns (uint256) {\n if (debt == 0) {\n return 0;\n }\n uint256 amountToRepay = _getAmountToRepayDuringLiquidation(account, debt, collateral, ratio);\n if (amountToRepay > 1) {\n market.liquidateBorrow(account, amountToRepay, collateral);\n }\n return debt - amountToRepay;\n }\n\n function _getAmountToRepayDuringLiquidation(\n address account,\n uint256 debt,\n VToken collateral,\n uint256 ratio\n ) internal view returns (uint256) {\n if (debt == 0) {\n return 0;\n }\n uint256 seizeableVTokens = collateral.balanceOf(account);\n if (seizeableVTokens <= 1) {\n return debt;\n }\n // Ideally, we should round up here so that we repay as much as possible during the liquidation,\n // leaving no vTokens supplied, but due to anvil-zksync issues, we want to keep at least 1 wei\n // of vTokens supplied so that we're able to test it later\n uint256 amountToRepayForEntireCollateral = ((seizeableVTokens - 1) * 1e18) / ratio;\n // Keeping 1 wei of debt, for the same purpose\n return debt > amountToRepayForEntireCollateral ? amountToRepayForEntireCollateral : (debt - 1);\n }\n\n function _getBorrowedTokensToCollateralVTokensRatio(\n VToken borrowed,\n uint256 collateralPrice,\n uint256 collateralExchangeRate\n ) internal view returns (uint256) {\n uint256 borrowedPrice = ORACLE.getUnderlyingPrice(address(borrowed));\n return (borrowedPrice * LIQUIDATION_INCENTIVE * 1e18) / (collateralPrice * collateralExchangeRate);\n }\n\n function _isUnderwater(address account) internal view returns (bool) {\n (, uint256 liquidity, uint256 shortfall) = COMPTROLLER.getAccountLiquidity(account);\n return liquidity == 0 && shortfall > 0;\n }\n}\n" + }, + "hardhat-deploy/solc_0.8/openzeppelin/access/Ownable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../utils/Context.sol\";\n\n/**\n * @dev Contract module which provides a basic access control mechanism, where\n * there is an account (an owner) that can be granted exclusive access to\n * specific functions.\n *\n * By default, the owner account will be the one that deploys the contract. This\n * can later be changed with {transferOwnership}.\n *\n * This module is used through inheritance. It will make available the modifier\n * `onlyOwner`, which can be applied to your functions to restrict their use to\n * the owner.\n */\nabstract contract Ownable is Context {\n address private _owner;\n\n event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n\n /**\n * @dev Initializes the contract setting the deployer as the initial owner.\n */\n constructor (address initialOwner) {\n _transferOwnership(initialOwner);\n }\n\n /**\n * @dev Returns the address of the current owner.\n */\n function owner() public view virtual returns (address) {\n return _owner;\n }\n\n /**\n * @dev Throws if called by any account other than the owner.\n */\n modifier onlyOwner() {\n require(owner() == _msgSender(), \"Ownable: caller is not the owner\");\n _;\n }\n\n /**\n * @dev Leaves the contract without owner. It will not be possible to call\n * `onlyOwner` functions anymore. Can only be called by the current owner.\n *\n * NOTE: Renouncing ownership will leave the contract without an owner,\n * thereby removing any functionality that is only available to the owner.\n */\n function renounceOwnership() public virtual onlyOwner {\n _transferOwnership(address(0));\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Can only be called by the current owner.\n */\n function transferOwnership(address newOwner) public virtual onlyOwner {\n require(newOwner != address(0), \"Ownable: new owner is the zero address\");\n _transferOwnership(newOwner);\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Internal function without access restriction.\n */\n function _transferOwnership(address newOwner) internal virtual {\n address oldOwner = _owner;\n _owner = newOwner;\n emit OwnershipTransferred(oldOwner, newOwner);\n }\n}\n" + }, + "hardhat-deploy/solc_0.8/openzeppelin/interfaces/draft-IERC1822.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.5.0-rc.0) (interfaces/draft-IERC1822.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev ERC1822: Universal Upgradeable Proxy Standard (UUPS) documents a method for upgradeability through a simplified\n * proxy whose upgrades are fully controlled by the current implementation.\n */\ninterface IERC1822Proxiable {\n /**\n * @dev Returns the storage slot that the proxiable contract assumes is being used to store the implementation\n * address.\n *\n * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks\n * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this\n * function revert if invoked through a proxy.\n */\n function proxiableUUID() external view returns (bytes32);\n}\n" + }, + "hardhat-deploy/solc_0.8/openzeppelin/proxy/beacon/IBeacon.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (proxy/beacon/IBeacon.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev This is the interface that {BeaconProxy} expects of its beacon.\n */\ninterface IBeacon {\n /**\n * @dev Must return an address that can be used as a delegate call target.\n *\n * {BeaconProxy} will check that this address is a contract.\n */\n function implementation() external view returns (address);\n}\n" + }, + "hardhat-deploy/solc_0.8/openzeppelin/proxy/ERC1967/ERC1967Proxy.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (proxy/ERC1967/ERC1967Proxy.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../Proxy.sol\";\nimport \"./ERC1967Upgrade.sol\";\n\n/**\n * @dev This contract implements an upgradeable proxy. It is upgradeable because calls are delegated to an\n * implementation address that can be changed. This address is stored in storage in the location specified by\n * https://eips.ethereum.org/EIPS/eip-1967[EIP1967], so that it doesn't conflict with the storage layout of the\n * implementation behind the proxy.\n */\ncontract ERC1967Proxy is Proxy, ERC1967Upgrade {\n /**\n * @dev Initializes the upgradeable proxy with an initial implementation specified by `_logic`.\n *\n * If `_data` is nonempty, it's used as data in a delegate call to `_logic`. This will typically be an encoded\n * function call, and allows initializating the storage of the proxy like a Solidity constructor.\n */\n constructor(address _logic, bytes memory _data) payable {\n assert(_IMPLEMENTATION_SLOT == bytes32(uint256(keccak256(\"eip1967.proxy.implementation\")) - 1));\n _upgradeToAndCall(_logic, _data, false);\n }\n\n /**\n * @dev Returns the current implementation address.\n */\n function _implementation() internal view virtual override returns (address impl) {\n return ERC1967Upgrade._getImplementation();\n }\n}\n" + }, + "hardhat-deploy/solc_0.8/openzeppelin/proxy/ERC1967/ERC1967Upgrade.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.5.0-rc.0) (proxy/ERC1967/ERC1967Upgrade.sol)\n\npragma solidity ^0.8.2;\n\nimport \"../beacon/IBeacon.sol\";\nimport \"../../interfaces/draft-IERC1822.sol\";\nimport \"../../utils/Address.sol\";\nimport \"../../utils/StorageSlot.sol\";\n\n/**\n * @dev This abstract contract provides getters and event emitting update functions for\n * https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots.\n *\n * _Available since v4.1._\n *\n * @custom:oz-upgrades-unsafe-allow delegatecall\n */\nabstract contract ERC1967Upgrade {\n // This is the keccak-256 hash of \"eip1967.proxy.rollback\" subtracted by 1\n bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143;\n\n /**\n * @dev Storage slot with the address of the current implementation.\n * This is the keccak-256 hash of \"eip1967.proxy.implementation\" subtracted by 1, and is\n * validated in the constructor.\n */\n bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;\n\n /**\n * @dev Emitted when the implementation is upgraded.\n */\n event Upgraded(address indexed implementation);\n\n /**\n * @dev Returns the current implementation address.\n */\n function _getImplementation() internal view returns (address) {\n return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;\n }\n\n /**\n * @dev Stores a new address in the EIP1967 implementation slot.\n */\n function _setImplementation(address newImplementation) private {\n require(Address.isContract(newImplementation), \"ERC1967: new implementation is not a contract\");\n StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;\n }\n\n /**\n * @dev Perform implementation upgrade\n *\n * Emits an {Upgraded} event.\n */\n function _upgradeTo(address newImplementation) internal {\n _setImplementation(newImplementation);\n emit Upgraded(newImplementation);\n }\n\n /**\n * @dev Perform implementation upgrade with additional setup call.\n *\n * Emits an {Upgraded} event.\n */\n function _upgradeToAndCall(\n address newImplementation,\n bytes memory data,\n bool forceCall\n ) internal {\n _upgradeTo(newImplementation);\n if (data.length > 0 || forceCall) {\n Address.functionDelegateCall(newImplementation, data);\n }\n }\n\n /**\n * @dev Perform implementation upgrade with security checks for UUPS proxies, and additional setup call.\n *\n * Emits an {Upgraded} event.\n */\n function _upgradeToAndCallUUPS(\n address newImplementation,\n bytes memory data,\n bool forceCall\n ) internal {\n // Upgrades from old implementations will perform a rollback test. This test requires the new\n // implementation to upgrade back to the old, non-ERC1822 compliant, implementation. Removing\n // this special case will break upgrade paths from old UUPS implementation to new ones.\n if (StorageSlot.getBooleanSlot(_ROLLBACK_SLOT).value) {\n _setImplementation(newImplementation);\n } else {\n try IERC1822Proxiable(newImplementation).proxiableUUID() returns (bytes32 slot) {\n require(slot == _IMPLEMENTATION_SLOT, \"ERC1967Upgrade: unsupported proxiableUUID\");\n } catch {\n revert(\"ERC1967Upgrade: new implementation is not UUPS\");\n }\n _upgradeToAndCall(newImplementation, data, forceCall);\n }\n }\n\n /**\n * @dev Storage slot with the admin of the contract.\n * This is the keccak-256 hash of \"eip1967.proxy.admin\" subtracted by 1, and is\n * validated in the constructor.\n */\n bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;\n\n /**\n * @dev Emitted when the admin account has changed.\n */\n event AdminChanged(address previousAdmin, address newAdmin);\n\n /**\n * @dev Returns the current admin.\n */\n function _getAdmin() internal view virtual returns (address) {\n return StorageSlot.getAddressSlot(_ADMIN_SLOT).value;\n }\n\n /**\n * @dev Stores a new address in the EIP1967 admin slot.\n */\n function _setAdmin(address newAdmin) private {\n require(newAdmin != address(0), \"ERC1967: new admin is the zero address\");\n StorageSlot.getAddressSlot(_ADMIN_SLOT).value = newAdmin;\n }\n\n /**\n * @dev Changes the admin of the proxy.\n *\n * Emits an {AdminChanged} event.\n */\n function _changeAdmin(address newAdmin) internal {\n emit AdminChanged(_getAdmin(), newAdmin);\n _setAdmin(newAdmin);\n }\n\n /**\n * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.\n * This is bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)) and is validated in the constructor.\n */\n bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;\n\n /**\n * @dev Emitted when the beacon is upgraded.\n */\n event BeaconUpgraded(address indexed beacon);\n\n /**\n * @dev Returns the current beacon.\n */\n function _getBeacon() internal view returns (address) {\n return StorageSlot.getAddressSlot(_BEACON_SLOT).value;\n }\n\n /**\n * @dev Stores a new beacon in the EIP1967 beacon slot.\n */\n function _setBeacon(address newBeacon) private {\n require(Address.isContract(newBeacon), \"ERC1967: new beacon is not a contract\");\n require(Address.isContract(IBeacon(newBeacon).implementation()), \"ERC1967: beacon implementation is not a contract\");\n StorageSlot.getAddressSlot(_BEACON_SLOT).value = newBeacon;\n }\n\n /**\n * @dev Perform beacon upgrade with additional setup call. Note: This upgrades the address of the beacon, it does\n * not upgrade the implementation contained in the beacon (see {UpgradeableBeacon-_setImplementation} for that).\n *\n * Emits a {BeaconUpgraded} event.\n */\n function _upgradeBeaconToAndCall(\n address newBeacon,\n bytes memory data,\n bool forceCall\n ) internal {\n _setBeacon(newBeacon);\n emit BeaconUpgraded(newBeacon);\n if (data.length > 0 || forceCall) {\n Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data);\n }\n }\n}\n" + }, + "hardhat-deploy/solc_0.8/openzeppelin/proxy/Proxy.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.5.0-rc.0) (proxy/Proxy.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM\n * instruction `delegatecall`. We refer to the second contract as the _implementation_ behind the proxy, and it has to\n * be specified by overriding the virtual {_implementation} function.\n *\n * Additionally, delegation to the implementation can be triggered manually through the {_fallback} function, or to a\n * different contract through the {_delegate} function.\n *\n * The success and return data of the delegated call will be returned back to the caller of the proxy.\n */\nabstract contract Proxy {\n /**\n * @dev Delegates the current call to `implementation`.\n *\n * This function does not return to its internal call site, it will return directly to the external caller.\n */\n function _delegate(address implementation) internal virtual {\n assembly {\n // Copy msg.data. We take full control of memory in this inline assembly\n // block because it will not return to Solidity code. We overwrite the\n // Solidity scratch pad at memory position 0.\n calldatacopy(0, 0, calldatasize())\n\n // Call the implementation.\n // out and outsize are 0 because we don't know the size yet.\n let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)\n\n // Copy the returned data.\n returndatacopy(0, 0, returndatasize())\n\n switch result\n // delegatecall returns 0 on error.\n case 0 {\n revert(0, returndatasize())\n }\n default {\n return(0, returndatasize())\n }\n }\n }\n\n /**\n * @dev This is a virtual function that should be overriden so it returns the address to which the fallback function\n * and {_fallback} should delegate.\n */\n function _implementation() internal view virtual returns (address);\n\n /**\n * @dev Delegates the current call to the address returned by `_implementation()`.\n *\n * This function does not return to its internall call site, it will return directly to the external caller.\n */\n function _fallback() internal virtual {\n _beforeFallback();\n _delegate(_implementation());\n }\n\n /**\n * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other\n * function in the contract matches the call data.\n */\n fallback() external payable virtual {\n _fallback();\n }\n\n /**\n * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if call data\n * is empty.\n */\n receive() external payable virtual {\n _fallback();\n }\n\n /**\n * @dev Hook that is called before falling back to the implementation. Can happen as part of a manual `_fallback`\n * call, or as part of the Solidity `fallback` or `receive` functions.\n *\n * If overriden should call `super._beforeFallback()`.\n */\n function _beforeFallback() internal virtual {}\n}\n" + }, + "hardhat-deploy/solc_0.8/openzeppelin/proxy/transparent/ProxyAdmin.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (proxy/transparent/ProxyAdmin.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./TransparentUpgradeableProxy.sol\";\nimport \"../../access/Ownable.sol\";\n\n/**\n * @dev This is an auxiliary contract meant to be assigned as the admin of a {TransparentUpgradeableProxy}. For an\n * explanation of why you would want to use this see the documentation for {TransparentUpgradeableProxy}.\n */\ncontract ProxyAdmin is Ownable {\n\n constructor (address initialOwner) Ownable(initialOwner) {}\n\n /**\n * @dev Returns the current implementation of `proxy`.\n *\n * Requirements:\n *\n * - This contract must be the admin of `proxy`.\n */\n function getProxyImplementation(TransparentUpgradeableProxy proxy) public view virtual returns (address) {\n // We need to manually run the static call since the getter cannot be flagged as view\n // bytes4(keccak256(\"implementation()\")) == 0x5c60da1b\n (bool success, bytes memory returndata) = address(proxy).staticcall(hex\"5c60da1b\");\n require(success);\n return abi.decode(returndata, (address));\n }\n\n /**\n * @dev Returns the current admin of `proxy`.\n *\n * Requirements:\n *\n * - This contract must be the admin of `proxy`.\n */\n function getProxyAdmin(TransparentUpgradeableProxy proxy) public view virtual returns (address) {\n // We need to manually run the static call since the getter cannot be flagged as view\n // bytes4(keccak256(\"admin()\")) == 0xf851a440\n (bool success, bytes memory returndata) = address(proxy).staticcall(hex\"f851a440\");\n require(success);\n return abi.decode(returndata, (address));\n }\n\n /**\n * @dev Changes the admin of `proxy` to `newAdmin`.\n *\n * Requirements:\n *\n * - This contract must be the current admin of `proxy`.\n */\n function changeProxyAdmin(TransparentUpgradeableProxy proxy, address newAdmin) public virtual onlyOwner {\n proxy.changeAdmin(newAdmin);\n }\n\n /**\n * @dev Upgrades `proxy` to `implementation`. See {TransparentUpgradeableProxy-upgradeTo}.\n *\n * Requirements:\n *\n * - This contract must be the admin of `proxy`.\n */\n function upgrade(TransparentUpgradeableProxy proxy, address implementation) public virtual onlyOwner {\n proxy.upgradeTo(implementation);\n }\n\n /**\n * @dev Upgrades `proxy` to `implementation` and calls a function on the new implementation. See\n * {TransparentUpgradeableProxy-upgradeToAndCall}.\n *\n * Requirements:\n *\n * - This contract must be the admin of `proxy`.\n */\n function upgradeAndCall(\n TransparentUpgradeableProxy proxy,\n address implementation,\n bytes memory data\n ) public payable virtual onlyOwner {\n proxy.upgradeToAndCall{value: msg.value}(implementation, data);\n }\n}\n" + }, + "hardhat-deploy/solc_0.8/openzeppelin/proxy/transparent/TransparentUpgradeableProxy.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (proxy/transparent/TransparentUpgradeableProxy.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../ERC1967/ERC1967Proxy.sol\";\n\n/**\n * @dev This contract implements a proxy that is upgradeable by an admin.\n *\n * To avoid https://medium.com/nomic-labs-blog/malicious-backdoors-in-ethereum-proxies-62629adf3357[proxy selector\n * clashing], which can potentially be used in an attack, this contract uses the\n * https://blog.openzeppelin.com/the-transparent-proxy-pattern/[transparent proxy pattern]. This pattern implies two\n * things that go hand in hand:\n *\n * 1. If any account other than the admin calls the proxy, the call will be forwarded to the implementation, even if\n * that call matches one of the admin functions exposed by the proxy itself.\n * 2. If the admin calls the proxy, it can access the admin functions, but its calls will never be forwarded to the\n * implementation. If the admin tries to call a function on the implementation it will fail with an error that says\n * \"admin cannot fallback to proxy target\".\n *\n * These properties mean that the admin account can only be used for admin actions like upgrading the proxy or changing\n * the admin, so it's best if it's a dedicated account that is not used for anything else. This will avoid headaches due\n * to sudden errors when trying to call a function from the proxy implementation.\n *\n * Our recommendation is for the dedicated account to be an instance of the {ProxyAdmin} contract. If set up this way,\n * you should think of the `ProxyAdmin` instance as the real administrative interface of your proxy.\n */\ncontract TransparentUpgradeableProxy is ERC1967Proxy {\n /**\n * @dev Initializes an upgradeable proxy managed by `_admin`, backed by the implementation at `_logic`, and\n * optionally initialized with `_data` as explained in {ERC1967Proxy-constructor}.\n */\n constructor(\n address _logic,\n address admin_,\n bytes memory _data\n ) payable ERC1967Proxy(_logic, _data) {\n assert(_ADMIN_SLOT == bytes32(uint256(keccak256(\"eip1967.proxy.admin\")) - 1));\n _changeAdmin(admin_);\n }\n\n /**\n * @dev Modifier used internally that will delegate the call to the implementation unless the sender is the admin.\n */\n modifier ifAdmin() {\n if (msg.sender == _getAdmin()) {\n _;\n } else {\n _fallback();\n }\n }\n\n /**\n * @dev Returns the current admin.\n *\n * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyAdmin}.\n *\n * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the\n * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.\n * `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103`\n */\n function admin() external ifAdmin returns (address admin_) {\n admin_ = _getAdmin();\n }\n\n /**\n * @dev Returns the current implementation.\n *\n * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyImplementation}.\n *\n * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the\n * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.\n * `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc`\n */\n function implementation() external ifAdmin returns (address implementation_) {\n implementation_ = _implementation();\n }\n\n /**\n * @dev Changes the admin of the proxy.\n *\n * Emits an {AdminChanged} event.\n *\n * NOTE: Only the admin can call this function. See {ProxyAdmin-changeProxyAdmin}.\n */\n function changeAdmin(address newAdmin) external virtual ifAdmin {\n _changeAdmin(newAdmin);\n }\n\n /**\n * @dev Upgrade the implementation of the proxy.\n *\n * NOTE: Only the admin can call this function. See {ProxyAdmin-upgrade}.\n */\n function upgradeTo(address newImplementation) external ifAdmin {\n _upgradeToAndCall(newImplementation, bytes(\"\"), false);\n }\n\n /**\n * @dev Upgrade the implementation of the proxy, and then call a function from the new implementation as specified\n * by `data`, which should be an encoded function call. This is useful to initialize new storage variables in the\n * proxied contract.\n *\n * NOTE: Only the admin can call this function. See {ProxyAdmin-upgradeAndCall}.\n */\n function upgradeToAndCall(address newImplementation, bytes calldata data) external payable ifAdmin {\n _upgradeToAndCall(newImplementation, data, true);\n }\n\n /**\n * @dev Returns the current admin.\n */\n function _admin() internal view virtual returns (address) {\n return _getAdmin();\n }\n\n /**\n * @dev Makes sure the admin cannot access the fallback function. See {Proxy-_beforeFallback}.\n */\n function _beforeFallback() internal virtual override {\n require(msg.sender != _getAdmin(), \"TransparentUpgradeableProxy: admin cannot fallback to proxy target\");\n super._beforeFallback();\n }\n}\n" + }, + "hardhat-deploy/solc_0.8/openzeppelin/utils/Address.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.5.0-rc.0) (utils/Address.sol)\n\npragma solidity ^0.8.1;\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n /**\n * @dev Returns true if `account` is a contract.\n *\n * [IMPORTANT]\n * ====\n * It is unsafe to assume that an address for which this function returns\n * false is an externally-owned account (EOA) and not a contract.\n *\n * Among others, `isContract` will return false for the following\n * types of addresses:\n *\n * - an externally-owned account\n * - a contract in construction\n * - an address where a contract will be created\n * - an address where a contract lived, but was destroyed\n * ====\n *\n * [IMPORTANT]\n * ====\n * You shouldn't rely on `isContract` to protect against flash loan attacks!\n *\n * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets\n * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract\n * constructor.\n * ====\n */\n function isContract(address account) internal view returns (bool) {\n // This method relies on extcodesize/address.code.length, which returns 0\n // for contracts in construction, since the code is only stored at the end\n // of the constructor execution.\n\n return account.code.length > 0;\n }\n\n /**\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n * `recipient`, forwarding all available gas and reverting on errors.\n *\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\n * imposed by `transfer`, making them unable to receive funds via\n * `transfer`. {sendValue} removes this limitation.\n *\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n *\n * IMPORTANT: because control is transferred to `recipient`, care must be\n * taken to not create reentrancy vulnerabilities. Consider using\n * {ReentrancyGuard} or the\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n */\n function sendValue(address payable recipient, uint256 amount) internal {\n require(address(this).balance >= amount, \"Address: insufficient balance\");\n\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Address: unable to send value, recipient may have reverted\");\n }\n\n /**\n * @dev Performs a Solidity function call using a low level `call`. A\n * plain `call` is an unsafe replacement for a function call: use this\n * function instead.\n *\n * If `target` reverts with a revert reason, it is bubbled up by this\n * function (like regular Solidity function calls).\n *\n * Returns the raw returned data. To convert to the expected return value,\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n *\n * Requirements:\n *\n * - `target` must be a contract.\n * - calling `target` with `data` must not revert.\n *\n * _Available since v3.1._\n */\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionCall(target, data, \"Address: low-level call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n * `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but also transferring `value` wei to `target`.\n *\n * Requirements:\n *\n * - the calling contract must have an ETH balance of at least `value`.\n * - the called Solidity function must be `payable`.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n * with `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(address(this).balance >= value, \"Address: insufficient balance for call\");\n require(isContract(target), \"Address: call to non-contract\");\n\n (bool success, bytes memory returndata) = target.call{value: value}(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n return functionStaticCall(target, data, \"Address: low-level static call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n require(isContract(target), \"Address: static call to non-contract\");\n\n (bool success, bytes memory returndata) = target.staticcall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(isContract(target), \"Address: delegate call to non-contract\");\n\n (bool success, bytes memory returndata) = target.delegatecall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\n * revert reason using the provided one.\n *\n * _Available since v4.3._\n */\n function verifyCallResult(\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal pure returns (bytes memory) {\n if (success) {\n return returndata;\n } else {\n // Look for revert reason and bubble it up if present\n if (returndata.length > 0) {\n // The easiest way to bubble the revert reason is using memory via assembly\n\n assembly {\n let returndata_size := mload(returndata)\n revert(add(32, returndata), returndata_size)\n }\n } else {\n revert(errorMessage);\n }\n }\n }\n}\n" + }, + "hardhat-deploy/solc_0.8/openzeppelin/utils/Context.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract Context {\n function _msgSender() internal view virtual returns (address) {\n return msg.sender;\n }\n\n function _msgData() internal view virtual returns (bytes calldata) {\n return msg.data;\n }\n}\n" + }, + "hardhat-deploy/solc_0.8/openzeppelin/utils/StorageSlot.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/StorageSlot.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Library for reading and writing primitive types to specific storage slots.\n *\n * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.\n * This library helps with reading and writing to such slots without the need for inline assembly.\n *\n * The functions in this library return Slot structs that contain a `value` member that can be used to read or write.\n *\n * Example usage to set ERC1967 implementation slot:\n * ```\n * contract ERC1967 {\n * bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;\n *\n * function _getImplementation() internal view returns (address) {\n * return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;\n * }\n *\n * function _setImplementation(address newImplementation) internal {\n * require(Address.isContract(newImplementation), \"ERC1967: new implementation is not a contract\");\n * StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;\n * }\n * }\n * ```\n *\n * _Available since v4.1 for `address`, `bool`, `bytes32`, and `uint256`._\n */\nlibrary StorageSlot {\n struct AddressSlot {\n address value;\n }\n\n struct BooleanSlot {\n bool value;\n }\n\n struct Bytes32Slot {\n bytes32 value;\n }\n\n struct Uint256Slot {\n uint256 value;\n }\n\n /**\n * @dev Returns an `AddressSlot` with member `value` located at `slot`.\n */\n function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {\n assembly {\n r.slot := slot\n }\n }\n\n /**\n * @dev Returns an `BooleanSlot` with member `value` located at `slot`.\n */\n function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {\n assembly {\n r.slot := slot\n }\n }\n\n /**\n * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.\n */\n function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {\n assembly {\n r.slot := slot\n }\n }\n\n /**\n * @dev Returns an `Uint256Slot` with member `value` located at `slot`.\n */\n function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {\n assembly {\n r.slot := slot\n }\n }\n}\n" + }, + "hardhat-deploy/solc_0.8/proxy/OptimizedTransparentUpgradeableProxy.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (proxy/transparent/TransparentUpgradeableProxy.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../openzeppelin/proxy/ERC1967/ERC1967Proxy.sol\";\n\n/**\n * @dev This contract implements a proxy that is upgradeable by an admin.\n *\n * To avoid https://medium.com/nomic-labs-blog/malicious-backdoors-in-ethereum-proxies-62629adf3357[proxy selector\n * clashing], which can potentially be used in an attack, this contract uses the\n * https://blog.openzeppelin.com/the-transparent-proxy-pattern/[transparent proxy pattern]. This pattern implies two\n * things that go hand in hand:\n *\n * 1. If any account other than the admin calls the proxy, the call will be forwarded to the implementation, even if\n * that call matches one of the admin functions exposed by the proxy itself.\n * 2. If the admin calls the proxy, it can access the admin functions, but its calls will never be forwarded to the\n * implementation. If the admin tries to call a function on the implementation it will fail with an error that says\n * \"admin cannot fallback to proxy target\".\n *\n * These properties mean that the admin account can only be used for admin actions like upgrading the proxy or changing\n * the admin, so it's best if it's a dedicated account that is not used for anything else. This will avoid headaches due\n * to sudden errors when trying to call a function from the proxy implementation.\n *\n * Our recommendation is for the dedicated account to be an instance of the {ProxyAdmin} contract. If set up this way,\n * you should think of the `ProxyAdmin` instance as the real administrative interface of your proxy.\n */\ncontract OptimizedTransparentUpgradeableProxy is ERC1967Proxy {\n address internal immutable _ADMIN;\n\n /**\n * @dev Initializes an upgradeable proxy managed by `_admin`, backed by the implementation at `_logic`, and\n * optionally initialized with `_data` as explained in {ERC1967Proxy-constructor}.\n */\n constructor(\n address _logic,\n address admin_,\n bytes memory _data\n ) payable ERC1967Proxy(_logic, _data) {\n assert(_ADMIN_SLOT == bytes32(uint256(keccak256(\"eip1967.proxy.admin\")) - 1));\n _ADMIN = admin_;\n\n // still store it to work with EIP-1967\n bytes32 slot = _ADMIN_SLOT;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(slot, admin_)\n }\n emit AdminChanged(address(0), admin_);\n }\n\n /**\n * @dev Modifier used internally that will delegate the call to the implementation unless the sender is the admin.\n */\n modifier ifAdmin() {\n if (msg.sender == _getAdmin()) {\n _;\n } else {\n _fallback();\n }\n }\n\n /**\n * @dev Returns the current admin.\n *\n * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyAdmin}.\n *\n * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the\n * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.\n * `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103`\n */\n function admin() external ifAdmin returns (address admin_) {\n admin_ = _getAdmin();\n }\n\n /**\n * @dev Returns the current implementation.\n *\n * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyImplementation}.\n *\n * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the\n * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.\n * `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc`\n */\n function implementation() external ifAdmin returns (address implementation_) {\n implementation_ = _implementation();\n }\n\n /**\n * @dev Upgrade the implementation of the proxy.\n *\n * NOTE: Only the admin can call this function. See {ProxyAdmin-upgrade}.\n */\n function upgradeTo(address newImplementation) external ifAdmin {\n _upgradeToAndCall(newImplementation, bytes(\"\"), false);\n }\n\n /**\n * @dev Upgrade the implementation of the proxy, and then call a function from the new implementation as specified\n * by `data`, which should be an encoded function call. This is useful to initialize new storage variables in the\n * proxied contract.\n *\n * NOTE: Only the admin can call this function. See {ProxyAdmin-upgradeAndCall}.\n */\n function upgradeToAndCall(address newImplementation, bytes calldata data) external payable ifAdmin {\n _upgradeToAndCall(newImplementation, data, true);\n }\n\n /**\n * @dev Returns the current admin.\n */\n function _admin() internal view virtual returns (address) {\n return _getAdmin();\n }\n\n /**\n * @dev Makes sure the admin cannot access the fallback function. See {Proxy-_beforeFallback}.\n */\n function _beforeFallback() internal virtual override {\n require(msg.sender != _getAdmin(), \"TransparentUpgradeableProxy: admin cannot fallback to proxy target\");\n super._beforeFallback();\n }\n\n function _getAdmin() internal view virtual override returns (address) {\n return _ADMIN;\n }\n}\n" + } + }, + "settings": { + "optimizer": { + "enabled": true, + "runs": 200, + "details": { + "yul": true + } + }, + "evmVersion": "paris", + "outputSelection": { + "*": { + "*": [ + "storageLayout", + "abi", + "evm.bytecode", + "evm.deployedBytecode", + "evm.methodIdentifiers", + "metadata", + "devdoc", + "userdoc", + "evm.gasEstimates" + ], + "": ["ast"] + } + }, + "metadata": { + "useLiteralContent": true + } + } +} diff --git a/deployments/bsctestnet/solcInputs/1e952eaff4761c9a4f4e4da7e68f34d1.json b/deployments/bsctestnet/solcInputs/1e952eaff4761c9a4f4e4da7e68f34d1.json new file mode 100644 index 000000000..d1492f137 --- /dev/null +++ b/deployments/bsctestnet/solcInputs/1e952eaff4761c9a4f4e4da7e68f34d1.json @@ -0,0 +1,145 @@ +{ + "language": "Solidity", + "sources": { + "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable2Step.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./OwnableUpgradeable.sol\";\nimport {Initializable} from \"../proxy/utils/Initializable.sol\";\n\n/**\n * @dev Contract module which provides access control mechanism, where\n * there is an account (an owner) that can be granted exclusive access to\n * specific functions.\n *\n * By default, the owner account will be the one that deploys the contract. This\n * can later be changed with {transferOwnership} and {acceptOwnership}.\n *\n * This module is used through inheritance. It will make available all functions\n * from parent (Ownable).\n */\nabstract contract Ownable2StepUpgradeable is Initializable, OwnableUpgradeable {\n address private _pendingOwner;\n\n event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner);\n\n function __Ownable2Step_init() internal onlyInitializing {\n __Ownable_init_unchained();\n }\n\n function __Ownable2Step_init_unchained() internal onlyInitializing {\n }\n /**\n * @dev Returns the address of the pending owner.\n */\n function pendingOwner() public view virtual returns (address) {\n return _pendingOwner;\n }\n\n /**\n * @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one.\n * Can only be called by the current owner.\n */\n function transferOwnership(address newOwner) public virtual override onlyOwner {\n _pendingOwner = newOwner;\n emit OwnershipTransferStarted(owner(), newOwner);\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner.\n * Internal function without access restriction.\n */\n function _transferOwnership(address newOwner) internal virtual override {\n delete _pendingOwner;\n super._transferOwnership(newOwner);\n }\n\n /**\n * @dev The new owner accepts the ownership transfer.\n */\n function acceptOwnership() public virtual {\n address sender = _msgSender();\n require(pendingOwner() == sender, \"Ownable2Step: caller is not the new owner\");\n _transferOwnership(sender);\n }\n\n /**\n * @dev This empty reserved space is put in place to allow future versions to add new\n * variables without shifting down storage in the inheritance chain.\n * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\n */\n uint256[49] private __gap;\n}\n" + }, + "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../utils/ContextUpgradeable.sol\";\nimport {Initializable} from \"../proxy/utils/Initializable.sol\";\n\n/**\n * @dev Contract module which provides a basic access control mechanism, where\n * there is an account (an owner) that can be granted exclusive access to\n * specific functions.\n *\n * By default, the owner account will be the one that deploys the contract. This\n * can later be changed with {transferOwnership}.\n *\n * This module is used through inheritance. It will make available the modifier\n * `onlyOwner`, which can be applied to your functions to restrict their use to\n * the owner.\n */\nabstract contract OwnableUpgradeable is Initializable, ContextUpgradeable {\n address private _owner;\n\n event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n\n /**\n * @dev Initializes the contract setting the deployer as the initial owner.\n */\n function __Ownable_init() internal onlyInitializing {\n __Ownable_init_unchained();\n }\n\n function __Ownable_init_unchained() internal onlyInitializing {\n _transferOwnership(_msgSender());\n }\n\n /**\n * @dev Throws if called by any account other than the owner.\n */\n modifier onlyOwner() {\n _checkOwner();\n _;\n }\n\n /**\n * @dev Returns the address of the current owner.\n */\n function owner() public view virtual returns (address) {\n return _owner;\n }\n\n /**\n * @dev Throws if the sender is not the owner.\n */\n function _checkOwner() internal view virtual {\n require(owner() == _msgSender(), \"Ownable: caller is not the owner\");\n }\n\n /**\n * @dev Leaves the contract without owner. It will not be possible to call\n * `onlyOwner` functions. Can only be called by the current owner.\n *\n * NOTE: Renouncing ownership will leave the contract without an owner,\n * thereby disabling any functionality that is only available to the owner.\n */\n function renounceOwnership() public virtual onlyOwner {\n _transferOwnership(address(0));\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Can only be called by the current owner.\n */\n function transferOwnership(address newOwner) public virtual onlyOwner {\n require(newOwner != address(0), \"Ownable: new owner is the zero address\");\n _transferOwnership(newOwner);\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Internal function without access restriction.\n */\n function _transferOwnership(address newOwner) internal virtual {\n address oldOwner = _owner;\n _owner = newOwner;\n emit OwnershipTransferred(oldOwner, newOwner);\n }\n\n /**\n * @dev This empty reserved space is put in place to allow future versions to add new\n * variables without shifting down storage in the inheritance chain.\n * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\n */\n uint256[49] private __gap;\n}\n" + }, + "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.0) (proxy/utils/Initializable.sol)\n\npragma solidity ^0.8.2;\n\nimport \"../../utils/AddressUpgradeable.sol\";\n\n/**\n * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed\n * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an\n * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer\n * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.\n *\n * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be\n * reused. This mechanism prevents re-execution of each \"step\" but allows the creation of new initialization steps in\n * case an upgrade adds a module that needs to be initialized.\n *\n * For example:\n *\n * [.hljs-theme-light.nopadding]\n * ```solidity\n * contract MyToken is ERC20Upgradeable {\n * function initialize() initializer public {\n * __ERC20_init(\"MyToken\", \"MTK\");\n * }\n * }\n *\n * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {\n * function initializeV2() reinitializer(2) public {\n * __ERC20Permit_init(\"MyToken\");\n * }\n * }\n * ```\n *\n * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as\n * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.\n *\n * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure\n * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.\n *\n * [CAUTION]\n * ====\n * Avoid leaving a contract uninitialized.\n *\n * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation\n * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke\n * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:\n *\n * [.hljs-theme-light.nopadding]\n * ```\n * /// @custom:oz-upgrades-unsafe-allow constructor\n * constructor() {\n * _disableInitializers();\n * }\n * ```\n * ====\n */\nabstract contract Initializable {\n /**\n * @dev Indicates that the contract has been initialized.\n * @custom:oz-retyped-from bool\n */\n uint8 private _initialized;\n\n /**\n * @dev Indicates that the contract is in the process of being initialized.\n */\n bool private _initializing;\n\n /**\n * @dev Triggered when the contract has been initialized or reinitialized.\n */\n event Initialized(uint8 version);\n\n /**\n * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,\n * `onlyInitializing` functions can be used to initialize parent contracts.\n *\n * Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a\n * constructor.\n *\n * Emits an {Initialized} event.\n */\n modifier initializer() {\n bool isTopLevelCall = !_initializing;\n require(\n (isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1),\n \"Initializable: contract is already initialized\"\n );\n _initialized = 1;\n if (isTopLevelCall) {\n _initializing = true;\n }\n _;\n if (isTopLevelCall) {\n _initializing = false;\n emit Initialized(1);\n }\n }\n\n /**\n * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the\n * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be\n * used to initialize parent contracts.\n *\n * A reinitializer may be used after the original initialization step. This is essential to configure modules that\n * are added through upgrades and that require initialization.\n *\n * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`\n * cannot be nested. If one is invoked in the context of another, execution will revert.\n *\n * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in\n * a contract, executing them in the right order is up to the developer or operator.\n *\n * WARNING: setting the version to 255 will prevent any future reinitialization.\n *\n * Emits an {Initialized} event.\n */\n modifier reinitializer(uint8 version) {\n require(!_initializing && _initialized < version, \"Initializable: contract is already initialized\");\n _initialized = version;\n _initializing = true;\n _;\n _initializing = false;\n emit Initialized(version);\n }\n\n /**\n * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the\n * {initializer} and {reinitializer} modifiers, directly or indirectly.\n */\n modifier onlyInitializing() {\n require(_initializing, \"Initializable: contract is not initializing\");\n _;\n }\n\n /**\n * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.\n * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized\n * to any version. It is recommended to use this to lock implementation contracts that are designed to be called\n * through proxies.\n *\n * Emits an {Initialized} event the first time it is successfully executed.\n */\n function _disableInitializers() internal virtual {\n require(!_initializing, \"Initializable: contract is initializing\");\n if (_initialized != type(uint8).max) {\n _initialized = type(uint8).max;\n emit Initialized(type(uint8).max);\n }\n }\n\n /**\n * @dev Returns the highest version that has been initialized. See {reinitializer}.\n */\n function _getInitializedVersion() internal view returns (uint8) {\n return _initialized;\n }\n\n /**\n * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.\n */\n function _isInitializing() internal view returns (bool) {\n return _initializing;\n }\n}\n" + }, + "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/IERC20PermitUpgradeable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.4) (token/ERC20/extensions/IERC20Permit.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in\n * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].\n *\n * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by\n * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't\n * need to send a transaction, and thus is not required to hold Ether at all.\n *\n * ==== Security Considerations\n *\n * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature\n * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be\n * considered as an intention to spend the allowance in any specific way. The second is that because permits have\n * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should\n * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be\n * generally recommended is:\n *\n * ```solidity\n * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {\n * try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}\n * doThing(..., value);\n * }\n *\n * function doThing(..., uint256 value) public {\n * token.safeTransferFrom(msg.sender, address(this), value);\n * ...\n * }\n * ```\n *\n * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of\n * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also\n * {SafeERC20-safeTransferFrom}).\n *\n * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so\n * contracts should have entry points that don't rely on permit.\n */\ninterface IERC20PermitUpgradeable {\n /**\n * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,\n * given ``owner``'s signed approval.\n *\n * IMPORTANT: The same issues {IERC20-approve} has related to transaction\n * ordering also apply here.\n *\n * Emits an {Approval} event.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n * - `deadline` must be a timestamp in the future.\n * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`\n * over the EIP712-formatted function arguments.\n * - the signature must use ``owner``'s current nonce (see {nonces}).\n *\n * For more information on the signature format, see the\n * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP\n * section].\n *\n * CAUTION: See Security Considerations above.\n */\n function permit(\n address owner,\n address spender,\n uint256 value,\n uint256 deadline,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) external;\n\n /**\n * @dev Returns the current nonce for `owner`. This value must be\n * included whenever a signature is generated for {permit}.\n *\n * Every successful call to {permit} increases ``owner``'s nonce by one. This\n * prevents a signature from being used multiple times.\n */\n function nonces(address owner) external view returns (uint256);\n\n /**\n * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.\n */\n // solhint-disable-next-line func-name-mixedcase\n function DOMAIN_SEPARATOR() external view returns (bytes32);\n}\n" + }, + "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20Upgradeable {\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n\n /**\n * @dev Returns the amount of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves `amount` tokens from the caller's account to `to`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address to, uint256 amount) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender's allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @dev Moves `amount` tokens from `from` to `to` using the\n * allowance mechanism. `amount` is then deducted from the caller's\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(address from, address to, uint256 amount) external returns (bool);\n}\n" + }, + "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.3) (token/ERC20/utils/SafeERC20.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC20Upgradeable.sol\";\nimport \"../extensions/IERC20PermitUpgradeable.sol\";\nimport \"../../../utils/AddressUpgradeable.sol\";\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20Upgradeable {\n using AddressUpgradeable for address;\n\n /**\n * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,\n * non-reverting calls are assumed to be successful.\n */\n function safeTransfer(IERC20Upgradeable token, address to, uint256 value) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\n }\n\n /**\n * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the\n * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.\n */\n function safeTransferFrom(IERC20Upgradeable token, address from, address to, uint256 value) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\n }\n\n /**\n * @dev Deprecated. This function has issues similar to the ones found in\n * {IERC20-approve}, and its usage is discouraged.\n *\n * Whenever possible, use {safeIncreaseAllowance} and\n * {safeDecreaseAllowance} instead.\n */\n function safeApprove(IERC20Upgradeable token, address spender, uint256 value) internal {\n // safeApprove should only be called when setting an initial allowance,\n // or when resetting it to zero. To increase and decrease it, use\n // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\n require(\n (value == 0) || (token.allowance(address(this), spender) == 0),\n \"SafeERC20: approve from non-zero to non-zero allowance\"\n );\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\n }\n\n /**\n * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,\n * non-reverting calls are assumed to be successful.\n */\n function safeIncreaseAllowance(IERC20Upgradeable token, address spender, uint256 value) internal {\n uint256 oldAllowance = token.allowance(address(this), spender);\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));\n }\n\n /**\n * @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,\n * non-reverting calls are assumed to be successful.\n */\n function safeDecreaseAllowance(IERC20Upgradeable token, address spender, uint256 value) internal {\n unchecked {\n uint256 oldAllowance = token.allowance(address(this), spender);\n require(oldAllowance >= value, \"SafeERC20: decreased allowance below zero\");\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));\n }\n }\n\n /**\n * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,\n * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval\n * to be set to zero before setting it to a non-zero value, such as USDT.\n */\n function forceApprove(IERC20Upgradeable token, address spender, uint256 value) internal {\n bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);\n\n if (!_callOptionalReturnBool(token, approvalCall)) {\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));\n _callOptionalReturn(token, approvalCall);\n }\n }\n\n /**\n * @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.\n * Revert on invalid signature.\n */\n function safePermit(\n IERC20PermitUpgradeable token,\n address owner,\n address spender,\n uint256 value,\n uint256 deadline,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) internal {\n uint256 nonceBefore = token.nonces(owner);\n token.permit(owner, spender, value, deadline, v, r, s);\n uint256 nonceAfter = token.nonces(owner);\n require(nonceAfter == nonceBefore + 1, \"SafeERC20: permit did not succeed\");\n }\n\n /**\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n * on the return value: the return value is optional (but if data is returned, it must not be false).\n * @param token The token targeted by the call.\n * @param data The call data (encoded using abi.encode or one of its variants).\n */\n function _callOptionalReturn(IERC20Upgradeable token, bytes memory data) private {\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that\n // the target address contains contract code and also asserts for success in the low-level call.\n\n bytes memory returndata = address(token).functionCall(data, \"SafeERC20: low-level call failed\");\n require(returndata.length == 0 || abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n }\n\n /**\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n * on the return value: the return value is optional (but if data is returned, it must not be false).\n * @param token The token targeted by the call.\n * @param data The call data (encoded using abi.encode or one of its variants).\n *\n * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.\n */\n function _callOptionalReturnBool(IERC20Upgradeable token, bytes memory data) private returns (bool) {\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false\n // and not revert is the subcall reverts.\n\n (bool success, bytes memory returndata) = address(token).call(data);\n return\n success && (returndata.length == 0 || abi.decode(returndata, (bool))) && AddressUpgradeable.isContract(address(token));\n }\n}\n" + }, + "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)\n\npragma solidity ^0.8.1;\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary AddressUpgradeable {\n /**\n * @dev Returns true if `account` is a contract.\n *\n * [IMPORTANT]\n * ====\n * It is unsafe to assume that an address for which this function returns\n * false is an externally-owned account (EOA) and not a contract.\n *\n * Among others, `isContract` will return false for the following\n * types of addresses:\n *\n * - an externally-owned account\n * - a contract in construction\n * - an address where a contract will be created\n * - an address where a contract lived, but was destroyed\n *\n * Furthermore, `isContract` will also return true if the target contract within\n * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,\n * which only has an effect at the end of a transaction.\n * ====\n *\n * [IMPORTANT]\n * ====\n * You shouldn't rely on `isContract` to protect against flash loan attacks!\n *\n * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets\n * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract\n * constructor.\n * ====\n */\n function isContract(address account) internal view returns (bool) {\n // This method relies on extcodesize/address.code.length, which returns 0\n // for contracts in construction, since the code is only stored at the end\n // of the constructor execution.\n\n return account.code.length > 0;\n }\n\n /**\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n * `recipient`, forwarding all available gas and reverting on errors.\n *\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\n * imposed by `transfer`, making them unable to receive funds via\n * `transfer`. {sendValue} removes this limitation.\n *\n * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n *\n * IMPORTANT: because control is transferred to `recipient`, care must be\n * taken to not create reentrancy vulnerabilities. Consider using\n * {ReentrancyGuard} or the\n * https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n */\n function sendValue(address payable recipient, uint256 amount) internal {\n require(address(this).balance >= amount, \"Address: insufficient balance\");\n\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Address: unable to send value, recipient may have reverted\");\n }\n\n /**\n * @dev Performs a Solidity function call using a low level `call`. A\n * plain `call` is an unsafe replacement for a function call: use this\n * function instead.\n *\n * If `target` reverts with a revert reason, it is bubbled up by this\n * function (like regular Solidity function calls).\n *\n * Returns the raw returned data. To convert to the expected return value,\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n *\n * Requirements:\n *\n * - `target` must be a contract.\n * - calling `target` with `data` must not revert.\n *\n * _Available since v3.1._\n */\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, \"Address: low-level call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n * `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but also transferring `value` wei to `target`.\n *\n * Requirements:\n *\n * - the calling contract must have an ETH balance of at least `value`.\n * - the called Solidity function must be `payable`.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {\n return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n * with `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(address(this).balance >= value, \"Address: insufficient balance for call\");\n (bool success, bytes memory returndata) = target.call{value: value}(data);\n return verifyCallResultFromTarget(target, success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n return functionStaticCall(target, data, \"Address: low-level static call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n (bool success, bytes memory returndata) = target.staticcall(data);\n return verifyCallResultFromTarget(target, success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n (bool success, bytes memory returndata) = target.delegatecall(data);\n return verifyCallResultFromTarget(target, success, returndata, errorMessage);\n }\n\n /**\n * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling\n * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.\n *\n * _Available since v4.8._\n */\n function verifyCallResultFromTarget(\n address target,\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n if (success) {\n if (returndata.length == 0) {\n // only check isContract if the call was successful and the return data is empty\n // otherwise we already know that it was a contract\n require(isContract(target), \"Address: call to non-contract\");\n }\n return returndata;\n } else {\n _revert(returndata, errorMessage);\n }\n }\n\n /**\n * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the\n * revert reason or using the provided one.\n *\n * _Available since v4.3._\n */\n function verifyCallResult(\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal pure returns (bytes memory) {\n if (success) {\n return returndata;\n } else {\n _revert(returndata, errorMessage);\n }\n }\n\n function _revert(bytes memory returndata, string memory errorMessage) private pure {\n // Look for revert reason and bubble it up if present\n if (returndata.length > 0) {\n // The easiest way to bubble the revert reason is using memory via assembly\n /// @solidity memory-safe-assembly\n assembly {\n let returndata_size := mload(returndata)\n revert(add(32, returndata), returndata_size)\n }\n } else {\n revert(errorMessage);\n }\n }\n}\n" + }, + "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.4) (utils/Context.sol)\n\npragma solidity ^0.8.0;\nimport {Initializable} from \"../proxy/utils/Initializable.sol\";\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract ContextUpgradeable is Initializable {\n function __Context_init() internal onlyInitializing {\n }\n\n function __Context_init_unchained() internal onlyInitializing {\n }\n function _msgSender() internal view virtual returns (address) {\n return msg.sender;\n }\n\n function _msgData() internal view virtual returns (bytes calldata) {\n return msg.data;\n }\n\n function _contextSuffixLength() internal view virtual returns (uint256) {\n return 0;\n }\n\n /**\n * @dev This empty reserved space is put in place to allow future versions to add new\n * variables without shifting down storage in the inheritance chain.\n * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\n */\n uint256[50] private __gap;\n}\n" + }, + "@openzeppelin/contracts/access/IAccessControl.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev External interface of AccessControl declared to support ERC165 detection.\n */\ninterface IAccessControl {\n /**\n * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`\n *\n * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite\n * {RoleAdminChanged} not being emitted signaling this.\n *\n * _Available since v3.1._\n */\n event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);\n\n /**\n * @dev Emitted when `account` is granted `role`.\n *\n * `sender` is the account that originated the contract call, an admin role\n * bearer except when using {AccessControl-_setupRole}.\n */\n event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);\n\n /**\n * @dev Emitted when `account` is revoked `role`.\n *\n * `sender` is the account that originated the contract call:\n * - if using `revokeRole`, it is the admin role bearer\n * - if using `renounceRole`, it is the role bearer (i.e. `account`)\n */\n event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);\n\n /**\n * @dev Returns `true` if `account` has been granted `role`.\n */\n function hasRole(bytes32 role, address account) external view returns (bool);\n\n /**\n * @dev Returns the admin role that controls `role`. See {grantRole} and\n * {revokeRole}.\n *\n * To change a role's admin, use {AccessControl-_setRoleAdmin}.\n */\n function getRoleAdmin(bytes32 role) external view returns (bytes32);\n\n /**\n * @dev Grants `role` to `account`.\n *\n * If `account` had not been already granted `role`, emits a {RoleGranted}\n * event.\n *\n * Requirements:\n *\n * - the caller must have ``role``'s admin role.\n */\n function grantRole(bytes32 role, address account) external;\n\n /**\n * @dev Revokes `role` from `account`.\n *\n * If `account` had been granted `role`, emits a {RoleRevoked} event.\n *\n * Requirements:\n *\n * - the caller must have ``role``'s admin role.\n */\n function revokeRole(bytes32 role, address account) external;\n\n /**\n * @dev Revokes `role` from the calling account.\n *\n * Roles are often managed via {grantRole} and {revokeRole}: this function's\n * purpose is to provide a mechanism for accounts to lose their privileges\n * if they are compromised (such as when a trusted device is misplaced).\n *\n * If the calling account had been granted `role`, emits a {RoleRevoked}\n * event.\n *\n * Requirements:\n *\n * - the caller must be `account`.\n */\n function renounceRole(bytes32 role, address account) external;\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC20.sol\";\n\n/**\n * @dev Interface for the optional metadata functions from the ERC20 standard.\n *\n * _Available since v4.1._\n */\ninterface IERC20Metadata is IERC20 {\n /**\n * @dev Returns the name of the token.\n */\n function name() external view returns (string memory);\n\n /**\n * @dev Returns the symbol of the token.\n */\n function symbol() external view returns (string memory);\n\n /**\n * @dev Returns the decimals places of the token.\n */\n function decimals() external view returns (uint8);\n}\n" + }, + "@openzeppelin/contracts/token/ERC20/IERC20.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n\n /**\n * @dev Returns the amount of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves `amount` tokens from the caller's account to `to`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address to, uint256 amount) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender's allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @dev Moves `amount` tokens from `from` to `to` using the\n * allowance mechanism. `amount` is then deducted from the caller's\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(address from, address to, uint256 amount) external returns (bool);\n}\n" + }, + "@venusprotocol/governance-contracts/contracts/Governance/AccessControlledV8.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\n\nimport \"@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol\";\nimport \"@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol\";\n\nimport \"./IAccessControlManagerV8.sol\";\n\n/**\n * @title AccessControlledV8\n * @author Venus\n * @notice This contract is helper between access control manager and actual contract. This contract further inherited by other contract (using solidity 0.8.13)\n * to integrate access controlled mechanism. It provides initialise methods and verifying access methods.\n */\nabstract contract AccessControlledV8 is Initializable, Ownable2StepUpgradeable {\n /// @notice Access control manager contract\n IAccessControlManagerV8 internal _accessControlManager;\n\n /**\n * @dev This empty reserved space is put in place to allow future versions to add new\n * variables without shifting down storage in the inheritance chain.\n * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\n */\n uint256[49] private __gap;\n\n /// @notice Emitted when access control manager contract address is changed\n event NewAccessControlManager(address oldAccessControlManager, address newAccessControlManager);\n\n /// @notice Thrown when the action is prohibited by AccessControlManager\n error Unauthorized(address sender, address calledContract, string methodSignature);\n\n function __AccessControlled_init(address accessControlManager_) internal onlyInitializing {\n __Ownable2Step_init();\n __AccessControlled_init_unchained(accessControlManager_);\n }\n\n function __AccessControlled_init_unchained(address accessControlManager_) internal onlyInitializing {\n _setAccessControlManager(accessControlManager_);\n }\n\n /**\n * @notice Sets the address of AccessControlManager\n * @dev Admin function to set address of AccessControlManager\n * @param accessControlManager_ The new address of the AccessControlManager\n * @custom:event Emits NewAccessControlManager event\n * @custom:access Only Governance\n */\n function setAccessControlManager(address accessControlManager_) external onlyOwner {\n _setAccessControlManager(accessControlManager_);\n }\n\n /**\n * @notice Returns the address of the access control manager contract\n */\n function accessControlManager() external view returns (IAccessControlManagerV8) {\n return _accessControlManager;\n }\n\n /**\n * @dev Internal function to set address of AccessControlManager\n * @param accessControlManager_ The new address of the AccessControlManager\n */\n function _setAccessControlManager(address accessControlManager_) internal {\n require(address(accessControlManager_) != address(0), \"invalid acess control manager address\");\n address oldAccessControlManager = address(_accessControlManager);\n _accessControlManager = IAccessControlManagerV8(accessControlManager_);\n emit NewAccessControlManager(oldAccessControlManager, accessControlManager_);\n }\n\n /**\n * @notice Reverts if the call is not allowed by AccessControlManager\n * @param signature Method signature\n */\n function _checkAccessAllowed(string memory signature) internal view {\n bool isAllowedToCall = _accessControlManager.isAllowedToCall(msg.sender, signature);\n\n if (!isAllowedToCall) {\n revert Unauthorized(msg.sender, address(this), signature);\n }\n }\n}\n" + }, + "@venusprotocol/governance-contracts/contracts/Governance/IAccessControlManagerV8.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity ^0.8.25;\n\nimport \"@openzeppelin/contracts/access/IAccessControl.sol\";\n\n/**\n * @title IAccessControlManagerV8\n * @author Venus\n * @notice Interface implemented by the `AccessControlManagerV8` contract.\n */\ninterface IAccessControlManagerV8 is IAccessControl {\n function giveCallPermission(address contractAddress, string calldata functionSig, address accountToPermit) external;\n\n function revokeCallPermission(\n address contractAddress,\n string calldata functionSig,\n address accountToRevoke\n ) external;\n\n function isAllowedToCall(address account, string calldata functionSig) external view returns (bool);\n\n function hasPermission(\n address account,\n address contractAddress,\n string calldata functionSig\n ) external view returns (bool);\n}\n" + }, + "@venusprotocol/oracle/contracts/interfaces/IDeviationBoundedOracle.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\n\ninterface IDeviationBoundedOracle {\n // --- Enums ---\n\n /// @notice Identifies whether a price bound is a minimum or maximum\n enum PriceBoundType {\n MIN,\n MAX\n }\n\n /// @notice Identifies which keeper action a single syncPriceBoundsAndProtections item performs\n enum KeeperAction {\n SetMinPrice,\n SetMaxPrice,\n ExitProtectionMode\n }\n\n // --- Structs ---\n\n /// @notice Per-asset protection state tracking the min/max price window\n struct MarketProtectionState {\n /// @notice Lowest price observed in the current window (packed with maxPrice in one slot)\n uint128 minPrice;\n /// @notice Highest price observed in the current window\n uint128 maxPrice;\n /// @notice Whether protected price is currently being used\n bool currentlyUsingProtectedPrice;\n /// @notice Whether this market is whitelisted for bounded pricing\n bool isBoundedPricingEnabled;\n /// @notice Timestamp of the last protection trigger — reset on every trigger\n uint64 lastProtectionTriggeredAt;\n /// @notice Minimum time protection stays active after last trigger\n uint64 cooldownPeriod;\n /// @notice The underlying asset address, used to verify initialization\n address asset;\n /// @notice Entry deviation threshold (mantissa, e.g. 0.1667e18 = 16.67%); packed with resetThreshold\n uint128 triggerThreshold;\n /// @notice Exit threshold (mantissa); window must converge below this for protection to be disabled\n uint128 resetThreshold;\n /// @notice Whether transient caching of the bounded (collateral, debt) pair is enabled for this asset\n bool cachingEnabled;\n }\n\n /// @notice One item in an syncPriceBoundsAndProtections payload\n /// @dev `value` is interpreted per-action: the new bound price for SetMinPrice / SetMaxPrice, ignored for ExitProtectionMode\n struct KeeperActionItem {\n address asset;\n KeeperAction action;\n uint256 value;\n }\n\n /// @notice One item in a setTokenConfigs payload\n struct TokenConfigInput {\n /// @notice The underlying asset address\n address asset;\n /// @notice Minimum time protection stays active after the last trigger (seconds)\n uint64 cooldownPeriod;\n /// @notice Entry deviation threshold (mantissa). Must be between 5% and 50%.\n uint256 triggerThreshold;\n /// @notice Exit deviation threshold (mantissa). Must be non-zero and below triggerThreshold.\n uint256 resetThreshold;\n /// @notice Whether to enable bounded pricing immediately upon initialization\n bool enableBoundedPricing;\n /// @notice Whether transient caching of the bounded (collateral, debt) pair is enabled for this asset\n bool enableCaching;\n }\n\n // --- Events ---\n\n /// @notice Emitted when protection is initialized for an asset\n event ProtectionInitialized(\n address indexed asset,\n uint128 minPrice,\n uint128 maxPrice,\n uint64 cooldownPeriod,\n uint256 triggerThreshold\n );\n\n /// @notice Emitted when protection mode is triggered for an asset\n event ProtectionTriggered(address indexed asset, uint256 spotPrice, uint128 minPrice, uint128 maxPrice);\n\n /// @notice Emitted when protection mode is disabled for an asset\n event ProtectionModeExited(address indexed asset);\n\n /// @notice Emitted when the keeper updates the minimum price for an asset\n event MinPriceUpdated(address indexed asset, uint128 oldMin, uint128 newMin);\n\n /// @notice Emitted when the keeper updates the maximum price for an asset\n event MaxPriceUpdated(address indexed asset, uint128 oldMax, uint128 newMax);\n\n /// @notice Emitted when the entry threshold is updated for an asset\n event TriggerThresholdSet(address indexed asset, uint256 oldThreshold, uint256 newThreshold);\n\n /// @notice Emitted when the exit threshold is updated for an asset\n event ResetThresholdSet(address indexed asset, uint256 oldExitThreshold, uint256 newExitThreshold);\n\n /// @notice Emitted when the cooldown period is updated for an asset\n event CooldownPeriodSet(address indexed asset, uint64 oldCooldown, uint64 newCooldown);\n\n /// @notice Emitted when an asset's whitelist status changes\n event BoundedPricingWhitelistUpdated(address indexed asset, bool whitelisted);\n\n /// @notice Emitted when the per-asset transient caching flag is toggled\n event CachingEnabledUpdated(address indexed asset, bool oldEnabled, bool newEnabled);\n\n // --- Errors ---\n\n /// @notice Thrown when trying to use or update protection for an asset that has not been initialized\n error MarketNotInitialized(address asset);\n\n /// @notice Thrown when trying to initialize an already initialized market\n error MarketAlreadyInitialized(address asset);\n\n /// @notice Thrown when trying to disable protection that is not active\n error ProtectedPriceInactive(address asset);\n\n /// @notice Thrown when trying to disable protection before cooldown has elapsed\n error CooldownNotElapsed(address asset, uint64 lastProtectionTriggeredAt, uint64 cooldownPeriod);\n\n /// @notice Thrown when trying to disable protection before price range has converged\n error PriceRangeNotConverged(address asset, uint256 currentRangeRatio, uint256 resetThreshold);\n\n /// @notice Thrown when keeper tries to set minPrice above current spot\n error InvalidMinPrice(address asset, uint128 newMin, uint256 currentSpot);\n\n /// @notice Thrown when keeper tries to set maxPrice below current spot\n error InvalidMaxPrice(address asset, uint128 newMax, uint256 currentSpot);\n\n /// @notice Thrown when threshold is set below the minimum allowed value\n error ThresholdBelowMinimum(uint256 threshold, uint256 minimum);\n\n /// @notice Thrown when threshold is set above the maximum allowed value\n error ThresholdAboveMaximum(uint256 threshold, uint256 maximum);\n\n /// @notice Thrown when a price exceeds uint128 max\n error PriceExceedsUint128(uint256 price);\n\n /// @notice Thrown when a zero price is provided where a non-zero price is required\n error ZeroPriceNotAllowed();\n\n /// @notice Thrown when trying to initialize protection for VAI\n error VAINotAllowed();\n\n /// @notice Thrown when trying to disable bounded pricing for an asset while protection is active\n error ProtectedPriceActive(address asset);\n\n /// @notice Thrown when the lengths of the arrays are not equal\n error InvalidArrayLength();\n\n /// @notice Thrown when the exit threshold is set at or above the trigger threshold\n error InvalidResetThreshold(uint256 resetThreshold);\n\n /// @notice Thrown when an syncPriceBoundsAndProtections item carries an unsupported action enum value\n error InvalidKeeperAction(uint8 action);\n\n // --- Non-view price functions (update window + trigger protection) ---\n\n /**\n * @notice Gets the bounded collateral price for a given vToken, updating protection state\n * @param vToken vToken address\n * @return collateralPrice The bounded collateral price\n * @custom:error PriceExceedsUint128 if the spot price overflows uint128\n * @custom:event MinPriceUpdated if a new window minimum is recorded\n * @custom:event MaxPriceUpdated if a new window maximum is recorded\n * @custom:event ProtectionTriggered if the spot price deviates beyond the threshold\n */\n function getBoundedCollateralPrice(address vToken) external returns (uint256 collateralPrice);\n\n /**\n * @notice Gets the bounded debt price for a given vToken, updating protection state\n * @param vToken vToken address\n * @return debtPrice The bounded debt price\n * @custom:error PriceExceedsUint128 if the spot price overflows uint128\n * @custom:event MinPriceUpdated if a new window minimum is recorded\n * @custom:event MaxPriceUpdated if a new window maximum is recorded\n * @custom:event ProtectionTriggered if the spot price deviates beyond the threshold\n */\n function getBoundedDebtPrice(address vToken) external returns (uint256 debtPrice);\n\n /**\n * @notice Gets both the bounded collateral and debt prices for a given vToken, updating protection state\n * @param vToken vToken address\n * @return collateralPrice The bounded collateral price\n * @return debtPrice The bounded debt price\n * @custom:error PriceExceedsUint128 if the spot price overflows uint128\n * @custom:event MinPriceUpdated if a new window minimum is recorded\n * @custom:event MaxPriceUpdated if a new window maximum is recorded\n * @custom:event ProtectionTriggered if the spot price deviates beyond the threshold\n */\n function getBoundedPrices(address vToken) external returns (uint256 collateralPrice, uint256 debtPrice);\n\n // --- State update (call before view price reads to populate transient cache) ---\n\n /**\n * @notice Updates the protection state for a given vToken, caching the resolved collateral and debt prices\n * @dev Called by PolicyFacet before liquidity calculations so subsequent view price\n * reads in the same transaction are served from transient storage. The transient\n * cache is only populated when the asset's `cachingEnabled` flag is `true`.\n * @param vToken vToken address\n * @custom:error PriceExceedsUint128 if the spot price overflows uint128\n * @custom:event MinPriceUpdated if a new window minimum is recorded\n * @custom:event MaxPriceUpdated if a new window maximum is recorded\n * @custom:event ProtectionTriggered if the spot price deviates beyond the threshold\n */\n function updateProtectionState(address vToken) external;\n\n // --- View price functions (read stored/cached state only) ---\n\n /**\n * @notice Gets the bounded collateral price for a given vToken (view variant)\n * @dev Reads from transient cache first when the asset's `cachingEnabled` flag is `true`;\n * falls back to ResilientOracle on cache miss or when caching is disabled.\n * @param vToken vToken address\n * @return price The bounded collateral price\n * @custom:error PriceExceedsUint128 if the spot price overflows uint128 (cache miss path only)\n */\n function getBoundedCollateralPriceView(address vToken) external view returns (uint256 price);\n\n /**\n * @notice Gets the bounded debt price for a given vToken (view variant)\n * @dev Reads from transient cache first when the asset's `cachingEnabled` flag is `true`;\n * falls back to ResilientOracle on cache miss or when caching is disabled.\n * @param vToken vToken address\n * @return price The bounded debt price\n * @custom:error PriceExceedsUint128 if the spot price overflows uint128 (cache miss path only)\n */\n function getBoundedDebtPriceView(address vToken) external view returns (uint256 price);\n\n /**\n * @notice Gets both the bounded collateral and debt prices for a given vToken (view variant)\n * @dev Reads from transient cache first when the asset's `cachingEnabled` flag is `true`;\n * falls back to ResilientOracle on cache miss or when caching is disabled.\n * @param vToken vToken address\n * @return collateralPrice The bounded collateral price\n * @return debtPrice The bounded debt price\n * @custom:error PriceExceedsUint128 if the spot price overflows uint128 (cache miss path only)\n */\n function getBoundedPricesView(address vToken) external view returns (uint256 collateralPrice, uint256 debtPrice);\n\n // --- Keeper functions ---\n\n /**\n * @notice Updates the minimum price in the rolling window for a given asset\n * @param asset The underlying asset address\n * @param newMin The new minimum price; must be at or below the current spot and below maxPrice\n * @custom:access Only authorized keeper addresses\n * @custom:error ZeroPriceNotAllowed if newMin is zero\n * @custom:error MarketNotInitialized if the asset has not been initialized\n * @custom:error InvalidMinPrice if newMin exceeds the current spot or is at or above maxPrice\n * @custom:event MinPriceUpdated\n */\n function updateMinPrice(address asset, uint128 newMin) external;\n\n /**\n * @notice Updates the maximum price in the rolling window for a given asset\n * @param asset The underlying asset address\n * @param newMax The new maximum price; must be at or above the current spot and above minPrice\n * @custom:access Only authorized keeper addresses\n * @custom:error ZeroPriceNotAllowed if newMax is zero\n * @custom:error MarketNotInitialized if the asset has not been initialized\n * @custom:error InvalidMaxPrice if newMax is below the current spot or is at or below minPrice\n * @custom:event MaxPriceUpdated\n */\n function updateMaxPrice(address asset, uint128 newMax) external;\n\n /**\n * @notice Exits protection mode for a given asset once conditions are met\n * @param asset The underlying asset address\n * @custom:access Only authorized monitor/keeper addresses\n * @custom:error ProtectedPriceInactive if protection is not currently active\n * @custom:error CooldownNotElapsed if the cooldown period has not elapsed since the last trigger\n * @custom:error PriceRangeNotConverged if the window range is still above the exit threshold\n * @custom:event ProtectionModeExited\n */\n function exitProtectionMode(address asset) external;\n\n /**\n * @notice Dispatches a batch of keeper-only actions (set min, set max, or exit protection) under a single ACM check\n * @dev Each item is processed in array order; any item revert rolls back the whole batch.\n * `value` is interpreted as the new bound price for SetMinPrice / SetMaxPrice and ignored for ExitProtectionMode.\n * Empty `actions` is a no-op success.\n * @param actions The list of keeper actions to apply\n * @custom:access Only authorized keeper addresses\n * @custom:error InvalidKeeperAction if an item carries an unsupported action enum value\n * @custom:error PriceExceedsUint128 if a SetMin/SetMax item value overflows uint128\n * @custom:error ZeroPriceNotAllowed if a SetMin/SetMax item value is zero\n * @custom:error MarketNotInitialized if any referenced asset has not been initialized\n * @custom:error InvalidMinPrice if a SetMinPrice item violates the spot/maxPrice constraints\n * @custom:error InvalidMaxPrice if a SetMaxPrice item violates the spot/minPrice constraints\n * @custom:error ProtectedPriceInactive if an ExitProtectionMode item targets an asset whose protection is not active\n * @custom:error CooldownNotElapsed if an ExitProtectionMode item is submitted before cooldown elapsed\n * @custom:error PriceRangeNotConverged if an ExitProtectionMode item is submitted before window convergence\n * @custom:event MinPriceUpdated, MaxPriceUpdated, ProtectionModeExited\n */\n function syncPriceBoundsAndProtections(KeeperActionItem[] calldata actions) external;\n\n // --- Admin functions (governance-gated) ---\n\n /**\n * @notice Initializes protection parameters for a new asset\n * @dev Seeds the initial min/max window from the current ResilientOracle spot price,\n * confirming the oracle is live for this asset before it is listed.\n * @param tokenConfig_ Token config input for the asset\n * @custom:access Only Governance\n * @custom:error ZeroAddressNotAllowed if asset is the zero address\n * @custom:error ZeroValueNotAllowed if cooldownPeriod, triggerThreshold, or resetThreshold is zero\n * @custom:error MarketAlreadyInitialized if the asset has already been initialized\n * @custom:error ThresholdBelowMinimum if triggerThreshold is below 5%\n * @custom:error ThresholdAboveMaximum if triggerThreshold is above 50%\n * @custom:error InvalidResetThreshold if resetThreshold is at or above triggerThreshold\n * @custom:error VAINotAllowed if asset is the VAI token\n * @custom:error PriceExceedsUint128 if the spot price overflows uint128\n * @custom:event ProtectionInitialized\n * @custom:event BoundedPricingWhitelistUpdated\n */\n function setTokenConfig(TokenConfigInput calldata tokenConfig_) external;\n\n /**\n * @notice Batch-initializes protection parameters for multiple assets in a single transaction\n * @param tokenConfigs_ Array of token config inputs, one per asset\n * @custom:access Only Governance\n * @custom:error InvalidArrayLength if the input array is empty\n * @custom:error ZeroAddressNotAllowed if any asset is the zero address\n * @custom:error ZeroValueNotAllowed if any cooldownPeriod, triggerThreshold, or resetThreshold is zero\n * @custom:error MarketAlreadyInitialized if any asset has already been initialized\n * @custom:error ThresholdBelowMinimum if any triggerThreshold is below 5%\n * @custom:error ThresholdAboveMaximum if any triggerThreshold is above 50%\n * @custom:error InvalidResetThreshold if any resetThreshold is at or above its triggerThreshold\n * @custom:error VAINotAllowed if any asset is the VAI token\n * @custom:error PriceExceedsUint128 if the spot price for any asset overflows uint128\n * @custom:event ProtectionInitialized for each asset\n * @custom:event BoundedPricingWhitelistUpdated for each asset\n */\n function setTokenConfigs(TokenConfigInput[] calldata tokenConfigs_) external;\n\n /**\n * @notice Sets the cooldown period for an asset\n * @param asset The underlying asset address\n * @param newCooldown The new cooldown period in seconds; must be non-zero\n * @custom:access Only Governance\n * @custom:error MarketNotInitialized if the asset has not been initialized\n * @custom:event CooldownPeriodSet\n */\n function setCooldownPeriod(address asset, uint64 newCooldown) external;\n\n /**\n * @notice Sets the trigger and reset thresholds for an asset\n * @param asset The underlying asset address\n * @param newTriggerThreshold The new trigger threshold (mantissa). Must be between 5% and 50% and above the reset threshold.\n * @param newResetThreshold The new reset threshold (mantissa). Must be non-zero and below the trigger threshold.\n * @custom:access Only Governance\n * @custom:error MarketNotInitialized if the asset has not been initialized\n * @custom:error ThresholdBelowMinimum if newTriggerThreshold is below 5%\n * @custom:error ThresholdAboveMaximum if newTriggerThreshold is above 50%\n * @custom:error InvalidResetThreshold if newResetThreshold is at or above newTriggerThreshold\n * @custom:event TriggerThresholdSet if the trigger threshold changed\n * @custom:event ResetThresholdSet if the reset threshold changed\n */\n function setThresholds(address asset, uint256 newTriggerThreshold, uint256 newResetThreshold) external;\n\n /**\n * @notice Sets whether bounded pricing is enabled for an asset\n * @param asset The underlying asset address\n * @param enabled Whether bounded pricing should be enabled for the asset\n * @custom:access Only Governance\n * @custom:error MarketNotInitialized if the asset has not been initialized\n * @custom:error ProtectedPriceActive if trying to disable an asset while protection is active\n * @custom:event BoundedPricingWhitelistUpdated\n */\n function setAssetBoundedPricingEnabled(address asset, bool enabled) external;\n\n /**\n * @notice Toggles transient caching of the bounded (collateral, debt) pair for an asset\n * @dev When disabled, each view/non-view price call recomputes bounded prices from the\n * live spot instead of reading or writing the transient slots. The initial value is\n * set via the `enableCaching` argument of `setTokenConfig`.\n * @param asset The underlying asset address\n * @param enabled Whether transient caching is enabled for this asset\n * @custom:access Only Governance\n * @custom:error MarketNotInitialized if the asset has not been initialized\n * @custom:event CachingEnabledUpdated\n */\n function setCachingEnabled(address asset, bool enabled) external;\n\n // --- View helpers ---\n\n /**\n * @notice Returns the full protection state for an asset\n * @param asset The underlying asset address\n * @return minPrice Lowest price observed in the current window\n * @return maxPrice Highest price observed in the current window\n * @return currentlyUsingProtectedPrice Whether protected price is currently active\n * @return isBoundedPricingEnabled Whether the asset is whitelisted for bounded pricing\n * @return lastProtectionTriggeredAt Timestamp of the last protection trigger\n * @return cooldownPeriod Minimum time protection stays active after last trigger\n * @return assetAddr The underlying asset address stored in the struct\n * @return triggerThreshold Entry deviation threshold (mantissa) that activates protection\n * @return resetThreshold Exit deviation threshold (mantissa) below which protection can be disabled\n * @return cachingEnabled Whether transient caching of the bounded pair is enabled for the asset\n */\n function assetProtectionConfig(\n address asset\n )\n external\n view\n returns (\n uint128 minPrice,\n uint128 maxPrice,\n bool currentlyUsingProtectedPrice,\n bool isBoundedPricingEnabled,\n uint64 lastProtectionTriggeredAt,\n uint64 cooldownPeriod,\n address assetAddr,\n uint128 triggerThreshold,\n uint128 resetThreshold,\n bool cachingEnabled\n );\n\n /**\n * @notice Checks if an asset is whitelisted for bounded pricing\n * @param asset The underlying asset address\n * @return True if the asset is whitelisted\n */\n function isBoundedPricingEnabled(address asset) external view returns (bool);\n\n /**\n * @notice Checks if the asset is currently using the protected (bounded) price\n * @param asset The underlying asset address\n * @return True if the asset is currently using the protected price instead of spot\n */\n function currentlyUsingProtectedPrice(address asset) external view returns (bool);\n\n /**\n * @notice Checks if protection can be exited for a given asset\n * @param asset The underlying asset address\n * @return True if both the cooldown has elapsed and the price range has converged below the exit threshold\n */\n function canExitProtection(address asset) external view returns (bool);\n\n /**\n * @notice Returns the initialized asset at the given index (auto-generated array getter)\n * @param index Array index\n * @return The asset address at the given index\n */\n function allAssets(uint256 index) external view returns (address);\n\n /**\n * @notice Returns all currently whitelisted asset addresses\n * @return result Array of whitelisted asset addresses\n */\n function getAllBoundedPricingEnabledAssets() external view returns (address[] memory result);\n\n /**\n * @notice Returns all asset addresses that have ever been initialized\n * @return Array of all initialized asset addresses\n */\n function getInitializedAssets() external view returns (address[] memory);\n\n /**\n * @notice Batch-checks which assets' on-chain min/max have drifted beyond the keeper deadband\n * @param assets Array of asset addresses to check\n * @param proposedMins Keeper's proposed window minimum prices\n * @param proposedMaxs Keeper's proposed window maximum prices\n * @return needsMinUpdate Whether minPrice drift exceeds the deadband for each asset\n * @return needsMaxUpdate Whether maxPrice drift exceeds the deadband for each asset\n * @custom:error InvalidArrayLength if the input array lengths do not match\n */\n function checkAndGetWindowDrift(\n address[] calldata assets,\n uint128[] calldata proposedMins,\n uint128[] calldata proposedMaxs\n ) external view returns (bool[] memory needsMinUpdate, bool[] memory needsMaxUpdate);\n}\n" + }, + "@venusprotocol/oracle/contracts/interfaces/OracleInterface.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity ^0.8.25;\n\ninterface OracleInterface {\n function getPrice(address asset) external view returns (uint256);\n}\n\ninterface ResilientOracleInterface is OracleInterface {\n function updatePrice(address vToken) external;\n\n function updateAssetPrice(address asset) external;\n\n function getUnderlyingPrice(address vToken) external view returns (uint256);\n}\n\ninterface BoundValidatorInterface {\n function validatePriceWithAnchorPrice(\n address asset,\n uint256 reporterPrice,\n uint256 anchorPrice\n ) external view returns (bool);\n}\n" + }, + "@venusprotocol/protocol-reserve/contracts/Interfaces/IProtocolShareReserve.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity ^0.8.25;\n\ninterface IProtocolShareReserve {\n /// @notice it represents the type of vToken income\n enum IncomeType {\n SPREAD,\n LIQUIDATION,\n ERC4626_WRAPPER_REWARDS,\n FLASHLOAN\n }\n\n function updateAssetsState(\n address comptroller,\n address asset,\n IncomeType incomeType\n ) external;\n}\n" + }, + "@venusprotocol/solidity-utilities/contracts/constants.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity ^0.8.25;\n\n/// @dev Base unit for computations, usually used in scaling (multiplications, divisions)\nuint256 constant EXP_SCALE = 1e18;\n\n/// @dev A unit (literal one) in EXP_SCALE, usually used in additions/subtractions\nuint256 constant MANTISSA_ONE = EXP_SCALE;\n\n/// @dev The approximate number of seconds per year\nuint256 constant SECONDS_PER_YEAR = 31_536_000;\n" + }, + "@venusprotocol/solidity-utilities/contracts/TimeManagerV8.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\n\nimport { SECONDS_PER_YEAR } from \"./constants.sol\";\n\nabstract contract TimeManagerV8 {\n /// @notice Stores blocksPerYear if isTimeBased is true else secondsPerYear is stored\n /// @custom:oz-upgrades-unsafe-allow state-variable-immutable\n uint256 public immutable blocksOrSecondsPerYear;\n\n /// @notice Acknowledges if a contract is time based or not\n /// @custom:oz-upgrades-unsafe-allow state-variable-immutable\n bool public immutable isTimeBased;\n\n /// @notice Stores the current block timestamp or block number depending on isTimeBased\n /// @custom:oz-upgrades-unsafe-allow state-variable-immutable\n function() view returns (uint256) private immutable _getCurrentSlot;\n\n /**\n * @dev This empty reserved space is put in place to allow future versions to add new\n * variables without shifting down storage in the inheritance chain\n * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\n */\n uint256[48] private __gap;\n\n /// @notice Thrown when blocks per year is invalid\n error InvalidBlocksPerYear();\n\n /// @notice Thrown when time based but blocks per year is provided\n error InvalidTimeBasedConfiguration();\n\n /**\n * @param timeBased_ A boolean indicating whether the contract is based on time or block\n * If timeBased is true than blocksPerYear_ param is ignored as blocksOrSecondsPerYear is set to SECONDS_PER_YEAR\n * @param blocksPerYear_ The number of blocks per year\n * @custom:error InvalidBlocksPerYear is thrown if blocksPerYear entered is zero and timeBased is false\n * @custom:error InvalidTimeBasedConfiguration is thrown if blocksPerYear entered is non zero and timeBased is true\n * @custom:oz-upgrades-unsafe-allow constructor\n */\n constructor(bool timeBased_, uint256 blocksPerYear_) {\n if (!timeBased_ && blocksPerYear_ == 0) {\n revert InvalidBlocksPerYear();\n }\n\n if (timeBased_ && blocksPerYear_ != 0) {\n revert InvalidTimeBasedConfiguration();\n }\n\n isTimeBased = timeBased_;\n blocksOrSecondsPerYear = timeBased_ ? SECONDS_PER_YEAR : blocksPerYear_;\n _getCurrentSlot = timeBased_ ? _getBlockTimestamp : _getBlockNumber;\n }\n\n /**\n * @dev Function to simply retrieve block number or block timestamp\n * @return Current block number or block timestamp\n */\n function getBlockNumberOrTimestamp() public view virtual returns (uint256) {\n return _getCurrentSlot();\n }\n\n /**\n * @dev Returns the current timestamp in seconds\n * @return The current timestamp\n */\n function _getBlockTimestamp() private view returns (uint256) {\n return block.timestamp;\n }\n\n /**\n * @dev Returns the current block number\n * @return The current block number\n */\n function _getBlockNumber() private view returns (uint256) {\n return block.number;\n }\n}\n" + }, + "@venusprotocol/venus-protocol/contracts/Tokens/Prime/Interfaces/IPrime.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity ^0.8.25;\n\nimport { PrimeStorageV1 } from \"../PrimeStorage.sol\";\n\n/**\n * @title IPrime\n * @author Venus\n * @notice Interface for Prime Token\n */\ninterface IPrime {\n struct APRInfo {\n // supply APR of the user in BPS\n uint256 supplyAPR;\n // borrow APR of the user in BPS\n uint256 borrowAPR;\n // total score of the market\n uint256 totalScore;\n // score of the user\n uint256 userScore;\n // capped XVS balance of the user\n uint256 xvsBalanceForScore;\n // capital of the user\n uint256 capital;\n // capped supply of the user\n uint256 cappedSupply;\n // capped borrow of the user\n uint256 cappedBorrow;\n // capped supply of user in USD\n uint256 supplyCapUSD;\n // capped borrow of user in USD\n uint256 borrowCapUSD;\n }\n\n struct Capital {\n // capital of the user\n uint256 capital;\n // capped supply of the user\n uint256 cappedSupply;\n // capped borrow of the user\n uint256 cappedBorrow;\n // capped supply of user in USD\n uint256 supplyCapUSD;\n // capped borrow of user in USD\n uint256 borrowCapUSD;\n }\n\n /**\n * @notice Returns boosted pending interest accrued for a user for all markets\n * @param user the account for which to get the accrued interests\n * @return pendingRewards the number of underlying tokens accrued by the user for all markets\n */\n function getPendingRewards(address user) external returns (PrimeStorageV1.PendingReward[] memory pendingRewards);\n\n /**\n * @notice Update total score of multiple users and market\n * @param users accounts for which we need to update score\n */\n function updateScores(address[] memory users) external;\n\n /**\n * @notice Update value of alpha\n * @param _alphaNumerator numerator of alpha. If alpha is 0.5 then numerator is 1\n * @param _alphaDenominator denominator of alpha. If alpha is 0.5 then denominator is 2\n */\n function updateAlpha(uint128 _alphaNumerator, uint128 _alphaDenominator) external;\n\n /**\n * @notice Update multipliers for a market\n * @param market address of the market vToken\n * @param supplyMultiplier new supply multiplier for the market, scaled by 1e18\n * @param borrowMultiplier new borrow multiplier for the market, scaled by 1e18\n */\n function updateMultipliers(address market, uint256 supplyMultiplier, uint256 borrowMultiplier) external;\n\n /**\n * @notice Add a market to prime program\n * @param comptroller address of the comptroller\n * @param market address of the market vToken\n * @param supplyMultiplier the multiplier for supply cap. It should be converted to 1e18\n * @param borrowMultiplier the multiplier for borrow cap. It should be converted to 1e18\n */\n function addMarket(\n address comptroller,\n address market,\n uint256 supplyMultiplier,\n uint256 borrowMultiplier\n ) external;\n\n /**\n * @notice Set limits for total tokens that can be minted\n * @param _irrevocableLimit total number of irrevocable tokens that can be minted\n * @param _revocableLimit total number of revocable tokens that can be minted\n */\n function setLimit(uint256 _irrevocableLimit, uint256 _revocableLimit) external;\n\n /**\n * @notice Directly issue prime tokens to users\n * @param isIrrevocable are the tokens being issued\n * @param users list of address to issue tokens to\n */\n function issue(bool isIrrevocable, address[] calldata users) external;\n\n /**\n * @notice Executed by XVSVault whenever user's XVSVault balance changes\n * @param user the account address whose balance was updated\n */\n function xvsUpdated(address user) external;\n\n /**\n * @notice accrues interest and updates score for an user for a specific market\n * @param user the account address for which to accrue interest and update score\n * @param market the market for which to accrue interest and update score\n */\n function accrueInterestAndUpdateScore(address user, address market) external;\n\n /**\n * @notice For claiming prime token when staking period is completed\n */\n function claim() external;\n\n /**\n * @notice For burning any prime token\n * @param user the account address for which the prime token will be burned\n */\n function burn(address user) external;\n\n /**\n * @notice To pause or unpause claiming of interest\n */\n function togglePause() external;\n\n /**\n * @notice For user to claim boosted yield\n * @param vToken the market for which claim the accrued interest\n * @return amount the amount of tokens transferred to the user\n */\n function claimInterest(address vToken) external returns (uint256);\n\n /**\n * @notice For user to claim boosted yield\n * @param vToken the market for which claim the accrued interest\n * @param user the user for which to claim the accrued interest\n * @return amount the amount of tokens transferred to the user\n */\n function claimInterest(address vToken, address user) external returns (uint256);\n\n /**\n * @notice Distributes income from market since last distribution\n * @param vToken the market for which to distribute the income\n */\n function accrueInterest(address vToken) external;\n\n /**\n * @notice Returns boosted interest accrued for a user\n * @param vToken the market for which to fetch the accrued interest\n * @param user the account for which to get the accrued interest\n * @return interestAccrued the number of underlying tokens accrued by the user since the last accrual\n */\n function getInterestAccrued(address vToken, address user) external returns (uint256);\n\n /**\n * @notice Retrieves an array of all available markets\n * @return an array of addresses representing all available markets\n */\n function getAllMarkets() external view returns (address[] memory);\n\n /**\n * @notice fetch the numbers of seconds remaining for staking period to complete\n * @param user the account address for which we are checking the remaining time\n * @return timeRemaining the number of seconds the user needs to wait to claim prime token\n */\n function claimTimeRemaining(address user) external view returns (uint256);\n\n /**\n * @notice Returns supply and borrow APR for user for a given market\n * @param market the market for which to fetch the APR\n * @param user the account for which to get the APR\n * @return aprInfo APR information for the user for the given market\n */\n function calculateAPR(address market, address user) external view returns (APRInfo memory aprInfo);\n\n /**\n * @notice Returns supply and borrow APR for estimated supply, borrow and XVS staked\n * @param market the market for which to fetch the APR\n * @param user the account for which to get the APR\n * @param borrow hypothetical borrow amount\n * @param supply hypothetical supply amount\n * @param xvsStaked hypothetical staked XVS amount\n * @return aprInfo APR information for the user for the given market\n */\n function estimateAPR(\n address market,\n address user,\n uint256 borrow,\n uint256 supply,\n uint256 xvsStaked\n ) external view returns (APRInfo memory aprInfo);\n\n /**\n * @notice the total income that's going to be distributed in a year to prime token holders\n * @param vToken the market for which to fetch the total income that's going to distributed in a year\n * @return amount the total income\n */\n function incomeDistributionYearly(address vToken) external view returns (uint256 amount);\n\n /**\n * @notice Returns if user is a prime holder\n * @return isPrimeHolder true if user is a prime holder\n */\n function isUserPrimeHolder(address user) external view returns (bool);\n\n /**\n * @notice Set the limit for the loops can iterate to avoid the DOS\n * @param loopsLimit Number of loops limit\n */\n function setMaxLoopsLimit(uint256 loopsLimit) external;\n\n /**\n * @notice Update staked at timestamp for multiple users\n * @param users accounts for which we need to update staked at timestamp\n * @param timestamps new staked at timestamp for the users\n */\n function setStakedAt(address[] calldata users, uint256[] calldata timestamps) external;\n}\n" + }, + "@venusprotocol/venus-protocol/contracts/Tokens/Prime/PrimeStorage.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\n\nimport { ResilientOracleInterface } from \"@venusprotocol/oracle/contracts/interfaces/OracleInterface.sol\";\n\n/**\n * @title PrimeStorageV1\n * @author Venus\n * @notice Storage for Prime Token\n */\ncontract PrimeStorageV1 {\n struct Token {\n bool exists;\n bool isIrrevocable;\n }\n\n struct Market {\n uint256 supplyMultiplier;\n uint256 borrowMultiplier;\n uint256 rewardIndex;\n uint256 sumOfMembersScore;\n bool exists;\n }\n\n struct Interest {\n uint256 accrued;\n uint256 score;\n uint256 rewardIndex;\n }\n\n struct PendingReward {\n address vToken;\n address rewardToken;\n uint256 amount;\n }\n\n /// @notice Base unit for computations, usually used in scaling (multiplications, divisions)\n uint256 internal constant EXP_SCALE = 1e18;\n\n /// @notice maximum BPS = 100%\n uint256 internal constant MAXIMUM_BPS = 1e4;\n\n /// @notice Mapping to get prime token's metadata\n mapping(address => Token) public tokens;\n\n /// @notice Tracks total irrevocable tokens minted\n uint256 public totalIrrevocable;\n\n /// @notice Tracks total revocable tokens minted\n uint256 public totalRevocable;\n\n /// @notice Indicates maximum revocable tokens that can be minted\n uint256 public revocableLimit;\n\n /// @notice Indicates maximum irrevocable tokens that can be minted\n uint256 public irrevocableLimit;\n\n /// @notice Tracks when prime token eligible users started staking for claiming prime token\n mapping(address => uint256) public stakedAt;\n\n /// @notice vToken to market configuration\n mapping(address => Market) public markets;\n\n /// @notice vToken to user to user index\n mapping(address => mapping(address => Interest)) public interests;\n\n /// @notice A list of boosted markets\n address[] internal _allMarkets;\n\n /// @notice numerator of alpha. Ex: if alpha is 0.5 then this will be 1\n uint128 public alphaNumerator;\n\n /// @notice denominator of alpha. Ex: if alpha is 0.5 then this will be 2\n uint128 public alphaDenominator;\n\n /// @notice address of XVS vault\n address public xvsVault;\n\n /// @notice address of XVS vault reward token\n address public xvsVaultRewardToken;\n\n /// @notice address of XVS vault pool id\n uint256 public xvsVaultPoolId;\n\n /// @notice mapping to check if a account's score was updated in the round\n mapping(uint256 => mapping(address => bool)) public isScoreUpdated;\n\n /// @notice unique id for next round\n uint256 public nextScoreUpdateRoundId;\n\n /// @notice total number of accounts whose score needs to be updated\n uint256 public totalScoreUpdatesRequired;\n\n /// @notice total number of accounts whose score is yet to be updated\n uint256 public pendingScoreUpdates;\n\n /// @notice mapping used to find if an asset is part of prime markets\n mapping(address => address) public vTokenForAsset;\n\n /// @notice Address of core pool comptroller contract\n address internal corePoolComptroller;\n\n /// @notice unreleased income from PLP that's already distributed to prime holders\n /// @dev mapping of asset address => amount\n mapping(address => uint256) public unreleasedPLPIncome;\n\n /// @notice The address of PLP contract\n address public primeLiquidityProvider;\n\n /// @notice The address of ResilientOracle contract\n ResilientOracleInterface public oracle;\n\n /// @notice The address of PoolRegistry contract\n address public poolRegistry;\n\n /// @dev This empty reserved space is put in place to allow future versions to add new\n /// variables without shifting down storage in the inheritance chain.\n uint256[26] private __gap;\n}\n" + }, + "contracts/Comptroller.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\n\nimport { Ownable2StepUpgradeable } from \"@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol\";\nimport { ResilientOracleInterface } from \"@venusprotocol/oracle/contracts/interfaces/OracleInterface.sol\";\nimport { AccessControlledV8 } from \"@venusprotocol/governance-contracts/contracts/Governance/AccessControlledV8.sol\";\nimport { IPrime } from \"@venusprotocol/venus-protocol/contracts/Tokens/Prime/Interfaces/IPrime.sol\";\n\nimport { ComptrollerInterface, Action } from \"./ComptrollerInterface.sol\";\nimport { ComptrollerStorage } from \"./ComptrollerStorage.sol\";\nimport { ExponentialNoError } from \"./ExponentialNoError.sol\";\nimport { VToken } from \"./VToken.sol\";\nimport { RewardsDistributor } from \"./Rewards/RewardsDistributor.sol\";\nimport { MaxLoopsLimitHelper } from \"./MaxLoopsLimitHelper.sol\";\nimport { ensureNonzeroAddress } from \"./lib/validators.sol\";\n\n/**\n * @title Comptroller\n * @author Venus\n * @notice The Comptroller is designed to provide checks for all minting, redeeming, transferring, borrowing, lending, repaying, liquidating,\n * and seizing done by the `vToken` contract. Each pool has one `Comptroller` checking these interactions across markets. When a user interacts\n * with a given market by one of these main actions, a call is made to a corresponding hook in the associated `Comptroller`, which either allows\n * or reverts the transaction. These hooks also update supply and borrow rewards as they are called. The comptroller holds the logic for assessing\n * liquidity snapshots of an account via the collateral factor and liquidation threshold. This check determines the collateral needed for a borrow,\n * as well as how much of a borrow may be liquidated. A user may borrow a portion of their collateral with the maximum amount determined by the\n * markets collateral factor. However, if their borrowed amount exceeds an amount calculated using the market’s corresponding liquidation threshold,\n * the borrow is eligible for liquidation.\n *\n * The `Comptroller` also includes two functions `liquidateAccount()` and `healAccount()`, which are meant to handle accounts that do not exceed\n * the `minLiquidatableCollateral` for the `Comptroller`:\n *\n * - `healAccount()`: This function is called to seize all of a given user’s collateral, requiring the `msg.sender` repay a certain percentage\n * of the debt calculated by `collateral/(borrows*liquidationIncentive)`. The function can only be called if the calculated percentage does not exceed\n * 100%, because otherwise no `badDebt` would be created and `liquidateAccount()` should be used instead. The difference in the actual amount of debt\n * and debt paid off is recorded as `badDebt` for each market, which can then be auctioned off for the risk reserves of the associated pool.\n * - `liquidateAccount()`: This function can only be called if the collateral seized will cover all borrows of an account, as well as the liquidation\n * incentive. Otherwise, the pool will incur bad debt, in which case the function `healAccount()` should be used instead. This function skips the logic\n * verifying that the repay amount does not exceed the close factor.\n */\ncontract Comptroller is\n Ownable2StepUpgradeable,\n AccessControlledV8,\n ComptrollerStorage,\n ComptrollerInterface,\n ExponentialNoError,\n MaxLoopsLimitHelper\n{\n // PoolRegistry, immutable to save on gas\n /// @custom:oz-upgrades-unsafe-allow state-variable-immutable\n address public immutable poolRegistry;\n\n /// @notice Emitted when an account enters a market\n event MarketEntered(VToken indexed vToken, address indexed account);\n\n /// @notice Emitted when an account exits a market\n event MarketExited(VToken indexed vToken, address indexed account);\n\n /// @notice Emitted when close factor is changed by admin\n event NewCloseFactor(uint256 oldCloseFactorMantissa, uint256 newCloseFactorMantissa);\n\n /// @notice Emitted when a collateral factor is changed by admin\n event NewCollateralFactor(VToken vToken, uint256 oldCollateralFactorMantissa, uint256 newCollateralFactorMantissa);\n\n /// @notice Emitted when liquidation threshold is changed by admin\n event NewLiquidationThreshold(\n VToken vToken,\n uint256 oldLiquidationThresholdMantissa,\n uint256 newLiquidationThresholdMantissa\n );\n\n /// @notice Emitted when liquidation incentive is changed by admin\n event NewLiquidationIncentive(uint256 oldLiquidationIncentiveMantissa, uint256 newLiquidationIncentiveMantissa);\n\n /// @notice Emitted when price oracle is changed\n event NewPriceOracle(ResilientOracleInterface oldPriceOracle, ResilientOracleInterface newPriceOracle);\n\n /// @notice Emitted when an action is paused on a market\n event ActionPausedMarket(VToken vToken, Action action, bool pauseState);\n\n /// @notice Emitted when borrow cap for a vToken is changed\n event NewBorrowCap(VToken indexed vToken, uint256 newBorrowCap);\n\n /// @notice Emitted when the collateral threshold (in USD) for non-batch liquidations is changed\n event NewMinLiquidatableCollateral(uint256 oldMinLiquidatableCollateral, uint256 newMinLiquidatableCollateral);\n\n /// @notice Emitted when supply cap for a vToken is changed\n event NewSupplyCap(VToken indexed vToken, uint256 newSupplyCap);\n\n /// @notice Emitted when a rewards distributor is added\n event NewRewardsDistributor(address indexed rewardsDistributor, address indexed rewardToken);\n\n /// @notice Emitted when a market is supported\n event MarketSupported(VToken vToken);\n\n /// @notice Emitted when prime token contract address is changed\n event NewPrimeToken(IPrime oldPrimeToken, IPrime newPrimeToken);\n\n /// @notice Emitted when forced liquidation is enabled or disabled for a market\n event IsForcedLiquidationEnabledUpdated(address indexed vToken, bool enable);\n\n /// @notice Emitted when a market is unlisted\n event MarketUnlisted(address indexed vToken);\n /// @notice Emitted when the borrowing or redeeming delegate rights are updated for an account\n event DelegateUpdated(address indexed approver, address indexed delegate, bool approved);\n\n /// @notice Thrown when collateral factor exceeds the upper bound\n error InvalidCollateralFactor();\n\n /// @notice Thrown when liquidation threshold exceeds the collateral factor\n error InvalidLiquidationThreshold();\n\n /// @notice Thrown when the action is only available to specific sender, but the real sender was different\n error UnexpectedSender(address expectedSender, address actualSender);\n\n /// @notice Thrown when the oracle returns an invalid price for some asset\n error PriceError(address vToken);\n\n /// @notice Thrown if VToken unexpectedly returned a nonzero error code while trying to get account snapshot\n error SnapshotError(address vToken, address user);\n\n /// @notice Thrown when the market is not listed\n error MarketNotListed(address market);\n\n /// @notice Thrown when a market has an unexpected comptroller\n error ComptrollerMismatch();\n\n /// @notice Thrown when user is not member of market\n error MarketNotCollateral(address vToken, address user);\n\n /// @notice Thrown when borrow action is not paused\n error BorrowActionNotPaused();\n\n /// @notice Thrown when mint action is not paused\n error MintActionNotPaused();\n\n /// @notice Thrown when redeem action is not paused\n error RedeemActionNotPaused();\n\n /// @notice Thrown when repay action is not paused\n error RepayActionNotPaused();\n\n /// @notice Thrown when seize action is not paused\n error SeizeActionNotPaused();\n\n /// @notice Thrown when exit market action is not paused\n error ExitMarketActionNotPaused();\n\n /// @notice Thrown when transfer action is not paused\n error TransferActionNotPaused();\n\n /// @notice Thrown when enter market action is not paused\n error EnterMarketActionNotPaused();\n\n /// @notice Thrown when liquidate action is not paused\n error LiquidateActionNotPaused();\n\n /// @notice Thrown when borrow cap is not zero\n error BorrowCapIsNotZero();\n\n /// @notice Thrown when supply cap is not zero\n error SupplyCapIsNotZero();\n\n /// @notice Thrown when collateral factor is not zero\n error CollateralFactorIsNotZero();\n\n /**\n * @notice Thrown during the liquidation if user's total collateral amount is lower than\n * a predefined threshold. In this case only batch liquidations (either liquidateAccount\n * or healAccount) are available.\n */\n error MinimalCollateralViolated(uint256 expectedGreaterThan, uint256 actual);\n error CollateralExceedsThreshold(uint256 expectedLessThanOrEqualTo, uint256 actual);\n error InsufficientCollateral(uint256 collateralToSeize, uint256 availableCollateral);\n\n /// @notice Thrown when the account doesn't have enough liquidity to redeem or borrow\n error InsufficientLiquidity();\n\n /// @notice Thrown when trying to liquidate a healthy account\n error InsufficientShortfall();\n\n /// @notice Thrown when trying to repay more than allowed by close factor\n error TooMuchRepay();\n\n /// @notice Thrown if the user is trying to exit a market in which they have an outstanding debt\n error NonzeroBorrowBalance();\n\n /// @notice Thrown when trying to perform an action that is paused\n error ActionPaused(address market, Action action);\n\n /// @notice Thrown when trying to add a market that is already listed\n error MarketAlreadyListed(address market);\n\n /// @notice Thrown if the supply cap is exceeded\n error SupplyCapExceeded(address market, uint256 cap);\n\n /// @notice Thrown if the borrow cap is exceeded\n error BorrowCapExceeded(address market, uint256 cap);\n\n /// @notice Thrown if delegate approval status is already set to the requested value\n error DelegationStatusUnchanged();\n\n /// @param poolRegistry_ Pool registry address\n /// @custom:oz-upgrades-unsafe-allow constructor\n /// @custom:error ZeroAddressNotAllowed is thrown when pool registry address is zero\n constructor(address poolRegistry_) {\n ensureNonzeroAddress(poolRegistry_);\n\n poolRegistry = poolRegistry_;\n _disableInitializers();\n }\n\n /**\n * @param loopLimit Limit for the loops can iterate to avoid the DOS\n * @param accessControlManager Access control manager contract address\n */\n function initialize(uint256 loopLimit, address accessControlManager) external initializer {\n __Ownable2Step_init();\n __AccessControlled_init_unchained(accessControlManager);\n\n _setMaxLoopsLimit(loopLimit);\n }\n\n /**\n * @notice Add assets to be included in account liquidity calculation; enabling them to be used as collateral\n * @param vTokens The list of addresses of the vToken markets to be enabled\n * @return errors An array of NO_ERROR for compatibility with Venus core tooling\n * @custom:event MarketEntered is emitted for each market on success\n * @custom:error ActionPaused error is thrown if entering any of the markets is paused\n * @custom:error MarketNotListed error is thrown if any of the markets is not listed\n * @custom:access Not restricted\n */\n function enterMarkets(address[] memory vTokens) external override returns (uint256[] memory) {\n uint256 len = vTokens.length;\n\n uint256[] memory results = new uint256[](len);\n for (uint256 i; i < len; ++i) {\n VToken vToken = VToken(vTokens[i]);\n\n _addToMarket(vToken, msg.sender);\n results[i] = NO_ERROR;\n }\n\n return results;\n }\n\n /**\n * @notice Unlist a market by setting isListed to false\n * @dev Checks if all actions are paused, borrow/supply caps is set to 0 and collateral factor is to 0.\n * @param market The address of the market (token) to unlist\n * @return uint256 Always NO_ERROR for compatibility with Venus core tooling\n * @custom:event MarketUnlisted is emitted on success\n * @custom:error MarketNotListed error is thrown when the market is not listed\n * @custom:error BorrowActionNotPaused error is thrown if borrow action is not paused\n * @custom:error MintActionNotPaused error is thrown if mint action is not paused\n * @custom:error RedeemActionNotPaused error is thrown if redeem action is not paused\n * @custom:error RepayActionNotPaused error is thrown if repay action is not paused\n * @custom:error EnterMarketActionNotPaused error is thrown if enter market action is not paused\n * @custom:error LiquidateActionNotPaused error is thrown if liquidate action is not paused\n * @custom:error BorrowCapIsNotZero error is thrown if borrow cap is not zero\n * @custom:error SupplyCapIsNotZero error is thrown if supply cap is not zero\n * @custom:error CollateralFactorIsNotZero error is thrown if collateral factor is not zero\n */\n function unlistMarket(address market) external returns (uint256) {\n _checkAccessAllowed(\"unlistMarket(address)\");\n\n Market storage _market = markets[market];\n\n if (!_market.isListed) {\n revert MarketNotListed(market);\n }\n\n if (!actionPaused(market, Action.BORROW)) {\n revert BorrowActionNotPaused();\n }\n\n if (!actionPaused(market, Action.MINT)) {\n revert MintActionNotPaused();\n }\n\n if (!actionPaused(market, Action.REDEEM)) {\n revert RedeemActionNotPaused();\n }\n\n if (!actionPaused(market, Action.REPAY)) {\n revert RepayActionNotPaused();\n }\n\n if (!actionPaused(market, Action.SEIZE)) {\n revert SeizeActionNotPaused();\n }\n\n if (!actionPaused(market, Action.ENTER_MARKET)) {\n revert EnterMarketActionNotPaused();\n }\n\n if (!actionPaused(market, Action.LIQUIDATE)) {\n revert LiquidateActionNotPaused();\n }\n\n if (!actionPaused(market, Action.TRANSFER)) {\n revert TransferActionNotPaused();\n }\n\n if (!actionPaused(market, Action.EXIT_MARKET)) {\n revert ExitMarketActionNotPaused();\n }\n\n if (borrowCaps[market] != 0) {\n revert BorrowCapIsNotZero();\n }\n\n if (supplyCaps[market] != 0) {\n revert SupplyCapIsNotZero();\n }\n\n if (_market.collateralFactorMantissa != 0) {\n revert CollateralFactorIsNotZero();\n }\n\n _market.isListed = false;\n emit MarketUnlisted(market);\n\n return NO_ERROR;\n }\n\n /**\n * @notice Grants or revokes the borrowing or redeeming delegate rights to / from an account\n * If allowed, the delegate will be able to borrow funds on behalf of the sender\n * Upon a delegated borrow, the delegate will receive the funds, and the borrower\n * will see the debt on their account\n * Upon a delegated redeem, the delegate will receive the redeemed amount and the approver\n * will see a deduction in his vToken balance\n * @param delegate The address to update the rights for\n * @param approved Whether to grant (true) or revoke (false) the borrowing or redeeming rights\n * @custom:event DelegateUpdated emits on success\n * @custom:error ZeroAddressNotAllowed is thrown when delegate address is zero\n * @custom:error DelegationStatusUnchanged is thrown if approval status is already set to the requested value\n * @custom:access Not restricted\n */\n function updateDelegate(address delegate, bool approved) external {\n ensureNonzeroAddress(delegate);\n if (approvedDelegates[msg.sender][delegate] == approved) {\n revert DelegationStatusUnchanged();\n }\n\n approvedDelegates[msg.sender][delegate] = approved;\n emit DelegateUpdated(msg.sender, delegate, approved);\n }\n\n /**\n * @notice Removes asset from sender's account liquidity calculation; disabling them as collateral\n * @dev Sender must not have an outstanding borrow balance in the asset,\n * or be providing necessary collateral for an outstanding borrow.\n * @param vTokenAddress The address of the asset to be removed\n * @return error Always NO_ERROR for compatibility with Venus core tooling\n * @custom:event MarketExited is emitted on success\n * @custom:error ActionPaused error is thrown if exiting the market is paused\n * @custom:error NonzeroBorrowBalance error is thrown if the user has an outstanding borrow in this market\n * @custom:error MarketNotListed error is thrown when the market is not listed\n * @custom:error InsufficientLiquidity error is thrown if exiting the market would lead to user's insolvency\n * @custom:error SnapshotError is thrown if some vToken fails to return the account's supply and borrows\n * @custom:error PriceError is thrown if the oracle returns an incorrect price for some asset\n * @custom:access Not restricted\n */\n function exitMarket(address vTokenAddress) external override returns (uint256) {\n _checkActionPauseState(vTokenAddress, Action.EXIT_MARKET);\n VToken vToken = VToken(vTokenAddress);\n /* Get sender tokensHeld and amountOwed underlying from the vToken */\n (uint256 tokensHeld, uint256 amountOwed, ) = _safeGetAccountSnapshot(vToken, msg.sender);\n\n /* Fail if the sender has a borrow balance */\n if (amountOwed != 0) {\n revert NonzeroBorrowBalance();\n }\n\n /* Fail if the sender is not permitted to redeem all of their tokens */\n _checkRedeemAllowed(vTokenAddress, msg.sender, tokensHeld);\n\n Market storage marketToExit = markets[address(vToken)];\n\n /* Return true if the sender is not already ‘in’ the market */\n if (!marketToExit.accountMembership[msg.sender]) {\n return NO_ERROR;\n }\n\n /* Set vToken account membership to false */\n delete marketToExit.accountMembership[msg.sender];\n\n /* Delete vToken from the account’s list of assets */\n // load into memory for faster iteration\n VToken[] memory userAssetList = accountAssets[msg.sender];\n uint256 len = userAssetList.length;\n\n uint256 assetIndex = len;\n for (uint256 i; i < len; ++i) {\n if (userAssetList[i] == vToken) {\n assetIndex = i;\n break;\n }\n }\n\n // We *must* have found the asset in the list or our redundant data structure is broken\n assert(assetIndex < len);\n\n // copy last item in list to location of item to be removed, reduce length by 1\n VToken[] storage storedList = accountAssets[msg.sender];\n storedList[assetIndex] = storedList[storedList.length - 1];\n storedList.pop();\n\n emit MarketExited(vToken, msg.sender);\n\n return NO_ERROR;\n }\n\n /*** Policy Hooks ***/\n\n /**\n * @notice Checks if the account should be allowed to mint tokens in the given market\n * @param vToken The market to verify the mint against\n * @param minter The account which would get the minted tokens\n * @param mintAmount The amount of underlying being supplied to the market in exchange for tokens\n * @custom:error ActionPaused error is thrown if supplying to this market is paused\n * @custom:error MarketNotListed error is thrown when the market is not listed\n * @custom:error SupplyCapExceeded error is thrown if the total supply exceeds the cap after minting\n * @custom:access Not restricted\n */\n function preMintHook(address vToken, address minter, uint256 mintAmount) external override {\n _checkActionPauseState(vToken, Action.MINT);\n\n if (!markets[vToken].isListed) {\n revert MarketNotListed(address(vToken));\n }\n\n uint256 supplyCap = supplyCaps[vToken];\n // Skipping the cap check for uncapped coins to save some gas\n if (supplyCap != type(uint256).max) {\n uint256 vTokenSupply = VToken(vToken).totalSupply();\n Exp memory exchangeRate = Exp({ mantissa: VToken(vToken).exchangeRateStored() });\n uint256 nextTotalSupply = mul_ScalarTruncateAddUInt(exchangeRate, vTokenSupply, mintAmount);\n if (nextTotalSupply > supplyCap) {\n revert SupplyCapExceeded(vToken, supplyCap);\n }\n }\n\n // Keep the flywheel moving\n uint256 rewardDistributorsCount = rewardsDistributors.length;\n\n for (uint256 i; i < rewardDistributorsCount; ++i) {\n RewardsDistributor rewardsDistributor = rewardsDistributors[i];\n rewardsDistributor.updateRewardTokenSupplyIndex(vToken);\n rewardsDistributor.distributeSupplierRewardToken(vToken, minter);\n }\n }\n\n /**\n * @notice Validates mint, accrues interest and updates score in prime. Reverts on rejection. May emit logs.\n * @param vToken Asset being minted\n * @param minter The address minting the tokens\n * @param actualMintAmount The amount of the underlying asset being minted\n * @param mintTokens The number of tokens being minted\n */\n // solhint-disable-next-line no-unused-vars\n function mintVerify(address vToken, address minter, uint256 actualMintAmount, uint256 mintTokens) external {\n if (address(prime) != address(0)) {\n prime.accrueInterestAndUpdateScore(minter, vToken);\n }\n }\n\n /**\n * @notice Checks if the account should be allowed to redeem tokens in the given market\n * @param vToken The market to verify the redeem against\n * @param redeemer The account which would redeem the tokens\n * @param redeemTokens The number of vTokens to exchange for the underlying asset in the market\n * @custom:error ActionPaused error is thrown if withdrawals are paused in this market\n * @custom:error MarketNotListed error is thrown when the market is not listed\n * @custom:error InsufficientLiquidity error is thrown if the withdrawal would lead to user's insolvency\n * @custom:error SnapshotError is thrown if some vToken fails to return the account's supply and borrows\n * @custom:error PriceError is thrown if the oracle returns an incorrect price for some asset\n * @custom:access Not restricted\n */\n function preRedeemHook(address vToken, address redeemer, uint256 redeemTokens) external override {\n _checkActionPauseState(vToken, Action.REDEEM);\n\n _checkRedeemAllowed(vToken, redeemer, redeemTokens);\n\n // Keep the flywheel moving\n uint256 rewardDistributorsCount = rewardsDistributors.length;\n\n for (uint256 i; i < rewardDistributorsCount; ++i) {\n RewardsDistributor rewardsDistributor = rewardsDistributors[i];\n rewardsDistributor.updateRewardTokenSupplyIndex(vToken);\n rewardsDistributor.distributeSupplierRewardToken(vToken, redeemer);\n }\n }\n\n /**\n * @notice Validates redeem, accrues interest and updates score in prime. Reverts on rejection. May emit logs.\n * @param vToken Asset being redeemed\n * @param redeemer The address redeeming the tokens\n * @param redeemAmount The amount of the underlying asset being redeemed\n * @param redeemTokens The number of tokens being redeemed\n */\n function redeemVerify(address vToken, address redeemer, uint256 redeemAmount, uint256 redeemTokens) external {\n if (address(prime) != address(0)) {\n prime.accrueInterestAndUpdateScore(redeemer, vToken);\n }\n }\n\n /**\n * @notice Validates repayBorrow, accrues interest and updates score in prime. Reverts on rejection. May emit logs.\n * @param vToken Asset being repaid\n * @param payer The address repaying the borrow\n * @param borrower The address of the borrower\n * @param actualRepayAmount The amount of underlying being repaid\n */\n function repayBorrowVerify(\n address vToken,\n address payer, // solhint-disable-line no-unused-vars\n address borrower,\n uint256 actualRepayAmount, // solhint-disable-line no-unused-vars\n uint256 borrowerIndex // solhint-disable-line no-unused-vars\n ) external {\n if (address(prime) != address(0)) {\n prime.accrueInterestAndUpdateScore(borrower, vToken);\n }\n }\n\n /**\n * @notice Validates liquidateBorrow, accrues interest and updates score in prime. Reverts on rejection. May emit logs.\n * @param vTokenBorrowed Asset which was borrowed by the borrower\n * @param vTokenCollateral Asset which was used as collateral and will be seized\n * @param liquidator The address repaying the borrow and seizing the collateral\n * @param borrower The address of the borrower\n * @param actualRepayAmount The amount of underlying being repaid\n * @param seizeTokens The amount of collateral token that will be seized\n */\n function liquidateBorrowVerify(\n address vTokenBorrowed,\n address vTokenCollateral, // solhint-disable-line no-unused-vars\n address liquidator,\n address borrower,\n uint256 actualRepayAmount, // solhint-disable-line no-unused-vars\n uint256 seizeTokens // solhint-disable-line no-unused-vars\n ) external {\n if (address(prime) != address(0)) {\n prime.accrueInterestAndUpdateScore(borrower, vTokenBorrowed);\n prime.accrueInterestAndUpdateScore(liquidator, vTokenBorrowed);\n }\n }\n\n /**\n * @notice Validates seize, accrues interest and updates score in prime. Reverts on rejection. May emit logs.\n * @param vTokenCollateral Asset which was used as collateral and will be seized\n * @param vTokenBorrowed Asset which was borrowed by the borrower\n * @param liquidator The address repaying the borrow and seizing the collateral\n * @param borrower The address of the borrower\n * @param seizeTokens The number of collateral tokens to seize\n */\n function seizeVerify(\n address vTokenCollateral,\n address vTokenBorrowed, // solhint-disable-line no-unused-vars\n address liquidator,\n address borrower,\n uint256 seizeTokens // solhint-disable-line no-unused-vars\n ) external {\n if (address(prime) != address(0)) {\n prime.accrueInterestAndUpdateScore(borrower, vTokenCollateral);\n prime.accrueInterestAndUpdateScore(liquidator, vTokenCollateral);\n }\n }\n\n /**\n * @notice Validates transfer, accrues interest and updates score in prime. Reverts on rejection. May emit logs.\n * @param vToken Asset being transferred\n * @param src The account which sources the tokens\n * @param dst The account which receives the tokens\n * @param transferTokens The number of vTokens to transfer\n */\n // solhint-disable-next-line no-unused-vars\n function transferVerify(address vToken, address src, address dst, uint256 transferTokens) external {\n if (address(prime) != address(0)) {\n prime.accrueInterestAndUpdateScore(src, vToken);\n prime.accrueInterestAndUpdateScore(dst, vToken);\n }\n }\n\n /**\n * @notice Checks if the account should be allowed to borrow the underlying asset of the given market\n * @param vToken The market to verify the borrow against\n * @param borrower The account which would borrow the asset\n * @param borrowAmount The amount of underlying the account would borrow\n * @custom:error ActionPaused error is thrown if borrowing is paused in this market\n * @custom:error MarketNotListed error is thrown when the market is not listed\n * @custom:error InsufficientLiquidity error is thrown if there is not enough collateral to borrow\n * @custom:error BorrowCapExceeded is thrown if the borrow cap will be exceeded should this borrow succeed\n * @custom:error SnapshotError is thrown if some vToken fails to return the account's supply and borrows\n * @custom:error PriceError is thrown if the oracle returns an incorrect price for some asset\n * @custom:access Not restricted if vToken is enabled as collateral, otherwise only vToken\n */\n /// disable-eslint\n function preBorrowHook(address vToken, address borrower, uint256 borrowAmount) external override {\n _checkActionPauseState(vToken, Action.BORROW);\n\n if (!markets[vToken].isListed) {\n revert MarketNotListed(address(vToken));\n }\n\n if (!markets[vToken].accountMembership[borrower]) {\n // only vTokens may call borrowAllowed if borrower not in market\n _checkSenderIs(vToken);\n\n // attempt to add borrower to the market or revert\n _addToMarket(VToken(msg.sender), borrower);\n }\n\n // Update the prices of tokens\n updatePrices(borrower);\n\n if (oracle.getUnderlyingPrice(vToken) == 0) {\n revert PriceError(address(vToken));\n }\n\n uint256 borrowCap = borrowCaps[vToken];\n // Skipping the cap check for uncapped coins to save some gas\n if (borrowCap != type(uint256).max) {\n uint256 totalBorrows = VToken(vToken).totalBorrows();\n uint256 badDebt = VToken(vToken).badDebt();\n uint256 nextTotalBorrows = totalBorrows + borrowAmount + badDebt;\n if (nextTotalBorrows > borrowCap) {\n revert BorrowCapExceeded(vToken, borrowCap);\n }\n }\n\n AccountLiquiditySnapshot memory snapshot = _getHypotheticalLiquiditySnapshot(\n borrower,\n VToken(vToken),\n 0,\n borrowAmount,\n _getCollateralFactor\n );\n\n if (snapshot.shortfall > 0) {\n revert InsufficientLiquidity();\n }\n\n Exp memory borrowIndex = Exp({ mantissa: VToken(vToken).borrowIndex() });\n\n // Keep the flywheel moving\n uint256 rewardDistributorsCount = rewardsDistributors.length;\n\n for (uint256 i; i < rewardDistributorsCount; ++i) {\n RewardsDistributor rewardsDistributor = rewardsDistributors[i];\n rewardsDistributor.updateRewardTokenBorrowIndex(vToken, borrowIndex);\n rewardsDistributor.distributeBorrowerRewardToken(vToken, borrower, borrowIndex);\n }\n }\n\n /**\n * @notice Validates borrow, accrues interest and updates score in prime. Reverts on rejection. May emit logs.\n * @param vToken Asset whose underlying is being borrowed\n * @param borrower The address borrowing the underlying\n * @param borrowAmount The amount of the underlying asset requested to borrow\n */\n // solhint-disable-next-line no-unused-vars\n function borrowVerify(address vToken, address borrower, uint256 borrowAmount) external {\n if (address(prime) != address(0)) {\n prime.accrueInterestAndUpdateScore(borrower, vToken);\n }\n }\n\n /**\n * @notice Checks if the account should be allowed to repay a borrow in the given market\n * @param vToken The market to verify the repay against\n * @param borrower The account which would borrowed the asset\n * @custom:error ActionPaused error is thrown if repayments are paused in this market\n * @custom:error MarketNotListed error is thrown when the market is not listed\n * @custom:access Not restricted\n */\n function preRepayHook(address vToken, address borrower) external override {\n _checkActionPauseState(vToken, Action.REPAY);\n\n oracle.updatePrice(vToken);\n\n if (!markets[vToken].isListed) {\n revert MarketNotListed(address(vToken));\n }\n\n // Keep the flywheel moving\n uint256 rewardDistributorsCount = rewardsDistributors.length;\n\n for (uint256 i; i < rewardDistributorsCount; ++i) {\n Exp memory borrowIndex = Exp({ mantissa: VToken(vToken).borrowIndex() });\n RewardsDistributor rewardsDistributor = rewardsDistributors[i];\n rewardsDistributor.updateRewardTokenBorrowIndex(vToken, borrowIndex);\n rewardsDistributor.distributeBorrowerRewardToken(vToken, borrower, borrowIndex);\n }\n }\n\n /**\n * @notice Checks if the liquidation should be allowed to occur\n * @param vTokenBorrowed Asset which was borrowed by the borrower\n * @param vTokenCollateral Asset which was used as collateral and will be seized\n * @param borrower The address of the borrower\n * @param repayAmount The amount of underlying being repaid\n * @param skipLiquidityCheck Allows the borrow to be liquidated regardless of the account liquidity\n * @custom:error ActionPaused error is thrown if liquidations are paused in this market\n * @custom:error MarketNotListed error is thrown if either collateral or borrowed token is not listed\n * @custom:error TooMuchRepay error is thrown if the liquidator is trying to repay more than allowed by close factor\n * @custom:error MinimalCollateralViolated is thrown if the users' total collateral is lower than the threshold for non-batch liquidations\n * @custom:error InsufficientShortfall is thrown when trying to liquidate a healthy account\n * @custom:error SnapshotError is thrown if some vToken fails to return the account's supply and borrows\n * @custom:error PriceError is thrown if the oracle returns an incorrect price for some asset\n */\n function preLiquidateHook(\n address vTokenBorrowed,\n address vTokenCollateral,\n address borrower,\n uint256 repayAmount,\n bool skipLiquidityCheck\n ) external override {\n // Pause Action.LIQUIDATE on BORROWED TOKEN to prevent liquidating it.\n // If we want to pause liquidating to vTokenCollateral, we should pause\n // Action.SEIZE on it\n _checkActionPauseState(vTokenBorrowed, Action.LIQUIDATE);\n\n // Update the prices of tokens\n updatePrices(borrower);\n\n if (!markets[vTokenBorrowed].isListed) {\n revert MarketNotListed(address(vTokenBorrowed));\n }\n if (!markets[vTokenCollateral].isListed) {\n revert MarketNotListed(address(vTokenCollateral));\n }\n\n uint256 borrowBalance = VToken(vTokenBorrowed).borrowBalanceStored(borrower);\n\n /* Allow accounts to be liquidated if it is a forced liquidation */\n if (skipLiquidityCheck || isForcedLiquidationEnabled[vTokenBorrowed]) {\n if (repayAmount > borrowBalance) {\n revert TooMuchRepay();\n }\n return;\n }\n\n /* The borrower must have shortfall and collateral > threshold in order to be liquidatable */\n AccountLiquiditySnapshot memory snapshot = _getCurrentLiquiditySnapshot(borrower, _getLiquidationThreshold);\n\n if (snapshot.totalCollateral <= minLiquidatableCollateral) {\n /* The liquidator should use either liquidateAccount or healAccount */\n revert MinimalCollateralViolated(minLiquidatableCollateral, snapshot.totalCollateral);\n }\n\n if (snapshot.shortfall == 0) {\n revert InsufficientShortfall();\n }\n\n /* The liquidator may not repay more than what is allowed by the closeFactor */\n uint256 maxClose = mul_ScalarTruncate(Exp({ mantissa: closeFactorMantissa }), borrowBalance);\n if (repayAmount > maxClose) {\n revert TooMuchRepay();\n }\n }\n\n /**\n * @notice Checks if the seizing of assets should be allowed to occur\n * @param vTokenCollateral Asset which was used as collateral and will be seized\n * @param seizerContract Contract that tries to seize the asset (either borrowed vToken or Comptroller)\n * @param liquidator The address repaying the borrow and seizing the collateral\n * @param borrower The address of the borrower\n * @custom:error ActionPaused error is thrown if seizing this type of collateral is paused\n * @custom:error MarketNotListed error is thrown if either collateral or borrowed token is not listed\n * @custom:error ComptrollerMismatch error is when seizer contract or seized asset belong to different pools\n * @custom:access Not restricted\n */\n function preSeizeHook(\n address vTokenCollateral,\n address seizerContract,\n address liquidator,\n address borrower\n ) external override {\n // Pause Action.SEIZE on COLLATERAL to prevent seizing it.\n // If we want to pause liquidating vTokenBorrowed, we should pause\n // Action.LIQUIDATE on it\n _checkActionPauseState(vTokenCollateral, Action.SEIZE);\n\n Market storage market = markets[vTokenCollateral];\n\n if (!market.isListed) {\n revert MarketNotListed(vTokenCollateral);\n }\n\n if (seizerContract == address(this)) {\n // If Comptroller is the seizer, just check if collateral's comptroller\n // is equal to the current address\n if (address(VToken(vTokenCollateral).comptroller()) != address(this)) {\n revert ComptrollerMismatch();\n }\n } else {\n // If the seizer is not the Comptroller, check that the seizer is a\n // listed market, and that the markets' comptrollers match\n if (!markets[seizerContract].isListed) {\n revert MarketNotListed(seizerContract);\n }\n if (VToken(vTokenCollateral).comptroller() != VToken(seizerContract).comptroller()) {\n revert ComptrollerMismatch();\n }\n }\n\n if (!market.accountMembership[borrower]) {\n revert MarketNotCollateral(vTokenCollateral, borrower);\n }\n\n // Keep the flywheel moving\n uint256 rewardDistributorsCount = rewardsDistributors.length;\n\n for (uint256 i; i < rewardDistributorsCount; ++i) {\n RewardsDistributor rewardsDistributor = rewardsDistributors[i];\n rewardsDistributor.updateRewardTokenSupplyIndex(vTokenCollateral);\n rewardsDistributor.distributeSupplierRewardToken(vTokenCollateral, borrower);\n rewardsDistributor.distributeSupplierRewardToken(vTokenCollateral, liquidator);\n }\n }\n\n /**\n * @notice Checks if the account should be allowed to transfer tokens in the given market\n * @param vToken The market to verify the transfer against\n * @param src The account which sources the tokens\n * @param dst The account which receives the tokens\n * @param transferTokens The number of vTokens to transfer\n * @custom:error ActionPaused error is thrown if withdrawals are paused in this market\n * @custom:error MarketNotListed error is thrown when the market is not listed\n * @custom:error InsufficientLiquidity error is thrown if the withdrawal would lead to user's insolvency\n * @custom:error SnapshotError is thrown if some vToken fails to return the account's supply and borrows\n * @custom:error PriceError is thrown if the oracle returns an incorrect price for some asset\n * @custom:access Not restricted\n */\n function preTransferHook(address vToken, address src, address dst, uint256 transferTokens) external override {\n _checkActionPauseState(vToken, Action.TRANSFER);\n\n // Currently the only consideration is whether or not\n // the src is allowed to redeem this many tokens\n _checkRedeemAllowed(vToken, src, transferTokens);\n\n // Keep the flywheel moving\n uint256 rewardDistributorsCount = rewardsDistributors.length;\n\n for (uint256 i; i < rewardDistributorsCount; ++i) {\n RewardsDistributor rewardsDistributor = rewardsDistributors[i];\n rewardsDistributor.updateRewardTokenSupplyIndex(vToken);\n rewardsDistributor.distributeSupplierRewardToken(vToken, src);\n rewardsDistributor.distributeSupplierRewardToken(vToken, dst);\n }\n }\n\n /*** Pool-level operations ***/\n\n /**\n * @notice Seizes all the remaining collateral, makes msg.sender repay the existing\n * borrows, and treats the rest of the debt as bad debt (for each market).\n * The sender has to repay a certain percentage of the debt, computed as\n * collateral / (borrows * liquidationIncentive).\n * @param user account to heal\n * @custom:error CollateralExceedsThreshold error is thrown when the collateral is too big for healing\n * @custom:error SnapshotError is thrown if some vToken fails to return the account's supply and borrows\n * @custom:error PriceError is thrown if the oracle returns an incorrect price for some asset\n * @custom:access Not restricted\n */\n function healAccount(address user) external {\n VToken[] memory userAssets = getAssetsIn(user);\n uint256 userAssetsCount = userAssets.length;\n\n address liquidator = msg.sender;\n {\n ResilientOracleInterface oracle_ = oracle;\n // We need all user's markets to be fresh for the computations to be correct\n for (uint256 i; i < userAssetsCount; ++i) {\n userAssets[i].accrueInterest();\n oracle_.updatePrice(address(userAssets[i]));\n }\n }\n\n AccountLiquiditySnapshot memory snapshot = _getCurrentLiquiditySnapshot(user, _getLiquidationThreshold);\n\n if (snapshot.totalCollateral > minLiquidatableCollateral) {\n revert CollateralExceedsThreshold(minLiquidatableCollateral, snapshot.totalCollateral);\n }\n\n if (snapshot.shortfall == 0) {\n revert InsufficientShortfall();\n }\n\n // percentage = collateral / (borrows * liquidation incentive)\n Exp memory collateral = Exp({ mantissa: snapshot.totalCollateral });\n Exp memory scaledBorrows = mul_(\n Exp({ mantissa: snapshot.borrows }),\n Exp({ mantissa: liquidationIncentiveMantissa })\n );\n\n Exp memory percentage = div_(collateral, scaledBorrows);\n if (lessThanExp(Exp({ mantissa: MANTISSA_ONE }), percentage)) {\n revert CollateralExceedsThreshold(scaledBorrows.mantissa, collateral.mantissa);\n }\n\n for (uint256 i; i < userAssetsCount; ++i) {\n VToken market = userAssets[i];\n\n (uint256 tokens, uint256 borrowBalance, ) = _safeGetAccountSnapshot(market, user);\n uint256 repaymentAmount = mul_ScalarTruncate(percentage, borrowBalance);\n\n // Seize the entire collateral\n if (tokens != 0) {\n market.seize(liquidator, user, tokens);\n }\n // Repay a certain percentage of the borrow, forgive the rest\n if (borrowBalance != 0) {\n market.healBorrow(liquidator, user, repaymentAmount);\n }\n }\n }\n\n /**\n * @notice Liquidates all borrows of the borrower. Callable only if the collateral is less than\n * a predefined threshold, and the account collateral can be seized to cover all borrows. If\n * the collateral is higher than the threshold, use regular liquidations. If the collateral is\n * below the threshold, and the account is insolvent, use healAccount.\n * @param borrower the borrower address\n * @param orders an array of liquidation orders\n * @custom:error CollateralExceedsThreshold error is thrown when the collateral is too big for a batch liquidation\n * @custom:error InsufficientCollateral error is thrown when there is not enough collateral to cover the debt\n * @custom:error SnapshotError is thrown if some vToken fails to return the account's supply and borrows\n * @custom:error PriceError is thrown if the oracle returns an incorrect price for some asset\n * @custom:access Not restricted\n */\n function liquidateAccount(address borrower, LiquidationOrder[] calldata orders) external {\n // We will accrue interest and update the oracle prices later during the liquidation\n\n AccountLiquiditySnapshot memory snapshot = _getCurrentLiquiditySnapshot(borrower, _getLiquidationThreshold);\n\n if (snapshot.totalCollateral > minLiquidatableCollateral) {\n // You should use the regular vToken.liquidateBorrow(...) call\n revert CollateralExceedsThreshold(minLiquidatableCollateral, snapshot.totalCollateral);\n }\n\n uint256 collateralToSeize = mul_ScalarTruncate(\n Exp({ mantissa: liquidationIncentiveMantissa }),\n snapshot.borrows\n );\n if (collateralToSeize >= snapshot.totalCollateral) {\n // There is not enough collateral to seize. Use healBorrow to repay some part of the borrow\n // and record bad debt.\n revert InsufficientCollateral(collateralToSeize, snapshot.totalCollateral);\n }\n\n if (snapshot.shortfall == 0) {\n revert InsufficientShortfall();\n }\n\n uint256 ordersCount = orders.length;\n\n _ensureMaxLoops(ordersCount / 2);\n\n for (uint256 i; i < ordersCount; ++i) {\n if (!markets[address(orders[i].vTokenBorrowed)].isListed) {\n revert MarketNotListed(address(orders[i].vTokenBorrowed));\n }\n if (!markets[address(orders[i].vTokenCollateral)].isListed) {\n revert MarketNotListed(address(orders[i].vTokenCollateral));\n }\n\n LiquidationOrder calldata order = orders[i];\n order.vTokenBorrowed.forceLiquidateBorrow(\n msg.sender,\n borrower,\n order.repayAmount,\n order.vTokenCollateral,\n true\n );\n }\n\n VToken[] memory borrowMarkets = getAssetsIn(borrower);\n uint256 marketsCount = borrowMarkets.length;\n\n for (uint256 i; i < marketsCount; ++i) {\n (, uint256 borrowBalance, ) = _safeGetAccountSnapshot(borrowMarkets[i], borrower);\n require(borrowBalance == 0, \"Nonzero borrow balance after liquidation\");\n }\n }\n\n /**\n * @notice Sets the closeFactor to use when liquidating borrows\n * @param newCloseFactorMantissa New close factor, scaled by 1e18\n * @custom:event Emits NewCloseFactor on success\n * @custom:access Controlled by AccessControlManager\n */\n function setCloseFactor(uint256 newCloseFactorMantissa) external {\n _checkAccessAllowed(\"setCloseFactor(uint256)\");\n require(MAX_CLOSE_FACTOR_MANTISSA >= newCloseFactorMantissa, \"Close factor greater than maximum close factor\");\n require(MIN_CLOSE_FACTOR_MANTISSA <= newCloseFactorMantissa, \"Close factor smaller than minimum close factor\");\n\n uint256 oldCloseFactorMantissa = closeFactorMantissa;\n closeFactorMantissa = newCloseFactorMantissa;\n emit NewCloseFactor(oldCloseFactorMantissa, newCloseFactorMantissa);\n }\n\n /**\n * @notice Sets the collateralFactor for a market\n * @dev This function is restricted by the AccessControlManager\n * @param vToken The market to set the factor on\n * @param newCollateralFactorMantissa The new collateral factor, scaled by 1e18\n * @param newLiquidationThresholdMantissa The new liquidation threshold, scaled by 1e18\n * @custom:event Emits NewCollateralFactor when collateral factor is updated\n * and NewLiquidationThreshold when liquidation threshold is updated\n * @custom:error MarketNotListed error is thrown when the market is not listed\n * @custom:error InvalidCollateralFactor error is thrown when collateral factor is too high\n * @custom:error InvalidLiquidationThreshold error is thrown when liquidation threshold is lower than collateral factor\n * @custom:error PriceError is thrown when the oracle returns an invalid price for the asset\n * @custom:access Controlled by AccessControlManager\n */\n function setCollateralFactor(\n VToken vToken,\n uint256 newCollateralFactorMantissa,\n uint256 newLiquidationThresholdMantissa\n ) external {\n _checkAccessAllowed(\"setCollateralFactor(address,uint256,uint256)\");\n\n // Verify market is listed\n Market storage market = markets[address(vToken)];\n if (!market.isListed) {\n revert MarketNotListed(address(vToken));\n }\n\n // Check collateral factor <= 0.9\n if (newCollateralFactorMantissa > MAX_COLLATERAL_FACTOR_MANTISSA) {\n revert InvalidCollateralFactor();\n }\n\n // Ensure that liquidation threshold <= 1\n if (newLiquidationThresholdMantissa > MANTISSA_ONE) {\n revert InvalidLiquidationThreshold();\n }\n\n // Ensure that liquidation threshold >= CF\n if (newLiquidationThresholdMantissa < newCollateralFactorMantissa) {\n revert InvalidLiquidationThreshold();\n }\n\n // If collateral factor != 0, fail if price == 0\n if (newCollateralFactorMantissa != 0 && oracle.getUnderlyingPrice(address(vToken)) == 0) {\n revert PriceError(address(vToken));\n }\n\n uint256 oldCollateralFactorMantissa = market.collateralFactorMantissa;\n if (newCollateralFactorMantissa != oldCollateralFactorMantissa) {\n market.collateralFactorMantissa = newCollateralFactorMantissa;\n emit NewCollateralFactor(vToken, oldCollateralFactorMantissa, newCollateralFactorMantissa);\n }\n\n uint256 oldLiquidationThresholdMantissa = market.liquidationThresholdMantissa;\n if (newLiquidationThresholdMantissa != oldLiquidationThresholdMantissa) {\n market.liquidationThresholdMantissa = newLiquidationThresholdMantissa;\n emit NewLiquidationThreshold(vToken, oldLiquidationThresholdMantissa, newLiquidationThresholdMantissa);\n }\n }\n\n /**\n * @notice Sets liquidationIncentive\n * @dev This function is restricted by the AccessControlManager\n * @param newLiquidationIncentiveMantissa New liquidationIncentive scaled by 1e18\n * @custom:event Emits NewLiquidationIncentive on success\n * @custom:access Controlled by AccessControlManager\n */\n function setLiquidationIncentive(uint256 newLiquidationIncentiveMantissa) external {\n require(newLiquidationIncentiveMantissa >= MANTISSA_ONE, \"liquidation incentive should be greater than 1e18\");\n\n _checkAccessAllowed(\"setLiquidationIncentive(uint256)\");\n\n // Save current value for use in log\n uint256 oldLiquidationIncentiveMantissa = liquidationIncentiveMantissa;\n\n // Set liquidation incentive to new incentive\n liquidationIncentiveMantissa = newLiquidationIncentiveMantissa;\n\n // Emit event with old incentive, new incentive\n emit NewLiquidationIncentive(oldLiquidationIncentiveMantissa, newLiquidationIncentiveMantissa);\n }\n\n /**\n * @notice Add the market to the markets mapping and set it as listed\n * @dev Only callable by the PoolRegistry\n * @param vToken The address of the market (token) to list\n * @custom:error MarketAlreadyListed is thrown if the market is already listed in this pool\n * @custom:access Only PoolRegistry\n */\n function supportMarket(VToken vToken) external {\n _checkSenderIs(poolRegistry);\n\n if (markets[address(vToken)].isListed) {\n revert MarketAlreadyListed(address(vToken));\n }\n\n require(vToken.isVToken(), \"Comptroller: Invalid vToken\"); // Sanity check to make sure its really a VToken\n\n Market storage newMarket = markets[address(vToken)];\n newMarket.isListed = true;\n newMarket.collateralFactorMantissa = 0;\n newMarket.liquidationThresholdMantissa = 0;\n\n _addMarket(address(vToken));\n\n uint256 rewardDistributorsCount = rewardsDistributors.length;\n\n for (uint256 i; i < rewardDistributorsCount; ++i) {\n rewardsDistributors[i].initializeMarket(address(vToken));\n }\n\n emit MarketSupported(vToken);\n }\n\n /**\n * @notice Set the given borrow caps for the given vToken markets. Borrowing that brings total borrows to or above borrow cap will revert.\n * @dev This function is restricted by the AccessControlManager\n * @dev A borrow cap of type(uint256).max corresponds to unlimited borrowing.\n * @dev Borrow caps smaller than the current total borrows are accepted. This way, new borrows will not be allowed\n until the total borrows amount goes below the new borrow cap\n * @param vTokens The addresses of the markets (tokens) to change the borrow caps for\n * @param newBorrowCaps The new borrow cap values in underlying to be set. A value of type(uint256).max corresponds to unlimited borrowing.\n * @custom:access Controlled by AccessControlManager\n */\n function setMarketBorrowCaps(VToken[] calldata vTokens, uint256[] calldata newBorrowCaps) external {\n _checkAccessAllowed(\"setMarketBorrowCaps(address[],uint256[])\");\n\n uint256 numMarkets = vTokens.length;\n uint256 numBorrowCaps = newBorrowCaps.length;\n\n require(numMarkets != 0 && numMarkets == numBorrowCaps, \"invalid input\");\n\n _ensureMaxLoops(numMarkets);\n\n for (uint256 i; i < numMarkets; ++i) {\n borrowCaps[address(vTokens[i])] = newBorrowCaps[i];\n emit NewBorrowCap(vTokens[i], newBorrowCaps[i]);\n }\n }\n\n /**\n * @notice Set the given supply caps for the given vToken markets. Supply that brings total Supply to or above supply cap will revert.\n * @dev This function is restricted by the AccessControlManager\n * @dev A supply cap of type(uint256).max corresponds to unlimited supply.\n * @dev Supply caps smaller than the current total supplies are accepted. This way, new supplies will not be allowed\n until the total supplies amount goes below the new supply cap\n * @param vTokens The addresses of the markets (tokens) to change the supply caps for\n * @param newSupplyCaps The new supply cap values in underlying to be set. A value of type(uint256).max corresponds to unlimited supply.\n * @custom:access Controlled by AccessControlManager\n */\n function setMarketSupplyCaps(VToken[] calldata vTokens, uint256[] calldata newSupplyCaps) external {\n _checkAccessAllowed(\"setMarketSupplyCaps(address[],uint256[])\");\n uint256 vTokensCount = vTokens.length;\n\n require(vTokensCount != 0, \"invalid number of markets\");\n require(vTokensCount == newSupplyCaps.length, \"invalid number of markets\");\n\n _ensureMaxLoops(vTokensCount);\n\n for (uint256 i; i < vTokensCount; ++i) {\n supplyCaps[address(vTokens[i])] = newSupplyCaps[i];\n emit NewSupplyCap(vTokens[i], newSupplyCaps[i]);\n }\n }\n\n /**\n * @notice Pause/unpause specified actions\n * @dev This function is restricted by the AccessControlManager\n * @param marketsList Markets to pause/unpause the actions on\n * @param actionsList List of action ids to pause/unpause\n * @param paused The new paused state (true=paused, false=unpaused)\n * @custom:access Controlled by AccessControlManager\n */\n function setActionsPaused(VToken[] calldata marketsList, Action[] calldata actionsList, bool paused) external {\n _checkAccessAllowed(\"setActionsPaused(address[],uint256[],bool)\");\n\n uint256 marketsCount = marketsList.length;\n uint256 actionsCount = actionsList.length;\n\n _ensureMaxLoops(marketsCount * actionsCount);\n\n for (uint256 marketIdx; marketIdx < marketsCount; ++marketIdx) {\n for (uint256 actionIdx; actionIdx < actionsCount; ++actionIdx) {\n _setActionPaused(address(marketsList[marketIdx]), actionsList[actionIdx], paused);\n }\n }\n }\n\n /**\n * @notice Set the given collateral threshold for non-batch liquidations. Regular liquidations\n * will fail if the collateral amount is less than this threshold. Liquidators should use batch\n * operations like liquidateAccount or healAccount.\n * @dev This function is restricted by the AccessControlManager\n * @param newMinLiquidatableCollateral The new min liquidatable collateral (in USD).\n * @custom:access Controlled by AccessControlManager\n */\n function setMinLiquidatableCollateral(uint256 newMinLiquidatableCollateral) external {\n _checkAccessAllowed(\"setMinLiquidatableCollateral(uint256)\");\n\n uint256 oldMinLiquidatableCollateral = minLiquidatableCollateral;\n minLiquidatableCollateral = newMinLiquidatableCollateral;\n emit NewMinLiquidatableCollateral(oldMinLiquidatableCollateral, newMinLiquidatableCollateral);\n }\n\n /**\n * @notice Add a new RewardsDistributor and initialize it with all markets. We can add several RewardsDistributor\n * contracts with the same rewardToken, and there could be overlaping among them considering the last reward slot (block or second)\n * @dev Only callable by the admin\n * @param _rewardsDistributor Address of the RewardDistributor contract to add\n * @custom:access Only Governance\n * @custom:event Emits NewRewardsDistributor with distributor address\n */\n function addRewardsDistributor(RewardsDistributor _rewardsDistributor) external onlyOwner {\n require(!rewardsDistributorExists[address(_rewardsDistributor)], \"already exists\");\n\n uint256 rewardsDistributorsLen = rewardsDistributors.length;\n _ensureMaxLoops(rewardsDistributorsLen + 1);\n\n rewardsDistributors.push(_rewardsDistributor);\n rewardsDistributorExists[address(_rewardsDistributor)] = true;\n\n uint256 marketsCount = allMarkets.length;\n\n for (uint256 i; i < marketsCount; ++i) {\n _rewardsDistributor.initializeMarket(address(allMarkets[i]));\n }\n\n emit NewRewardsDistributor(address(_rewardsDistributor), address(_rewardsDistributor.rewardToken()));\n }\n\n /**\n * @notice Sets a new price oracle for the Comptroller\n * @dev Only callable by the admin\n * @param newOracle Address of the new price oracle to set\n * @custom:event Emits NewPriceOracle on success\n * @custom:error ZeroAddressNotAllowed is thrown when the new oracle address is zero\n */\n function setPriceOracle(ResilientOracleInterface newOracle) external onlyOwner {\n ensureNonzeroAddress(address(newOracle));\n\n ResilientOracleInterface oldOracle = oracle;\n oracle = newOracle;\n emit NewPriceOracle(oldOracle, newOracle);\n }\n\n /**\n * @notice Set the for loop iteration limit to avoid DOS\n * @param limit Limit for the max loops can execute at a time\n */\n function setMaxLoopsLimit(uint256 limit) external onlyOwner {\n _setMaxLoopsLimit(limit);\n }\n\n /**\n * @notice Sets the prime token contract for the comptroller\n * @param _prime Address of the Prime contract\n */\n function setPrimeToken(IPrime _prime) external onlyOwner {\n ensureNonzeroAddress(address(_prime));\n\n emit NewPrimeToken(prime, _prime);\n prime = _prime;\n }\n\n /**\n * @notice Enables forced liquidations for a market. If forced liquidation is enabled,\n * borrows in the market may be liquidated regardless of the account liquidity\n * @param vTokenBorrowed Borrowed vToken\n * @param enable Whether to enable forced liquidations\n */\n function setForcedLiquidation(address vTokenBorrowed, bool enable) external {\n _checkAccessAllowed(\"setForcedLiquidation(address,bool)\");\n ensureNonzeroAddress(vTokenBorrowed);\n\n if (!markets[vTokenBorrowed].isListed) {\n revert MarketNotListed(vTokenBorrowed);\n }\n\n isForcedLiquidationEnabled[vTokenBorrowed] = enable;\n emit IsForcedLiquidationEnabledUpdated(vTokenBorrowed, enable);\n }\n\n /**\n * @notice Determine the current account liquidity with respect to liquidation threshold requirements\n * @dev The interface of this function is intentionally kept compatible with Compound and Venus Core\n * @param account The account get liquidity for\n * @return error Always NO_ERROR for compatibility with Venus core tooling\n * @return liquidity Account liquidity in excess of liquidation threshold requirements,\n * @return shortfall Account shortfall below liquidation threshold requirements\n */\n function getAccountLiquidity(\n address account\n ) external view returns (uint256 error, uint256 liquidity, uint256 shortfall) {\n AccountLiquiditySnapshot memory snapshot = _getCurrentLiquiditySnapshot(account, _getLiquidationThreshold);\n return (NO_ERROR, snapshot.liquidity, snapshot.shortfall);\n }\n\n /**\n * @notice Determine the current account liquidity with respect to collateral requirements\n * @dev The interface of this function is intentionally kept compatible with Compound and Venus Core\n * @param account The account get liquidity for\n * @return error Always NO_ERROR for compatibility with Venus core tooling\n * @return liquidity Account liquidity in excess of collateral requirements,\n * @return shortfall Account shortfall below collateral requirements\n */\n function getBorrowingPower(\n address account\n ) external view returns (uint256 error, uint256 liquidity, uint256 shortfall) {\n AccountLiquiditySnapshot memory snapshot = _getCurrentLiquiditySnapshot(account, _getCollateralFactor);\n return (NO_ERROR, snapshot.liquidity, snapshot.shortfall);\n }\n\n /**\n * @notice Determine what the account liquidity would be if the given amounts were redeemed/borrowed\n * @dev The interface of this function is intentionally kept compatible with Compound and Venus Core\n * @param vTokenModify The market to hypothetically redeem/borrow in\n * @param account The account to determine liquidity for\n * @param redeemTokens The number of tokens to hypothetically redeem\n * @param borrowAmount The amount of underlying to hypothetically borrow\n * @return error Always NO_ERROR for compatibility with Venus core tooling\n * @return liquidity Hypothetical account liquidity in excess of collateral requirements,\n * @return shortfall Hypothetical account shortfall below collateral requirements\n */\n function getHypotheticalAccountLiquidity(\n address account,\n address vTokenModify,\n uint256 redeemTokens,\n uint256 borrowAmount\n ) external view returns (uint256 error, uint256 liquidity, uint256 shortfall) {\n AccountLiquiditySnapshot memory snapshot = _getHypotheticalLiquiditySnapshot(\n account,\n VToken(vTokenModify),\n redeemTokens,\n borrowAmount,\n _getCollateralFactor\n );\n return (NO_ERROR, snapshot.liquidity, snapshot.shortfall);\n }\n\n /**\n * @notice Return all of the markets\n * @dev The automatic getter may be used to access an individual market.\n * @return markets The list of market addresses\n */\n function getAllMarkets() external view override returns (VToken[] memory) {\n return allMarkets;\n }\n\n /**\n * @notice Check if a market is marked as listed (active)\n * @param vToken vToken Address for the market to check\n * @return listed True if listed otherwise false\n */\n function isMarketListed(VToken vToken) external view returns (bool) {\n return markets[address(vToken)].isListed;\n }\n\n /*** Assets You Are In ***/\n\n /**\n * @notice Returns whether the given account is entered in a given market\n * @param account The address of the account to check\n * @param vToken The vToken to check\n * @return True if the account is in the market specified, otherwise false.\n */\n function checkMembership(address account, VToken vToken) external view returns (bool) {\n return markets[address(vToken)].accountMembership[account];\n }\n\n /**\n * @notice Calculate number of tokens of collateral asset to seize given an underlying amount\n * @dev Used in liquidation (called in vToken.liquidateBorrowFresh)\n * @param vTokenBorrowed The address of the borrowed vToken\n * @param vTokenCollateral The address of the collateral vToken\n * @param actualRepayAmount The amount of vTokenBorrowed underlying to convert into vTokenCollateral tokens\n * @return error Always NO_ERROR for compatibility with Venus core tooling\n * @return tokensToSeize Number of vTokenCollateral tokens to be seized in a liquidation\n * @custom:error PriceError if the oracle returns an invalid price\n */\n function liquidateCalculateSeizeTokens(\n address vTokenBorrowed,\n address vTokenCollateral,\n uint256 actualRepayAmount\n ) external view override returns (uint256 error, uint256 tokensToSeize) {\n /* Read oracle prices for borrowed and collateral markets */\n uint256 priceBorrowedMantissa = _safeGetUnderlyingPrice(VToken(vTokenBorrowed));\n uint256 priceCollateralMantissa = _safeGetUnderlyingPrice(VToken(vTokenCollateral));\n\n /*\n * Get the exchange rate and calculate the number of collateral tokens to seize:\n * seizeAmount = actualRepayAmount * liquidationIncentive * priceBorrowed / priceCollateral\n * seizeTokens = seizeAmount / exchangeRate\n * = actualRepayAmount * (liquidationIncentive * priceBorrowed) / (priceCollateral * exchangeRate)\n */\n uint256 exchangeRateMantissa = VToken(vTokenCollateral).exchangeRateStored(); // Note: reverts on error\n uint256 seizeTokens;\n Exp memory numerator;\n Exp memory denominator;\n Exp memory ratio;\n\n numerator = mul_(Exp({ mantissa: liquidationIncentiveMantissa }), Exp({ mantissa: priceBorrowedMantissa }));\n denominator = mul_(Exp({ mantissa: priceCollateralMantissa }), Exp({ mantissa: exchangeRateMantissa }));\n ratio = div_(numerator, denominator);\n\n seizeTokens = mul_ScalarTruncate(ratio, actualRepayAmount);\n\n return (NO_ERROR, seizeTokens);\n }\n\n /**\n * @notice Returns reward speed given a vToken\n * @param vToken The vToken to get the reward speeds for\n * @return rewardSpeeds Array of total supply and borrow speeds and reward token for all reward distributors\n */\n function getRewardsByMarket(address vToken) external view returns (RewardSpeeds[] memory rewardSpeeds) {\n uint256 rewardsDistributorsLength = rewardsDistributors.length;\n rewardSpeeds = new RewardSpeeds[](rewardsDistributorsLength);\n for (uint256 i; i < rewardsDistributorsLength; ++i) {\n RewardsDistributor rewardsDistributor = rewardsDistributors[i];\n address rewardToken = address(rewardsDistributor.rewardToken());\n rewardSpeeds[i] = RewardSpeeds({\n rewardToken: rewardToken,\n supplySpeed: rewardsDistributor.rewardTokenSupplySpeeds(vToken),\n borrowSpeed: rewardsDistributor.rewardTokenBorrowSpeeds(vToken)\n });\n }\n return rewardSpeeds;\n }\n\n /**\n * @notice Return all reward distributors for this pool\n * @return Array of RewardDistributor addresses\n */\n function getRewardDistributors() external view returns (RewardsDistributor[] memory) {\n return rewardsDistributors;\n }\n\n /**\n * @notice A marker method that returns true for a valid Comptroller contract\n * @return Always true\n */\n function isComptroller() external pure override returns (bool) {\n return true;\n }\n\n /**\n * @notice Update the prices of all the tokens associated with the provided account\n * @param account Address of the account to get associated tokens with\n */\n function updatePrices(address account) public {\n VToken[] memory vTokens = getAssetsIn(account);\n uint256 vTokensCount = vTokens.length;\n\n ResilientOracleInterface oracle_ = oracle;\n\n for (uint256 i; i < vTokensCount; ++i) {\n oracle_.updatePrice(address(vTokens[i]));\n }\n }\n\n /**\n * @notice Checks if a certain action is paused on a market\n * @param market vToken address\n * @param action Action to check\n * @return paused True if the action is paused otherwise false\n */\n function actionPaused(address market, Action action) public view returns (bool) {\n return _actionPaused[market][action];\n }\n\n /**\n * @notice Returns the assets an account has entered\n * @param account The address of the account to pull assets for\n * @return A list with the assets the account has entered\n */\n function getAssetsIn(address account) public view returns (VToken[] memory) {\n uint256 len;\n VToken[] memory _accountAssets = accountAssets[account];\n uint256 _accountAssetsLength = _accountAssets.length;\n\n VToken[] memory assetsIn = new VToken[](_accountAssetsLength);\n\n for (uint256 i; i < _accountAssetsLength; ++i) {\n Market storage market = markets[address(_accountAssets[i])];\n if (market.isListed) {\n assetsIn[len] = _accountAssets[i];\n ++len;\n }\n }\n\n assembly {\n mstore(assetsIn, len)\n }\n\n return assetsIn;\n }\n\n /**\n * @notice Add the market to the borrower's \"assets in\" for liquidity calculations\n * @param vToken The market to enter\n * @param borrower The address of the account to modify\n */\n function _addToMarket(VToken vToken, address borrower) internal {\n _checkActionPauseState(address(vToken), Action.ENTER_MARKET);\n Market storage marketToJoin = markets[address(vToken)];\n\n if (!marketToJoin.isListed) {\n revert MarketNotListed(address(vToken));\n }\n\n if (marketToJoin.accountMembership[borrower]) {\n // already joined\n return;\n }\n\n // survived the gauntlet, add to list\n // NOTE: we store these somewhat redundantly as a significant optimization\n // this avoids having to iterate through the list for the most common use cases\n // that is, only when we need to perform liquidity checks\n // and not whenever we want to check if an account is in a particular market\n marketToJoin.accountMembership[borrower] = true;\n accountAssets[borrower].push(vToken);\n\n emit MarketEntered(vToken, borrower);\n }\n\n /**\n * @notice Internal function to validate that a market hasn't already been added\n * and if it hasn't adds it\n * @param vToken The market to support\n */\n function _addMarket(address vToken) internal {\n uint256 marketsCount = allMarkets.length;\n\n for (uint256 i; i < marketsCount; ++i) {\n if (allMarkets[i] == VToken(vToken)) {\n revert MarketAlreadyListed(vToken);\n }\n }\n allMarkets.push(VToken(vToken));\n marketsCount = allMarkets.length;\n _ensureMaxLoops(marketsCount);\n }\n\n /**\n * @dev Pause/unpause an action on a market\n * @param market Market to pause/unpause the action on\n * @param action Action id to pause/unpause\n * @param paused The new paused state (true=paused, false=unpaused)\n */\n function _setActionPaused(address market, Action action, bool paused) internal {\n require(markets[market].isListed, \"cannot pause a market that is not listed\");\n _actionPaused[market][action] = paused;\n emit ActionPausedMarket(VToken(market), action, paused);\n }\n\n /**\n * @dev Internal function to check that vTokens can be safely redeemed for the underlying asset.\n * @param vToken Address of the vTokens to redeem\n * @param redeemer Account redeeming the tokens\n * @param redeemTokens The number of tokens to redeem\n */\n function _checkRedeemAllowed(address vToken, address redeemer, uint256 redeemTokens) internal {\n Market storage market = markets[vToken];\n\n if (!market.isListed) {\n revert MarketNotListed(address(vToken));\n }\n\n /* If the redeemer is not 'in' the market, then we can bypass the liquidity check */\n if (!market.accountMembership[redeemer]) {\n return;\n }\n\n // Update the prices of tokens\n updatePrices(redeemer);\n\n /* Otherwise, perform a hypothetical liquidity check to guard against shortfall */\n AccountLiquiditySnapshot memory snapshot = _getHypotheticalLiquiditySnapshot(\n redeemer,\n VToken(vToken),\n redeemTokens,\n 0,\n _getCollateralFactor\n );\n if (snapshot.shortfall > 0) {\n revert InsufficientLiquidity();\n }\n }\n\n /**\n * @notice Get the total collateral, weighted collateral, borrow balance, liquidity, shortfall\n * @param account The account to get the snapshot for\n * @param weight The function to compute the weight of the collateral – either collateral factor or\n * liquidation threshold. Accepts the address of the vToken and returns the weight as Exp.\n * @dev Note that we calculate the exchangeRateStored for each collateral vToken using stored data,\n * without calculating accumulated interest.\n * @return snapshot Account liquidity snapshot\n */\n function _getCurrentLiquiditySnapshot(\n address account,\n function(VToken) internal view returns (Exp memory) weight\n ) internal view returns (AccountLiquiditySnapshot memory snapshot) {\n return _getHypotheticalLiquiditySnapshot(account, VToken(address(0)), 0, 0, weight);\n }\n\n /**\n * @notice Determine what the supply/borrow balances would be if the given amounts were redeemed/borrowed\n * @param vTokenModify The market to hypothetically redeem/borrow in\n * @param account The account to determine liquidity for\n * @param redeemTokens The number of tokens to hypothetically redeem\n * @param borrowAmount The amount of underlying to hypothetically borrow\n * @param weight The function to compute the weight of the collateral – either collateral factor or\n liquidation threshold. Accepts the address of the VToken and returns the weight\n * @dev Note that we calculate the exchangeRateStored for each collateral vToken using stored data,\n * without calculating accumulated interest.\n * @return snapshot Account liquidity snapshot\n */\n function _getHypotheticalLiquiditySnapshot(\n address account,\n VToken vTokenModify,\n uint256 redeemTokens,\n uint256 borrowAmount,\n function(VToken) internal view returns (Exp memory) weight\n ) internal view returns (AccountLiquiditySnapshot memory snapshot) {\n // For each asset the account is in\n VToken[] memory assets = getAssetsIn(account);\n uint256 assetsCount = assets.length;\n\n for (uint256 i; i < assetsCount; ++i) {\n VToken asset = assets[i];\n\n // Read the balances and exchange rate from the vToken\n (uint256 vTokenBalance, uint256 borrowBalance, uint256 exchangeRateMantissa) = _safeGetAccountSnapshot(\n asset,\n account\n );\n\n // Get the normalized price of the asset\n Exp memory oraclePrice = Exp({ mantissa: _safeGetUnderlyingPrice(asset) });\n\n // Pre-compute conversion factors from vTokens -> usd\n Exp memory vTokenPrice = mul_(Exp({ mantissa: exchangeRateMantissa }), oraclePrice);\n Exp memory weightedVTokenPrice = mul_(weight(asset), vTokenPrice);\n\n // weightedCollateral += weightedVTokenPrice * vTokenBalance\n snapshot.weightedCollateral = mul_ScalarTruncateAddUInt(\n weightedVTokenPrice,\n vTokenBalance,\n snapshot.weightedCollateral\n );\n\n // totalCollateral += vTokenPrice * vTokenBalance\n snapshot.totalCollateral = mul_ScalarTruncateAddUInt(vTokenPrice, vTokenBalance, snapshot.totalCollateral);\n\n // borrows += oraclePrice * borrowBalance\n snapshot.borrows = mul_ScalarTruncateAddUInt(oraclePrice, borrowBalance, snapshot.borrows);\n\n // Calculate effects of interacting with vTokenModify\n if (asset == vTokenModify) {\n // redeem effect\n // effects += tokensToDenom * redeemTokens\n snapshot.effects = mul_ScalarTruncateAddUInt(weightedVTokenPrice, redeemTokens, snapshot.effects);\n\n // borrow effect\n // effects += oraclePrice * borrowAmount\n snapshot.effects = mul_ScalarTruncateAddUInt(oraclePrice, borrowAmount, snapshot.effects);\n }\n }\n\n uint256 borrowPlusEffects = snapshot.borrows + snapshot.effects;\n // These are safe, as the underflow condition is checked first\n unchecked {\n if (snapshot.weightedCollateral > borrowPlusEffects) {\n snapshot.liquidity = snapshot.weightedCollateral - borrowPlusEffects;\n snapshot.shortfall = 0;\n } else {\n snapshot.liquidity = 0;\n snapshot.shortfall = borrowPlusEffects - snapshot.weightedCollateral;\n }\n }\n\n return snapshot;\n }\n\n /**\n * @dev Retrieves price from oracle for an asset and checks it is nonzero\n * @param asset Address for asset to query price\n * @return Underlying price\n */\n function _safeGetUnderlyingPrice(VToken asset) internal view returns (uint256) {\n uint256 oraclePriceMantissa = oracle.getUnderlyingPrice(address(asset));\n if (oraclePriceMantissa == 0) {\n revert PriceError(address(asset));\n }\n return oraclePriceMantissa;\n }\n\n /**\n * @dev Return collateral factor for a market\n * @param asset Address for asset\n * @return Collateral factor as exponential\n */\n function _getCollateralFactor(VToken asset) internal view returns (Exp memory) {\n return Exp({ mantissa: markets[address(asset)].collateralFactorMantissa });\n }\n\n /**\n * @dev Retrieves liquidation threshold for a market as an exponential\n * @param asset Address for asset to liquidation threshold\n * @return Liquidation threshold as exponential\n */\n function _getLiquidationThreshold(VToken asset) internal view returns (Exp memory) {\n return Exp({ mantissa: markets[address(asset)].liquidationThresholdMantissa });\n }\n\n /**\n * @dev Returns supply and borrow balances of user in vToken, reverts on failure\n * @param vToken Market to query\n * @param user Account address\n * @return vTokenBalance Balance of vTokens, the same as vToken.balanceOf(user)\n * @return borrowBalance Borrowed amount, including the interest\n * @return exchangeRateMantissa Stored exchange rate\n */\n function _safeGetAccountSnapshot(\n VToken vToken,\n address user\n ) internal view returns (uint256 vTokenBalance, uint256 borrowBalance, uint256 exchangeRateMantissa) {\n uint256 err;\n (err, vTokenBalance, borrowBalance, exchangeRateMantissa) = vToken.getAccountSnapshot(user);\n if (err != 0) {\n revert SnapshotError(address(vToken), user);\n }\n return (vTokenBalance, borrowBalance, exchangeRateMantissa);\n }\n\n /// @notice Reverts if the call is not from expectedSender\n /// @param expectedSender Expected transaction sender\n function _checkSenderIs(address expectedSender) internal view {\n if (msg.sender != expectedSender) {\n revert UnexpectedSender(expectedSender, msg.sender);\n }\n }\n\n /// @notice Reverts if a certain action is paused on a market\n /// @param market Market to check\n /// @param action Action to check\n function _checkActionPauseState(address market, Action action) private view {\n if (actionPaused(market, action)) {\n revert ActionPaused(market, action);\n }\n }\n}\n" + }, + "contracts/ComptrollerInterface.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity ^0.8.25;\n\nimport { ResilientOracleInterface } from \"@venusprotocol/oracle/contracts/interfaces/OracleInterface.sol\";\n\nimport { VToken } from \"./VToken.sol\";\nimport { RewardsDistributor } from \"./Rewards/RewardsDistributor.sol\";\n\nenum Action {\n MINT,\n REDEEM,\n BORROW,\n REPAY,\n SEIZE,\n LIQUIDATE,\n TRANSFER,\n ENTER_MARKET,\n EXIT_MARKET\n}\n\n/**\n * @title ComptrollerInterface\n * @author Venus\n * @notice Interface implemented by the `Comptroller` contract.\n */\ninterface ComptrollerInterface {\n /*** Assets You Are In ***/\n\n function enterMarkets(address[] calldata vTokens) external returns (uint256[] memory);\n\n function exitMarket(address vToken) external returns (uint256);\n\n /*** Policy Hooks ***/\n\n function preMintHook(address vToken, address minter, uint256 mintAmount) external;\n\n function preRedeemHook(address vToken, address redeemer, uint256 redeemTokens) external;\n\n function preBorrowHook(address vToken, address borrower, uint256 borrowAmount) external;\n\n function preRepayHook(address vToken, address borrower) external;\n\n function preLiquidateHook(\n address vTokenBorrowed,\n address vTokenCollateral,\n address borrower,\n uint256 repayAmount,\n bool skipLiquidityCheck\n ) external;\n\n function preSeizeHook(\n address vTokenCollateral,\n address vTokenBorrowed,\n address liquidator,\n address borrower\n ) external;\n\n function borrowVerify(address vToken, address borrower, uint borrowAmount) external;\n\n function mintVerify(address vToken, address minter, uint mintAmount, uint mintTokens) external;\n\n function redeemVerify(address vToken, address redeemer, uint redeemAmount, uint redeemTokens) external;\n\n function repayBorrowVerify(\n address vToken,\n address payer,\n address borrower,\n uint repayAmount,\n uint borrowerIndex\n ) external;\n\n function liquidateBorrowVerify(\n address vTokenBorrowed,\n address vTokenCollateral,\n address liquidator,\n address borrower,\n uint repayAmount,\n uint seizeTokens\n ) external;\n\n function seizeVerify(\n address vTokenCollateral,\n address vTokenBorrowed,\n address liquidator,\n address borrower,\n uint seizeTokens\n ) external;\n\n function transferVerify(address vToken, address src, address dst, uint transferTokens) external;\n\n function preTransferHook(address vToken, address src, address dst, uint256 transferTokens) external;\n\n function isComptroller() external view returns (bool);\n\n /*** Liquidity/Liquidation Calculations ***/\n\n function liquidateCalculateSeizeTokens(\n address vTokenBorrowed,\n address vTokenCollateral,\n uint256 repayAmount\n ) external view returns (uint256, uint256);\n\n function getAllMarkets() external view returns (VToken[] memory);\n\n function actionPaused(address market, Action action) external view returns (bool);\n}\n\n/**\n * @title ComptrollerViewInterface\n * @author Venus\n * @notice Interface implemented by the `Comptroller` contract, including only some util view functions.\n */\ninterface ComptrollerViewInterface {\n function markets(address) external view returns (bool, uint256);\n\n function oracle() external view returns (ResilientOracleInterface);\n\n function getAssetsIn(address) external view returns (VToken[] memory);\n\n function closeFactorMantissa() external view returns (uint256);\n\n function liquidationIncentiveMantissa() external view returns (uint256);\n\n function minLiquidatableCollateral() external view returns (uint256);\n\n function getRewardDistributors() external view returns (RewardsDistributor[] memory);\n\n function getAllMarkets() external view returns (VToken[] memory);\n\n function borrowCaps(address) external view returns (uint256);\n\n function supplyCaps(address) external view returns (uint256);\n\n function approvedDelegates(address user, address delegate) external view returns (bool);\n}\n" + }, + "contracts/ComptrollerStorage.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\n\nimport { ResilientOracleInterface } from \"@venusprotocol/oracle/contracts/interfaces/OracleInterface.sol\";\n\nimport { VToken } from \"./VToken.sol\";\nimport { RewardsDistributor } from \"./Rewards/RewardsDistributor.sol\";\nimport { IPrime } from \"@venusprotocol/venus-protocol/contracts/Tokens/Prime/Interfaces/IPrime.sol\";\nimport { Action } from \"./ComptrollerInterface.sol\";\n\n/**\n * @title ComptrollerStorage\n * @author Venus\n * @notice Storage layout for the `Comptroller` contract.\n */\ncontract ComptrollerStorage {\n struct LiquidationOrder {\n VToken vTokenCollateral;\n VToken vTokenBorrowed;\n uint256 repayAmount;\n }\n\n struct AccountLiquiditySnapshot {\n uint256 totalCollateral;\n uint256 weightedCollateral;\n uint256 borrows;\n uint256 effects;\n uint256 liquidity;\n uint256 shortfall;\n }\n\n struct RewardSpeeds {\n address rewardToken;\n uint256 supplySpeed;\n uint256 borrowSpeed;\n }\n\n struct Market {\n // Whether or not this market is listed\n bool isListed;\n // Multiplier representing the most one can borrow against their collateral in this market.\n // For instance, 0.9 to allow borrowing 90% of collateral value.\n // Must be between 0 and 1, and stored as a mantissa.\n uint256 collateralFactorMantissa;\n // Multiplier representing the collateralization after which the borrow is eligible\n // for liquidation. For instance, 0.8 liquidate when the borrow is 80% of collateral\n // value. Must be between 0 and collateral factor, stored as a mantissa.\n uint256 liquidationThresholdMantissa;\n // Per-market mapping of \"accounts in this asset\"\n mapping(address => bool) accountMembership;\n }\n\n /**\n * @notice Oracle which gives the price of any given asset\n */\n ResilientOracleInterface public oracle;\n\n /**\n * @notice Multiplier used to calculate the maximum repayAmount when liquidating a borrow\n */\n uint256 public closeFactorMantissa;\n\n /**\n * @notice Multiplier representing the discount on collateral that a liquidator receives\n */\n uint256 public liquidationIncentiveMantissa;\n\n /**\n * @notice Per-account mapping of \"assets you are in\"\n */\n mapping(address => VToken[]) public accountAssets;\n\n /**\n * @notice Official mapping of vTokens -> Market metadata\n * @dev Used e.g. to determine if a market is supported\n */\n mapping(address => Market) public markets;\n\n /// @notice A list of all markets\n VToken[] public allMarkets;\n\n /// @notice Borrow caps enforced by borrowAllowed for each vToken address. Defaults to zero which restricts borrowing.\n mapping(address => uint256) public borrowCaps;\n\n /// @notice Minimal collateral required for regular (non-batch) liquidations\n uint256 public minLiquidatableCollateral;\n\n /// @notice Supply caps enforced by mintAllowed for each vToken address. Defaults to zero which corresponds to minting not allowed\n mapping(address => uint256) public supplyCaps;\n\n /// @notice True if a certain action is paused on a certain market\n mapping(address => mapping(Action => bool)) internal _actionPaused;\n\n // List of Reward Distributors added\n RewardsDistributor[] internal rewardsDistributors;\n\n // Used to check if rewards distributor is added\n mapping(address => bool) internal rewardsDistributorExists;\n\n /// @notice Flag indicating whether forced liquidation enabled for a market\n mapping(address => bool) public isForcedLiquidationEnabled;\n\n uint256 internal constant NO_ERROR = 0;\n\n // closeFactorMantissa must be strictly greater than this value\n uint256 internal constant MIN_CLOSE_FACTOR_MANTISSA = 0.05e18; // 0.05\n\n // closeFactorMantissa must not exceed this value\n uint256 internal constant MAX_CLOSE_FACTOR_MANTISSA = 0.9e18; // 0.9\n\n // No collateralFactorMantissa may exceed this value\n uint256 internal constant MAX_COLLATERAL_FACTOR_MANTISSA = 0.95e18; // 0.95\n\n /// Prime token address\n IPrime public prime;\n\n /// @notice Whether the delegate is allowed to borrow or redeem on behalf of the user\n //mapping(address user => mapping (address delegate => bool approved)) public approvedDelegates;\n mapping(address => mapping(address => bool)) public approvedDelegates;\n\n /**\n * @dev This empty reserved space is put in place to allow future versions to add new\n * variables without shifting down storage in the inheritance chain.\n * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\n */\n uint256[47] private __gap;\n}\n" + }, + "contracts/ErrorReporter.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\n\n/**\n * @title TokenErrorReporter\n * @author Venus\n * @notice Errors that can be thrown by the `VToken` contract.\n */\ncontract TokenErrorReporter {\n uint256 public constant NO_ERROR = 0; // support legacy return codes\n\n error TransferNotAllowed();\n\n error MintFreshnessCheck();\n\n error RedeemFreshnessCheck();\n error RedeemTransferOutNotPossible();\n\n error BorrowFreshnessCheck();\n error BorrowCashNotAvailable();\n error DelegateNotApproved();\n\n error RepayBorrowFreshnessCheck();\n\n error HealBorrowUnauthorized();\n error ForceLiquidateBorrowUnauthorized();\n\n error LiquidateFreshnessCheck();\n error LiquidateCollateralFreshnessCheck();\n error LiquidateAccrueCollateralInterestFailed(uint256 errorCode);\n error LiquidateLiquidatorIsBorrower();\n error LiquidateCloseAmountIsZero();\n error LiquidateCloseAmountIsUintMax();\n\n error LiquidateSeizeLiquidatorIsBorrower();\n\n error ProtocolSeizeShareTooBig();\n\n error SetReserveFactorFreshCheck();\n error SetReserveFactorBoundsCheck();\n\n error AddReservesFactorFreshCheck(uint256 actualAddAmount);\n\n error ReduceReservesFreshCheck();\n error ReduceReservesCashNotAvailable();\n error ReduceReservesCashValidation();\n\n error SetInterestRateModelFreshCheck();\n}\n" + }, + "contracts/ExponentialNoError.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\n\nimport { EXP_SCALE as EXP_SCALE_, MANTISSA_ONE as MANTISSA_ONE_ } from \"./lib/constants.sol\";\n\n/**\n * @title Exponential module for storing fixed-precision decimals\n * @author Compound\n * @notice Exp is a struct which stores decimals with a fixed precision of 18 decimal places.\n * Thus, if we wanted to store the 5.1, mantissa would store 5.1e18. That is:\n * `Exp({mantissa: 5100000000000000000})`.\n */\ncontract ExponentialNoError {\n struct Exp {\n uint256 mantissa;\n }\n\n struct Double {\n uint256 mantissa;\n }\n\n uint256 internal constant EXP_SCALE = EXP_SCALE_;\n uint256 internal constant DOUBLE_SCALE = 1e36;\n uint256 internal constant HALF_EXP_SCALE = EXP_SCALE / 2;\n uint256 internal constant MANTISSA_ONE = MANTISSA_ONE_;\n\n /**\n * @dev Truncates the given exp to a whole number value.\n * For example, truncate(Exp{mantissa: 15 * EXP_SCALE}) = 15\n */\n function truncate(Exp memory exp) internal pure returns (uint256) {\n // Note: We are not using careful math here as we're performing a division that cannot fail\n return exp.mantissa / EXP_SCALE;\n }\n\n /**\n * @dev Multiply an Exp by a scalar, then truncate to return an unsigned integer.\n */\n // solhint-disable-next-line func-name-mixedcase\n function mul_ScalarTruncate(Exp memory a, uint256 scalar) internal pure returns (uint256) {\n Exp memory product = mul_(a, scalar);\n return truncate(product);\n }\n\n /**\n * @dev Multiply an Exp by a scalar, truncate, then add an to an unsigned integer, returning an unsigned integer.\n */\n // solhint-disable-next-line func-name-mixedcase\n function mul_ScalarTruncateAddUInt(Exp memory a, uint256 scalar, uint256 addend) internal pure returns (uint256) {\n Exp memory product = mul_(a, scalar);\n return add_(truncate(product), addend);\n }\n\n /**\n * @dev Checks if first Exp is less than second Exp.\n */\n function lessThanExp(Exp memory left, Exp memory right) internal pure returns (bool) {\n return left.mantissa < right.mantissa;\n }\n\n function safe224(uint256 n, string memory errorMessage) internal pure returns (uint224) {\n require(n <= type(uint224).max, errorMessage);\n return uint224(n);\n }\n\n function safe32(uint256 n, string memory errorMessage) internal pure returns (uint32) {\n require(n <= type(uint32).max, errorMessage);\n return uint32(n);\n }\n\n function add_(Exp memory a, Exp memory b) internal pure returns (Exp memory) {\n return Exp({ mantissa: add_(a.mantissa, b.mantissa) });\n }\n\n function add_(Double memory a, Double memory b) internal pure returns (Double memory) {\n return Double({ mantissa: add_(a.mantissa, b.mantissa) });\n }\n\n function add_(uint256 a, uint256 b) internal pure returns (uint256) {\n return a + b;\n }\n\n function sub_(Exp memory a, Exp memory b) internal pure returns (Exp memory) {\n return Exp({ mantissa: sub_(a.mantissa, b.mantissa) });\n }\n\n function sub_(Double memory a, Double memory b) internal pure returns (Double memory) {\n return Double({ mantissa: sub_(a.mantissa, b.mantissa) });\n }\n\n function sub_(uint256 a, uint256 b) internal pure returns (uint256) {\n return a - b;\n }\n\n function mul_(Exp memory a, Exp memory b) internal pure returns (Exp memory) {\n return Exp({ mantissa: mul_(a.mantissa, b.mantissa) / EXP_SCALE });\n }\n\n function mul_(Exp memory a, uint256 b) internal pure returns (Exp memory) {\n return Exp({ mantissa: mul_(a.mantissa, b) });\n }\n\n function mul_(uint256 a, Exp memory b) internal pure returns (uint256) {\n return mul_(a, b.mantissa) / EXP_SCALE;\n }\n\n function mul_(Double memory a, Double memory b) internal pure returns (Double memory) {\n return Double({ mantissa: mul_(a.mantissa, b.mantissa) / DOUBLE_SCALE });\n }\n\n function mul_(Double memory a, uint256 b) internal pure returns (Double memory) {\n return Double({ mantissa: mul_(a.mantissa, b) });\n }\n\n function mul_(uint256 a, Double memory b) internal pure returns (uint256) {\n return mul_(a, b.mantissa) / DOUBLE_SCALE;\n }\n\n function mul_(uint256 a, uint256 b) internal pure returns (uint256) {\n return a * b;\n }\n\n function div_(Exp memory a, Exp memory b) internal pure returns (Exp memory) {\n return Exp({ mantissa: div_(mul_(a.mantissa, EXP_SCALE), b.mantissa) });\n }\n\n function div_(Exp memory a, uint256 b) internal pure returns (Exp memory) {\n return Exp({ mantissa: div_(a.mantissa, b) });\n }\n\n function div_(uint256 a, Exp memory b) internal pure returns (uint256) {\n return div_(mul_(a, EXP_SCALE), b.mantissa);\n }\n\n function div_(Double memory a, Double memory b) internal pure returns (Double memory) {\n return Double({ mantissa: div_(mul_(a.mantissa, DOUBLE_SCALE), b.mantissa) });\n }\n\n function div_(Double memory a, uint256 b) internal pure returns (Double memory) {\n return Double({ mantissa: div_(a.mantissa, b) });\n }\n\n function div_(uint256 a, Double memory b) internal pure returns (uint256) {\n return div_(mul_(a, DOUBLE_SCALE), b.mantissa);\n }\n\n function div_(uint256 a, uint256 b) internal pure returns (uint256) {\n return a / b;\n }\n\n function fraction(uint256 a, uint256 b) internal pure returns (Double memory) {\n return Double({ mantissa: div_(mul_(a, DOUBLE_SCALE), b) });\n }\n}\n" + }, + "contracts/InterestRateModel.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\n\n/**\n * @title Compound's InterestRateModel Interface\n * @author Compound\n */\nabstract contract InterestRateModel {\n /**\n * @notice Calculates the current borrow interest rate per slot (block or second)\n * @param cash The total amount of cash the market has\n * @param borrows The total amount of borrows the market has outstanding\n * @param reserves The total amount of reserves the market has\n * @param badDebt The amount of badDebt in the market\n * @return The borrow rate percentage per slot (block or second) as a mantissa (scaled by EXP_SCALE)\n */\n function getBorrowRate(\n uint256 cash,\n uint256 borrows,\n uint256 reserves,\n uint256 badDebt\n ) external view virtual returns (uint256);\n\n /**\n * @notice Calculates the current supply interest rate per slot (block or second)\n * @param cash The total amount of cash the market has\n * @param borrows The total amount of borrows the market has outstanding\n * @param reserves The total amount of reserves the market has\n * @param reserveFactorMantissa The current reserve factor the market has\n * @param badDebt The amount of badDebt in the market\n * @return The supply rate percentage per slot (block or second) as a mantissa (scaled by EXP_SCALE)\n */\n function getSupplyRate(\n uint256 cash,\n uint256 borrows,\n uint256 reserves,\n uint256 reserveFactorMantissa,\n uint256 badDebt\n ) external view virtual returns (uint256);\n\n /**\n * @notice Indicator that this is an InterestRateModel contract (for inspection)\n * @return Always true\n */\n function isInterestRateModel() external pure virtual returns (bool) {\n return true;\n }\n}\n" + }, + "contracts/Lens/SpokePoolLens.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IERC20Metadata } from \"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol\";\nimport { ResilientOracleInterface } from \"@venusprotocol/oracle/contracts/interfaces/OracleInterface.sol\";\nimport { TimeManagerV8 } from \"@venusprotocol/solidity-utilities/contracts/TimeManagerV8.sol\";\n\nimport { ExponentialNoError } from \"../ExponentialNoError.sol\";\nimport { VToken } from \"../VToken.sol\";\nimport { Action, ComptrollerInterface, ComptrollerViewInterface } from \"../ComptrollerInterface.sol\";\nimport { PoolRegistryInterface } from \"../Pool/PoolRegistryInterface.sol\";\nimport { PoolRegistry } from \"../Pool/PoolRegistry.sol\";\nimport { RewardsDistributor } from \"../Rewards/RewardsDistributor.sol\";\nimport { SpokeComptrollerViewInterface } from \"../Spoke/SpokeComptrollerInterface.sol\";\n\n/**\n * @title SpokePoolLens\n * @author Venus\n * @notice Reads pool and market state specific to a spoke pool\n *\n * @dev Spoke pools expose additional state that is not included in the shared PoolLens data, including the\n * deviation-bounded oracle, the supply and liquidation allowlists, liquidation thresholds and liquidation incentives.\n * Those reads are named `spoke*` or `getSpokePool*` and return spoke-shaped structs.\n *\n * The rest of the surface mirrors `PoolLens` under the same names and struct shapes, so that reading a spoke pool\n * takes one address rather than two. Those reads go through getters both kinds of pool share.\n *\n * Access-controlled call permissions are deliberately not reported: `AccessControlManager` derives the role from its\n * own caller, so asked from a lens it answers `false` for an account that can in fact call.\n *\n * This contract holds no state and is versioned by redeployment.\n */\ncontract SpokePoolLens is ExponentialNoError, TimeManagerV8 {\n /**\n * @dev Mirrors `PoolLens.PoolData` with spoke-pool-specific fields.\n */\n struct SpokePoolData {\n string name;\n address creator;\n address comptroller;\n uint256 blockPosted;\n uint256 timestampPosted;\n string category;\n string logoURL;\n string description;\n address priceOracle;\n uint256 closeFactor;\n /// @notice The pool-wide liquidation incentive, scaled by 1e18\n uint256 poolLiquidationIncentiveMantissa;\n uint256 minLiquidatableCollateral;\n /// @notice Oracle used to bound collateral prices for borrowing and redeeming. Zero until governance sets it,\n /// and while it is zero both actions revert\n address deviationBoundedOracle;\n /// @notice Whether liquidation is restricted to allowlisted accounts\n bool liquidationAllowlistEnabled;\n SpokeVTokenMetadata[] vTokens;\n }\n\n /**\n * @dev Mirrors `PoolLens.VTokenMetadata` with spoke-pool-specific fields.\n */\n struct SpokeVTokenMetadata {\n address vToken;\n uint256 exchangeRateCurrent;\n uint256 supplyRatePerBlockOrTimestamp;\n uint256 borrowRatePerBlockOrTimestamp;\n uint256 reserveFactorMantissa;\n uint256 supplyCaps;\n uint256 borrowCaps;\n uint256 totalBorrows;\n uint256 totalReserves;\n uint256 totalSupply;\n uint256 totalCash;\n bool isListed;\n uint256 collateralFactorMantissa;\n /// @notice The collateral weight above which a position becomes liquidatable, scaled by 1e18\n uint256 liquidationThresholdMantissa;\n address underlyingAssetAddress;\n uint256 vTokenDecimals;\n uint256 underlyingDecimals;\n uint256 pausedActions;\n /// @notice The liquidation incentive effective for this market, scaled by 1e18\n uint256 effectiveLiquidationIncentiveMantissa;\n /// @notice The market-specific liquidation incentive, or zero when using the pool-wide value\n uint256 ownLiquidationIncentiveMantissa;\n /// @notice Whether supply is restricted to allowlisted accounts\n bool supplyAllowlistEnabled;\n /// @notice Whether the market allows full liquidation without a shortfall\n bool forcedLiquidationEnabled;\n }\n\n /**\n * @dev Returns both the market's allowlist status and whether the account is allowlisted.\n */\n struct SpokeSupplyPermission {\n address vToken;\n /// @notice Whether supply is restricted to allowlisted accounts\n bool allowlistEnabled;\n /// @notice Whether the account is allowlisted for the market\n bool accountAllowlisted;\n }\n\n /**\n * @dev Struct for VTokenBalance.\n */\n struct VTokenBalances {\n address vToken;\n uint256 balanceOf;\n uint256 borrowBalanceCurrent;\n uint256 balanceOfUnderlying;\n uint256 tokenBalance;\n uint256 tokenAllowance;\n }\n\n /**\n * @dev Struct for underlyingPrice of VToken.\n */\n struct VTokenUnderlyingPrice {\n address vToken;\n uint256 underlyingPrice;\n }\n\n /**\n * @dev Struct with pending reward info for a market.\n */\n struct PendingReward {\n address vTokenAddress;\n uint256 amount;\n }\n\n /**\n * @dev Struct with reward distribution totals for a single reward token and distributor.\n */\n struct RewardSummary {\n address distributorAddress;\n address rewardTokenAddress;\n uint256 totalRewards;\n PendingReward[] pendingRewards;\n }\n\n /**\n * @dev Struct used in RewardDistributor to save last updated market state.\n */\n struct RewardTokenState {\n // The market's last updated rewardTokenBorrowIndex or rewardTokenSupplyIndex\n uint224 index;\n // The block number or timestamp the index was last updated at\n uint256 blockOrTimestamp;\n // The block number or timestamp at which to stop rewards\n uint256 lastRewardingBlockOrTimestamp;\n }\n\n /**\n * @dev Struct with bad debt of a market denominated\n */\n struct BadDebt {\n address vTokenAddress;\n uint256 badDebtUsd;\n }\n\n /**\n * @dev Struct with bad debt total denominated in usd for a pool and an array of BadDebt structs for each market\n */\n struct BadDebtSummary {\n address comptroller;\n uint256 totalBadDebtUsd;\n BadDebt[] badDebts;\n }\n\n /**\n * @param timeBased_ A boolean indicating whether the contract is based on time or block\n * @param blocksPerYear_ The number of blocks per year\n * @custom:oz-upgrades-unsafe-allow constructor\n */\n constructor(bool timeBased_, uint256 blocksPerYear_) TimeManagerV8(timeBased_, blocksPerYear_) {}\n\n /**\n * @notice Queries the user's supply/borrow balances in vTokens\n * @param vTokens The list of vToken addresses\n * @param account The user Account\n * @return A list of structs containing balances data\n */\n function vTokenBalancesAll(VToken[] calldata vTokens, address account) external returns (VTokenBalances[] memory) {\n uint256 vTokenCount = vTokens.length;\n VTokenBalances[] memory res = new VTokenBalances[](vTokenCount);\n for (uint256 i; i < vTokenCount; ++i) {\n res[i] = vTokenBalances(vTokens[i], account);\n }\n return res;\n }\n\n /**\n * @notice Queries every pool and market in a spoke pool registry\n * @dev Not intended to be called in a transaction due to its gas cost. The registry must contain only spoke pools.\n * @param poolRegistryAddress The registry to enumerate\n * @return The data of every pool in the registry\n */\n function getAllSpokePools(address poolRegistryAddress) external view returns (SpokePoolData[] memory) {\n PoolRegistry.VenusPool[] memory pools = PoolRegistryInterface(poolRegistryAddress).getAllPools();\n uint256 poolLength = pools.length;\n\n SpokePoolData[] memory poolDataItems = new SpokePoolData[](poolLength);\n\n for (uint256 i; i < poolLength; ++i) {\n poolDataItems[i] = getSpokePoolData(poolRegistryAddress, pools[i]);\n }\n\n return poolDataItems;\n }\n\n /**\n * @notice Queries a spoke pool by its comptroller address\n * @param poolRegistryAddress The registry containing the pool\n * @param comptroller The pool's comptroller\n * @return The pool's data\n */\n function getSpokePoolByComptroller(\n address poolRegistryAddress,\n address comptroller\n ) external view returns (SpokePoolData memory) {\n PoolRegistryInterface poolRegistry = PoolRegistryInterface(poolRegistryAddress);\n return getSpokePoolData(poolRegistryAddress, poolRegistry.getPoolByComptroller(comptroller));\n }\n\n /**\n * @notice Returns whether an account may supply to each specified market\n * @dev The account checked is the one receiving the minted vTokens, which can differ from the payer when using\n * `mintBehalf`.\n * @param comptroller The spoke pool's comptroller\n * @param vTokens The markets to test\n * @param account The account to test\n * @return One entry per market, in the order given\n */\n function spokeSupplyPermissions(\n address comptroller,\n VToken[] memory vTokens,\n address account\n ) external view returns (SpokeSupplyPermission[] memory) {\n SpokeComptrollerViewInterface spoke = SpokeComptrollerViewInterface(comptroller);\n uint256 len = vTokens.length;\n\n SpokeSupplyPermission[] memory permissions = new SpokeSupplyPermission[](len);\n\n for (uint256 i; i < len; ++i) {\n address vToken = address(vTokens[i]);\n permissions[i] = SpokeSupplyPermission({\n vToken: vToken,\n allowlistEnabled: spoke.isSupplyAllowlistEnabled(vToken),\n accountAllowlisted: spoke.isAllowedSupplier(vToken, account)\n });\n }\n\n return permissions;\n }\n\n /**\n * @notice Returns whether an account may seize collateral in a spoke pool\n * @param comptroller The spoke pool's comptroller\n * @param liquidator The account to test\n * @return allowlistEnabled Whether liquidation is restricted to allowlisted accounts\n * @return accountAllowlisted Whether the account is allowlisted\n */\n function spokeLiquidationPermission(\n address comptroller,\n address liquidator\n ) external view returns (bool allowlistEnabled, bool accountAllowlisted) {\n SpokeComptrollerViewInterface spoke = SpokeComptrollerViewInterface(comptroller);\n return (spoke.isLiquidationAllowlistEnabled(), spoke.isAllowedLiquidator(liquidator));\n }\n\n /**\n * @notice Returns the price data for the underlying assets of the specified vTokens\n * @param vTokens The list of vToken addresses\n * @return An array containing the price data for each asset\n */\n function vTokenUnderlyingPriceAll(\n VToken[] calldata vTokens\n ) external view returns (VTokenUnderlyingPrice[] memory) {\n uint256 vTokenCount = vTokens.length;\n VTokenUnderlyingPrice[] memory res = new VTokenUnderlyingPrice[](vTokenCount);\n for (uint256 i; i < vTokenCount; ++i) {\n res[i] = vTokenUnderlyingPrice(vTokens[i]);\n }\n return res;\n }\n\n /**\n * @notice Returns the pending rewards for a user for a given pool.\n * @param account The user account.\n * @param comptrollerAddress address\n * @return Pending rewards array\n */\n function getPendingRewards(\n address account,\n address comptrollerAddress\n ) external view returns (RewardSummary[] memory) {\n VToken[] memory markets = ComptrollerInterface(comptrollerAddress).getAllMarkets();\n RewardsDistributor[] memory rewardsDistributors = ComptrollerViewInterface(comptrollerAddress)\n .getRewardDistributors();\n RewardSummary[] memory rewardSummary = new RewardSummary[](rewardsDistributors.length);\n for (uint256 i; i < rewardsDistributors.length; ++i) {\n RewardSummary memory reward;\n reward.distributorAddress = address(rewardsDistributors[i]);\n reward.rewardTokenAddress = address(rewardsDistributors[i].rewardToken());\n reward.totalRewards = rewardsDistributors[i].rewardTokenAccrued(account);\n reward.pendingRewards = _calculateNotDistributedAwards(account, markets, rewardsDistributors[i]);\n rewardSummary[i] = reward;\n }\n return rewardSummary;\n }\n\n /**\n * @notice Returns a summary of a pool's bad debt broken down by market\n *\n * @param comptrollerAddress Address of the comptroller\n *\n * @return badDebtSummary A struct with comptroller address, total bad debut denominated in usd, and\n * a break down of bad debt by market\n */\n function getPoolBadDebt(address comptrollerAddress) external view returns (BadDebtSummary memory) {\n uint256 totalBadDebtUsd;\n\n // Get every market in the pool\n ComptrollerViewInterface comptroller = ComptrollerViewInterface(comptrollerAddress);\n VToken[] memory markets = comptroller.getAllMarkets();\n ResilientOracleInterface priceOracle = comptroller.oracle();\n\n BadDebt[] memory badDebts = new BadDebt[](markets.length);\n\n BadDebtSummary memory badDebtSummary;\n badDebtSummary.comptroller = comptrollerAddress;\n badDebtSummary.badDebts = badDebts;\n\n // // Calculate the bad debt is USD per market\n for (uint256 i; i < markets.length; ++i) {\n BadDebt memory badDebt;\n badDebt.vTokenAddress = address(markets[i]);\n badDebt.badDebtUsd =\n (VToken(address(markets[i])).badDebt() * priceOracle.getUnderlyingPrice(address(markets[i]))) /\n EXP_SCALE;\n badDebtSummary.badDebts[i] = badDebt;\n totalBadDebtUsd = totalBadDebtUsd + badDebt.badDebtUsd;\n }\n\n badDebtSummary.totalBadDebtUsd = totalBadDebtUsd;\n\n return badDebtSummary;\n }\n\n /**\n * @notice Returns vToken holding the specified underlying asset in the specified pool\n * @param poolRegistryAddress The address of the PoolRegistry contract\n * @param comptroller The pool comptroller\n * @param asset The underlyingAsset of VToken\n * @return Address of the vToken\n */\n function getVTokenForAsset(\n address poolRegistryAddress,\n address comptroller,\n address asset\n ) external view returns (address) {\n PoolRegistryInterface poolRegistryInterface = PoolRegistryInterface(poolRegistryAddress);\n return poolRegistryInterface.getVTokenForAsset(comptroller, asset);\n }\n\n /**\n * @notice Returns all pools that support the specified underlying asset\n * @param poolRegistryAddress The address of the PoolRegistry contract\n * @param asset The underlying asset of vToken\n * @return A list of Comptroller contracts\n */\n function getPoolsSupportedByAsset(\n address poolRegistryAddress,\n address asset\n ) external view returns (address[] memory) {\n PoolRegistryInterface poolRegistryInterface = PoolRegistryInterface(poolRegistryAddress);\n return poolRegistryInterface.getPoolsSupportedByAsset(asset);\n }\n\n /**\n * @notice Queries the user's supply/borrow balances in the specified vToken\n * @param vToken vToken address\n * @param account The user Account\n * @return A struct containing the balances data\n */\n function vTokenBalances(VToken vToken, address account) public returns (VTokenBalances memory) {\n uint256 balanceOf = vToken.balanceOf(account);\n uint256 borrowBalanceCurrent = vToken.borrowBalanceCurrent(account);\n uint256 balanceOfUnderlying = vToken.balanceOfUnderlying(account);\n uint256 tokenBalance;\n uint256 tokenAllowance;\n\n IERC20 underlying = IERC20(vToken.underlying());\n tokenBalance = underlying.balanceOf(account);\n tokenAllowance = underlying.allowance(account, address(vToken));\n\n return\n VTokenBalances({\n vToken: address(vToken),\n balanceOf: balanceOf,\n borrowBalanceCurrent: borrowBalanceCurrent,\n balanceOfUnderlying: balanceOfUnderlying,\n tokenBalance: tokenBalance,\n tokenAllowance: tokenAllowance\n });\n }\n\n /**\n * @notice Queries a spoke pool from its registry entry\n * @param poolRegistryAddress The registry containing the pool\n * @param venusPool The pool's registry entry\n * @return The pool's data, including its markets\n */\n function getSpokePoolData(\n address poolRegistryAddress,\n PoolRegistry.VenusPool memory venusPool\n ) public view returns (SpokePoolData memory) {\n address comptroller = venusPool.comptroller;\n\n SpokeVTokenMetadata[] memory vTokenMetadataItems = spokeVTokenMetadataAll(\n ComptrollerInterface(comptroller).getAllMarkets()\n );\n\n PoolRegistry.VenusPoolMetaData memory metaData = PoolRegistryInterface(poolRegistryAddress)\n .getVenusPoolMetadata(comptroller);\n\n ComptrollerViewInterface pooledView = ComptrollerViewInterface(comptroller);\n SpokeComptrollerViewInterface spokeView = SpokeComptrollerViewInterface(comptroller);\n\n SpokePoolData memory poolData;\n poolData.name = venusPool.name;\n poolData.creator = venusPool.creator;\n poolData.comptroller = comptroller;\n poolData.blockPosted = venusPool.blockPosted;\n poolData.timestampPosted = venusPool.timestampPosted;\n poolData.category = metaData.category;\n poolData.logoURL = metaData.logoURL;\n poolData.description = metaData.description;\n poolData.priceOracle = address(pooledView.oracle());\n poolData.closeFactor = pooledView.closeFactorMantissa();\n // This call is made from the lens, so the comptroller returns the pool-wide incentive.\n poolData.poolLiquidationIncentiveMantissa = pooledView.liquidationIncentiveMantissa();\n poolData.minLiquidatableCollateral = pooledView.minLiquidatableCollateral();\n poolData.deviationBoundedOracle = address(spokeView.deviationBoundedOracle());\n poolData.liquidationAllowlistEnabled = spokeView.isLiquidationAllowlistEnabled();\n poolData.vTokens = vTokenMetadataItems;\n\n return poolData;\n }\n\n /**\n * @notice Queries the metadata of every given market of a spoke pool\n * @param vTokens The markets to read\n * @return One entry per market, in the order given\n */\n function spokeVTokenMetadataAll(VToken[] memory vTokens) public view returns (SpokeVTokenMetadata[] memory) {\n uint256 len = vTokens.length;\n\n SpokeVTokenMetadata[] memory metadataItems = new SpokeVTokenMetadata[](len);\n\n for (uint256 i; i < len; ++i) {\n metadataItems[i] = spokeVTokenMetadata(vTokens[i]);\n }\n\n return metadataItems;\n }\n\n /**\n * @notice Queries the metadata of one market of a spoke pool\n * @param vToken The market to read\n * @return The market's metadata\n */\n function spokeVTokenMetadata(VToken vToken) public view returns (SpokeVTokenMetadata memory) {\n address vTokenAddress = address(vToken);\n address comptroller = address(vToken.comptroller());\n\n ComptrollerViewInterface pooledView = ComptrollerViewInterface(comptroller);\n SpokeComptrollerViewInterface spokeView = SpokeComptrollerViewInterface(comptroller);\n\n // Read through the spoke interface, which declares all three returns. `ComptrollerViewInterface` declares\n // the first two, so reading it there drops the liquidation threshold with no error.\n (bool isListed, uint256 collateralFactorMantissa, uint256 liquidationThresholdMantissa) = spokeView.markets(\n vTokenAddress\n );\n\n address underlying = vToken.underlying();\n\n return\n SpokeVTokenMetadata({\n vToken: vTokenAddress,\n exchangeRateCurrent: vToken.exchangeRateStored(),\n // Zero for an empty market, as `PoolLens` reports: the rate model divides by the market's supply.\n supplyRatePerBlockOrTimestamp: vToken.totalSupply() > 0 ? vToken.supplyRatePerBlock() : 0,\n borrowRatePerBlockOrTimestamp: vToken.borrowRatePerBlock(),\n reserveFactorMantissa: vToken.reserveFactorMantissa(),\n supplyCaps: pooledView.supplyCaps(vTokenAddress),\n borrowCaps: pooledView.borrowCaps(vTokenAddress),\n totalBorrows: vToken.totalBorrows(),\n totalReserves: vToken.totalReserves(),\n totalSupply: vToken.totalSupply(),\n totalCash: vToken.getCash(),\n isListed: isListed,\n collateralFactorMantissa: collateralFactorMantissa,\n liquidationThresholdMantissa: liquidationThresholdMantissa,\n underlyingAssetAddress: underlying,\n vTokenDecimals: vToken.decimals(),\n underlyingDecimals: IERC20Metadata(underlying).decimals(),\n pausedActions: _pausedActions(comptroller, vTokenAddress),\n effectiveLiquidationIncentiveMantissa: spokeView.effectiveLiquidationIncentive(vTokenAddress),\n ownLiquidationIncentiveMantissa: spokeView.liquidationIncentives(vTokenAddress),\n supplyAllowlistEnabled: spokeView.isSupplyAllowlistEnabled(vTokenAddress),\n forcedLiquidationEnabled: spokeView.isForcedLiquidationEnabled(vTokenAddress)\n });\n }\n\n /**\n * @notice Returns the price data for the underlying asset of the specified vToken\n * @param vToken vToken address\n * @return The price data for each asset\n */\n function vTokenUnderlyingPrice(VToken vToken) public view returns (VTokenUnderlyingPrice memory) {\n ComptrollerViewInterface comptroller = ComptrollerViewInterface(address(vToken.comptroller()));\n ResilientOracleInterface priceOracle = comptroller.oracle();\n\n return\n VTokenUnderlyingPrice({\n vToken: address(vToken),\n underlyingPrice: priceOracle.getUnderlyingPrice(address(vToken))\n });\n }\n\n function _calculateNotDistributedAwards(\n address account,\n VToken[] memory markets,\n RewardsDistributor rewardsDistributor\n ) internal view returns (PendingReward[] memory) {\n PendingReward[] memory pendingRewards = new PendingReward[](markets.length);\n\n for (uint256 i; i < markets.length; ++i) {\n // Market borrow and supply state we will modify update in-memory, in order to not modify storage\n RewardTokenState memory borrowState;\n RewardTokenState memory supplyState;\n\n if (isTimeBased) {\n (\n borrowState.index,\n borrowState.blockOrTimestamp,\n borrowState.lastRewardingBlockOrTimestamp\n ) = rewardsDistributor.rewardTokenBorrowStateTimeBased(address(markets[i]));\n (\n supplyState.index,\n supplyState.blockOrTimestamp,\n supplyState.lastRewardingBlockOrTimestamp\n ) = rewardsDistributor.rewardTokenSupplyStateTimeBased(address(markets[i]));\n } else {\n (\n borrowState.index,\n borrowState.blockOrTimestamp,\n borrowState.lastRewardingBlockOrTimestamp\n ) = rewardsDistributor.rewardTokenBorrowState(address(markets[i]));\n (\n supplyState.index,\n supplyState.blockOrTimestamp,\n supplyState.lastRewardingBlockOrTimestamp\n ) = rewardsDistributor.rewardTokenSupplyState(address(markets[i]));\n }\n\n Exp memory marketBorrowIndex = Exp({ mantissa: markets[i].borrowIndex() });\n\n // Update market supply and borrow index in-memory\n updateMarketBorrowIndex(address(markets[i]), rewardsDistributor, borrowState, marketBorrowIndex);\n updateMarketSupplyIndex(address(markets[i]), rewardsDistributor, supplyState);\n\n // Calculate pending rewards\n uint256 borrowReward = calculateBorrowerReward(\n address(markets[i]),\n rewardsDistributor,\n account,\n borrowState,\n marketBorrowIndex\n );\n uint256 supplyReward = calculateSupplierReward(\n address(markets[i]),\n rewardsDistributor,\n account,\n supplyState\n );\n\n PendingReward memory pendingReward;\n pendingReward.vTokenAddress = address(markets[i]);\n pendingReward.amount = borrowReward + supplyReward;\n pendingRewards[i] = pendingReward;\n }\n return pendingRewards;\n }\n\n function updateMarketBorrowIndex(\n address vToken,\n RewardsDistributor rewardsDistributor,\n RewardTokenState memory borrowState,\n Exp memory marketBorrowIndex\n ) internal view {\n uint256 borrowSpeed = rewardsDistributor.rewardTokenBorrowSpeeds(vToken);\n uint256 blockNumberOrTimestamp = getBlockNumberOrTimestamp();\n\n if (\n borrowState.lastRewardingBlockOrTimestamp > 0 &&\n blockNumberOrTimestamp > borrowState.lastRewardingBlockOrTimestamp\n ) {\n blockNumberOrTimestamp = borrowState.lastRewardingBlockOrTimestamp;\n }\n\n uint256 deltaBlocksOrTimestamp = sub_(blockNumberOrTimestamp, borrowState.blockOrTimestamp);\n if (deltaBlocksOrTimestamp > 0 && borrowSpeed > 0) {\n // Remove the total earned interest rate since the opening of the market from total borrows\n uint256 borrowAmount = div_(VToken(vToken).totalBorrows(), marketBorrowIndex);\n uint256 tokensAccrued = mul_(deltaBlocksOrTimestamp, borrowSpeed);\n Double memory ratio = borrowAmount > 0 ? fraction(tokensAccrued, borrowAmount) : Double({ mantissa: 0 });\n Double memory index = add_(Double({ mantissa: borrowState.index }), ratio);\n borrowState.index = safe224(index.mantissa, \"new index overflows\");\n borrowState.blockOrTimestamp = blockNumberOrTimestamp;\n } else if (deltaBlocksOrTimestamp > 0) {\n borrowState.blockOrTimestamp = blockNumberOrTimestamp;\n }\n }\n\n function updateMarketSupplyIndex(\n address vToken,\n RewardsDistributor rewardsDistributor,\n RewardTokenState memory supplyState\n ) internal view {\n uint256 supplySpeed = rewardsDistributor.rewardTokenSupplySpeeds(vToken);\n uint256 blockNumberOrTimestamp = getBlockNumberOrTimestamp();\n\n if (\n supplyState.lastRewardingBlockOrTimestamp > 0 &&\n blockNumberOrTimestamp > supplyState.lastRewardingBlockOrTimestamp\n ) {\n blockNumberOrTimestamp = supplyState.lastRewardingBlockOrTimestamp;\n }\n\n uint256 deltaBlocksOrTimestamp = sub_(blockNumberOrTimestamp, supplyState.blockOrTimestamp);\n if (deltaBlocksOrTimestamp > 0 && supplySpeed > 0) {\n uint256 supplyTokens = VToken(vToken).totalSupply();\n uint256 tokensAccrued = mul_(deltaBlocksOrTimestamp, supplySpeed);\n Double memory ratio = supplyTokens > 0 ? fraction(tokensAccrued, supplyTokens) : Double({ mantissa: 0 });\n Double memory index = add_(Double({ mantissa: supplyState.index }), ratio);\n supplyState.index = safe224(index.mantissa, \"new index overflows\");\n supplyState.blockOrTimestamp = blockNumberOrTimestamp;\n } else if (deltaBlocksOrTimestamp > 0) {\n supplyState.blockOrTimestamp = blockNumberOrTimestamp;\n }\n }\n\n function calculateBorrowerReward(\n address vToken,\n RewardsDistributor rewardsDistributor,\n address borrower,\n RewardTokenState memory borrowState,\n Exp memory marketBorrowIndex\n ) internal view returns (uint256) {\n Double memory borrowIndex = Double({ mantissa: borrowState.index });\n Double memory borrowerIndex = Double({\n mantissa: rewardsDistributor.rewardTokenBorrowerIndex(vToken, borrower)\n });\n if (borrowerIndex.mantissa == 0 && borrowIndex.mantissa >= rewardsDistributor.INITIAL_INDEX()) {\n // Covers the case where users borrowed tokens before the market's borrow state index was set\n borrowerIndex.mantissa = rewardsDistributor.INITIAL_INDEX();\n }\n Double memory deltaIndex = sub_(borrowIndex, borrowerIndex);\n uint256 borrowerAmount = div_(VToken(vToken).borrowBalanceStored(borrower), marketBorrowIndex);\n uint256 borrowerDelta = mul_(borrowerAmount, deltaIndex);\n return borrowerDelta;\n }\n\n function calculateSupplierReward(\n address vToken,\n RewardsDistributor rewardsDistributor,\n address supplier,\n RewardTokenState memory supplyState\n ) internal view returns (uint256) {\n Double memory supplyIndex = Double({ mantissa: supplyState.index });\n Double memory supplierIndex = Double({\n mantissa: rewardsDistributor.rewardTokenSupplierIndex(vToken, supplier)\n });\n if (supplierIndex.mantissa == 0 && supplyIndex.mantissa >= rewardsDistributor.INITIAL_INDEX()) {\n // Covers the case where users supplied tokens before the market's supply state index was set\n supplierIndex.mantissa = rewardsDistributor.INITIAL_INDEX();\n }\n Double memory deltaIndex = sub_(supplyIndex, supplierIndex);\n uint256 supplierTokens = VToken(vToken).balanceOf(supplier);\n uint256 supplierDelta = mul_(supplierTokens, deltaIndex);\n return supplierDelta;\n }\n\n /**\n * @dev Encodes paused actions using the same bit positions as `PoolLens`.\n * @param comptroller The market's comptroller\n * @param vToken The market to read\n * @return A bitmask of the paused actions\n */\n function _pausedActions(address comptroller, address vToken) private view returns (uint256) {\n uint256 pausedActions;\n\n for (uint8 i; i <= uint8(type(Action).max); ++i) {\n uint256 paused = ComptrollerInterface(comptroller).actionPaused(vToken, Action(i)) ? 1 : 0;\n pausedActions |= paused << i;\n }\n\n return pausedActions;\n }\n}\n" + }, + "contracts/lib/constants.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity ^0.8.25;\n\n/// @dev The approximate number of seconds per year\nuint256 constant SECONDS_PER_YEAR = 31_536_000;\n\n/// @dev Base unit for computations, usually used in scaling (multiplications, divisions)\nuint256 constant EXP_SCALE = 1e18;\n\n/// @dev A unit (literal one) in EXP_SCALE, usually used in additions/subtractions\nuint256 constant MANTISSA_ONE = EXP_SCALE;\n" + }, + "contracts/lib/validators.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\n\n/// @notice Thrown if the supplied address is a zero address where it is not allowed\nerror ZeroAddressNotAllowed();\n\n/// @notice Checks if the provided address is nonzero, reverts otherwise\n/// @param address_ Address to check\n/// @custom:error ZeroAddressNotAllowed is thrown if the provided address is a zero address\nfunction ensureNonzeroAddress(address address_) pure {\n if (address_ == address(0)) {\n revert ZeroAddressNotAllowed();\n }\n}\n" + }, + "contracts/MaxLoopsLimitHelper.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\n\n/**\n * @title MaxLoopsLimitHelper\n * @author Venus\n * @notice Abstract contract used to avoid collection with too many items that would generate gas errors and DoS.\n */\nabstract contract MaxLoopsLimitHelper {\n // Limit for the loops to avoid the DOS\n uint256 public maxLoopsLimit;\n\n /**\n * @dev This empty reserved space is put in place to allow future versions to add new\n * variables without shifting down storage in the inheritance chain.\n * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\n */\n uint256[49] private __gap;\n\n /// @notice Emitted when max loops limit is set\n event MaxLoopsLimitUpdated(uint256 oldMaxLoopsLimit, uint256 newmaxLoopsLimit);\n\n /// @notice Thrown an error on maxLoopsLimit exceeds for any loop\n error MaxLoopsLimitExceeded(uint256 loopsLimit, uint256 requiredLoops);\n\n /**\n * @notice Set the limit for the loops can iterate to avoid the DOS\n * @param limit Limit for the max loops can execute at a time\n */\n function _setMaxLoopsLimit(uint256 limit) internal {\n require(limit > maxLoopsLimit, \"Comptroller: Invalid maxLoopsLimit\");\n\n uint256 oldMaxLoopsLimit = maxLoopsLimit;\n maxLoopsLimit = limit;\n\n emit MaxLoopsLimitUpdated(oldMaxLoopsLimit, limit);\n }\n\n /**\n * @notice Compare the maxLoopsLimit with number of the times loop iterate\n * @param len Length of the loops iterate\n * @custom:error MaxLoopsLimitExceeded error is thrown when loops length exceeds maxLoopsLimit\n */\n function _ensureMaxLoops(uint256 len) internal view {\n if (len > maxLoopsLimit) {\n revert MaxLoopsLimitExceeded(maxLoopsLimit, len);\n }\n }\n}\n" + }, + "contracts/Pool/PoolRegistry.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\n\nimport { Ownable2StepUpgradeable } from \"@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol\";\nimport { SafeERC20Upgradeable } from \"@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol\";\nimport { IERC20Upgradeable } from \"@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol\";\nimport { AccessControlledV8 } from \"@venusprotocol/governance-contracts/contracts/Governance/AccessControlledV8.sol\";\n\nimport { PoolRegistryInterface } from \"./PoolRegistryInterface.sol\";\nimport { Comptroller } from \"../Comptroller.sol\";\nimport { VToken } from \"../VToken.sol\";\nimport { ensureNonzeroAddress } from \"../lib/validators.sol\";\n\n/**\n * @title PoolRegistry\n * @author Venus\n * @notice The Isolated Pools architecture centers around the `PoolRegistry` contract. The `PoolRegistry` maintains a directory of isolated lending\n * pools and can perform actions like creating and registering new pools, adding new markets to existing pools, setting and updating the pool's required\n * metadata, and providing the getter methods to get information on the pools.\n *\n * Isolated lending has three main components: PoolRegistry, pools, and markets. The PoolRegistry is responsible for managing pools.\n * It can create new pools, update pool metadata and manage markets within pools. PoolRegistry contains getter methods to get the details of\n * any existing pool like `getVTokenForAsset` and `getPoolsSupportedByAsset`. It also contains methods for updating pool metadata (`updatePoolMetadata`)\n * and setting pool name (`setPoolName`).\n *\n * The directory of pools is managed through two mappings: `_poolByComptroller` which is a hashmap with the comptroller address as the key and `VenusPool` as\n * the value and `_poolsByID` which is an array of comptroller addresses. Individual pools can be accessed by calling `getPoolByComptroller` with the pool's\n * comptroller address. `_poolsByID` is used to iterate through all of the pools.\n *\n * PoolRegistry also contains a map of asset addresses called `_supportedPools` that maps to an array of assets suppored by each pool. This array of pools by\n * asset is retrieved by calling `getPoolsSupportedByAsset`.\n *\n * PoolRegistry registers new isolated pools in the directory with the `createRegistryPool` method. Isolated pools are composed of independent markets with\n * specific assets and custom risk management configurations according to their markets.\n */\ncontract PoolRegistry is Ownable2StepUpgradeable, AccessControlledV8, PoolRegistryInterface {\n using SafeERC20Upgradeable for IERC20Upgradeable;\n\n struct AddMarketInput {\n VToken vToken;\n uint256 collateralFactor;\n uint256 liquidationThreshold;\n uint256 initialSupply;\n address vTokenReceiver;\n uint256 supplyCap;\n uint256 borrowCap;\n }\n\n uint256 internal constant MAX_POOL_NAME_LENGTH = 100;\n\n /**\n * @notice Maps pool's comptroller address to metadata.\n */\n mapping(address => VenusPoolMetaData) public metadata;\n\n /**\n * @dev Maps pool ID to pool's comptroller address\n */\n mapping(uint256 => address) private _poolsByID;\n\n /**\n * @dev Total number of pools created.\n */\n uint256 private _numberOfPools;\n\n /**\n * @dev Maps comptroller address to Venus pool Index.\n */\n mapping(address => VenusPool) private _poolByComptroller;\n\n /**\n * @dev Maps pool's comptroller address to asset to vToken.\n */\n mapping(address => mapping(address => address)) private _vTokens;\n\n /**\n * @dev Maps asset to list of supported pools.\n */\n mapping(address => address[]) private _supportedPools;\n\n /**\n * @notice Emitted when a new Venus pool is added to the directory.\n */\n event PoolRegistered(address indexed comptroller, VenusPool pool);\n\n /**\n * @notice Emitted when a pool name is set.\n */\n event PoolNameSet(address indexed comptroller, string oldName, string newName);\n\n /**\n * @notice Emitted when a pool metadata is updated.\n */\n event PoolMetadataUpdated(\n address indexed comptroller,\n VenusPoolMetaData oldMetadata,\n VenusPoolMetaData newMetadata\n );\n\n /**\n * @notice Emitted when a Market is added to the pool.\n */\n event MarketAdded(address indexed comptroller, address indexed vTokenAddress);\n\n /// @custom:oz-upgrades-unsafe-allow constructor\n constructor() {\n // Note that the contract is upgradeable. Use initialize() or reinitializers\n // to set the state variables.\n _disableInitializers();\n }\n\n /**\n * @notice Initializes the deployer to owner\n * @param accessControlManager_ AccessControlManager contract address\n */\n function initialize(address accessControlManager_) external initializer {\n __Ownable2Step_init();\n __AccessControlled_init_unchained(accessControlManager_);\n }\n\n /**\n * @notice Adds a new Venus pool to the directory\n * @dev Price oracle must be configured before adding a pool\n * @param name The name of the pool\n * @param comptroller Pool's Comptroller contract\n * @param closeFactor The pool's close factor (scaled by 1e18)\n * @param liquidationIncentive The pool's liquidation incentive (scaled by 1e18)\n * @param minLiquidatableCollateral Minimal collateral for regular (non-batch) liquidations flow\n * @return index The index of the registered Venus pool\n * @custom:error ZeroAddressNotAllowed is thrown when Comptroller address is zero\n * @custom:error ZeroAddressNotAllowed is thrown when price oracle address is zero\n */\n function addPool(\n string calldata name,\n Comptroller comptroller,\n uint256 closeFactor,\n uint256 liquidationIncentive,\n uint256 minLiquidatableCollateral\n ) external virtual returns (uint256 index) {\n _checkAccessAllowed(\"addPool(string,address,uint256,uint256,uint256)\");\n // Input validation\n ensureNonzeroAddress(address(comptroller));\n ensureNonzeroAddress(address(comptroller.oracle()));\n\n uint256 poolId = _registerPool(name, address(comptroller));\n\n // Set Venus pool parameters\n comptroller.setCloseFactor(closeFactor);\n comptroller.setLiquidationIncentive(liquidationIncentive);\n comptroller.setMinLiquidatableCollateral(minLiquidatableCollateral);\n\n return poolId;\n }\n\n /**\n * @notice Add a market to an existing pool and then mint to provide initial supply\n * @param input The structure describing the parameters for adding a market to a pool\n * @custom:error ZeroAddressNotAllowed is thrown when vToken address is zero\n * @custom:error ZeroAddressNotAllowed is thrown when vTokenReceiver address is zero\n */\n function addMarket(AddMarketInput memory input) external {\n _checkAccessAllowed(\"addMarket(AddMarketInput)\");\n ensureNonzeroAddress(address(input.vToken));\n ensureNonzeroAddress(input.vTokenReceiver);\n require(input.initialSupply > 0, \"PoolRegistry: initialSupply is zero\");\n\n VToken vToken = input.vToken;\n address vTokenAddress = address(vToken);\n address comptrollerAddress = address(vToken.comptroller());\n Comptroller comptroller = Comptroller(comptrollerAddress);\n address underlyingAddress = vToken.underlying();\n IERC20Upgradeable underlying = IERC20Upgradeable(underlyingAddress);\n\n require(_poolByComptroller[comptrollerAddress].creator != address(0), \"PoolRegistry: Pool not registered\");\n // solhint-disable-next-line reason-string\n require(\n _vTokens[comptrollerAddress][underlyingAddress] == address(0),\n \"PoolRegistry: Market already added for asset comptroller combination\"\n );\n\n comptroller.supportMarket(vToken);\n comptroller.setCollateralFactor(vToken, input.collateralFactor, input.liquidationThreshold);\n\n uint256[] memory newSupplyCaps = new uint256[](1);\n uint256[] memory newBorrowCaps = new uint256[](1);\n VToken[] memory vTokens = new VToken[](1);\n\n newSupplyCaps[0] = input.supplyCap;\n newBorrowCaps[0] = input.borrowCap;\n vTokens[0] = vToken;\n\n comptroller.setMarketSupplyCaps(vTokens, newSupplyCaps);\n comptroller.setMarketBorrowCaps(vTokens, newBorrowCaps);\n\n _vTokens[comptrollerAddress][underlyingAddress] = vTokenAddress;\n _supportedPools[underlyingAddress].push(comptrollerAddress);\n\n uint256 amountToSupply = _transferIn(underlying, msg.sender, input.initialSupply);\n underlying.forceApprove(vTokenAddress, 0);\n underlying.forceApprove(vTokenAddress, amountToSupply);\n vToken.mintBehalf(input.vTokenReceiver, amountToSupply);\n\n emit MarketAdded(comptrollerAddress, vTokenAddress);\n }\n\n /**\n * @notice Modify existing Venus pool name\n * @param comptroller Pool's Comptroller\n * @param name New pool name\n */\n function setPoolName(address comptroller, string calldata name) external {\n _checkAccessAllowed(\"setPoolName(address,string)\");\n _ensureValidName(name);\n VenusPool storage pool = _poolByComptroller[comptroller];\n string memory oldName = pool.name;\n pool.name = name;\n emit PoolNameSet(comptroller, oldName, name);\n }\n\n /**\n * @notice Update metadata of an existing pool\n * @param comptroller Pool's Comptroller\n * @param metadata_ New pool metadata\n */\n function updatePoolMetadata(address comptroller, VenusPoolMetaData calldata metadata_) external {\n _checkAccessAllowed(\"updatePoolMetadata(address,VenusPoolMetaData)\");\n VenusPoolMetaData memory oldMetadata = metadata[comptroller];\n metadata[comptroller] = metadata_;\n emit PoolMetadataUpdated(comptroller, oldMetadata, metadata_);\n }\n\n /**\n * @notice Returns arrays of all Venus pools' data\n * @dev This function is not designed to be called in a transaction: it is too gas-intensive\n * @return A list of all pools within PoolRegistry, with details for each pool\n */\n function getAllPools() external view override returns (VenusPool[] memory) {\n uint256 numberOfPools_ = _numberOfPools; // storage load to save gas\n VenusPool[] memory _pools = new VenusPool[](numberOfPools_);\n for (uint256 i = 1; i <= numberOfPools_; ++i) {\n address comptroller = _poolsByID[i];\n _pools[i - 1] = (_poolByComptroller[comptroller]);\n }\n return _pools;\n }\n\n /**\n * @param comptroller The comptroller proxy address associated to the pool\n * @return Returns Venus pool\n */\n function getPoolByComptroller(address comptroller) external view override returns (VenusPool memory) {\n return _poolByComptroller[comptroller];\n }\n\n /**\n * @param comptroller comptroller of Venus pool\n * @return Returns Metadata of Venus pool\n */\n function getVenusPoolMetadata(address comptroller) external view override returns (VenusPoolMetaData memory) {\n return metadata[comptroller];\n }\n\n function getVTokenForAsset(address comptroller, address asset) external view override returns (address) {\n return _vTokens[comptroller][asset];\n }\n\n function getPoolsSupportedByAsset(address asset) external view override returns (address[] memory) {\n return _supportedPools[asset];\n }\n\n /**\n * @dev Adds a new Venus pool to the directory (without checking msg.sender).\n * @param name The name of the pool\n * @param comptroller The pool's Comptroller proxy contract address\n * @return The index of the registered Venus pool\n */\n function _registerPool(string calldata name, address comptroller) internal returns (uint256) {\n VenusPool storage storedPool = _poolByComptroller[comptroller];\n\n require(storedPool.creator == address(0), \"PoolRegistry: Pool already exists in the directory.\");\n _ensureValidName(name);\n\n ++_numberOfPools;\n uint256 numberOfPools_ = _numberOfPools; // cache on stack to save storage read gas\n\n VenusPool memory pool = VenusPool(name, msg.sender, comptroller, block.number, block.timestamp);\n\n _poolsByID[numberOfPools_] = comptroller;\n _poolByComptroller[comptroller] = pool;\n\n emit PoolRegistered(comptroller, pool);\n return numberOfPools_;\n }\n\n function _transferIn(IERC20Upgradeable token, address from, uint256 amount) internal returns (uint256) {\n uint256 balanceBefore = token.balanceOf(address(this));\n token.safeTransferFrom(from, address(this), amount);\n uint256 balanceAfter = token.balanceOf(address(this));\n return balanceAfter - balanceBefore;\n }\n\n function _ensureValidName(string calldata name) internal pure {\n require(bytes(name).length <= MAX_POOL_NAME_LENGTH, \"Pool's name is too large\");\n }\n}\n" + }, + "contracts/Pool/PoolRegistryInterface.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity ^0.8.25;\n\n/**\n * @title PoolRegistryInterface\n * @author Venus\n * @notice Interface implemented by `PoolRegistry`.\n */\ninterface PoolRegistryInterface {\n /**\n * @notice Struct for a Venus interest rate pool.\n */\n struct VenusPool {\n string name;\n address creator;\n address comptroller;\n uint256 blockPosted;\n uint256 timestampPosted;\n }\n\n /**\n * @notice Struct for a Venus interest rate pool metadata.\n */\n struct VenusPoolMetaData {\n string category;\n string logoURL;\n string description;\n }\n\n /// @notice Get all pools in PoolRegistry\n function getAllPools() external view returns (VenusPool[] memory);\n\n /// @notice Get a pool by comptroller address\n function getPoolByComptroller(address comptroller) external view returns (VenusPool memory);\n\n /// @notice Get the address of the VToken contract in the Pool where the underlying token is the provided asset\n function getVTokenForAsset(address comptroller, address asset) external view returns (address);\n\n /// @notice Get the addresss of the Pools supported that include a market for the provided asset\n function getPoolsSupportedByAsset(address asset) external view returns (address[] memory);\n\n /// @notice Get the metadata of a Pool by comptroller address\n function getVenusPoolMetadata(address comptroller) external view returns (VenusPoolMetaData memory);\n}\n" + }, + "contracts/Rewards/RewardsDistributor.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\n\nimport { Ownable2StepUpgradeable } from \"@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol\";\nimport { IERC20Upgradeable } from \"@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol\";\nimport { SafeERC20Upgradeable } from \"@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol\";\nimport { AccessControlledV8 } from \"@venusprotocol/governance-contracts/contracts/Governance/AccessControlledV8.sol\";\nimport { TimeManagerV8 } from \"@venusprotocol/solidity-utilities/contracts/TimeManagerV8.sol\";\n\nimport { ExponentialNoError } from \"../ExponentialNoError.sol\";\nimport { VToken } from \"../VToken.sol\";\nimport { Comptroller } from \"../Comptroller.sol\";\nimport { MaxLoopsLimitHelper } from \"../MaxLoopsLimitHelper.sol\";\nimport { RewardsDistributorStorage } from \"./RewardsDistributorStorage.sol\";\n\n/**\n * @title `RewardsDistributor`\n * @author Venus\n * @notice Contract used to configure, track and distribute rewards to users based on their actions (borrows and supplies) in the protocol.\n * Users can receive additional rewards through a `RewardsDistributor`. Each `RewardsDistributor` proxy is initialized with a specific reward\n * token and `Comptroller`, which can then distribute the reward token to users that supply or borrow in the associated pool.\n * Authorized users can set the reward token borrow and supply speeds for each market in the pool. This sets a fixed amount of reward\n * token to be released each slot (block or second) for borrowers and suppliers, which is distributed based on a user’s percentage of the borrows or supplies\n * respectively. The owner can also set up reward distributions to contributor addresses (distinct from suppliers and borrowers) by setting\n * their contributor reward token speed, which similarly allocates a fixed amount of reward token per slot (block or second).\n *\n * The owner has the ability to transfer any amount of reward tokens held by the contract to any other address. Rewards are not distributed\n * automatically and must be claimed by a user calling `claimRewardToken()`. Users should be aware that it is up to the owner and other centralized\n * entities to ensure that the `RewardsDistributor` holds enough tokens to distribute the accumulated rewards of users and contributors.\n */\ncontract RewardsDistributor is\n ExponentialNoError,\n Ownable2StepUpgradeable,\n AccessControlledV8,\n MaxLoopsLimitHelper,\n RewardsDistributorStorage,\n TimeManagerV8\n{\n using SafeERC20Upgradeable for IERC20Upgradeable;\n\n /// @notice The initial REWARD TOKEN index for a market\n uint224 public constant INITIAL_INDEX = 1e36;\n\n /// @notice Emitted when REWARD TOKEN is distributed to a supplier\n event DistributedSupplierRewardToken(\n VToken indexed vToken,\n address indexed supplier,\n uint256 rewardTokenDelta,\n uint256 rewardTokenTotal,\n uint256 rewardTokenSupplyIndex\n );\n\n /// @notice Emitted when REWARD TOKEN is distributed to a borrower\n event DistributedBorrowerRewardToken(\n VToken indexed vToken,\n address indexed borrower,\n uint256 rewardTokenDelta,\n uint256 rewardTokenTotal,\n uint256 rewardTokenBorrowIndex\n );\n\n /// @notice Emitted when a new supply-side REWARD TOKEN speed is calculated for a market\n event RewardTokenSupplySpeedUpdated(VToken indexed vToken, uint256 newSpeed);\n\n /// @notice Emitted when a new borrow-side REWARD TOKEN speed is calculated for a market\n event RewardTokenBorrowSpeedUpdated(VToken indexed vToken, uint256 newSpeed);\n\n /// @notice Emitted when REWARD TOKEN is granted by admin\n event RewardTokenGranted(address indexed recipient, uint256 amount);\n\n /// @notice Emitted when a new REWARD TOKEN speed is set for a contributor\n event ContributorRewardTokenSpeedUpdated(address indexed contributor, uint256 newSpeed);\n\n /// @notice Emitted when a market is initialized\n event MarketInitialized(address indexed vToken);\n\n /// @notice Emitted when a reward token supply index is updated\n event RewardTokenSupplyIndexUpdated(address indexed vToken);\n\n /// @notice Emitted when a reward token borrow index is updated\n event RewardTokenBorrowIndexUpdated(address indexed vToken, Exp marketBorrowIndex);\n\n /// @notice Emitted when a reward for contributor is updated\n event ContributorRewardsUpdated(address indexed contributor, uint256 rewardAccrued);\n\n /// @notice Emitted when a reward token last rewarding block for supply is updated\n event SupplyLastRewardingBlockUpdated(address indexed vToken, uint32 newBlock);\n\n /// @notice Emitted when a reward token last rewarding block for borrow is updated\n event BorrowLastRewardingBlockUpdated(address indexed vToken, uint32 newBlock);\n\n /// @notice Emitted when a reward token last rewarding timestamp for supply is updated\n event SupplyLastRewardingBlockTimestampUpdated(address indexed vToken, uint256 newTimestamp);\n\n /// @notice Emitted when a reward token last rewarding timestamp for borrow is updated\n event BorrowLastRewardingBlockTimestampUpdated(address indexed vToken, uint256 newTimestamp);\n\n modifier onlyComptroller() {\n require(address(comptroller) == msg.sender, \"Only comptroller can call this function\");\n _;\n }\n\n /**\n * @param timeBased_ A boolean indicating whether the contract is based on time or block.\n * @param blocksPerYear_ The number of blocks per year\n * @custom:oz-upgrades-unsafe-allow constructor\n */\n constructor(bool timeBased_, uint256 blocksPerYear_) TimeManagerV8(timeBased_, blocksPerYear_) {\n // Note that the contract is upgradeable. Use initialize() or reinitializers\n // to set the state variables.\n _disableInitializers();\n }\n\n /**\n * @notice RewardsDistributor initializer\n * @dev Initializes the deployer to owner\n * @param comptroller_ Comptroller to attach the reward distributor to\n * @param rewardToken_ Reward token to distribute\n * @param loopsLimit_ Maximum number of iterations for the loops in this contract\n * @param accessControlManager_ AccessControlManager contract address\n */\n function initialize(\n Comptroller comptroller_,\n IERC20Upgradeable rewardToken_,\n uint256 loopsLimit_,\n address accessControlManager_\n ) external initializer {\n comptroller = comptroller_;\n rewardToken = rewardToken_;\n __Ownable2Step_init();\n __AccessControlled_init_unchained(accessControlManager_);\n\n _setMaxLoopsLimit(loopsLimit_);\n }\n\n /**\n * @notice Initializes the market state for a specific vToken\n * @param vToken The address of the vToken to be initialized\n * @custom:event MarketInitialized emits on success\n * @custom:access Only Comptroller\n */\n function initializeMarket(address vToken) external onlyComptroller {\n uint256 blockNumberOrTimestamp = getBlockNumberOrTimestamp();\n\n isTimeBased\n ? _initializeMarketTimestampBased(vToken, blockNumberOrTimestamp)\n : _initializeMarketBlockBased(vToken, safe32(blockNumberOrTimestamp, \"block number exceeds 32 bits\"));\n\n emit MarketInitialized(vToken);\n }\n\n /*** Reward Token Distribution ***/\n\n /**\n * @notice Calculate reward token accrued by a borrower and possibly transfer it to them\n * Borrowers will begin to accrue after the first interaction with the protocol.\n * @dev This function should only be called when the user has a borrow position in the market\n * (e.g. Comptroller.preBorrowHook, and Comptroller.preRepayHook)\n * We avoid an external call to check if they are in the market to save gas because this function is called in many places\n * @param vToken The market in which the borrower is interacting\n * @param borrower The address of the borrower to distribute REWARD TOKEN to\n * @param marketBorrowIndex The current global borrow index of vToken\n */\n function distributeBorrowerRewardToken(\n address vToken,\n address borrower,\n Exp memory marketBorrowIndex\n ) external onlyComptroller {\n _distributeBorrowerRewardToken(vToken, borrower, marketBorrowIndex);\n }\n\n function updateRewardTokenSupplyIndex(address vToken) external onlyComptroller {\n _updateRewardTokenSupplyIndex(vToken);\n }\n\n /**\n * @notice Transfer REWARD TOKEN to the recipient\n * @dev Note: If there is not enough REWARD TOKEN, we do not perform the transfer all\n * @param recipient The address of the recipient to transfer REWARD TOKEN to\n * @param amount The amount of REWARD TOKEN to (possibly) transfer\n */\n function grantRewardToken(address recipient, uint256 amount) external onlyOwner {\n uint256 amountLeft = _grantRewardToken(recipient, amount);\n require(amountLeft == 0, \"insufficient rewardToken for grant\");\n emit RewardTokenGranted(recipient, amount);\n }\n\n function updateRewardTokenBorrowIndex(address vToken, Exp memory marketBorrowIndex) external onlyComptroller {\n _updateRewardTokenBorrowIndex(vToken, marketBorrowIndex);\n }\n\n /**\n * @notice Set REWARD TOKEN borrow and supply speeds for the specified markets\n * @param vTokens The markets whose REWARD TOKEN speed to update\n * @param supplySpeeds New supply-side REWARD TOKEN speed for the corresponding market\n * @param borrowSpeeds New borrow-side REWARD TOKEN speed for the corresponding market\n */\n function setRewardTokenSpeeds(\n VToken[] memory vTokens,\n uint256[] memory supplySpeeds,\n uint256[] memory borrowSpeeds\n ) external {\n _checkAccessAllowed(\"setRewardTokenSpeeds(address[],uint256[],uint256[])\");\n uint256 numTokens = vTokens.length;\n require(numTokens == supplySpeeds.length && numTokens == borrowSpeeds.length, \"invalid setRewardTokenSpeeds\");\n\n for (uint256 i; i < numTokens; ++i) {\n _setRewardTokenSpeed(vTokens[i], supplySpeeds[i], borrowSpeeds[i]);\n }\n }\n\n /**\n * @notice Set REWARD TOKEN last rewarding block for the specified markets, used when contract is block based\n * @param vTokens The markets whose REWARD TOKEN last rewarding block to update\n * @param supplyLastRewardingBlocks New supply-side REWARD TOKEN last rewarding block for the corresponding market\n * @param borrowLastRewardingBlocks New borrow-side REWARD TOKEN last rewarding block for the corresponding market\n */\n function setLastRewardingBlocks(\n VToken[] calldata vTokens,\n uint32[] calldata supplyLastRewardingBlocks,\n uint32[] calldata borrowLastRewardingBlocks\n ) external {\n _checkAccessAllowed(\"setLastRewardingBlocks(address[],uint32[],uint32[])\");\n require(!isTimeBased, \"Block-based operation only\");\n\n uint256 numTokens = vTokens.length;\n require(\n numTokens == supplyLastRewardingBlocks.length && numTokens == borrowLastRewardingBlocks.length,\n \"RewardsDistributor::setLastRewardingBlocks invalid input\"\n );\n\n for (uint256 i; i < numTokens; ) {\n _setLastRewardingBlock(vTokens[i], supplyLastRewardingBlocks[i], borrowLastRewardingBlocks[i]);\n unchecked {\n ++i;\n }\n }\n }\n\n /**\n * @notice Set REWARD TOKEN last rewarding block timestamp for the specified markets, used when contract is time based\n * @param vTokens The markets whose REWARD TOKEN last rewarding block to update\n * @param supplyLastRewardingBlockTimestamps New supply-side REWARD TOKEN last rewarding block timestamp for the corresponding market\n * @param borrowLastRewardingBlockTimestamps New borrow-side REWARD TOKEN last rewarding block timestamp for the corresponding market\n */\n function setLastRewardingBlockTimestamps(\n VToken[] calldata vTokens,\n uint256[] calldata supplyLastRewardingBlockTimestamps,\n uint256[] calldata borrowLastRewardingBlockTimestamps\n ) external {\n _checkAccessAllowed(\"setLastRewardingBlockTimestamps(address[],uint256[],uint256[])\");\n require(isTimeBased, \"Time-based operation only\");\n\n uint256 numTokens = vTokens.length;\n require(\n numTokens == supplyLastRewardingBlockTimestamps.length &&\n numTokens == borrowLastRewardingBlockTimestamps.length,\n \"RewardsDistributor::setLastRewardingBlockTimestamps invalid input\"\n );\n\n for (uint256 i; i < numTokens; ) {\n _setLastRewardingBlockTimestamp(\n vTokens[i],\n supplyLastRewardingBlockTimestamps[i],\n borrowLastRewardingBlockTimestamps[i]\n );\n unchecked {\n ++i;\n }\n }\n }\n\n /**\n * @notice Set REWARD TOKEN speed for a single contributor\n * @param contributor The contributor whose REWARD TOKEN speed to update\n * @param rewardTokenSpeed New REWARD TOKEN speed for contributor\n */\n function setContributorRewardTokenSpeed(address contributor, uint256 rewardTokenSpeed) external onlyOwner {\n // note that REWARD TOKEN speed could be set to 0 to halt liquidity rewards for a contributor\n updateContributorRewards(contributor);\n if (rewardTokenSpeed == 0) {\n // release storage\n delete lastContributorBlock[contributor];\n } else {\n lastContributorBlock[contributor] = getBlockNumberOrTimestamp();\n }\n rewardTokenContributorSpeeds[contributor] = rewardTokenSpeed;\n\n emit ContributorRewardTokenSpeedUpdated(contributor, rewardTokenSpeed);\n }\n\n function distributeSupplierRewardToken(address vToken, address supplier) external onlyComptroller {\n _distributeSupplierRewardToken(vToken, supplier);\n }\n\n /**\n * @notice Claim all the rewardToken accrued by holder in all markets\n * @param holder The address to claim REWARD TOKEN for\n */\n function claimRewardToken(address holder) external {\n return claimRewardToken(holder, comptroller.getAllMarkets());\n }\n\n /**\n * @notice Set the limit for the loops can iterate to avoid the DOS\n * @param limit Limit for the max loops can execute at a time\n */\n function setMaxLoopsLimit(uint256 limit) external onlyOwner {\n _setMaxLoopsLimit(limit);\n }\n\n /**\n * @notice Calculate additional accrued REWARD TOKEN for a contributor since last accrual\n * @param contributor The address to calculate contributor rewards for\n */\n function updateContributorRewards(address contributor) public {\n uint256 rewardTokenSpeed = rewardTokenContributorSpeeds[contributor];\n uint256 blockNumberOrTimestamp = getBlockNumberOrTimestamp();\n uint256 deltaBlocksOrTimestamp = sub_(blockNumberOrTimestamp, lastContributorBlock[contributor]);\n if (deltaBlocksOrTimestamp > 0 && rewardTokenSpeed > 0) {\n uint256 newAccrued = mul_(deltaBlocksOrTimestamp, rewardTokenSpeed);\n uint256 contributorAccrued = add_(rewardTokenAccrued[contributor], newAccrued);\n\n rewardTokenAccrued[contributor] = contributorAccrued;\n lastContributorBlock[contributor] = blockNumberOrTimestamp;\n\n emit ContributorRewardsUpdated(contributor, rewardTokenAccrued[contributor]);\n }\n }\n\n /**\n * @notice Claim all the rewardToken accrued by holder in the specified markets\n * @param holder The address to claim REWARD TOKEN for\n * @param vTokens The list of markets to claim REWARD TOKEN in\n */\n function claimRewardToken(address holder, VToken[] memory vTokens) public {\n uint256 vTokensCount = vTokens.length;\n\n _ensureMaxLoops(vTokensCount);\n\n for (uint256 i; i < vTokensCount; ++i) {\n VToken vToken = vTokens[i];\n require(comptroller.isMarketListed(vToken), \"market must be listed\");\n Exp memory borrowIndex = Exp({ mantissa: vToken.borrowIndex() });\n _updateRewardTokenBorrowIndex(address(vToken), borrowIndex);\n _distributeBorrowerRewardToken(address(vToken), holder, borrowIndex);\n _updateRewardTokenSupplyIndex(address(vToken));\n _distributeSupplierRewardToken(address(vToken), holder);\n }\n rewardTokenAccrued[holder] = _grantRewardToken(holder, rewardTokenAccrued[holder]);\n }\n\n /**\n * @notice Set REWARD TOKEN last rewarding block for a single market.\n * @param vToken market's whose reward token last rewarding block to be updated\n * @param supplyLastRewardingBlock New supply-side REWARD TOKEN last rewarding block for market\n * @param borrowLastRewardingBlock New borrow-side REWARD TOKEN last rewarding block for market\n */\n function _setLastRewardingBlock(\n VToken vToken,\n uint32 supplyLastRewardingBlock,\n uint32 borrowLastRewardingBlock\n ) internal {\n require(comptroller.isMarketListed(vToken), \"rewardToken market is not listed\");\n\n uint256 blockNumber = getBlockNumberOrTimestamp();\n\n require(supplyLastRewardingBlock > blockNumber, \"setting last rewarding block in the past is not allowed\");\n require(borrowLastRewardingBlock > blockNumber, \"setting last rewarding block in the past is not allowed\");\n\n uint32 currentSupplyLastRewardingBlock = rewardTokenSupplyState[address(vToken)].lastRewardingBlock;\n uint32 currentBorrowLastRewardingBlock = rewardTokenBorrowState[address(vToken)].lastRewardingBlock;\n\n require(\n currentSupplyLastRewardingBlock == 0 || currentSupplyLastRewardingBlock > blockNumber,\n \"this RewardsDistributor is already locked\"\n );\n require(\n currentBorrowLastRewardingBlock == 0 || currentBorrowLastRewardingBlock > blockNumber,\n \"this RewardsDistributor is already locked\"\n );\n\n if (currentSupplyLastRewardingBlock != supplyLastRewardingBlock) {\n rewardTokenSupplyState[address(vToken)].lastRewardingBlock = supplyLastRewardingBlock;\n emit SupplyLastRewardingBlockUpdated(address(vToken), supplyLastRewardingBlock);\n }\n\n if (currentBorrowLastRewardingBlock != borrowLastRewardingBlock) {\n rewardTokenBorrowState[address(vToken)].lastRewardingBlock = borrowLastRewardingBlock;\n emit BorrowLastRewardingBlockUpdated(address(vToken), borrowLastRewardingBlock);\n }\n }\n\n /**\n * @notice Set REWARD TOKEN last rewarding timestamp for a single market.\n * @param vToken market's whose reward token last rewarding timestamp to be updated\n * @param supplyLastRewardingBlockTimestamp New supply-side REWARD TOKEN last rewarding timestamp for market\n * @param borrowLastRewardingBlockTimestamp New borrow-side REWARD TOKEN last rewarding timestamp for market\n */\n function _setLastRewardingBlockTimestamp(\n VToken vToken,\n uint256 supplyLastRewardingBlockTimestamp,\n uint256 borrowLastRewardingBlockTimestamp\n ) internal {\n require(comptroller.isMarketListed(vToken), \"rewardToken market is not listed\");\n\n uint256 blockTimestamp = getBlockNumberOrTimestamp();\n\n require(\n supplyLastRewardingBlockTimestamp > blockTimestamp,\n \"setting last rewarding timestamp in the past is not allowed\"\n );\n require(\n borrowLastRewardingBlockTimestamp > blockTimestamp,\n \"setting last rewarding timestamp in the past is not allowed\"\n );\n\n uint256 currentSupplyLastRewardingBlockTimestamp = rewardTokenSupplyStateTimeBased[address(vToken)]\n .lastRewardingTimestamp;\n uint256 currentBorrowLastRewardingBlockTimestamp = rewardTokenBorrowStateTimeBased[address(vToken)]\n .lastRewardingTimestamp;\n\n require(\n currentSupplyLastRewardingBlockTimestamp == 0 || currentSupplyLastRewardingBlockTimestamp > blockTimestamp,\n \"this RewardsDistributor is already locked\"\n );\n require(\n currentBorrowLastRewardingBlockTimestamp == 0 || currentBorrowLastRewardingBlockTimestamp > blockTimestamp,\n \"this RewardsDistributor is already locked\"\n );\n\n if (currentSupplyLastRewardingBlockTimestamp != supplyLastRewardingBlockTimestamp) {\n rewardTokenSupplyStateTimeBased[address(vToken)].lastRewardingTimestamp = supplyLastRewardingBlockTimestamp;\n emit SupplyLastRewardingBlockTimestampUpdated(address(vToken), supplyLastRewardingBlockTimestamp);\n }\n\n if (currentBorrowLastRewardingBlockTimestamp != borrowLastRewardingBlockTimestamp) {\n rewardTokenBorrowStateTimeBased[address(vToken)].lastRewardingTimestamp = borrowLastRewardingBlockTimestamp;\n emit BorrowLastRewardingBlockTimestampUpdated(address(vToken), borrowLastRewardingBlockTimestamp);\n }\n }\n\n /**\n * @notice Set REWARD TOKEN speed for a single market.\n * @param vToken market's whose reward token rate to be updated\n * @param supplySpeed New supply-side REWARD TOKEN speed for market\n * @param borrowSpeed New borrow-side REWARD TOKEN speed for market\n */\n function _setRewardTokenSpeed(VToken vToken, uint256 supplySpeed, uint256 borrowSpeed) internal {\n require(comptroller.isMarketListed(vToken), \"rewardToken market is not listed\");\n\n if (rewardTokenSupplySpeeds[address(vToken)] != supplySpeed) {\n // Supply speed updated so let's update supply state to ensure that\n // 1. REWARD TOKEN accrued properly for the old speed, and\n // 2. REWARD TOKEN accrued at the new speed starts after this block.\n _updateRewardTokenSupplyIndex(address(vToken));\n\n // Update speed and emit event\n rewardTokenSupplySpeeds[address(vToken)] = supplySpeed;\n emit RewardTokenSupplySpeedUpdated(vToken, supplySpeed);\n }\n\n if (rewardTokenBorrowSpeeds[address(vToken)] != borrowSpeed) {\n // Borrow speed updated so let's update borrow state to ensure that\n // 1. REWARD TOKEN accrued properly for the old speed, and\n // 2. REWARD TOKEN accrued at the new speed starts after this block.\n Exp memory borrowIndex = Exp({ mantissa: vToken.borrowIndex() });\n _updateRewardTokenBorrowIndex(address(vToken), borrowIndex);\n\n // Update speed and emit event\n rewardTokenBorrowSpeeds[address(vToken)] = borrowSpeed;\n emit RewardTokenBorrowSpeedUpdated(vToken, borrowSpeed);\n }\n }\n\n /**\n * @notice Calculate REWARD TOKEN accrued by a supplier and possibly transfer it to them.\n * @param vToken The market in which the supplier is interacting\n * @param supplier The address of the supplier to distribute REWARD TOKEN to\n */\n function _distributeSupplierRewardToken(address vToken, address supplier) internal {\n RewardToken storage supplyState = rewardTokenSupplyState[vToken];\n TimeBasedRewardToken storage supplyStateTimeBased = rewardTokenSupplyStateTimeBased[vToken];\n\n uint256 supplyIndex = isTimeBased ? supplyStateTimeBased.index : supplyState.index;\n uint256 supplierIndex = rewardTokenSupplierIndex[vToken][supplier];\n\n // Update supplier's index to the current index since we are distributing accrued REWARD TOKEN\n rewardTokenSupplierIndex[vToken][supplier] = supplyIndex;\n\n if (supplierIndex == 0 && supplyIndex >= INITIAL_INDEX) {\n // Covers the case where users supplied tokens before the market's supply state index was set.\n // Rewards the user with REWARD TOKEN accrued from the start of when supplier rewards were first\n // set for the market.\n supplierIndex = INITIAL_INDEX;\n }\n\n // Calculate change in the cumulative sum of the REWARD TOKEN per vToken accrued\n Double memory deltaIndex = Double({ mantissa: sub_(supplyIndex, supplierIndex) });\n\n uint256 supplierTokens = VToken(vToken).balanceOf(supplier);\n\n // Calculate REWARD TOKEN accrued: vTokenAmount * accruedPerVToken\n uint256 supplierDelta = mul_(supplierTokens, deltaIndex);\n\n uint256 supplierAccrued = add_(rewardTokenAccrued[supplier], supplierDelta);\n rewardTokenAccrued[supplier] = supplierAccrued;\n\n emit DistributedSupplierRewardToken(VToken(vToken), supplier, supplierDelta, supplierAccrued, supplyIndex);\n }\n\n /**\n * @notice Calculate reward token accrued by a borrower and possibly transfer it to them.\n * @param vToken The market in which the borrower is interacting\n * @param borrower The address of the borrower to distribute REWARD TOKEN to\n * @param marketBorrowIndex The current global borrow index of vToken\n */\n function _distributeBorrowerRewardToken(address vToken, address borrower, Exp memory marketBorrowIndex) internal {\n RewardToken storage borrowState = rewardTokenBorrowState[vToken];\n TimeBasedRewardToken storage borrowStateTimeBased = rewardTokenBorrowStateTimeBased[vToken];\n\n uint256 borrowIndex = isTimeBased ? borrowStateTimeBased.index : borrowState.index;\n uint256 borrowerIndex = rewardTokenBorrowerIndex[vToken][borrower];\n\n // Update borrowers's index to the current index since we are distributing accrued REWARD TOKEN\n rewardTokenBorrowerIndex[vToken][borrower] = borrowIndex;\n\n if (borrowerIndex == 0 && borrowIndex >= INITIAL_INDEX) {\n // Covers the case where users borrowed tokens before the market's borrow state index was set.\n // Rewards the user with REWARD TOKEN accrued from the start of when borrower rewards were first\n // set for the market.\n borrowerIndex = INITIAL_INDEX;\n }\n\n // Calculate change in the cumulative sum of the REWARD TOKEN per borrowed unit accrued\n Double memory deltaIndex = Double({ mantissa: sub_(borrowIndex, borrowerIndex) });\n\n uint256 borrowerAmount = div_(VToken(vToken).borrowBalanceStored(borrower), marketBorrowIndex);\n\n // Calculate REWARD TOKEN accrued: vTokenAmount * accruedPerBorrowedUnit\n if (borrowerAmount != 0) {\n uint256 borrowerDelta = mul_(borrowerAmount, deltaIndex);\n\n uint256 borrowerAccrued = add_(rewardTokenAccrued[borrower], borrowerDelta);\n rewardTokenAccrued[borrower] = borrowerAccrued;\n\n emit DistributedBorrowerRewardToken(VToken(vToken), borrower, borrowerDelta, borrowerAccrued, borrowIndex);\n }\n }\n\n /**\n * @notice Transfer REWARD TOKEN to the user.\n * @dev Note: If there is not enough REWARD TOKEN, we do not perform the transfer all.\n * @param user The address of the user to transfer REWARD TOKEN to\n * @param amount The amount of REWARD TOKEN to (possibly) transfer\n * @return The amount of REWARD TOKEN which was NOT transferred to the user\n */\n function _grantRewardToken(address user, uint256 amount) internal returns (uint256) {\n uint256 rewardTokenRemaining = rewardToken.balanceOf(address(this));\n if (amount > 0 && amount <= rewardTokenRemaining) {\n rewardToken.safeTransfer(user, amount);\n return 0;\n }\n return amount;\n }\n\n /**\n * @notice Accrue REWARD TOKEN to the market by updating the supply index\n * @param vToken The market whose supply index to update\n * @dev Index is a cumulative sum of the REWARD TOKEN per vToken accrued\n */\n function _updateRewardTokenSupplyIndex(address vToken) internal {\n RewardToken storage supplyState = rewardTokenSupplyState[vToken];\n TimeBasedRewardToken storage supplyStateTimeBased = rewardTokenSupplyStateTimeBased[vToken];\n\n uint256 supplySpeed = rewardTokenSupplySpeeds[vToken];\n uint256 blockNumberOrTimestamp = getBlockNumberOrTimestamp();\n\n if (!isTimeBased) {\n safe32(blockNumberOrTimestamp, \"block number exceeds 32 bits\");\n }\n\n uint256 lastRewardingBlockOrTimestamp = isTimeBased\n ? supplyStateTimeBased.lastRewardingTimestamp\n : uint256(supplyState.lastRewardingBlock);\n\n if (lastRewardingBlockOrTimestamp > 0 && blockNumberOrTimestamp > lastRewardingBlockOrTimestamp) {\n blockNumberOrTimestamp = lastRewardingBlockOrTimestamp;\n }\n\n uint256 deltaBlocksOrTimestamp = sub_(\n blockNumberOrTimestamp,\n (isTimeBased ? supplyStateTimeBased.timestamp : uint256(supplyState.block))\n );\n if (deltaBlocksOrTimestamp > 0 && supplySpeed > 0) {\n uint256 supplyTokens = VToken(vToken).totalSupply();\n uint256 accruedSinceUpdate = mul_(deltaBlocksOrTimestamp, supplySpeed);\n Double memory ratio = supplyTokens > 0\n ? fraction(accruedSinceUpdate, supplyTokens)\n : Double({ mantissa: 0 });\n uint224 supplyIndex = isTimeBased ? supplyStateTimeBased.index : supplyState.index;\n uint224 index = safe224(\n add_(Double({ mantissa: supplyIndex }), ratio).mantissa,\n \"new index exceeds 224 bits\"\n );\n\n if (isTimeBased) {\n supplyStateTimeBased.index = index;\n supplyStateTimeBased.timestamp = blockNumberOrTimestamp;\n } else {\n supplyState.index = index;\n supplyState.block = uint32(blockNumberOrTimestamp);\n }\n } else if (deltaBlocksOrTimestamp > 0) {\n isTimeBased ? supplyStateTimeBased.timestamp = blockNumberOrTimestamp : supplyState.block = uint32(\n blockNumberOrTimestamp\n );\n }\n\n emit RewardTokenSupplyIndexUpdated(vToken);\n }\n\n /**\n * @notice Accrue REWARD TOKEN to the market by updating the borrow index\n * @param vToken The market whose borrow index to update\n * @param marketBorrowIndex The current global borrow index of vToken\n * @dev Index is a cumulative sum of the REWARD TOKEN per vToken accrued\n */\n function _updateRewardTokenBorrowIndex(address vToken, Exp memory marketBorrowIndex) internal {\n RewardToken storage borrowState = rewardTokenBorrowState[vToken];\n TimeBasedRewardToken storage borrowStateTimeBased = rewardTokenBorrowStateTimeBased[vToken];\n\n uint256 borrowSpeed = rewardTokenBorrowSpeeds[vToken];\n uint256 blockNumberOrTimestamp = getBlockNumberOrTimestamp();\n\n if (!isTimeBased) {\n safe32(blockNumberOrTimestamp, \"block number exceeds 32 bits\");\n }\n\n uint256 lastRewardingBlockOrTimestamp = isTimeBased\n ? borrowStateTimeBased.lastRewardingTimestamp\n : uint256(borrowState.lastRewardingBlock);\n\n if (lastRewardingBlockOrTimestamp > 0 && blockNumberOrTimestamp > lastRewardingBlockOrTimestamp) {\n blockNumberOrTimestamp = lastRewardingBlockOrTimestamp;\n }\n\n uint256 deltaBlocksOrTimestamp = sub_(\n blockNumberOrTimestamp,\n (isTimeBased ? borrowStateTimeBased.timestamp : uint256(borrowState.block))\n );\n if (deltaBlocksOrTimestamp > 0 && borrowSpeed > 0) {\n uint256 borrowAmount = div_(VToken(vToken).totalBorrows(), marketBorrowIndex);\n uint256 accruedSinceUpdate = mul_(deltaBlocksOrTimestamp, borrowSpeed);\n Double memory ratio = borrowAmount > 0\n ? fraction(accruedSinceUpdate, borrowAmount)\n : Double({ mantissa: 0 });\n uint224 borrowIndex = isTimeBased ? borrowStateTimeBased.index : borrowState.index;\n uint224 index = safe224(\n add_(Double({ mantissa: borrowIndex }), ratio).mantissa,\n \"new index exceeds 224 bits\"\n );\n\n if (isTimeBased) {\n borrowStateTimeBased.index = index;\n borrowStateTimeBased.timestamp = blockNumberOrTimestamp;\n } else {\n borrowState.index = index;\n borrowState.block = uint32(blockNumberOrTimestamp);\n }\n } else if (deltaBlocksOrTimestamp > 0) {\n if (isTimeBased) {\n borrowStateTimeBased.timestamp = blockNumberOrTimestamp;\n } else {\n borrowState.block = uint32(blockNumberOrTimestamp);\n }\n }\n\n emit RewardTokenBorrowIndexUpdated(vToken, marketBorrowIndex);\n }\n\n /**\n * @notice Initializes the market state for a specific vToken called when contract is block-based\n * @param vToken The address of the vToken to be initialized\n * @param blockNumber current block number\n */\n function _initializeMarketBlockBased(address vToken, uint32 blockNumber) internal {\n RewardToken storage supplyState = rewardTokenSupplyState[vToken];\n RewardToken storage borrowState = rewardTokenBorrowState[vToken];\n\n /*\n * Update market state indices\n */\n if (supplyState.index == 0) {\n // Initialize supply state index with default value\n supplyState.index = INITIAL_INDEX;\n }\n\n if (borrowState.index == 0) {\n // Initialize borrow state index with default value\n borrowState.index = INITIAL_INDEX;\n }\n\n /*\n * Update market state block numbers\n */\n supplyState.block = borrowState.block = blockNumber;\n }\n\n /**\n * @notice Initializes the market state for a specific vToken called when contract is time-based\n * @param vToken The address of the vToken to be initialized\n * @param blockTimestamp current block timestamp\n */\n function _initializeMarketTimestampBased(address vToken, uint256 blockTimestamp) internal {\n TimeBasedRewardToken storage supplyState = rewardTokenSupplyStateTimeBased[vToken];\n TimeBasedRewardToken storage borrowState = rewardTokenBorrowStateTimeBased[vToken];\n\n /*\n * Update market state indices\n */\n if (supplyState.index == 0) {\n // Initialize supply state index with default value\n supplyState.index = INITIAL_INDEX;\n }\n\n if (borrowState.index == 0) {\n // Initialize borrow state index with default value\n borrowState.index = INITIAL_INDEX;\n }\n\n /*\n * Update market state block timestamp\n */\n supplyState.timestamp = borrowState.timestamp = blockTimestamp;\n }\n}\n" + }, + "contracts/Rewards/RewardsDistributorStorage.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\n\nimport { IERC20Upgradeable } from \"@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol\";\n\nimport { Comptroller } from \"../Comptroller.sol\";\n\n/**\n * @title RewardsDistributorStorage\n * @author Venus\n * @dev Storage for RewardsDistributor\n */\ncontract RewardsDistributorStorage {\n struct RewardToken {\n // The market's last updated rewardTokenBorrowIndex or rewardTokenSupplyIndex\n uint224 index;\n // The block number the index was last updated at\n uint32 block;\n // The block number at which to stop rewards\n uint32 lastRewardingBlock;\n }\n\n struct TimeBasedRewardToken {\n // The market's last updated rewardTokenBorrowIndex or rewardTokenSupplyIndex\n uint224 index;\n // The block timestamp the index was last updated at\n uint256 timestamp;\n // The block timestamp at which to stop rewards\n uint256 lastRewardingTimestamp;\n }\n\n /// @notice The REWARD TOKEN market supply state for each market\n mapping(address => RewardToken) public rewardTokenSupplyState;\n\n /// @notice The REWARD TOKEN borrow index for each market for each supplier as of the last time they accrued REWARD TOKEN\n mapping(address => mapping(address => uint256)) public rewardTokenSupplierIndex;\n\n /// @notice The REWARD TOKEN accrued but not yet transferred to each user\n mapping(address => uint256) public rewardTokenAccrued;\n\n /// @notice The rate at which rewardToken is distributed to the corresponding borrow market per slot (block or second)\n mapping(address => uint256) public rewardTokenBorrowSpeeds;\n\n /// @notice The rate at which rewardToken is distributed to the corresponding supply market per slot (block or second)\n mapping(address => uint256) public rewardTokenSupplySpeeds;\n\n /// @notice The REWARD TOKEN market borrow state for each market\n mapping(address => RewardToken) public rewardTokenBorrowState;\n\n /// @notice The portion of REWARD TOKEN that each contributor receives per slot (block or second)\n mapping(address => uint256) public rewardTokenContributorSpeeds;\n\n /// @notice Last slot (block or second) at which a contributor's REWARD TOKEN rewards have been allocated\n mapping(address => uint256) public lastContributorBlock;\n\n /// @notice The REWARD TOKEN borrow index for each market for each borrower as of the last time they accrued REWARD TOKEN\n mapping(address => mapping(address => uint256)) public rewardTokenBorrowerIndex;\n\n Comptroller internal comptroller;\n\n IERC20Upgradeable public rewardToken;\n\n /// @notice The REWARD TOKEN market supply state for each market\n mapping(address => TimeBasedRewardToken) public rewardTokenSupplyStateTimeBased;\n\n /// @notice The REWARD TOKEN market borrow state for each market\n mapping(address => TimeBasedRewardToken) public rewardTokenBorrowStateTimeBased;\n\n /**\n * @dev This empty reserved space is put in place to allow future versions to add new\n * variables without shifting down storage in the inheritance chain\n * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\n */\n uint256[37] private __gap;\n}\n" + }, + "contracts/Spoke/SpokeComptrollerInterface.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity ^0.8.25;\n\nimport { ResilientOracleInterface } from \"@venusprotocol/oracle/contracts/interfaces/OracleInterface.sol\";\nimport { IDeviationBoundedOracle } from \"@venusprotocol/oracle/contracts/interfaces/IDeviationBoundedOracle.sol\";\n\nimport { ComptrollerInterface, Action } from \"../ComptrollerInterface.sol\";\nimport { VToken } from \"../VToken.sol\";\n\n/**\n * @title SpokeComptrollerInterface\n * @author Venus\n * @notice Interface implemented by the `SpokeComptroller` contract. It declares the events and errors that make up the\n * contract's observable surface, so integrators and off-chain consumers can decode them without depending on the\n * implementation.\n * @dev The getters this fork adds are declared separately, in `SpokeComptrollerViewInterface` below. They cannot live\n * here: `SpokeComptroller` implements this interface, and it serves those getters from public variables declared in\n * `SpokeComptrollerStorage`, which is a sibling base rather than a derived one. Solidity will not let a public\n * variable in one base satisfy a function declared in another, so declaring them here would force\n * `SpokeComptrollerStorage` to inherit this interface and mark six variables `override` - noise in the file that has\n * to be diffed by hand against `ComptrollerStorage`, for no gain over a standalone view interface.\n */\ninterface SpokeComptrollerInterface is ComptrollerInterface {\n /// @notice Emitted when an account enters a market\n event MarketEntered(VToken indexed vToken, address indexed account);\n\n /// @notice Emitted when an account exits a market\n event MarketExited(VToken indexed vToken, address indexed account);\n\n /// @notice Emitted when close factor is changed by admin\n event NewCloseFactor(uint256 oldCloseFactorMantissa, uint256 newCloseFactorMantissa);\n\n /// @notice Emitted when a collateral factor is changed by admin\n event NewCollateralFactor(VToken vToken, uint256 oldCollateralFactorMantissa, uint256 newCollateralFactorMantissa);\n\n /// @notice Emitted when liquidation threshold is changed by admin\n event NewLiquidationThreshold(\n VToken vToken,\n uint256 oldLiquidationThresholdMantissa,\n uint256 newLiquidationThresholdMantissa\n );\n\n /// @notice Emitted when liquidation incentive is changed by admin\n event NewLiquidationIncentive(uint256 oldLiquidationIncentiveMantissa, uint256 newLiquidationIncentiveMantissa);\n\n /// @notice Emitted when the liquidation incentive of a single market is changed by admin\n event NewMarketLiquidationIncentive(\n address indexed vToken,\n uint256 oldLiquidationIncentiveMantissa,\n uint256 newLiquidationIncentiveMantissa\n );\n\n /// @notice Emitted when price oracle is changed\n event NewPriceOracle(ResilientOracleInterface oldPriceOracle, ResilientOracleInterface newPriceOracle);\n\n /// @notice Emitted when the deviation-bounded oracle is changed\n event NewDeviationBoundedOracle(IDeviationBoundedOracle oldBoundedOracle, IDeviationBoundedOracle newBoundedOracle);\n\n /// @notice Emitted when an action is paused on a market\n event ActionPausedMarket(VToken vToken, Action action, bool pauseState);\n\n /// @notice Emitted when borrow cap for a vToken is changed\n event NewBorrowCap(VToken indexed vToken, uint256 newBorrowCap);\n\n /// @notice Emitted when the collateral threshold (in USD) for non-batch liquidations is changed\n event NewMinLiquidatableCollateral(uint256 oldMinLiquidatableCollateral, uint256 newMinLiquidatableCollateral);\n\n /// @notice Emitted when supply cap for a vToken is changed\n event NewSupplyCap(VToken indexed vToken, uint256 newSupplyCap);\n\n /// @notice Emitted when a rewards distributor is added\n event NewRewardsDistributor(address indexed rewardsDistributor, address indexed rewardToken);\n\n /// @notice Emitted when a market is supported\n event MarketSupported(VToken vToken);\n\n /// @notice Emitted when forced liquidation is enabled or disabled for a market\n event IsForcedLiquidationEnabledUpdated(address indexed vToken, bool enable);\n\n /// @notice Emitted when a market is unlisted\n event MarketUnlisted(address indexed vToken);\n\n /// @notice Emitted when the borrowing or redeeming delegate rights are updated for an account\n event DelegateUpdated(address indexed approver, address indexed delegate, bool approved);\n\n /// @notice Emitted when a market's supply allowlist is enabled or disabled\n event SupplyAllowlistEnabledUpdated(address indexed vToken, bool enabled);\n\n /// @notice Emitted when an account is added to or removed from a market's supply allowlist\n event AllowedSupplierUpdated(address indexed vToken, address indexed supplier, bool allowed);\n\n /// @notice Emitted when the pool's liquidation allowlist is enabled or disabled\n event LiquidationAllowlistEnabledUpdated(bool enabled);\n\n /// @notice Emitted when an account is added to or removed from the pool's liquidation allowlist\n event AllowedLiquidatorUpdated(address indexed liquidator, bool allowed);\n\n /// @notice Thrown when the close factor is outside the bounds set by `MIN_CLOSE_FACTOR_MANTISSA` and\n /// `MAX_CLOSE_FACTOR_MANTISSA`\n error InvalidCloseFactor();\n\n /// @notice Thrown when collateral factor exceeds the upper bound\n error InvalidCollateralFactor();\n\n /// @notice Thrown when liquidation threshold exceeds the collateral factor\n error InvalidLiquidationThreshold();\n\n /// @notice Thrown when a liquidation incentive is low enough that a liquidator would seize less value than it\n /// repaid: below `1e18 + protocolSeizeShareMantissa` for a single market, below\n /// `MIN_POOL_LIQUIDATION_INCENTIVE_MANTISSA` for the pool-wide fallback\n error InvalidLiquidationIncentive();\n\n /// @notice Thrown when the action is only available to specific sender, but the real sender was different\n error UnexpectedSender(address expectedSender, address actualSender);\n\n /// @notice Thrown when the oracle returns an invalid price for some asset\n error PriceError(address vToken);\n\n /// @notice Thrown if VToken unexpectedly returned a nonzero error code while trying to get account snapshot\n error SnapshotError(address vToken, address user);\n\n /// @notice Thrown when the market is not listed\n error MarketNotListed(address market);\n\n /// @notice Thrown when a market has an unexpected comptroller\n error ComptrollerMismatch();\n\n /// @notice Thrown when user is not member of market\n error MarketNotCollateral(address vToken, address user);\n\n /// @notice Thrown when borrow action is not paused\n error BorrowActionNotPaused();\n\n /// @notice Thrown when mint action is not paused\n error MintActionNotPaused();\n\n /// @notice Thrown when redeem action is not paused\n error RedeemActionNotPaused();\n\n /// @notice Thrown when repay action is not paused\n error RepayActionNotPaused();\n\n /// @notice Thrown when seize action is not paused\n error SeizeActionNotPaused();\n\n /// @notice Thrown when exit market action is not paused\n error ExitMarketActionNotPaused();\n\n /// @notice Thrown when transfer action is not paused\n error TransferActionNotPaused();\n\n /// @notice Thrown when enter market action is not paused\n error EnterMarketActionNotPaused();\n\n /// @notice Thrown when liquidate action is not paused\n error LiquidateActionNotPaused();\n\n /// @notice Thrown when borrow cap is not zero\n error BorrowCapIsNotZero();\n\n /// @notice Thrown when supply cap is not zero\n error SupplyCapIsNotZero();\n\n /// @notice Thrown when collateral factor is not zero\n error CollateralFactorIsNotZero();\n\n /**\n * @notice Thrown during the liquidation if user's total collateral amount is lower than\n * a predefined threshold. In this case only batch liquidations (either liquidateAccount\n * or healAccount) are available.\n */\n error MinimalCollateralViolated(uint256 expectedGreaterThan, uint256 actual);\n\n /**\n * @notice Thrown by the batch operations when the account's total collateral is above\n * `minLiquidatableCollateral`, which means it is large enough for a regular `VToken.liquidateBorrow`\n * @dev Same name, arguments and meaning as the shared `Comptroller`, so a decoder written against either one\n * reads this correctly.\n */\n error CollateralExceedsThreshold(uint256 expectedLessThanOrEqualTo, uint256 actual);\n\n /// @notice Thrown by `healAccount` when the collateral can clear the whole debt at each market's own liquidation\n /// incentive, so healing would forgive nothing and `liquidateAccount` has to be used instead\n error CollateralCoversDebt(uint256 borrows, uint256 maxClearableDebt);\n\n /// @notice Thrown by `liquidateAccount` when the debt is too large for the collateral to clear, so `healAccount`\n /// has to be used instead. Reports the debt and the largest debt the collateral could have cleared.\n /// @dev Deliberately not the shared `Comptroller`'s `InsufficientCollateral`: both arguments here are debt values\n /// rather than collateral values, and reusing that name would leave the two versions sharing one selector.\n error DebtExceedsClearableAmount(uint256 borrows, uint256 maxClearableDebt);\n\n /// @notice Thrown when the account doesn't have enough liquidity to redeem or borrow\n error InsufficientLiquidity();\n\n /// @notice Thrown when trying to liquidate a healthy account\n error InsufficientShortfall();\n\n /// @notice Thrown if the liquidation allowlist is enabled and the account liquidating is not on it\n error LiquidationNotAllowed(address liquidator);\n\n /// @notice Thrown when trying to repay more than allowed by close factor\n error TooMuchRepay();\n\n /// @notice Thrown if the user is trying to exit a market in which they have an outstanding debt\n error NonzeroBorrowBalance();\n\n /// @notice Thrown if a debt remains in any of the borrower's markets once every liquidation order has been\n /// executed, which means the orders passed to `liquidateAccount` did not cover the whole position\n error NonzeroBorrowBalanceAfterLiquidation();\n\n /// @notice Thrown when trying to perform an action that is paused\n error ActionPaused(address market, Action action);\n\n /// @notice Thrown when trying to add a market that is already listed\n error MarketAlreadyListed(address market);\n\n /// @notice Thrown when the market being listed does not identify itself as a VToken\n error InvalidVToken();\n\n /// @notice Thrown when an array argument is empty, or when two array arguments have different lengths\n error InvalidArrayLength();\n\n /// @notice Thrown if the supply cap is exceeded\n error SupplyCapExceeded(address market, uint256 cap);\n\n /// @notice Thrown if the account being credited with the minted vTokens is not on the market's supply allowlist\n error SupplyNotAllowed(address market, address supplier);\n\n /// @notice Thrown if the borrow cap is exceeded\n error BorrowCapExceeded(address market, uint256 cap);\n\n /// @notice Thrown if delegate approval status is already set to the requested value\n error DelegationStatusUnchanged();\n\n /// @notice Thrown when adding a rewards distributor that this pool already has\n error RewardsDistributorAlreadyExists();\n}\n\n/**\n * @title SpokeComptrollerViewInterface\n * @author Venus\n * @notice The getters `SpokeComptroller` adds on top of the pooled `Comptroller`, for integrators that read this pool\n * rather than implement it - the Liquidity Hub's spoke adapter, lenses, keepers and VIP tooling.\n * @dev Standalone and not implemented by `SpokeComptroller`, matching how `ComptrollerViewInterface` exposes the\n * shared pool's getters.\n *\n * Scope is the supply side: what an integrator that funds this pool has to read before and while it supplies, plus\n * the two allowlists and the per-market discount that only exist on this fork. `supplyCaps` and `actionPaused` are\n * repeated from `ComptrollerViewInterface` and `ComptrollerInterface` rather than inherited, so that a consumer of a\n * spoke pool needs one import and not three. `actionPaused` also has to be repeated on its own terms: the shared\n * declaration types its second argument as the `Action` enum, and a consumer that does not want this repo's enum in\n * its build needs the ABI-equivalent `uint8` form. The two share a selector, so they cannot both be declared in one\n * inheritance chain - which is the reason this interface inherits nothing.\n *\n * `markets` is repeated for a different reason: the shared declaration returns two of the three words the getter\n * actually produces, so a consumer reading it there silently loses `liquidationThresholdMantissa`. The form declared\n * here returns all three.\n *\n * Anything else the fork shares with the pooled `Comptroller` - `borrowCaps`, `oracle`, `closeFactorMantissa`,\n * `minLiquidatableCollateral` - is read through `ComptrollerViewInterface` as usual.\n */\ninterface SpokeComptrollerViewInterface {\n /**\n * @notice The listing state and both collateral weights of a market\n * @dev The auto-generated getter over the `markets` mapping. It returns three words, one per non-mapping member\n * of `Market`. `ComptrollerViewInterface` declares only the first two, so a caller reading the getter through\n * that declaration drops `liquidationThresholdMantissa` without any error: the extra word is simply trailing\n * return data the decoder ignores. Read it here to get all three.\n * @param vToken The market to query\n * @return isListed True once the market has been added to the pool\n * @return collateralFactorMantissa The most an account may borrow against this collateral, scaled by 1e18\n * @return liquidationThresholdMantissa The weighting past which the position becomes liquidatable, scaled by 1e18\n */\n function markets(\n address vToken\n ) external view returns (bool isListed, uint256 collateralFactorMantissa, uint256 liquidationThresholdMantissa);\n\n /**\n * @notice Whether a market may be liquidated while the borrower is still above the liquidation threshold\n * @dev Read in `preLiquidateHook`. While this is set the close factor no longer bounds the repayment, so a\n * position in the market can be closed in full in one call.\n * @param vToken The market to read the setting of\n * @return enabled True if this market allows liquidation without a shortfall\n */\n function isForcedLiquidationEnabled(address vToken) external view returns (bool enabled);\n\n /**\n * @notice The most underlying a market will hold before it stops accepting supply\n * @dev `type(uint256).max` disables the check. Zero is a real cap of zero rather than an \"unset\" sentinel:\n * `preMintHook` compares `nextTotalSupply > supplyCap`, so a market left at zero rejects every mint.\n * @param vToken The market to query\n * @return cap The market's supply cap, in underlying units\n */\n function supplyCaps(address vToken) external view returns (uint256 cap);\n\n /**\n * @notice Whether a market accepts supply only from the accounts on its supply allowlist\n * @dev Enforced in `preMintHook` alone. Redeeming is never restricted, and a market that has never had this\n * enabled accepts supply from anyone.\n * @param vToken The market to read the setting of\n * @return enabled True if the market's supply allowlist is armed\n */\n function isSupplyAllowlistEnabled(address vToken) external view returns (bool enabled);\n\n /**\n * @notice Whether an account may supply to a market while that market's supply allowlist is enabled\n * @dev The account this meters is the one CREDITED with the newly minted vTokens, not the one paying for them:\n * `mintBehalf` lets a third party fund a mint attributed to someone else, and metering the recipient is what\n * bounds the market's supply.\n * @param vToken The market whose allowlist to read\n * @param supplier The account to test\n * @return allowed True if `supplier` may be credited with this market's vTokens\n */\n function isAllowedSupplier(address vToken, address supplier) external view returns (bool allowed);\n\n /**\n * @notice Whether seizing collateral in this pool is restricted to the accounts on the liquidation allowlist\n * @dev Pool-wide rather than per market, because `healAccount` seizes across every market the borrower is in and\n * so cannot attribute a seizure to a single one of them.\n * @return enabled True if the pool's liquidation allowlist is armed\n */\n function isLiquidationAllowlistEnabled() external view returns (bool enabled);\n\n /**\n * @notice Whether an account may seize collateral in this pool while the liquidation allowlist is enabled\n * @param liquidator The account to test\n * @return allowed True if `liquidator` may seize collateral in this pool\n */\n function isAllowedLiquidator(address liquidator) external view returns (bool allowed);\n\n /**\n * @notice The discount a market has of its own, or zero when it takes the pool-wide one\n * @dev Zero is the \"unset\" sentinel rather than a real discount. Read `effectiveLiquidationIncentive` to get the\n * value that actually applies to a market.\n * @param vToken The collateral market to read the discount of\n * @return incentiveMantissa The market's own discount scaled by 1e18, or zero if it has none\n */\n function liquidationIncentives(address vToken) external view returns (uint256 incentiveMantissa);\n\n /**\n * @notice The discount that applies to a market: its own if it has one, otherwise the pool-wide value\n * @param vToken The collateral market to read the discount of\n * @return incentiveMantissa The discount that prices this market's collateral, scaled by 1e18\n */\n function effectiveLiquidationIncentive(address vToken) external view returns (uint256 incentiveMantissa);\n\n /**\n * @notice The oracle that bounds an asset's price against a recent window\n * @dev Read only where the collateral factor weights a position. The liquidation-threshold paths stay on the\n * `oracle`, because they route liquidations. Zero until governance sets it, and while it is zero borrowing and\n * redeeming fail closed.\n * @return boundedOracle The deviation-bounded oracle this pool prices borrowing capacity through\n */\n function deviationBoundedOracle() external view returns (IDeviationBoundedOracle boundedOracle);\n\n /**\n * @notice Whether an action is paused on a market\n * @dev Typed as `uint8` rather than `Action` so that a consumer can read this without importing the enum. The\n * encoding is identical; the deployed ordering is\n * `{ MINT, REDEEM, BORROW, REPAY, SEIZE, LIQUIDATE, TRANSFER, ENTER_MARKET, EXIT_MARKET }`.\n * @param market The market to query\n * @param action The action, as its position in `Action`\n * @return paused True if the action is paused on this market\n */\n function actionPaused(address market, uint8 action) external view returns (bool paused);\n}\n" + }, + "contracts/VToken.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\n\nimport { Ownable2StepUpgradeable } from \"@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol\";\nimport { IERC20Upgradeable } from \"@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol\";\nimport { SafeERC20Upgradeable } from \"@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol\";\nimport { AccessControlledV8 } from \"@venusprotocol/governance-contracts/contracts/Governance/AccessControlledV8.sol\";\nimport { IProtocolShareReserve } from \"@venusprotocol/protocol-reserve/contracts/Interfaces/IProtocolShareReserve.sol\";\n\nimport { VTokenInterface } from \"./VTokenInterfaces.sol\";\nimport { ComptrollerInterface, ComptrollerViewInterface } from \"./ComptrollerInterface.sol\";\nimport { TokenErrorReporter } from \"./ErrorReporter.sol\";\nimport { InterestRateModel } from \"./InterestRateModel.sol\";\nimport { ExponentialNoError } from \"./ExponentialNoError.sol\";\nimport { TimeManagerV8 } from \"@venusprotocol/solidity-utilities/contracts/TimeManagerV8.sol\";\nimport { ensureNonzeroAddress } from \"./lib/validators.sol\";\n\n/**\n * @title VToken\n * @author Venus\n * @notice Each asset that is supported by a pool is integrated through an instance of the `VToken` contract. As outlined in the protocol overview,\n * each isolated pool creates its own `vToken` corresponding to an asset. Within a given pool, each included `vToken` is referred to as a market of\n * the pool. The main actions a user regularly interacts with in a market are:\n\n- mint/redeem of vTokens;\n- transfer of vTokens;\n- borrow/repay a loan on an underlying asset;\n- liquidate a borrow or liquidate/heal an account.\n\n * A user supplies the underlying asset to a pool by minting `vTokens`, where the corresponding `vToken` amount is determined by the `exchangeRate`.\n * The `exchangeRate` will change over time, dependent on a number of factors, some of which accrue interest. Additionally, once users have minted\n * `vToken` in a pool, they can borrow any asset in the isolated pool by using their `vToken` as collateral. In order to borrow an asset or use a `vToken`\n * as collateral, the user must be entered into each corresponding market (else, the `vToken` will not be considered collateral for a borrow). Note that\n * a user may borrow up to a portion of their collateral determined by the market’s collateral factor. However, if their borrowed amount exceeds an amount\n * calculated using the market’s corresponding liquidation threshold, the borrow is eligible for liquidation. When a user repays a borrow, they must also\n * pay off interest accrued on the borrow.\n * \n * The Venus protocol includes unique mechanisms for healing an account and liquidating an account. These actions are performed in the `Comptroller`\n * and consider all borrows and collateral for which a given account is entered within a market. These functions may only be called on an account with a\n * total collateral amount that is no larger than a universal `minLiquidatableCollateral` value, which is used for all markets within a `Comptroller`.\n * Both functions settle all of an account’s borrows, but `healAccount()` may add `badDebt` to a vToken. For more detail, see the description of\n * `healAccount()` and `liquidateAccount()` in the `Comptroller` summary section below.\n */\ncontract VToken is\n Ownable2StepUpgradeable,\n AccessControlledV8,\n VTokenInterface,\n ExponentialNoError,\n TokenErrorReporter,\n TimeManagerV8\n{\n using SafeERC20Upgradeable for IERC20Upgradeable;\n\n uint256 internal constant DEFAULT_PROTOCOL_SEIZE_SHARE_MANTISSA = 5e16; // 5%\n\n // Maximum fraction of interest that can be set aside for reserves\n uint256 internal constant MAX_RESERVE_FACTOR_MANTISSA = 1e18;\n\n // Maximum borrow rate that can ever be applied per slot(block or second)\n /// @custom:oz-upgrades-unsafe-allow state-variable-immutable\n uint256 internal immutable MAX_BORROW_RATE_MANTISSA;\n\n /**\n * Reentrancy Guard **\n */\n\n /**\n * @dev Prevents a contract from calling itself, directly or indirectly.\n */\n modifier nonReentrant() {\n require(_notEntered, \"re-entered\");\n _notEntered = false;\n _;\n _notEntered = true; // get a gas-refund post-Istanbul\n }\n\n /**\n * @param timeBased_ A boolean indicating whether the contract is based on time or block.\n * @param blocksPerYear_ The number of blocks per year\n * @param maxBorrowRateMantissa_ The maximum value of borrowing rate mantissa\n * @custom:oz-upgrades-unsafe-allow constructor\n */\n constructor(\n bool timeBased_,\n uint256 blocksPerYear_,\n uint256 maxBorrowRateMantissa_\n ) TimeManagerV8(timeBased_, blocksPerYear_) {\n // Note that the contract is upgradeable. Use initialize() or reinitializers\n // to set the state variables.\n require(maxBorrowRateMantissa_ <= 1e18, \"Max borrow rate must be <= 1e18\");\n\n MAX_BORROW_RATE_MANTISSA = maxBorrowRateMantissa_;\n _disableInitializers();\n }\n\n /**\n * @notice Construct a new money market\n * @param underlying_ The address of the underlying asset\n * @param comptroller_ The address of the Comptroller\n * @param interestRateModel_ The address of the interest rate model\n * @param initialExchangeRateMantissa_ The initial exchange rate, scaled by 1e18\n * @param name_ ERC-20 name of this token\n * @param symbol_ ERC-20 symbol of this token\n * @param decimals_ ERC-20 decimal precision of this token\n * @param admin_ Address of the administrator of this token\n * @param accessControlManager_ AccessControlManager contract address\n * @param riskManagement Addresses of risk & income related contracts\n * @param reserveFactorMantissa_ Percentage of borrow interest that goes to reserves (from 0 to 1e18)\n * @custom:error ZeroAddressNotAllowed is thrown when admin address is zero\n * @custom:error ZeroAddressNotAllowed is thrown when shortfall contract address is zero\n * @custom:error ZeroAddressNotAllowed is thrown when protocol share reserve address is zero\n */\n function initialize(\n address underlying_,\n ComptrollerInterface comptroller_,\n InterestRateModel interestRateModel_,\n uint256 initialExchangeRateMantissa_,\n string memory name_,\n string memory symbol_,\n uint8 decimals_,\n address admin_,\n address accessControlManager_,\n RiskManagementInit memory riskManagement,\n uint256 reserveFactorMantissa_\n ) external initializer {\n ensureNonzeroAddress(admin_);\n\n // Initialize the market\n _initialize(\n underlying_,\n comptroller_,\n interestRateModel_,\n initialExchangeRateMantissa_,\n name_,\n symbol_,\n decimals_,\n admin_,\n accessControlManager_,\n riskManagement,\n reserveFactorMantissa_\n );\n }\n\n /**\n * @notice Transfer `amount` tokens from `msg.sender` to `dst`\n * @param dst The address of the destination account\n * @param amount The number of tokens to transfer\n * @return success True if the transfer succeeded, reverts otherwise\n * @custom:event Emits Transfer event on success\n * @custom:error TransferNotAllowed is thrown if trying to transfer to self\n * @custom:access Not restricted\n */\n function transfer(address dst, uint256 amount) external override nonReentrant returns (bool) {\n _transferTokens(msg.sender, msg.sender, dst, amount);\n return true;\n }\n\n /**\n * @notice Transfer `amount` tokens from `src` to `dst`\n * @param src The address of the source account\n * @param dst The address of the destination account\n * @param amount The number of tokens to transfer\n * @return success True if the transfer succeeded, reverts otherwise\n * @custom:event Emits Transfer event on success\n * @custom:error TransferNotAllowed is thrown if trying to transfer to self\n * @custom:access Not restricted\n */\n function transferFrom(address src, address dst, uint256 amount) external override nonReentrant returns (bool) {\n _transferTokens(msg.sender, src, dst, amount);\n return true;\n }\n\n /**\n * @notice Approve `spender` to transfer up to `amount` from `src`\n * @dev This will overwrite the approval amount for `spender`\n * and is subject to issues noted [here](https://eips.ethereum.org/EIPS/eip-20#approve)\n * @param spender The address of the account which may transfer tokens\n * @param amount The number of tokens that are approved (uint256.max means infinite)\n * @return success Whether or not the approval succeeded\n * @custom:event Emits Approval event\n * @custom:access Not restricted\n * @custom:error ZeroAddressNotAllowed is thrown when spender address is zero\n */\n function approve(address spender, uint256 amount) external override returns (bool) {\n ensureNonzeroAddress(spender);\n\n address src = msg.sender;\n transferAllowances[src][spender] = amount;\n emit Approval(src, spender, amount);\n return true;\n }\n\n /**\n * @notice Increase approval for `spender`\n * @param spender The address of the account which may transfer tokens\n * @param addedValue The number of additional tokens spender can transfer\n * @return success Whether or not the approval succeeded\n * @custom:event Emits Approval event\n * @custom:access Not restricted\n * @custom:error ZeroAddressNotAllowed is thrown when spender address is zero\n */\n function increaseAllowance(address spender, uint256 addedValue) external override returns (bool) {\n ensureNonzeroAddress(spender);\n\n address src = msg.sender;\n uint256 newAllowance = transferAllowances[src][spender];\n newAllowance += addedValue;\n transferAllowances[src][spender] = newAllowance;\n\n emit Approval(src, spender, newAllowance);\n return true;\n }\n\n /**\n * @notice Decreases approval for `spender`\n * @param spender The address of the account which may transfer tokens\n * @param subtractedValue The number of tokens to remove from total approval\n * @return success Whether or not the approval succeeded\n * @custom:event Emits Approval event\n * @custom:access Not restricted\n * @custom:error ZeroAddressNotAllowed is thrown when spender address is zero\n */\n function decreaseAllowance(address spender, uint256 subtractedValue) external override returns (bool) {\n ensureNonzeroAddress(spender);\n\n address src = msg.sender;\n uint256 currentAllowance = transferAllowances[src][spender];\n require(currentAllowance >= subtractedValue, \"decreased allowance below zero\");\n unchecked {\n currentAllowance -= subtractedValue;\n }\n\n transferAllowances[src][spender] = currentAllowance;\n\n emit Approval(src, spender, currentAllowance);\n return true;\n }\n\n /**\n * @notice Get the underlying balance of the `owner`\n * @dev This also accrues interest in a transaction\n * @param owner The address of the account to query\n * @return amount The amount of underlying owned by `owner`\n */\n function balanceOfUnderlying(address owner) external override returns (uint256) {\n Exp memory exchangeRate = Exp({ mantissa: exchangeRateCurrent() });\n return mul_ScalarTruncate(exchangeRate, accountTokens[owner]);\n }\n\n /**\n * @notice Returns the current total borrows plus accrued interest\n * @return totalBorrows The total borrows with interest\n */\n function totalBorrowsCurrent() external override nonReentrant returns (uint256) {\n accrueInterest();\n return totalBorrows;\n }\n\n /**\n * @notice Accrue interest to updated borrowIndex and then calculate account's borrow balance using the updated borrowIndex\n * @param account The address whose balance should be calculated after updating borrowIndex\n * @return borrowBalance The calculated balance\n */\n function borrowBalanceCurrent(address account) external override nonReentrant returns (uint256) {\n accrueInterest();\n return _borrowBalanceStored(account);\n }\n\n /**\n * @notice Sender supplies assets into the market and receives vTokens in exchange\n * @dev Accrues interest whether or not the operation succeeds, unless reverted\n * @param mintAmount The amount of the underlying asset to supply\n * @return error Always NO_ERROR for compatibility with Venus core tooling\n * @custom:event Emits Mint and Transfer events; may emit AccrueInterest\n * @custom:access Not restricted\n */\n function mint(uint256 mintAmount) external override nonReentrant returns (uint256) {\n accrueInterest();\n\n _mintFresh(msg.sender, msg.sender, mintAmount);\n return NO_ERROR;\n }\n\n /**\n * @notice Sender calls on-behalf of minter. minter supplies assets into the market and receives vTokens in exchange\n * @dev Accrues interest whether or not the operation succeeds, unless reverted\n * @param minter User whom the supply will be attributed to\n * @param mintAmount The amount of the underlying asset to supply\n * @return error Always NO_ERROR for compatibility with Venus core tooling\n * @custom:event Emits Mint and Transfer events; may emit AccrueInterest\n * @custom:access Not restricted\n * @custom:error ZeroAddressNotAllowed is thrown when minter address is zero\n */\n function mintBehalf(address minter, uint256 mintAmount) external override nonReentrant returns (uint256) {\n ensureNonzeroAddress(minter);\n\n accrueInterest();\n\n _mintFresh(msg.sender, minter, mintAmount);\n return NO_ERROR;\n }\n\n /**\n * @notice Sender redeems vTokens in exchange for the underlying asset\n * @dev Accrues interest whether or not the operation succeeds, unless reverted\n * @param redeemTokens The number of vTokens to redeem into underlying\n * @return error Always NO_ERROR for compatibility with Venus core tooling\n * @custom:event Emits Redeem and Transfer events; may emit AccrueInterest\n * @custom:error RedeemTransferOutNotPossible is thrown when the protocol has insufficient cash\n * @custom:access Not restricted\n */\n function redeem(uint256 redeemTokens) external override nonReentrant returns (uint256) {\n accrueInterest();\n\n _redeemFresh(msg.sender, msg.sender, redeemTokens, 0);\n return NO_ERROR;\n }\n\n /**\n * @notice Sender redeems assets on behalf of some other address. This function is only available\n * for senders, explicitly marked as delegates of the supplier using `comptroller.updateDelegate`\n * @dev Accrues interest whether or not the operation succeeds, unless reverted\n * @param redeemer The user on behalf of whom to redeem\n * @param redeemTokens The number of vTokens to redeem into underlying\n * @return error Always NO_ERROR for compatibility with Venus core tooling\n * @custom:error InsufficientRedeemApproval is thrown when sender is not approved by the redeemer for the given amount\n * @custom:error RedeemTransferOutNotPossible is thrown when the protocol has insufficient cash\n * @custom:event Emits Redeem and Transfer events; may emit AccrueInterest\n * @custom:access Not restricted\n */\n function redeemBehalf(address redeemer, uint256 redeemTokens) external override nonReentrant returns (uint256) {\n _ensureSenderIsDelegateOf(redeemer);\n\n accrueInterest();\n\n _redeemFresh(redeemer, msg.sender, redeemTokens, 0);\n return NO_ERROR;\n }\n\n /**\n * @notice Sender redeems vTokens in exchange for a specified amount of underlying asset\n * @dev Accrues interest whether or not the operation succeeds, unless reverted\n * @param redeemAmount The amount of underlying to receive from redeeming vTokens\n * @return error Always NO_ERROR for compatibility with Venus core tooling\n */\n function redeemUnderlying(uint256 redeemAmount) external override nonReentrant returns (uint256) {\n accrueInterest();\n\n _redeemFresh(msg.sender, msg.sender, 0, redeemAmount);\n return NO_ERROR;\n }\n\n /**\n * @notice Sender redeems underlying assets on behalf of some other address. This function is only available\n * for senders, explicitly marked as delegates of the supplier using `comptroller.updateDelegate`\n * @dev Accrues interest whether or not the operation succeeds, unless reverted\n * @param redeemer, on behalf of whom to redeem\n * @param redeemAmount The amount of underlying to receive from redeeming vTokens\n * @return error Always NO_ERROR for compatibility with Venus core tooling\n * @custom:error InsufficientRedeemApproval is thrown when sender is not approved by the redeemer for the given amount\n * @custom:event Emits Redeem and Transfer events; may emit AccrueInterest\n * @custom:access Not restricted\n */\n function redeemUnderlyingBehalf(\n address redeemer,\n uint256 redeemAmount\n ) external override nonReentrant returns (uint256) {\n _ensureSenderIsDelegateOf(redeemer);\n\n accrueInterest();\n\n _redeemFresh(redeemer, msg.sender, 0, redeemAmount);\n return NO_ERROR;\n }\n\n /**\n * @notice Sender borrows assets from the protocol to their own address\n * @param borrowAmount The amount of the underlying asset to borrow\n * @return error Always NO_ERROR for compatibility with Venus core tooling\n * @custom:event Emits Borrow event; may emit AccrueInterest\n * @custom:error BorrowCashNotAvailable is thrown when the protocol has insufficient cash\n * @custom:access Not restricted\n */\n function borrow(uint256 borrowAmount) external override nonReentrant returns (uint256) {\n accrueInterest();\n\n _borrowFresh(msg.sender, msg.sender, borrowAmount);\n return NO_ERROR;\n }\n\n /**\n * @notice Sender borrows assets on behalf of some other address. This function is only available\n * for senders, explicitly marked as delegates of the borrower using `comptroller.updateDelegate`\n * @param borrower The borrower, on behalf of whom to borrow\n * @param borrowAmount The amount of the underlying asset to borrow\n * @return error Always NO_ERROR for compatibility with Venus core tooling\n * @custom:error DelegateNotApproved is thrown if caller is not approved delegate\n * @custom:error BorrowCashNotAvailable is thrown when the protocol has insufficient cash\n * @custom:event Emits Borrow event; may emit AccrueInterest\n * @custom:access Not restricted\n */\n function borrowBehalf(address borrower, uint256 borrowAmount) external override returns (uint256) {\n _ensureSenderIsDelegateOf(borrower);\n accrueInterest();\n\n _borrowFresh(borrower, msg.sender, borrowAmount);\n return NO_ERROR;\n }\n\n /**\n * @notice Sender repays their own borrow\n * @param repayAmount The amount to repay, or type(uint256).max for the full outstanding amount\n * @return error Always NO_ERROR for compatibility with Venus core tooling\n * @custom:event Emits RepayBorrow event; may emit AccrueInterest\n * @custom:access Not restricted\n */\n function repayBorrow(uint256 repayAmount) external override nonReentrant returns (uint256) {\n accrueInterest();\n\n _repayBorrowFresh(msg.sender, msg.sender, repayAmount);\n return NO_ERROR;\n }\n\n /**\n * @notice Sender repays a borrow belonging to borrower\n * @param borrower the account with the debt being payed off\n * @param repayAmount The amount to repay, or type(uint256).max for the full outstanding amount\n * @return error Always NO_ERROR for compatibility with Venus core tooling\n * @custom:event Emits RepayBorrow event; may emit AccrueInterest\n * @custom:access Not restricted\n */\n function repayBorrowBehalf(address borrower, uint256 repayAmount) external override nonReentrant returns (uint256) {\n accrueInterest();\n\n _repayBorrowFresh(msg.sender, borrower, repayAmount);\n return NO_ERROR;\n }\n\n /**\n * @notice The sender liquidates the borrowers collateral.\n * The collateral seized is transferred to the liquidator.\n * @param borrower The borrower of this vToken to be liquidated\n * @param repayAmount The amount of the underlying borrowed asset to repay\n * @param vTokenCollateral The market in which to seize collateral from the borrower\n * @return error Always NO_ERROR for compatibility with Venus core tooling\n * @custom:event Emits LiquidateBorrow event; may emit AccrueInterest\n * @custom:error LiquidateAccrueCollateralInterestFailed is thrown when it is not possible to accrue interest on the collateral vToken\n * @custom:error LiquidateCollateralFreshnessCheck is thrown when interest has not been accrued on the collateral vToken\n * @custom:error LiquidateLiquidatorIsBorrower is thrown when trying to liquidate self\n * @custom:error LiquidateCloseAmountIsZero is thrown when repayment amount is zero\n * @custom:error LiquidateCloseAmountIsUintMax is thrown when repayment amount is UINT_MAX\n * @custom:access Not restricted\n */\n function liquidateBorrow(\n address borrower,\n uint256 repayAmount,\n VTokenInterface vTokenCollateral\n ) external override returns (uint256) {\n _liquidateBorrow(msg.sender, borrower, repayAmount, vTokenCollateral, false);\n return NO_ERROR;\n }\n\n /**\n * @notice sets protocol share accumulated from liquidations\n * @dev must be equal or less than liquidation incentive - 1\n * @param newProtocolSeizeShareMantissa_ new protocol share mantissa\n * @custom:event Emits NewProtocolSeizeShare event on success\n * @custom:error Unauthorized error is thrown when the call is not authorized by AccessControlManager\n * @custom:error ProtocolSeizeShareTooBig is thrown when the new seize share is too high\n * @custom:access Controlled by AccessControlManager\n */\n function setProtocolSeizeShare(uint256 newProtocolSeizeShareMantissa_) external {\n _checkAccessAllowed(\"setProtocolSeizeShare(uint256)\");\n uint256 liquidationIncentive = ComptrollerViewInterface(address(comptroller)).liquidationIncentiveMantissa();\n if (newProtocolSeizeShareMantissa_ + MANTISSA_ONE > liquidationIncentive) {\n revert ProtocolSeizeShareTooBig();\n }\n\n uint256 oldProtocolSeizeShareMantissa = protocolSeizeShareMantissa;\n protocolSeizeShareMantissa = newProtocolSeizeShareMantissa_;\n emit NewProtocolSeizeShare(oldProtocolSeizeShareMantissa, newProtocolSeizeShareMantissa_);\n }\n\n /**\n * @notice accrues interest and sets a new reserve factor for the protocol using _setReserveFactorFresh\n * @dev Admin function to accrue interest and set a new reserve factor\n * @param newReserveFactorMantissa New reserve factor (from 0 to 1e18)\n * @custom:event Emits NewReserveFactor event; may emit AccrueInterest\n * @custom:error Unauthorized error is thrown when the call is not authorized by AccessControlManager\n * @custom:error SetReserveFactorBoundsCheck is thrown when the new reserve factor is too high\n * @custom:access Controlled by AccessControlManager\n */\n function setReserveFactor(uint256 newReserveFactorMantissa) external override nonReentrant {\n _checkAccessAllowed(\"setReserveFactor(uint256)\");\n\n accrueInterest();\n _setReserveFactorFresh(newReserveFactorMantissa);\n }\n\n /**\n * @notice Accrues interest and reduces reserves by transferring to the protocol reserve contract\n * @dev Gracefully return if reserves already reduced in accrueInterest\n * @param reduceAmount Amount of reduction to reserves\n * @custom:event Emits ReservesReduced event; may emit AccrueInterest\n * @custom:error ReduceReservesCashNotAvailable is thrown when the vToken does not have sufficient cash\n * @custom:error ReduceReservesCashValidation is thrown when trying to withdraw more cash than the reserves have\n * @custom:access Not restricted\n */\n function reduceReserves(uint256 reduceAmount) external override nonReentrant {\n accrueInterest();\n if (reduceReservesBlockNumber == getBlockNumberOrTimestamp()) return;\n _reduceReservesFresh(reduceAmount);\n }\n\n /**\n * @notice The sender adds to reserves.\n * @param addAmount The amount of underlying token to add as reserves\n * @custom:event Emits ReservesAdded event; may emit AccrueInterest\n * @custom:access Not restricted\n */\n function addReserves(uint256 addAmount) external override nonReentrant {\n accrueInterest();\n _addReservesFresh(addAmount);\n }\n\n /**\n * @notice accrues interest and updates the interest rate model using _setInterestRateModelFresh\n * @dev Admin function to accrue interest and update the interest rate model\n * @param newInterestRateModel the new interest rate model to use\n * @custom:event Emits NewMarketInterestRateModel event; may emit AccrueInterest\n * @custom:error Unauthorized error is thrown when the call is not authorized by AccessControlManager\n * @custom:access Controlled by AccessControlManager\n */\n function setInterestRateModel(InterestRateModel newInterestRateModel) external override {\n _checkAccessAllowed(\"setInterestRateModel(address)\");\n\n accrueInterest();\n _setInterestRateModelFresh(newInterestRateModel);\n }\n\n /**\n * @notice Repays a certain amount of debt, treats the rest of the borrow as bad debt, essentially\n * \"forgiving\" the borrower. Healing is a situation that should rarely happen. However, some pools\n * may list risky assets or be configured improperly – we want to still handle such cases gracefully.\n * We assume that Comptroller does the seizing, so this function is only available to Comptroller.\n * @dev This function does not call any Comptroller hooks (like \"healAllowed\"), because we assume\n * the Comptroller does all the necessary checks before calling this function.\n * @param payer account who repays the debt\n * @param borrower account to heal\n * @param repayAmount amount to repay\n * @custom:event Emits RepayBorrow, BadDebtIncreased events; may emit AccrueInterest\n * @custom:error HealBorrowUnauthorized is thrown when the request does not come from Comptroller\n * @custom:access Only Comptroller\n */\n function healBorrow(address payer, address borrower, uint256 repayAmount) external override nonReentrant {\n if (repayAmount != 0) {\n comptroller.preRepayHook(address(this), borrower);\n }\n\n if (msg.sender != address(comptroller)) {\n revert HealBorrowUnauthorized();\n }\n\n uint256 accountBorrowsPrev = _borrowBalanceStored(borrower);\n uint256 totalBorrowsNew = totalBorrows;\n\n uint256 actualRepayAmount;\n if (repayAmount != 0) {\n // _doTransferIn reverts if anything goes wrong, since we can't be sure if side effects occurred.\n // We violate checks-effects-interactions here to account for tokens that take transfer fees\n actualRepayAmount = _doTransferIn(payer, repayAmount);\n totalBorrowsNew = totalBorrowsNew - actualRepayAmount;\n emit RepayBorrow(\n payer,\n borrower,\n actualRepayAmount,\n accountBorrowsPrev - actualRepayAmount,\n totalBorrowsNew\n );\n }\n\n // The transaction will fail if trying to repay too much\n uint256 badDebtDelta = accountBorrowsPrev - actualRepayAmount;\n if (badDebtDelta != 0) {\n uint256 badDebtOld = badDebt;\n uint256 badDebtNew = badDebtOld + badDebtDelta;\n totalBorrowsNew = totalBorrowsNew - badDebtDelta;\n badDebt = badDebtNew;\n\n // We treat healing as \"repayment\", where vToken is the payer\n emit RepayBorrow(address(this), borrower, badDebtDelta, 0, totalBorrowsNew);\n emit BadDebtIncreased(borrower, badDebtDelta, badDebtOld, badDebtNew);\n }\n\n accountBorrows[borrower].principal = 0;\n accountBorrows[borrower].interestIndex = borrowIndex;\n totalBorrows = totalBorrowsNew;\n\n emit HealBorrow(payer, borrower, repayAmount);\n }\n\n /**\n * @notice The extended version of liquidations, callable only by Comptroller. May skip\n * the close factor check. The collateral seized is transferred to the liquidator.\n * @param liquidator The address repaying the borrow and seizing collateral\n * @param borrower The borrower of this vToken to be liquidated\n * @param repayAmount The amount of the underlying borrowed asset to repay\n * @param vTokenCollateral The market in which to seize collateral from the borrower\n * @param skipLiquidityCheck If set to true, allows to liquidate up to 100% of the borrow\n * regardless of the account liquidity\n * @custom:event Emits LiquidateBorrow event; may emit AccrueInterest\n * @custom:error ForceLiquidateBorrowUnauthorized is thrown when the request does not come from Comptroller\n * @custom:error LiquidateAccrueCollateralInterestFailed is thrown when it is not possible to accrue interest on the collateral vToken\n * @custom:error LiquidateCollateralFreshnessCheck is thrown when interest has not been accrued on the collateral vToken\n * @custom:error LiquidateLiquidatorIsBorrower is thrown when trying to liquidate self\n * @custom:error LiquidateCloseAmountIsZero is thrown when repayment amount is zero\n * @custom:error LiquidateCloseAmountIsUintMax is thrown when repayment amount is UINT_MAX\n * @custom:access Only Comptroller\n */\n function forceLiquidateBorrow(\n address liquidator,\n address borrower,\n uint256 repayAmount,\n VTokenInterface vTokenCollateral,\n bool skipLiquidityCheck\n ) external override {\n if (msg.sender != address(comptroller)) {\n revert ForceLiquidateBorrowUnauthorized();\n }\n _liquidateBorrow(liquidator, borrower, repayAmount, vTokenCollateral, skipLiquidityCheck);\n }\n\n /**\n * @notice Transfers collateral tokens (this market) to the liquidator.\n * @dev Will fail unless called by another vToken during the process of liquidation.\n * It's absolutely critical to use msg.sender as the borrowed vToken and not a parameter.\n * @param liquidator The account receiving seized collateral\n * @param borrower The account having collateral seized\n * @param seizeTokens The number of vTokens to seize\n * @custom:event Emits Transfer, ReservesAdded events\n * @custom:error LiquidateSeizeLiquidatorIsBorrower is thrown when trying to liquidate self\n * @custom:access Not restricted\n */\n function seize(address liquidator, address borrower, uint256 seizeTokens) external override nonReentrant {\n _seize(msg.sender, liquidator, borrower, seizeTokens);\n }\n\n /**\n * @notice Updates bad debt\n * @dev Called only when bad debt is recovered from auction. Updates internal cash balance.\n * @param recoveredAmount_ The amount of bad debt recovered\n * @custom:event Emits BadDebtRecovered event\n * @custom:access Only Shortfall contract\n */\n function badDebtRecovered(uint256 recoveredAmount_) external {\n require(msg.sender == shortfall, \"only shortfall contract can update bad debt\");\n require(recoveredAmount_ <= badDebt, \"more than bad debt recovered from auction\");\n\n uint256 badDebtOld = badDebt;\n uint256 badDebtNew = badDebtOld - recoveredAmount_;\n badDebt = badDebtNew;\n internalCash += recoveredAmount_;\n\n emit BadDebtRecovered(badDebtOld, badDebtNew);\n }\n\n /**\n * @notice Sets protocol share reserve contract address\n * @param protocolShareReserve_ The address of the protocol share reserve contract\n * @custom:error ZeroAddressNotAllowed is thrown when protocol share reserve address is zero\n * @custom:access Only Governance\n */\n function setProtocolShareReserve(address payable protocolShareReserve_) external onlyOwner {\n _setProtocolShareReserve(protocolShareReserve_);\n }\n\n /**\n * @notice Sets shortfall contract address\n * @param shortfall_ The address of the shortfall contract\n * @custom:error ZeroAddressNotAllowed is thrown when shortfall contract address is zero\n * @custom:access Only Governance\n */\n function setShortfallContract(address shortfall_) external onlyOwner {\n _setShortfallContract(shortfall_);\n }\n\n /**\n * @notice A public function to sweep accidental ERC-20 transfers to this contract. Tokens are sent to admin (timelock)\n * @param token The address of the ERC-20 token to sweep\n * @custom:access Only Governance\n */\n function sweepToken(IERC20Upgradeable token) external override {\n require(msg.sender == owner(), \"VToken::sweepToken: only admin can sweep tokens\");\n require(address(token) != underlying, \"VToken::sweepToken: can not sweep underlying token\");\n uint256 balance = token.balanceOf(address(this));\n token.safeTransfer(owner(), balance);\n\n emit SweepToken(address(token));\n }\n\n /**\n * @notice Sync internalCash with the actual underlying token balance\n * @dev Used for one-time migration after upgrade to initialize internalCash\n * @custom:event Emits CashSynced\n * @custom:access Controlled by AccessControlManager\n */\n function syncCash() external override nonReentrant {\n _checkAccessAllowed(\"syncCash()\");\n uint256 oldInternalCash = internalCash;\n uint256 actualCash = IERC20Upgradeable(underlying).balanceOf(address(this));\n internalCash = actualCash;\n emit CashSynced(oldInternalCash, actualCash);\n }\n\n /**\n * @notice A public function to set new threshold of slot(block or second) difference after which funds will be sent to the protocol share reserve\n * @param _newReduceReservesBlockOrTimestampDelta slot(block or second) difference value\n * @custom:access Only Governance\n */\n function setReduceReservesBlockDelta(uint256 _newReduceReservesBlockOrTimestampDelta) external {\n _checkAccessAllowed(\"setReduceReservesBlockDelta(uint256)\");\n require(_newReduceReservesBlockOrTimestampDelta > 0, \"Invalid Input\");\n emit NewReduceReservesBlockDelta(reduceReservesBlockDelta, _newReduceReservesBlockOrTimestampDelta);\n reduceReservesBlockDelta = _newReduceReservesBlockOrTimestampDelta;\n }\n\n /**\n * @notice Get the current allowance from `owner` for `spender`\n * @param owner The address of the account which owns the tokens to be spent\n * @param spender The address of the account which may transfer tokens\n * @return amount The number of tokens allowed to be spent (type(uint256).max means infinite)\n */\n function allowance(address owner, address spender) external view override returns (uint256) {\n return transferAllowances[owner][spender];\n }\n\n /**\n * @notice Get the token balance of the `owner`\n * @param owner The address of the account to query\n * @return amount The number of tokens owned by `owner`\n */\n function balanceOf(address owner) external view override returns (uint256) {\n return accountTokens[owner];\n }\n\n /**\n * @notice Get a snapshot of the account's balances, and the cached exchange rate\n * @dev This is used by comptroller to more efficiently perform liquidity checks.\n * @param account Address of the account to snapshot\n * @return error Always NO_ERROR for compatibility with Venus core tooling\n * @return vTokenBalance User's balance of vTokens\n * @return borrowBalance Amount owed in terms of underlying\n * @return exchangeRate Stored exchange rate\n */\n function getAccountSnapshot(\n address account\n )\n external\n view\n override\n returns (uint256 error, uint256 vTokenBalance, uint256 borrowBalance, uint256 exchangeRate)\n {\n return (NO_ERROR, accountTokens[account], _borrowBalanceStored(account), _exchangeRateStored());\n }\n\n /**\n * @notice Get the internally tracked cash balance of this vToken in the underlying asset\n * @return cash The quantity of underlying asset tracked internally by this contract\n */\n function getCash() external view override returns (uint256) {\n return _getCashPrior();\n }\n\n /**\n * @notice Returns the current per slot(block or second) borrow interest rate for this vToken\n * @return rate The borrow interest rate per slot(block or second), scaled by 1e18\n */\n function borrowRatePerBlock() external view override returns (uint256) {\n return interestRateModel.getBorrowRate(_getCashPrior(), totalBorrows, totalReserves, badDebt);\n }\n\n /**\n * @notice Returns the current per-slot(block or second) supply interest rate for this v\n * @return rate The supply interest rate per slot(block or second), scaled by 1e18\n */\n function supplyRatePerBlock() external view override returns (uint256) {\n return\n interestRateModel.getSupplyRate(\n _getCashPrior(),\n totalBorrows,\n totalReserves,\n reserveFactorMantissa,\n badDebt\n );\n }\n\n /**\n * @notice Return the borrow balance of account based on stored data\n * @param account The address whose balance should be calculated\n * @return borrowBalance The calculated balance\n */\n function borrowBalanceStored(address account) external view override returns (uint256) {\n return _borrowBalanceStored(account);\n }\n\n /**\n * @notice Calculates the exchange rate from the underlying to the VToken\n * @dev This function does not accrue interest before calculating the exchange rate\n * @return exchangeRate Calculated exchange rate scaled by 1e18\n */\n function exchangeRateStored() external view override returns (uint256) {\n return _exchangeRateStored();\n }\n\n /**\n * @notice Accrue interest then return the up-to-date exchange rate\n * @return exchangeRate Calculated exchange rate scaled by 1e18\n */\n function exchangeRateCurrent() public override nonReentrant returns (uint256) {\n accrueInterest();\n return _exchangeRateStored();\n }\n\n /**\n * @notice Applies accrued interest to total borrows and reserves\n * @dev This calculates interest accrued from the last checkpointed slot(block or second)\n * up to the current slot(block or second) and writes new checkpoint to storage and\n * reduce spread reserves to protocol share reserve\n * if currentSlot - reduceReservesBlockNumber >= slotDelta\n * @return Always NO_ERROR\n * @custom:event Emits AccrueInterest event on success\n * @custom:access Not restricted\n */\n function accrueInterest() public virtual override returns (uint256) {\n /* Remember the initial block number or timestamp */\n uint256 currentSlotNumber = getBlockNumberOrTimestamp();\n uint256 accrualSlotNumberPrior = accrualBlockNumber;\n\n /* Short-circuit accumulating 0 interest */\n if (accrualSlotNumberPrior == currentSlotNumber) {\n return NO_ERROR;\n }\n\n /* Read the previous values out of storage */\n uint256 cashPrior = _getCashPrior();\n uint256 borrowsPrior = totalBorrows;\n uint256 reservesPrior = totalReserves;\n uint256 borrowIndexPrior = borrowIndex;\n\n /* Calculate the current borrow interest rate */\n uint256 borrowRateMantissa = interestRateModel.getBorrowRate(cashPrior, borrowsPrior, reservesPrior, badDebt);\n require(borrowRateMantissa <= MAX_BORROW_RATE_MANTISSA, \"borrow rate is absurdly high\");\n\n /* Calculate the number of slots elapsed since the last accrual */\n uint256 slotDelta = currentSlotNumber - accrualSlotNumberPrior;\n\n /*\n * Calculate the interest accumulated into borrows and reserves and the new index:\n * simpleInterestFactor = borrowRate * slotDelta\n * interestAccumulated = simpleInterestFactor * totalBorrows\n * totalBorrowsNew = interestAccumulated + totalBorrows\n * totalReservesNew = interestAccumulated * reserveFactor + totalReserves\n * borrowIndexNew = simpleInterestFactor * borrowIndex + borrowIndex\n */\n\n Exp memory simpleInterestFactor = mul_(Exp({ mantissa: borrowRateMantissa }), slotDelta);\n uint256 interestAccumulated = mul_ScalarTruncate(simpleInterestFactor, borrowsPrior);\n uint256 totalBorrowsNew = interestAccumulated + borrowsPrior;\n uint256 totalReservesNew = mul_ScalarTruncateAddUInt(\n Exp({ mantissa: reserveFactorMantissa }),\n interestAccumulated,\n reservesPrior\n );\n uint256 borrowIndexNew = mul_ScalarTruncateAddUInt(simpleInterestFactor, borrowIndexPrior, borrowIndexPrior);\n\n /////////////////////////\n // EFFECTS & INTERACTIONS\n // (No safe failures beyond this point)\n\n /* We write the previously calculated values into storage */\n accrualBlockNumber = currentSlotNumber;\n borrowIndex = borrowIndexNew;\n totalBorrows = totalBorrowsNew;\n totalReserves = totalReservesNew;\n\n if (currentSlotNumber - reduceReservesBlockNumber >= reduceReservesBlockDelta) {\n reduceReservesBlockNumber = currentSlotNumber;\n if (cashPrior < totalReservesNew) {\n _reduceReservesFresh(cashPrior);\n } else {\n _reduceReservesFresh(totalReservesNew);\n }\n }\n\n /* We emit an AccrueInterest event */\n emit AccrueInterest(cashPrior, interestAccumulated, borrowIndexNew, totalBorrowsNew);\n\n return NO_ERROR;\n }\n\n /**\n * @notice User supplies assets into the market and receives vTokens in exchange\n * @dev Assumes interest has already been accrued up to the current block or timestamp\n * @param payer The address of the account which is sending the assets for supply\n * @param minter The address of the account which is supplying the assets\n * @param mintAmount The amount of the underlying asset to supply\n */\n function _mintFresh(address payer, address minter, uint256 mintAmount) internal {\n /* Fail if mint not allowed */\n comptroller.preMintHook(address(this), minter, mintAmount);\n\n /* Verify market's slot(block or second) number equals current slot(block or second) number */\n if (accrualBlockNumber != getBlockNumberOrTimestamp()) {\n revert MintFreshnessCheck();\n }\n\n Exp memory exchangeRate = Exp({ mantissa: _exchangeRateStored() });\n\n /////////////////////////\n // EFFECTS & INTERACTIONS\n // (No safe failures beyond this point)\n\n /*\n * We call `_doTransferIn` for the minter and the mintAmount.\n * `_doTransferIn` reverts if anything goes wrong, since we can't be sure if\n * side-effects occurred. The function returns the amount actually transferred,\n * in case of a fee. On success, the vToken holds an additional `actualMintAmount`\n * of cash.\n */\n uint256 actualMintAmount = _doTransferIn(payer, mintAmount);\n\n /*\n * We get the current exchange rate and calculate the number of vTokens to be minted:\n * mintTokens = actualMintAmount / exchangeRate\n */\n\n uint256 mintTokens = div_(actualMintAmount, exchangeRate);\n\n /*\n * We calculate the new total supply of vTokens and minter token balance, checking for overflow:\n * totalSupplyNew = totalSupply + mintTokens\n * accountTokensNew = accountTokens[minter] + mintTokens\n * And write them into storage\n */\n totalSupply = totalSupply + mintTokens;\n uint256 balanceAfter = accountTokens[minter] + mintTokens;\n accountTokens[minter] = balanceAfter;\n\n /* We emit a Mint event, and a Transfer event */\n emit Mint(minter, actualMintAmount, mintTokens, balanceAfter);\n emit Transfer(address(0), minter, mintTokens);\n\n /* We call the defense and prime accrue interest hook */\n comptroller.mintVerify(address(this), minter, actualMintAmount, mintTokens);\n }\n\n /**\n * @notice Redeemer redeems vTokens in exchange for the underlying assets, transferred to the receiver. Redeemer and receiver can be the same\n * address, or different addresses if the receiver was previously approved by the redeemer as a valid delegate (see Comptroller.updateDelegate)\n * @dev Assumes interest has already been accrued up to the current slot(block or second)\n * @param redeemer The address of the account which is redeeming the tokens\n * @param receiver The receiver of the underlying tokens\n * @param redeemTokensIn The number of vTokens to redeem into underlying (only one of redeemTokensIn or redeemAmountIn may be non-zero)\n * @param redeemAmountIn The number of underlying tokens to receive from redeeming vTokens (only one of redeemTokensIn or redeemAmountIn may be non-zero)\n */\n function _redeemFresh(address redeemer, address receiver, uint256 redeemTokensIn, uint256 redeemAmountIn) internal {\n require(redeemTokensIn == 0 || redeemAmountIn == 0, \"one of redeemTokensIn or redeemAmountIn must be zero\");\n\n /* Verify market's slot(block or second) number equals current slot(block or second) number */\n if (accrualBlockNumber != getBlockNumberOrTimestamp()) {\n revert RedeemFreshnessCheck();\n }\n\n /* exchangeRate = invoke Exchange Rate Stored() */\n Exp memory exchangeRate = Exp({ mantissa: _exchangeRateStored() });\n\n uint256 redeemTokens;\n uint256 redeemAmount;\n\n /* If redeemTokensIn > 0: */\n if (redeemTokensIn > 0) {\n /*\n * We calculate the exchange rate and the amount of underlying to be redeemed:\n * redeemTokens = redeemTokensIn\n */\n redeemTokens = redeemTokensIn;\n } else {\n /*\n * We get the current exchange rate and calculate the amount to be redeemed:\n * redeemTokens = redeemAmountIn / exchangeRate\n */\n redeemTokens = div_(redeemAmountIn, exchangeRate);\n\n uint256 _redeemAmount = mul_(redeemTokens, exchangeRate);\n if (_redeemAmount != 0 && _redeemAmount != redeemAmountIn) redeemTokens++; // round up\n }\n\n // redeemAmount = exchangeRate * redeemTokens\n redeemAmount = mul_ScalarTruncate(exchangeRate, redeemTokens);\n\n // Revert if amount is zero\n if (redeemAmount == 0) {\n revert(\"redeemAmount is zero\");\n }\n\n /* Fail if redeem not allowed */\n comptroller.preRedeemHook(address(this), redeemer, redeemTokens);\n\n /* Fail gracefully if protocol has insufficient cash */\n if (_getCashPrior() - totalReserves < redeemAmount) {\n revert RedeemTransferOutNotPossible();\n }\n\n /////////////////////////\n // EFFECTS & INTERACTIONS\n // (No safe failures beyond this point)\n\n /*\n * We write the previously calculated values into storage.\n * Note: Avoid token reentrancy attacks by writing reduced supply before external transfer.\n */\n totalSupply = totalSupply - redeemTokens;\n uint256 balanceAfter = accountTokens[redeemer] - redeemTokens;\n accountTokens[redeemer] = balanceAfter;\n\n /*\n * We invoke _doTransferOut for the receiver and the redeemAmount.\n * On success, the vToken has redeemAmount less of cash.\n * _doTransferOut reverts if anything goes wrong, since we can't be sure if side effects occurred.\n */\n _doTransferOut(receiver, redeemAmount);\n\n /* We emit a Transfer event, and a Redeem event */\n emit Transfer(redeemer, address(this), redeemTokens);\n emit Redeem(redeemer, redeemAmount, redeemTokens, balanceAfter);\n\n /* We call the defense and prime accrue interest hook */\n comptroller.redeemVerify(address(this), redeemer, redeemAmount, redeemTokens);\n }\n\n /**\n * @notice Users or their delegates borrow assets from the protocol\n * @param borrower User who borrows the assets\n * @param receiver The receiver of the tokens, if called by a delegate\n * @param borrowAmount The amount of the underlying asset to borrow\n */\n function _borrowFresh(address borrower, address receiver, uint256 borrowAmount) internal {\n /* Fail if borrow not allowed */\n comptroller.preBorrowHook(address(this), borrower, borrowAmount);\n\n /* Verify market's slot(block or second) number equals current slot(block or second) number */\n if (accrualBlockNumber != getBlockNumberOrTimestamp()) {\n revert BorrowFreshnessCheck();\n }\n\n /* Fail gracefully if protocol has insufficient underlying cash */\n if (_getCashPrior() - totalReserves < borrowAmount) {\n revert BorrowCashNotAvailable();\n }\n\n /*\n * We calculate the new borrower and total borrow balances, failing on overflow:\n * accountBorrowNew = accountBorrow + borrowAmount\n * totalBorrowsNew = totalBorrows + borrowAmount\n */\n uint256 accountBorrowsPrev = _borrowBalanceStored(borrower);\n uint256 accountBorrowsNew = accountBorrowsPrev + borrowAmount;\n uint256 totalBorrowsNew = totalBorrows + borrowAmount;\n\n /////////////////////////\n // EFFECTS & INTERACTIONS\n // (No safe failures beyond this point)\n\n /*\n * We write the previously calculated values into storage.\n * Note: Avoid token reentrancy attacks by writing increased borrow before external transfer.\n `*/\n accountBorrows[borrower].principal = accountBorrowsNew;\n accountBorrows[borrower].interestIndex = borrowIndex;\n totalBorrows = totalBorrowsNew;\n\n /*\n * We invoke _doTransferOut for the receiver and the borrowAmount.\n * On success, the vToken borrowAmount less of cash.\n * _doTransferOut reverts if anything goes wrong, since we can't be sure if side effects occurred.\n */\n _doTransferOut(receiver, borrowAmount);\n\n /* We emit a Borrow event */\n emit Borrow(borrower, borrowAmount, accountBorrowsNew, totalBorrowsNew);\n\n /* We call the defense and prime accrue interest hook */\n comptroller.borrowVerify(address(this), borrower, borrowAmount);\n }\n\n /**\n * @notice Borrows are repaid by another user (possibly the borrower).\n * @param payer the account paying off the borrow\n * @param borrower the account with the debt being payed off\n * @param repayAmount the amount of underlying tokens being returned, or type(uint256).max for the full outstanding amount\n * @return (uint) the actual repayment amount.\n */\n function _repayBorrowFresh(address payer, address borrower, uint256 repayAmount) internal returns (uint256) {\n /* Fail if repayBorrow not allowed */\n comptroller.preRepayHook(address(this), borrower);\n\n /* Verify market's slot(block or second) number equals current slot(block or second) number */\n if (accrualBlockNumber != getBlockNumberOrTimestamp()) {\n revert RepayBorrowFreshnessCheck();\n }\n\n /* We fetch the amount the borrower owes, with accumulated interest */\n uint256 accountBorrowsPrev = _borrowBalanceStored(borrower);\n\n uint256 repayAmountFinal = repayAmount >= accountBorrowsPrev ? accountBorrowsPrev : repayAmount;\n\n /////////////////////////\n // EFFECTS & INTERACTIONS\n // (No safe failures beyond this point)\n\n /*\n * We call _doTransferIn for the payer and the repayAmount\n * On success, the vToken holds an additional repayAmount of cash.\n * _doTransferIn reverts if anything goes wrong, since we can't be sure if side effects occurred.\n * it returns the amount actually transferred, in case of a fee.\n */\n uint256 actualRepayAmount = _doTransferIn(payer, repayAmountFinal);\n\n /*\n * We calculate the new borrower and total borrow balances, failing on underflow:\n * accountBorrowsNew = accountBorrows - actualRepayAmount\n * totalBorrowsNew = totalBorrows - actualRepayAmount\n */\n uint256 accountBorrowsNew = accountBorrowsPrev - actualRepayAmount;\n uint256 totalBorrowsNew = totalBorrows - actualRepayAmount;\n\n /* We write the previously calculated values into storage */\n accountBorrows[borrower].principal = accountBorrowsNew;\n accountBorrows[borrower].interestIndex = borrowIndex;\n totalBorrows = totalBorrowsNew;\n\n /* We emit a RepayBorrow event */\n emit RepayBorrow(payer, borrower, actualRepayAmount, accountBorrowsNew, totalBorrowsNew);\n\n /* We call the defense and prime accrue interest hook */\n comptroller.repayBorrowVerify(address(this), payer, borrower, actualRepayAmount, borrowIndex);\n\n return actualRepayAmount;\n }\n\n /**\n * @notice The sender liquidates the borrowers collateral.\n * The collateral seized is transferred to the liquidator.\n * @param liquidator The address repaying the borrow and seizing collateral\n * @param borrower The borrower of this vToken to be liquidated\n * @param repayAmount The amount of the underlying borrowed asset to repay\n * @param vTokenCollateral The market in which to seize collateral from the borrower\n * @param skipLiquidityCheck If set to true, allows to liquidate up to 100% of the borrow\n * regardless of the account liquidity\n */\n function _liquidateBorrow(\n address liquidator,\n address borrower,\n uint256 repayAmount,\n VTokenInterface vTokenCollateral,\n bool skipLiquidityCheck\n ) internal nonReentrant {\n accrueInterest();\n\n uint256 error = vTokenCollateral.accrueInterest();\n if (error != NO_ERROR) {\n // accrueInterest emits logs on errors, but we still want to log the fact that an attempted liquidation failed\n revert LiquidateAccrueCollateralInterestFailed(error);\n }\n\n _liquidateBorrowFresh(liquidator, borrower, repayAmount, vTokenCollateral, skipLiquidityCheck);\n }\n\n /**\n * @notice The liquidator liquidates the borrowers collateral.\n * The collateral seized is transferred to the liquidator.\n * @param liquidator The address repaying the borrow and seizing collateral\n * @param borrower The borrower of this vToken to be liquidated\n * @param repayAmount The amount of the underlying borrowed asset to repay\n * @param vTokenCollateral The market in which to seize collateral from the borrower\n * @param skipLiquidityCheck If set to true, allows to liquidate up to 100% of the borrow\n * regardless of the account liquidity\n */\n function _liquidateBorrowFresh(\n address liquidator,\n address borrower,\n uint256 repayAmount,\n VTokenInterface vTokenCollateral,\n bool skipLiquidityCheck\n ) internal {\n /* Fail if liquidate not allowed */\n comptroller.preLiquidateHook(\n address(this),\n address(vTokenCollateral),\n borrower,\n repayAmount,\n skipLiquidityCheck\n );\n\n /* Verify market's slot(block or second) number equals current slot(block or second) number */\n if (accrualBlockNumber != getBlockNumberOrTimestamp()) {\n revert LiquidateFreshnessCheck();\n }\n\n /* Verify vTokenCollateral market's slot(block or second) number equals current slot(block or second) number */\n if (vTokenCollateral.accrualBlockNumber() != getBlockNumberOrTimestamp()) {\n revert LiquidateCollateralFreshnessCheck();\n }\n\n /* Fail if borrower = liquidator */\n if (borrower == liquidator) {\n revert LiquidateLiquidatorIsBorrower();\n }\n\n /* Fail if repayAmount = 0 */\n if (repayAmount == 0) {\n revert LiquidateCloseAmountIsZero();\n }\n\n /* Fail if repayAmount = type(uint256).max */\n if (repayAmount == type(uint256).max) {\n revert LiquidateCloseAmountIsUintMax();\n }\n\n /* Fail if repayBorrow fails */\n uint256 actualRepayAmount = _repayBorrowFresh(liquidator, borrower, repayAmount);\n\n /////////////////////////\n // EFFECTS & INTERACTIONS\n // (No safe failures beyond this point)\n\n /* We calculate the number of collateral tokens that will be seized */\n (uint256 amountSeizeError, uint256 seizeTokens) = comptroller.liquidateCalculateSeizeTokens(\n address(this),\n address(vTokenCollateral),\n actualRepayAmount\n );\n require(amountSeizeError == NO_ERROR, \"LIQUIDATE_COMPTROLLER_CALCULATE_AMOUNT_SEIZE_FAILED\");\n\n /* Revert if borrower collateral token balance < seizeTokens */\n require(vTokenCollateral.balanceOf(borrower) >= seizeTokens, \"LIQUIDATE_SEIZE_TOO_MUCH\");\n\n // If this is also the collateral, call _seize internally to avoid re-entrancy, otherwise make an external call\n if (address(vTokenCollateral) == address(this)) {\n _seize(address(this), liquidator, borrower, seizeTokens);\n } else {\n vTokenCollateral.seize(liquidator, borrower, seizeTokens);\n }\n\n /* We emit a LiquidateBorrow event */\n emit LiquidateBorrow(liquidator, borrower, actualRepayAmount, address(vTokenCollateral), seizeTokens);\n\n /* We call the defense and prime accrue interest hook */\n comptroller.liquidateBorrowVerify(\n address(this),\n address(vTokenCollateral),\n liquidator,\n borrower,\n actualRepayAmount,\n seizeTokens\n );\n }\n\n /**\n * @notice Transfers collateral tokens (this market) to the liquidator.\n * @dev Called only during an in-kind liquidation, or by liquidateBorrow during the liquidation of another VToken.\n * It's absolutely critical to use msg.sender as the seizer vToken and not a parameter.\n * @param seizerContract The contract seizing the collateral (either borrowed vToken or Comptroller)\n * @param liquidator The account receiving seized collateral\n * @param borrower The account having collateral seized\n * @param seizeTokens The number of vTokens to seize\n */\n function _seize(address seizerContract, address liquidator, address borrower, uint256 seizeTokens) internal {\n /* Fail if seize not allowed */\n comptroller.preSeizeHook(address(this), seizerContract, liquidator, borrower);\n\n /* Fail if borrower = liquidator */\n if (borrower == liquidator) {\n revert LiquidateSeizeLiquidatorIsBorrower();\n }\n\n /*\n * We calculate the new borrower and liquidator token balances, failing on underflow/overflow:\n * borrowerTokensNew = accountTokens[borrower] - seizeTokens\n * liquidatorTokensNew = accountTokens[liquidator] + seizeTokens\n */\n uint256 liquidationIncentiveMantissa = ComptrollerViewInterface(address(comptroller))\n .liquidationIncentiveMantissa();\n uint256 numerator = mul_(seizeTokens, Exp({ mantissa: protocolSeizeShareMantissa }));\n uint256 protocolSeizeTokens = div_(numerator, Exp({ mantissa: liquidationIncentiveMantissa }));\n uint256 liquidatorSeizeTokens = seizeTokens - protocolSeizeTokens;\n Exp memory exchangeRate = Exp({ mantissa: _exchangeRateStored() });\n uint256 protocolSeizeAmount = mul_ScalarTruncate(exchangeRate, protocolSeizeTokens);\n\n /////////////////////////\n // EFFECTS & INTERACTIONS\n // (No safe failures beyond this point)\n\n /* We write the calculated values into storage */\n totalSupply = totalSupply - protocolSeizeTokens;\n accountTokens[borrower] = accountTokens[borrower] - seizeTokens;\n accountTokens[liquidator] = accountTokens[liquidator] + liquidatorSeizeTokens;\n\n // _doTransferOut reverts if anything goes wrong, since we can't be sure if side effects occurred.\n // Transferring an underlying asset to the protocolShareReserve contract to channel the funds for different use.\n _doTransferOut(protocolShareReserve, protocolSeizeAmount);\n\n // Update the pool asset's state in the protocol share reserve for the above transfer.\n IProtocolShareReserve(protocolShareReserve).updateAssetsState(\n address(comptroller),\n underlying,\n IProtocolShareReserve.IncomeType.LIQUIDATION\n );\n\n /* Emit a Transfer event */\n emit Transfer(borrower, liquidator, liquidatorSeizeTokens);\n emit ProtocolSeize(borrower, protocolShareReserve, protocolSeizeAmount);\n\n /* We call the defense and prime accrue interest hook */\n comptroller.seizeVerify(address(this), seizerContract, liquidator, borrower, seizeTokens);\n }\n\n function _setComptroller(ComptrollerInterface newComptroller) internal {\n ComptrollerInterface oldComptroller = comptroller;\n // Ensure invoke comptroller.isComptroller() returns true\n require(newComptroller.isComptroller(), \"marker method returned false\");\n\n // Set market's comptroller to newComptroller\n comptroller = newComptroller;\n\n // Emit NewComptroller(oldComptroller, newComptroller)\n emit NewComptroller(oldComptroller, newComptroller);\n }\n\n /**\n * @notice Sets a new reserve factor for the protocol (*requires fresh interest accrual)\n * @dev Admin function to set a new reserve factor\n * @param newReserveFactorMantissa New reserve factor (from 0 to 1e18)\n */\n function _setReserveFactorFresh(uint256 newReserveFactorMantissa) internal {\n // Verify market's slot(block or second) number equals current slot(block or second) number\n if (accrualBlockNumber != getBlockNumberOrTimestamp()) {\n revert SetReserveFactorFreshCheck();\n }\n\n // Check newReserveFactor ≤ maxReserveFactor\n if (newReserveFactorMantissa > MAX_RESERVE_FACTOR_MANTISSA) {\n revert SetReserveFactorBoundsCheck();\n }\n\n uint256 oldReserveFactorMantissa = reserveFactorMantissa;\n reserveFactorMantissa = newReserveFactorMantissa;\n\n emit NewReserveFactor(oldReserveFactorMantissa, newReserveFactorMantissa);\n }\n\n /**\n * @notice Add reserves by transferring from caller\n * @dev Requires fresh interest accrual\n * @param addAmount Amount of addition to reserves\n * @return actualAddAmount The actual amount added, excluding the potential token fees\n */\n function _addReservesFresh(uint256 addAmount) internal returns (uint256) {\n // totalReserves + actualAddAmount\n uint256 totalReservesNew;\n uint256 actualAddAmount;\n\n // We fail gracefully unless market's slot(block or second) number equals current slot(block or second) number\n if (accrualBlockNumber != getBlockNumberOrTimestamp()) {\n revert AddReservesFactorFreshCheck(actualAddAmount);\n }\n\n actualAddAmount = _doTransferIn(msg.sender, addAmount);\n totalReservesNew = totalReserves + actualAddAmount;\n totalReserves = totalReservesNew;\n emit ReservesAdded(msg.sender, actualAddAmount, totalReservesNew);\n\n return actualAddAmount;\n }\n\n /**\n * @notice Reduces reserves by transferring to the protocol reserve contract\n * @dev Requires fresh interest accrual\n * @param reduceAmount Amount of reduction to reserves\n */\n function _reduceReservesFresh(uint256 reduceAmount) internal {\n if (reduceAmount == 0) {\n return;\n }\n // totalReserves - reduceAmount\n uint256 totalReservesNew;\n\n // We fail gracefully unless market's slot(block or second) number equals current slot(block or second) number\n if (accrualBlockNumber != getBlockNumberOrTimestamp()) {\n revert ReduceReservesFreshCheck();\n }\n\n // Fail gracefully if protocol has insufficient underlying cash\n if (_getCashPrior() < reduceAmount) {\n revert ReduceReservesCashNotAvailable();\n }\n\n // Check reduceAmount ≤ reserves[n] (totalReserves)\n if (reduceAmount > totalReserves) {\n revert ReduceReservesCashValidation();\n }\n\n /////////////////////////\n // EFFECTS & INTERACTIONS\n // (No safe failures beyond this point)\n\n totalReservesNew = totalReserves - reduceAmount;\n\n // Store reserves[n+1] = reserves[n] - reduceAmount\n totalReserves = totalReservesNew;\n\n // _doTransferOut reverts if anything goes wrong, since we can't be sure if side effects occurred.\n // Transferring an underlying asset to the protocolShareReserve contract to channel the funds for different use.\n _doTransferOut(protocolShareReserve, reduceAmount);\n\n // Update the pool asset's state in the protocol share reserve for the above transfer.\n IProtocolShareReserve(protocolShareReserve).updateAssetsState(\n address(comptroller),\n underlying,\n IProtocolShareReserve.IncomeType.SPREAD\n );\n\n emit SpreadReservesReduced(protocolShareReserve, reduceAmount, totalReservesNew);\n }\n\n /**\n * @notice updates the interest rate model (*requires fresh interest accrual)\n * @dev Admin function to update the interest rate model\n * @param newInterestRateModel the new interest rate model to use\n */\n function _setInterestRateModelFresh(InterestRateModel newInterestRateModel) internal {\n // Used to store old model for use in the event that is emitted on success\n InterestRateModel oldInterestRateModel;\n\n // We fail gracefully unless market's slot(block or second) number equals current slot(block or second) number\n if (accrualBlockNumber != getBlockNumberOrTimestamp()) {\n revert SetInterestRateModelFreshCheck();\n }\n\n // Track the market's current interest rate model\n oldInterestRateModel = interestRateModel;\n\n // Ensure invoke newInterestRateModel.isInterestRateModel() returns true\n require(newInterestRateModel.isInterestRateModel(), \"marker method returned false\");\n\n // Set the interest rate model to newInterestRateModel\n interestRateModel = newInterestRateModel;\n\n // Emit NewMarketInterestRateModel(oldInterestRateModel, newInterestRateModel)\n emit NewMarketInterestRateModel(oldInterestRateModel, newInterestRateModel);\n }\n\n /**\n * Safe Token **\n */\n\n /**\n * @dev Similar to ERC-20 transfer, but handles tokens that have transfer fees.\n * This function returns the actual amount received,\n * which may be less than `amount` if there is a fee attached to the transfer.\n * @param from Sender of the underlying tokens\n * @param amount Amount of underlying to transfer\n * @return Actual amount received\n */\n function _doTransferIn(address from, uint256 amount) internal virtual returns (uint256) {\n IERC20Upgradeable token = IERC20Upgradeable(underlying);\n uint256 balanceBefore = token.balanceOf(address(this));\n token.safeTransferFrom(from, address(this), amount);\n uint256 balanceAfter = token.balanceOf(address(this));\n uint256 actualAmount = balanceAfter - balanceBefore;\n internalCash += actualAmount;\n // Return the amount that was *actually* transferred\n return actualAmount;\n }\n\n /**\n * @dev Just a regular ERC-20 transfer, reverts on failure\n * @param to Receiver of the underlying tokens\n * @param amount Amount of underlying to transfer\n */\n function _doTransferOut(address to, uint256 amount) internal virtual {\n IERC20Upgradeable token = IERC20Upgradeable(underlying);\n internalCash -= amount;\n token.safeTransfer(to, amount);\n }\n\n /**\n * @notice Transfer `tokens` tokens from `src` to `dst` by `spender`\n * @dev Called by both `transfer` and `transferFrom` internally\n * @param spender The address of the account performing the transfer\n * @param src The address of the source account\n * @param dst The address of the destination account\n * @param tokens The number of tokens to transfer\n */\n function _transferTokens(address spender, address src, address dst, uint256 tokens) internal {\n /* Fail if transfer not allowed */\n comptroller.preTransferHook(address(this), src, dst, tokens);\n\n /* Do not allow self-transfers */\n if (src == dst) {\n revert TransferNotAllowed();\n }\n\n /* Get the allowance, infinite for the account owner */\n uint256 startingAllowance;\n if (spender == src) {\n startingAllowance = type(uint256).max;\n } else {\n startingAllowance = transferAllowances[src][spender];\n }\n\n /* Do the calculations, checking for {under,over}flow */\n uint256 allowanceNew = startingAllowance - tokens;\n uint256 srcTokensNew = accountTokens[src] - tokens;\n uint256 dstTokensNew = accountTokens[dst] + tokens;\n\n /////////////////////////\n // EFFECTS & INTERACTIONS\n\n accountTokens[src] = srcTokensNew;\n accountTokens[dst] = dstTokensNew;\n\n /* Eat some of the allowance (if necessary) */\n if (startingAllowance != type(uint256).max) {\n transferAllowances[src][spender] = allowanceNew;\n }\n\n /* We emit a Transfer event */\n emit Transfer(src, dst, tokens);\n\n comptroller.transferVerify(address(this), src, dst, tokens);\n }\n\n /**\n * @notice Initialize the money market\n * @param underlying_ The address of the underlying asset\n * @param comptroller_ The address of the Comptroller\n * @param interestRateModel_ The address of the interest rate model\n * @param initialExchangeRateMantissa_ The initial exchange rate, scaled by 1e18\n * @param name_ ERC-20 name of this token\n * @param symbol_ ERC-20 symbol of this token\n * @param decimals_ ERC-20 decimal precision of this token\n * @param admin_ Address of the administrator of this token\n * @param accessControlManager_ AccessControlManager contract address\n * @param riskManagement Addresses of risk & income related contracts\n * @param reserveFactorMantissa_ Percentage of borrow interest that goes to reserves (from 0 to 1e18)\n */\n function _initialize(\n address underlying_,\n ComptrollerInterface comptroller_,\n InterestRateModel interestRateModel_,\n uint256 initialExchangeRateMantissa_,\n string memory name_,\n string memory symbol_,\n uint8 decimals_,\n address admin_,\n address accessControlManager_,\n RiskManagementInit memory riskManagement,\n uint256 reserveFactorMantissa_\n ) internal onlyInitializing {\n __Ownable2Step_init();\n __AccessControlled_init_unchained(accessControlManager_);\n require(accrualBlockNumber == 0 && borrowIndex == 0, \"market may only be initialized once\");\n\n // Set initial exchange rate\n initialExchangeRateMantissa = initialExchangeRateMantissa_;\n require(initialExchangeRateMantissa > 0, \"initial exchange rate must be greater than zero.\");\n\n _setComptroller(comptroller_);\n\n // Initialize slot(block or second) number and borrow index (slot(block or second) number mocks depend on comptroller being set)\n accrualBlockNumber = getBlockNumberOrTimestamp();\n borrowIndex = MANTISSA_ONE;\n\n // Set the interest rate model (depends on slot(block or second) number / borrow index)\n _setInterestRateModelFresh(interestRateModel_);\n\n _setReserveFactorFresh(reserveFactorMantissa_);\n\n name = name_;\n symbol = symbol_;\n decimals = decimals_;\n _setShortfallContract(riskManagement.shortfall);\n _setProtocolShareReserve(riskManagement.protocolShareReserve);\n protocolSeizeShareMantissa = DEFAULT_PROTOCOL_SEIZE_SHARE_MANTISSA;\n\n // Set underlying and sanity check it\n underlying = underlying_;\n IERC20Upgradeable(underlying).totalSupply();\n\n // The counter starts true to prevent changing it from zero to non-zero (i.e. smaller cost/refund)\n _notEntered = true;\n _transferOwnership(admin_);\n }\n\n function _setShortfallContract(address shortfall_) internal {\n ensureNonzeroAddress(shortfall_);\n address oldShortfall = shortfall;\n shortfall = shortfall_;\n emit NewShortfallContract(oldShortfall, shortfall_);\n }\n\n function _setProtocolShareReserve(address payable protocolShareReserve_) internal {\n ensureNonzeroAddress(protocolShareReserve_);\n address oldProtocolShareReserve = address(protocolShareReserve);\n protocolShareReserve = protocolShareReserve_;\n emit NewProtocolShareReserve(oldProtocolShareReserve, address(protocolShareReserve_));\n }\n\n function _ensureSenderIsDelegateOf(address user) internal view {\n if (!ComptrollerViewInterface(address(comptroller)).approvedDelegates(user, msg.sender)) {\n revert DelegateNotApproved();\n }\n }\n\n /**\n * @notice Gets the internally tracked cash balance of this market\n * @dev Returns the internalCash state variable, which is updated on transfers in/out\n * @return The quantity of underlying tokens tracked internally by this contract\n */\n function _getCashPrior() internal view virtual returns (uint256) {\n return internalCash;\n }\n\n /**\n * @notice Return the borrow balance of account based on stored data\n * @param account The address whose balance should be calculated\n * @return borrowBalance the calculated balance\n */\n function _borrowBalanceStored(address account) internal view returns (uint256) {\n /* Get borrowBalance and borrowIndex */\n BorrowSnapshot memory borrowSnapshot = accountBorrows[account];\n\n /* If borrowBalance = 0 then borrowIndex is likely also 0.\n * Rather than failing the calculation with a division by 0, we immediately return 0 in this case.\n */\n if (borrowSnapshot.principal == 0) {\n return 0;\n }\n\n /* Calculate new borrow balance using the interest index:\n * recentBorrowBalance = borrower.borrowBalance * market.borrowIndex / borrower.borrowIndex\n */\n uint256 principalTimesIndex = borrowSnapshot.principal * borrowIndex;\n\n return principalTimesIndex / borrowSnapshot.interestIndex;\n }\n\n /**\n * @notice Calculates the exchange rate from the underlying to the VToken\n * @dev This function does not accrue interest before calculating the exchange rate\n * @return exchangeRate Calculated exchange rate scaled by 1e18\n */\n function _exchangeRateStored() internal view virtual returns (uint256) {\n uint256 _totalSupply = totalSupply;\n if (_totalSupply == 0) {\n /*\n * If there are no tokens minted:\n * exchangeRate = initialExchangeRate\n */\n return initialExchangeRateMantissa;\n }\n /*\n * Otherwise:\n * exchangeRate = (totalCash + totalBorrows + badDebt - totalReserves) / totalSupply\n */\n uint256 totalCash = _getCashPrior();\n uint256 cashPlusBorrowsMinusReserves = totalCash + totalBorrows + badDebt - totalReserves;\n uint256 exchangeRate = (cashPlusBorrowsMinusReserves * EXP_SCALE) / _totalSupply;\n\n return exchangeRate;\n }\n}\n" + }, + "contracts/VTokenInterfaces.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\n\nimport { IERC20Upgradeable } from \"@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol\";\nimport { ResilientOracleInterface } from \"@venusprotocol/oracle/contracts/interfaces/OracleInterface.sol\";\n\nimport { ComptrollerInterface } from \"./ComptrollerInterface.sol\";\nimport { InterestRateModel } from \"./InterestRateModel.sol\";\n\n/**\n * @title VTokenStorage\n * @author Venus\n * @notice Storage layout used by the `VToken` contract\n */\n// solhint-disable-next-line max-states-count\ncontract VTokenStorage {\n /**\n * @notice Container for borrow balance information\n * @member principal Total balance (with accrued interest), after applying the most recent balance-changing action\n * @member interestIndex Global borrowIndex as of the most recent balance-changing action\n */\n struct BorrowSnapshot {\n uint256 principal;\n uint256 interestIndex;\n }\n\n /**\n * @dev Guard variable for re-entrancy checks\n */\n bool internal _notEntered;\n\n /**\n * @notice Underlying asset for this VToken\n */\n address public underlying;\n\n /**\n * @notice EIP-20 token name for this token\n */\n string public name;\n\n /**\n * @notice EIP-20 token symbol for this token\n */\n string public symbol;\n\n /**\n * @notice EIP-20 token decimals for this token\n */\n uint8 public decimals;\n\n /**\n * @notice Protocol share Reserve contract address\n */\n address payable public protocolShareReserve;\n\n /**\n * @notice Contract which oversees inter-vToken operations\n */\n ComptrollerInterface public comptroller;\n\n /**\n * @notice Model which tells what the current interest rate should be\n */\n InterestRateModel public interestRateModel;\n\n // Initial exchange rate used when minting the first VTokens (used when totalSupply = 0)\n uint256 internal initialExchangeRateMantissa;\n\n /**\n * @notice Fraction of interest currently set aside for reserves\n */\n uint256 public reserveFactorMantissa;\n\n /**\n * @notice Slot(block or second) number that interest was last accrued at\n */\n uint256 public accrualBlockNumber;\n\n /**\n * @notice Accumulator of the total earned interest rate since the opening of the market\n */\n uint256 public borrowIndex;\n\n /**\n * @notice Total amount of outstanding borrows of the underlying in this market\n */\n uint256 public totalBorrows;\n\n /**\n * @notice Total amount of reserves of the underlying held in this market\n */\n uint256 public totalReserves;\n\n /**\n * @notice Total number of tokens in circulation\n */\n uint256 public totalSupply;\n\n /**\n * @notice Total bad debt of the market\n */\n uint256 public badDebt;\n\n // Official record of token balances for each account\n mapping(address => uint256) internal accountTokens;\n\n // Approved token transfer amounts on behalf of others\n mapping(address => mapping(address => uint256)) internal transferAllowances;\n\n // Mapping of account addresses to outstanding borrow balances\n mapping(address => BorrowSnapshot) internal accountBorrows;\n\n /**\n * @notice Share of seized collateral that is added to reserves\n */\n uint256 public protocolSeizeShareMantissa;\n\n /**\n * @notice Storage of Shortfall contract address\n */\n address public shortfall;\n\n /**\n * @notice delta slot (block or second) after which reserves will be reduced\n */\n uint256 public reduceReservesBlockDelta;\n\n /**\n * @notice last slot (block or second) number at which reserves were reduced\n */\n uint256 public reduceReservesBlockNumber;\n\n /**\n * @notice Tracked internal cash balance, immune to direct token transfers (donation attacks)\n * @dev Updated via _doTransferIn(), _doTransferOut(), syncCash(), and badDebtRecovered(). Must be initialized via syncCash() after upgrade.\n */\n uint256 public internalCash;\n\n /**\n * @dev This empty reserved space is put in place to allow future versions to add new\n * variables without shifting down storage in the inheritance chain.\n * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\n */\n uint256[47] private __gap;\n}\n\n/**\n * @title VTokenInterface\n * @author Venus\n * @notice Interface implemented by the `VToken` contract\n */\nabstract contract VTokenInterface is VTokenStorage {\n struct RiskManagementInit {\n address shortfall;\n address payable protocolShareReserve;\n }\n\n /*** Market Events ***/\n\n /**\n * @notice Event emitted when interest is accrued\n */\n event AccrueInterest(uint256 cashPrior, uint256 interestAccumulated, uint256 borrowIndex, uint256 totalBorrows);\n\n /**\n * @notice Event emitted when tokens are minted\n */\n event Mint(address indexed minter, uint256 mintAmount, uint256 mintTokens, uint256 accountBalance);\n\n /**\n * @notice Event emitted when tokens are redeemed\n */\n event Redeem(address indexed redeemer, uint256 redeemAmount, uint256 redeemTokens, uint256 accountBalance);\n\n /**\n * @notice Event emitted when underlying is borrowed\n */\n event Borrow(address indexed borrower, uint256 borrowAmount, uint256 accountBorrows, uint256 totalBorrows);\n\n /**\n * @notice Event emitted when a borrow is repaid\n */\n event RepayBorrow(\n address indexed payer,\n address indexed borrower,\n uint256 repayAmount,\n uint256 accountBorrows,\n uint256 totalBorrows\n );\n\n /**\n * @notice Event emitted when bad debt is accumulated on a market\n * @param borrower borrower to \"forgive\"\n * @param badDebtDelta amount of new bad debt recorded\n * @param badDebtOld previous bad debt value\n * @param badDebtNew new bad debt value\n */\n event BadDebtIncreased(address indexed borrower, uint256 badDebtDelta, uint256 badDebtOld, uint256 badDebtNew);\n\n /**\n * @notice Event emitted when bad debt is recovered via an auction\n * @param badDebtOld previous bad debt value\n * @param badDebtNew new bad debt value\n */\n event BadDebtRecovered(uint256 badDebtOld, uint256 badDebtNew);\n\n /**\n * @notice Event emitted when a borrow is liquidated\n */\n event LiquidateBorrow(\n address indexed liquidator,\n address indexed borrower,\n uint256 repayAmount,\n address indexed vTokenCollateral,\n uint256 seizeTokens\n );\n\n /*** Admin Events ***/\n\n /**\n * @notice Event emitted when comptroller is changed\n */\n event NewComptroller(ComptrollerInterface indexed oldComptroller, ComptrollerInterface indexed newComptroller);\n\n /**\n * @notice Event emitted when shortfall contract address is changed\n */\n event NewShortfallContract(address indexed oldShortfall, address indexed newShortfall);\n\n /**\n * @notice Event emitted when protocol share reserve contract address is changed\n */\n event NewProtocolShareReserve(address indexed oldProtocolShareReserve, address indexed newProtocolShareReserve);\n\n /**\n * @notice Event emitted when interestRateModel is changed\n */\n event NewMarketInterestRateModel(\n InterestRateModel indexed oldInterestRateModel,\n InterestRateModel indexed newInterestRateModel\n );\n\n /**\n * @notice Event emitted when protocol seize share is changed\n */\n event NewProtocolSeizeShare(uint256 oldProtocolSeizeShareMantissa, uint256 newProtocolSeizeShareMantissa);\n\n /**\n * @notice Event emitted when the reserve factor is changed\n */\n event NewReserveFactor(uint256 oldReserveFactorMantissa, uint256 newReserveFactorMantissa);\n\n /**\n * @notice Event emitted when the reserves are added\n */\n event ReservesAdded(address indexed benefactor, uint256 addAmount, uint256 newTotalReserves);\n\n /**\n * @notice Event emitted when the spread reserves are reduced\n */\n event SpreadReservesReduced(address indexed protocolShareReserve, uint256 reduceAmount, uint256 newTotalReserves);\n\n /**\n * @notice EIP20 Transfer event\n */\n event Transfer(address indexed from, address indexed to, uint256 amount);\n\n /**\n * @notice EIP20 Approval event\n */\n event Approval(address indexed owner, address indexed spender, uint256 amount);\n\n /**\n * @notice Event emitted when healing the borrow\n */\n event HealBorrow(address indexed payer, address indexed borrower, uint256 repayAmount);\n\n /**\n * @notice Event emitted when tokens are swept\n */\n event SweepToken(address indexed token);\n\n /**\n * @notice Event emitted when reduce reserves slot (block or second) delta is changed\n */\n event NewReduceReservesBlockDelta(\n uint256 oldReduceReservesBlockOrTimestampDelta,\n uint256 newReduceReservesBlockOrTimestampDelta\n );\n\n /**\n * @notice Event emitted when liquidation reserves are reduced\n */\n event ProtocolSeize(address indexed from, address indexed to, uint256 amount);\n\n /**\n * @notice Event emitted when internalCash is synced with actual token balance\n */\n event CashSynced(uint256 oldInternalCash, uint256 newInternalCash);\n\n /*** User Interface ***/\n\n function mint(uint256 mintAmount) external virtual returns (uint256);\n\n function mintBehalf(address minter, uint256 mintAllowed) external virtual returns (uint256);\n\n function redeem(uint256 redeemTokens) external virtual returns (uint256);\n\n function redeemBehalf(address redeemer, uint256 redeemTokens) external virtual returns (uint256);\n\n function redeemUnderlying(uint256 redeemAmount) external virtual returns (uint256);\n\n function redeemUnderlyingBehalf(address redeemer, uint256 redeemAmount) external virtual returns (uint256);\n\n function borrow(uint256 borrowAmount) external virtual returns (uint256);\n\n function borrowBehalf(address borrwwer, uint256 borrowAmount) external virtual returns (uint256);\n\n function repayBorrow(uint256 repayAmount) external virtual returns (uint256);\n\n function repayBorrowBehalf(address borrower, uint256 repayAmount) external virtual returns (uint256);\n\n function liquidateBorrow(\n address borrower,\n uint256 repayAmount,\n VTokenInterface vTokenCollateral\n ) external virtual returns (uint256);\n\n function healBorrow(address payer, address borrower, uint256 repayAmount) external virtual;\n\n function forceLiquidateBorrow(\n address liquidator,\n address borrower,\n uint256 repayAmount,\n VTokenInterface vTokenCollateral,\n bool skipCloseFactorCheck\n ) external virtual;\n\n function seize(address liquidator, address borrower, uint256 seizeTokens) external virtual;\n\n function transfer(address dst, uint256 amount) external virtual returns (bool);\n\n function transferFrom(address src, address dst, uint256 amount) external virtual returns (bool);\n\n function accrueInterest() external virtual returns (uint256);\n\n function sweepToken(IERC20Upgradeable token) external virtual;\n\n /*** Admin Functions ***/\n\n function setReserveFactor(uint256 newReserveFactorMantissa) external virtual;\n\n function reduceReserves(uint256 reduceAmount) external virtual;\n\n function exchangeRateCurrent() external virtual returns (uint256);\n\n function borrowBalanceCurrent(address account) external virtual returns (uint256);\n\n function setInterestRateModel(InterestRateModel newInterestRateModel) external virtual;\n\n function addReserves(uint256 addAmount) external virtual;\n\n function syncCash() external virtual;\n\n function totalBorrowsCurrent() external virtual returns (uint256);\n\n function balanceOfUnderlying(address owner) external virtual returns (uint256);\n\n function approve(address spender, uint256 amount) external virtual returns (bool);\n\n function increaseAllowance(address spender, uint256 addedValue) external virtual returns (bool);\n\n function decreaseAllowance(address spender, uint256 subtractedValue) external virtual returns (bool);\n\n function allowance(address owner, address spender) external view virtual returns (uint256);\n\n function balanceOf(address owner) external view virtual returns (uint256);\n\n function getAccountSnapshot(address account) external view virtual returns (uint256, uint256, uint256, uint256);\n\n function borrowRatePerBlock() external view virtual returns (uint256);\n\n function supplyRatePerBlock() external view virtual returns (uint256);\n\n function borrowBalanceStored(address account) external view virtual returns (uint256);\n\n function exchangeRateStored() external view virtual returns (uint256);\n\n function getCash() external view virtual returns (uint256);\n\n /**\n * @notice Indicator that this is a VToken contract (for inspection)\n * @return Always true\n */\n function isVToken() external pure virtual returns (bool) {\n return true;\n }\n}\n" + } + }, + "settings": { + "optimizer": { + "enabled": true, + "runs": 200, + "details": { + "yul": true + } + }, + "evmVersion": "paris", + "outputSelection": { + "*": { + "*": [ + "storageLayout", + "abi", + "evm.bytecode", + "evm.deployedBytecode", + "evm.methodIdentifiers", + "metadata", + "devdoc", + "userdoc", + "evm.gasEstimates" + ], + "": ["ast"] + } + }, + "metadata": { + "useLiteralContent": true + } + } +} diff --git a/deployments/bsctestnet/solcInputs/66f1153922b1795d76052d2879dd03ca.json b/deployments/bsctestnet/solcInputs/66f1153922b1795d76052d2879dd03ca.json new file mode 100644 index 000000000..a64ac9e29 --- /dev/null +++ b/deployments/bsctestnet/solcInputs/66f1153922b1795d76052d2879dd03ca.json @@ -0,0 +1,123 @@ +{ + "language": "Solidity", + "sources": { + "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable2Step.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./OwnableUpgradeable.sol\";\nimport {Initializable} from \"../proxy/utils/Initializable.sol\";\n\n/**\n * @dev Contract module which provides access control mechanism, where\n * there is an account (an owner) that can be granted exclusive access to\n * specific functions.\n *\n * By default, the owner account will be the one that deploys the contract. This\n * can later be changed with {transferOwnership} and {acceptOwnership}.\n *\n * This module is used through inheritance. It will make available all functions\n * from parent (Ownable).\n */\nabstract contract Ownable2StepUpgradeable is Initializable, OwnableUpgradeable {\n address private _pendingOwner;\n\n event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner);\n\n function __Ownable2Step_init() internal onlyInitializing {\n __Ownable_init_unchained();\n }\n\n function __Ownable2Step_init_unchained() internal onlyInitializing {\n }\n /**\n * @dev Returns the address of the pending owner.\n */\n function pendingOwner() public view virtual returns (address) {\n return _pendingOwner;\n }\n\n /**\n * @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one.\n * Can only be called by the current owner.\n */\n function transferOwnership(address newOwner) public virtual override onlyOwner {\n _pendingOwner = newOwner;\n emit OwnershipTransferStarted(owner(), newOwner);\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner.\n * Internal function without access restriction.\n */\n function _transferOwnership(address newOwner) internal virtual override {\n delete _pendingOwner;\n super._transferOwnership(newOwner);\n }\n\n /**\n * @dev The new owner accepts the ownership transfer.\n */\n function acceptOwnership() public virtual {\n address sender = _msgSender();\n require(pendingOwner() == sender, \"Ownable2Step: caller is not the new owner\");\n _transferOwnership(sender);\n }\n\n /**\n * @dev This empty reserved space is put in place to allow future versions to add new\n * variables without shifting down storage in the inheritance chain.\n * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\n */\n uint256[49] private __gap;\n}\n" + }, + "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../utils/ContextUpgradeable.sol\";\nimport {Initializable} from \"../proxy/utils/Initializable.sol\";\n\n/**\n * @dev Contract module which provides a basic access control mechanism, where\n * there is an account (an owner) that can be granted exclusive access to\n * specific functions.\n *\n * By default, the owner account will be the one that deploys the contract. This\n * can later be changed with {transferOwnership}.\n *\n * This module is used through inheritance. It will make available the modifier\n * `onlyOwner`, which can be applied to your functions to restrict their use to\n * the owner.\n */\nabstract contract OwnableUpgradeable is Initializable, ContextUpgradeable {\n address private _owner;\n\n event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n\n /**\n * @dev Initializes the contract setting the deployer as the initial owner.\n */\n function __Ownable_init() internal onlyInitializing {\n __Ownable_init_unchained();\n }\n\n function __Ownable_init_unchained() internal onlyInitializing {\n _transferOwnership(_msgSender());\n }\n\n /**\n * @dev Throws if called by any account other than the owner.\n */\n modifier onlyOwner() {\n _checkOwner();\n _;\n }\n\n /**\n * @dev Returns the address of the current owner.\n */\n function owner() public view virtual returns (address) {\n return _owner;\n }\n\n /**\n * @dev Throws if the sender is not the owner.\n */\n function _checkOwner() internal view virtual {\n require(owner() == _msgSender(), \"Ownable: caller is not the owner\");\n }\n\n /**\n * @dev Leaves the contract without owner. It will not be possible to call\n * `onlyOwner` functions. Can only be called by the current owner.\n *\n * NOTE: Renouncing ownership will leave the contract without an owner,\n * thereby disabling any functionality that is only available to the owner.\n */\n function renounceOwnership() public virtual onlyOwner {\n _transferOwnership(address(0));\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Can only be called by the current owner.\n */\n function transferOwnership(address newOwner) public virtual onlyOwner {\n require(newOwner != address(0), \"Ownable: new owner is the zero address\");\n _transferOwnership(newOwner);\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Internal function without access restriction.\n */\n function _transferOwnership(address newOwner) internal virtual {\n address oldOwner = _owner;\n _owner = newOwner;\n emit OwnershipTransferred(oldOwner, newOwner);\n }\n\n /**\n * @dev This empty reserved space is put in place to allow future versions to add new\n * variables without shifting down storage in the inheritance chain.\n * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\n */\n uint256[49] private __gap;\n}\n" + }, + "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.0) (proxy/utils/Initializable.sol)\n\npragma solidity ^0.8.2;\n\nimport \"../../utils/AddressUpgradeable.sol\";\n\n/**\n * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed\n * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an\n * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer\n * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.\n *\n * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be\n * reused. This mechanism prevents re-execution of each \"step\" but allows the creation of new initialization steps in\n * case an upgrade adds a module that needs to be initialized.\n *\n * For example:\n *\n * [.hljs-theme-light.nopadding]\n * ```solidity\n * contract MyToken is ERC20Upgradeable {\n * function initialize() initializer public {\n * __ERC20_init(\"MyToken\", \"MTK\");\n * }\n * }\n *\n * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {\n * function initializeV2() reinitializer(2) public {\n * __ERC20Permit_init(\"MyToken\");\n * }\n * }\n * ```\n *\n * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as\n * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.\n *\n * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure\n * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.\n *\n * [CAUTION]\n * ====\n * Avoid leaving a contract uninitialized.\n *\n * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation\n * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke\n * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:\n *\n * [.hljs-theme-light.nopadding]\n * ```\n * /// @custom:oz-upgrades-unsafe-allow constructor\n * constructor() {\n * _disableInitializers();\n * }\n * ```\n * ====\n */\nabstract contract Initializable {\n /**\n * @dev Indicates that the contract has been initialized.\n * @custom:oz-retyped-from bool\n */\n uint8 private _initialized;\n\n /**\n * @dev Indicates that the contract is in the process of being initialized.\n */\n bool private _initializing;\n\n /**\n * @dev Triggered when the contract has been initialized or reinitialized.\n */\n event Initialized(uint8 version);\n\n /**\n * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,\n * `onlyInitializing` functions can be used to initialize parent contracts.\n *\n * Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a\n * constructor.\n *\n * Emits an {Initialized} event.\n */\n modifier initializer() {\n bool isTopLevelCall = !_initializing;\n require(\n (isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1),\n \"Initializable: contract is already initialized\"\n );\n _initialized = 1;\n if (isTopLevelCall) {\n _initializing = true;\n }\n _;\n if (isTopLevelCall) {\n _initializing = false;\n emit Initialized(1);\n }\n }\n\n /**\n * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the\n * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be\n * used to initialize parent contracts.\n *\n * A reinitializer may be used after the original initialization step. This is essential to configure modules that\n * are added through upgrades and that require initialization.\n *\n * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`\n * cannot be nested. If one is invoked in the context of another, execution will revert.\n *\n * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in\n * a contract, executing them in the right order is up to the developer or operator.\n *\n * WARNING: setting the version to 255 will prevent any future reinitialization.\n *\n * Emits an {Initialized} event.\n */\n modifier reinitializer(uint8 version) {\n require(!_initializing && _initialized < version, \"Initializable: contract is already initialized\");\n _initialized = version;\n _initializing = true;\n _;\n _initializing = false;\n emit Initialized(version);\n }\n\n /**\n * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the\n * {initializer} and {reinitializer} modifiers, directly or indirectly.\n */\n modifier onlyInitializing() {\n require(_initializing, \"Initializable: contract is not initializing\");\n _;\n }\n\n /**\n * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.\n * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized\n * to any version. It is recommended to use this to lock implementation contracts that are designed to be called\n * through proxies.\n *\n * Emits an {Initialized} event the first time it is successfully executed.\n */\n function _disableInitializers() internal virtual {\n require(!_initializing, \"Initializable: contract is initializing\");\n if (_initialized != type(uint8).max) {\n _initialized = type(uint8).max;\n emit Initialized(type(uint8).max);\n }\n }\n\n /**\n * @dev Returns the highest version that has been initialized. See {reinitializer}.\n */\n function _getInitializedVersion() internal view returns (uint8) {\n return _initialized;\n }\n\n /**\n * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.\n */\n function _isInitializing() internal view returns (bool) {\n return _initializing;\n }\n}\n" + }, + "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/IERC20PermitUpgradeable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.4) (token/ERC20/extensions/IERC20Permit.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in\n * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].\n *\n * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by\n * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't\n * need to send a transaction, and thus is not required to hold Ether at all.\n *\n * ==== Security Considerations\n *\n * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature\n * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be\n * considered as an intention to spend the allowance in any specific way. The second is that because permits have\n * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should\n * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be\n * generally recommended is:\n *\n * ```solidity\n * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {\n * try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}\n * doThing(..., value);\n * }\n *\n * function doThing(..., uint256 value) public {\n * token.safeTransferFrom(msg.sender, address(this), value);\n * ...\n * }\n * ```\n *\n * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of\n * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also\n * {SafeERC20-safeTransferFrom}).\n *\n * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so\n * contracts should have entry points that don't rely on permit.\n */\ninterface IERC20PermitUpgradeable {\n /**\n * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,\n * given ``owner``'s signed approval.\n *\n * IMPORTANT: The same issues {IERC20-approve} has related to transaction\n * ordering also apply here.\n *\n * Emits an {Approval} event.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n * - `deadline` must be a timestamp in the future.\n * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`\n * over the EIP712-formatted function arguments.\n * - the signature must use ``owner``'s current nonce (see {nonces}).\n *\n * For more information on the signature format, see the\n * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP\n * section].\n *\n * CAUTION: See Security Considerations above.\n */\n function permit(\n address owner,\n address spender,\n uint256 value,\n uint256 deadline,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) external;\n\n /**\n * @dev Returns the current nonce for `owner`. This value must be\n * included whenever a signature is generated for {permit}.\n *\n * Every successful call to {permit} increases ``owner``'s nonce by one. This\n * prevents a signature from being used multiple times.\n */\n function nonces(address owner) external view returns (uint256);\n\n /**\n * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.\n */\n // solhint-disable-next-line func-name-mixedcase\n function DOMAIN_SEPARATOR() external view returns (bytes32);\n}\n" + }, + "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20Upgradeable {\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n\n /**\n * @dev Returns the amount of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves `amount` tokens from the caller's account to `to`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address to, uint256 amount) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender's allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @dev Moves `amount` tokens from `from` to `to` using the\n * allowance mechanism. `amount` is then deducted from the caller's\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(address from, address to, uint256 amount) external returns (bool);\n}\n" + }, + "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.3) (token/ERC20/utils/SafeERC20.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC20Upgradeable.sol\";\nimport \"../extensions/IERC20PermitUpgradeable.sol\";\nimport \"../../../utils/AddressUpgradeable.sol\";\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20Upgradeable {\n using AddressUpgradeable for address;\n\n /**\n * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,\n * non-reverting calls are assumed to be successful.\n */\n function safeTransfer(IERC20Upgradeable token, address to, uint256 value) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\n }\n\n /**\n * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the\n * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.\n */\n function safeTransferFrom(IERC20Upgradeable token, address from, address to, uint256 value) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\n }\n\n /**\n * @dev Deprecated. This function has issues similar to the ones found in\n * {IERC20-approve}, and its usage is discouraged.\n *\n * Whenever possible, use {safeIncreaseAllowance} and\n * {safeDecreaseAllowance} instead.\n */\n function safeApprove(IERC20Upgradeable token, address spender, uint256 value) internal {\n // safeApprove should only be called when setting an initial allowance,\n // or when resetting it to zero. To increase and decrease it, use\n // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\n require(\n (value == 0) || (token.allowance(address(this), spender) == 0),\n \"SafeERC20: approve from non-zero to non-zero allowance\"\n );\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\n }\n\n /**\n * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,\n * non-reverting calls are assumed to be successful.\n */\n function safeIncreaseAllowance(IERC20Upgradeable token, address spender, uint256 value) internal {\n uint256 oldAllowance = token.allowance(address(this), spender);\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));\n }\n\n /**\n * @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,\n * non-reverting calls are assumed to be successful.\n */\n function safeDecreaseAllowance(IERC20Upgradeable token, address spender, uint256 value) internal {\n unchecked {\n uint256 oldAllowance = token.allowance(address(this), spender);\n require(oldAllowance >= value, \"SafeERC20: decreased allowance below zero\");\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));\n }\n }\n\n /**\n * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,\n * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval\n * to be set to zero before setting it to a non-zero value, such as USDT.\n */\n function forceApprove(IERC20Upgradeable token, address spender, uint256 value) internal {\n bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);\n\n if (!_callOptionalReturnBool(token, approvalCall)) {\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));\n _callOptionalReturn(token, approvalCall);\n }\n }\n\n /**\n * @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.\n * Revert on invalid signature.\n */\n function safePermit(\n IERC20PermitUpgradeable token,\n address owner,\n address spender,\n uint256 value,\n uint256 deadline,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) internal {\n uint256 nonceBefore = token.nonces(owner);\n token.permit(owner, spender, value, deadline, v, r, s);\n uint256 nonceAfter = token.nonces(owner);\n require(nonceAfter == nonceBefore + 1, \"SafeERC20: permit did not succeed\");\n }\n\n /**\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n * on the return value: the return value is optional (but if data is returned, it must not be false).\n * @param token The token targeted by the call.\n * @param data The call data (encoded using abi.encode or one of its variants).\n */\n function _callOptionalReturn(IERC20Upgradeable token, bytes memory data) private {\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that\n // the target address contains contract code and also asserts for success in the low-level call.\n\n bytes memory returndata = address(token).functionCall(data, \"SafeERC20: low-level call failed\");\n require(returndata.length == 0 || abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n }\n\n /**\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n * on the return value: the return value is optional (but if data is returned, it must not be false).\n * @param token The token targeted by the call.\n * @param data The call data (encoded using abi.encode or one of its variants).\n *\n * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.\n */\n function _callOptionalReturnBool(IERC20Upgradeable token, bytes memory data) private returns (bool) {\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false\n // and not revert is the subcall reverts.\n\n (bool success, bytes memory returndata) = address(token).call(data);\n return\n success && (returndata.length == 0 || abi.decode(returndata, (bool))) && AddressUpgradeable.isContract(address(token));\n }\n}\n" + }, + "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)\n\npragma solidity ^0.8.1;\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary AddressUpgradeable {\n /**\n * @dev Returns true if `account` is a contract.\n *\n * [IMPORTANT]\n * ====\n * It is unsafe to assume that an address for which this function returns\n * false is an externally-owned account (EOA) and not a contract.\n *\n * Among others, `isContract` will return false for the following\n * types of addresses:\n *\n * - an externally-owned account\n * - a contract in construction\n * - an address where a contract will be created\n * - an address where a contract lived, but was destroyed\n *\n * Furthermore, `isContract` will also return true if the target contract within\n * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,\n * which only has an effect at the end of a transaction.\n * ====\n *\n * [IMPORTANT]\n * ====\n * You shouldn't rely on `isContract` to protect against flash loan attacks!\n *\n * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets\n * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract\n * constructor.\n * ====\n */\n function isContract(address account) internal view returns (bool) {\n // This method relies on extcodesize/address.code.length, which returns 0\n // for contracts in construction, since the code is only stored at the end\n // of the constructor execution.\n\n return account.code.length > 0;\n }\n\n /**\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n * `recipient`, forwarding all available gas and reverting on errors.\n *\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\n * imposed by `transfer`, making them unable to receive funds via\n * `transfer`. {sendValue} removes this limitation.\n *\n * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n *\n * IMPORTANT: because control is transferred to `recipient`, care must be\n * taken to not create reentrancy vulnerabilities. Consider using\n * {ReentrancyGuard} or the\n * https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n */\n function sendValue(address payable recipient, uint256 amount) internal {\n require(address(this).balance >= amount, \"Address: insufficient balance\");\n\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Address: unable to send value, recipient may have reverted\");\n }\n\n /**\n * @dev Performs a Solidity function call using a low level `call`. A\n * plain `call` is an unsafe replacement for a function call: use this\n * function instead.\n *\n * If `target` reverts with a revert reason, it is bubbled up by this\n * function (like regular Solidity function calls).\n *\n * Returns the raw returned data. To convert to the expected return value,\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n *\n * Requirements:\n *\n * - `target` must be a contract.\n * - calling `target` with `data` must not revert.\n *\n * _Available since v3.1._\n */\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, \"Address: low-level call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n * `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but also transferring `value` wei to `target`.\n *\n * Requirements:\n *\n * - the calling contract must have an ETH balance of at least `value`.\n * - the called Solidity function must be `payable`.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {\n return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n * with `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(address(this).balance >= value, \"Address: insufficient balance for call\");\n (bool success, bytes memory returndata) = target.call{value: value}(data);\n return verifyCallResultFromTarget(target, success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n return functionStaticCall(target, data, \"Address: low-level static call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n (bool success, bytes memory returndata) = target.staticcall(data);\n return verifyCallResultFromTarget(target, success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n (bool success, bytes memory returndata) = target.delegatecall(data);\n return verifyCallResultFromTarget(target, success, returndata, errorMessage);\n }\n\n /**\n * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling\n * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.\n *\n * _Available since v4.8._\n */\n function verifyCallResultFromTarget(\n address target,\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n if (success) {\n if (returndata.length == 0) {\n // only check isContract if the call was successful and the return data is empty\n // otherwise we already know that it was a contract\n require(isContract(target), \"Address: call to non-contract\");\n }\n return returndata;\n } else {\n _revert(returndata, errorMessage);\n }\n }\n\n /**\n * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the\n * revert reason or using the provided one.\n *\n * _Available since v4.3._\n */\n function verifyCallResult(\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal pure returns (bytes memory) {\n if (success) {\n return returndata;\n } else {\n _revert(returndata, errorMessage);\n }\n }\n\n function _revert(bytes memory returndata, string memory errorMessage) private pure {\n // Look for revert reason and bubble it up if present\n if (returndata.length > 0) {\n // The easiest way to bubble the revert reason is using memory via assembly\n /// @solidity memory-safe-assembly\n assembly {\n let returndata_size := mload(returndata)\n revert(add(32, returndata), returndata_size)\n }\n } else {\n revert(errorMessage);\n }\n }\n}\n" + }, + "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.4) (utils/Context.sol)\n\npragma solidity ^0.8.0;\nimport {Initializable} from \"../proxy/utils/Initializable.sol\";\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract ContextUpgradeable is Initializable {\n function __Context_init() internal onlyInitializing {\n }\n\n function __Context_init_unchained() internal onlyInitializing {\n }\n function _msgSender() internal view virtual returns (address) {\n return msg.sender;\n }\n\n function _msgData() internal view virtual returns (bytes calldata) {\n return msg.data;\n }\n\n function _contextSuffixLength() internal view virtual returns (uint256) {\n return 0;\n }\n\n /**\n * @dev This empty reserved space is put in place to allow future versions to add new\n * variables without shifting down storage in the inheritance chain.\n * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\n */\n uint256[50] private __gap;\n}\n" + }, + "@openzeppelin/contracts/access/IAccessControl.sol": { + "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev External interface of AccessControl declared to support ERC165 detection.\n */\ninterface IAccessControl {\n /**\n * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`\n *\n * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite\n * {RoleAdminChanged} not being emitted signaling this.\n *\n * _Available since v3.1._\n */\n event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);\n\n /**\n * @dev Emitted when `account` is granted `role`.\n *\n * `sender` is the account that originated the contract call, an admin role\n * bearer except when using {AccessControl-_setupRole}.\n */\n event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);\n\n /**\n * @dev Emitted when `account` is revoked `role`.\n *\n * `sender` is the account that originated the contract call:\n * - if using `revokeRole`, it is the admin role bearer\n * - if using `renounceRole`, it is the role bearer (i.e. `account`)\n */\n event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);\n\n /**\n * @dev Returns `true` if `account` has been granted `role`.\n */\n function hasRole(bytes32 role, address account) external view returns (bool);\n\n /**\n * @dev Returns the admin role that controls `role`. See {grantRole} and\n * {revokeRole}.\n *\n * To change a role's admin, use {AccessControl-_setRoleAdmin}.\n */\n function getRoleAdmin(bytes32 role) external view returns (bytes32);\n\n /**\n * @dev Grants `role` to `account`.\n *\n * If `account` had not been already granted `role`, emits a {RoleGranted}\n * event.\n *\n * Requirements:\n *\n * - the caller must have ``role``'s admin role.\n */\n function grantRole(bytes32 role, address account) external;\n\n /**\n * @dev Revokes `role` from `account`.\n *\n * If `account` had been granted `role`, emits a {RoleRevoked} event.\n *\n * Requirements:\n *\n * - the caller must have ``role``'s admin role.\n */\n function revokeRole(bytes32 role, address account) external;\n\n /**\n * @dev Revokes `role` from the calling account.\n *\n * Roles are often managed via {grantRole} and {revokeRole}: this function's\n * purpose is to provide a mechanism for accounts to lose their privileges\n * if they are compromised (such as when a trusted device is misplaced).\n *\n * If the calling account had been granted `role`, emits a {RoleRevoked}\n * event.\n *\n * Requirements:\n *\n * - the caller must be `account`.\n */\n function renounceRole(bytes32 role, address account) external;\n}\n" + }, + "@venusprotocol/governance-contracts/contracts/Governance/AccessControlledV8.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\n\nimport \"@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol\";\nimport \"@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol\";\n\nimport \"./IAccessControlManagerV8.sol\";\n\n/**\n * @title AccessControlledV8\n * @author Venus\n * @notice This contract is helper between access control manager and actual contract. This contract further inherited by other contract (using solidity 0.8.13)\n * to integrate access controlled mechanism. It provides initialise methods and verifying access methods.\n */\nabstract contract AccessControlledV8 is Initializable, Ownable2StepUpgradeable {\n /// @notice Access control manager contract\n IAccessControlManagerV8 internal _accessControlManager;\n\n /**\n * @dev This empty reserved space is put in place to allow future versions to add new\n * variables without shifting down storage in the inheritance chain.\n * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\n */\n uint256[49] private __gap;\n\n /// @notice Emitted when access control manager contract address is changed\n event NewAccessControlManager(address oldAccessControlManager, address newAccessControlManager);\n\n /// @notice Thrown when the action is prohibited by AccessControlManager\n error Unauthorized(address sender, address calledContract, string methodSignature);\n\n function __AccessControlled_init(address accessControlManager_) internal onlyInitializing {\n __Ownable2Step_init();\n __AccessControlled_init_unchained(accessControlManager_);\n }\n\n function __AccessControlled_init_unchained(address accessControlManager_) internal onlyInitializing {\n _setAccessControlManager(accessControlManager_);\n }\n\n /**\n * @notice Sets the address of AccessControlManager\n * @dev Admin function to set address of AccessControlManager\n * @param accessControlManager_ The new address of the AccessControlManager\n * @custom:event Emits NewAccessControlManager event\n * @custom:access Only Governance\n */\n function setAccessControlManager(address accessControlManager_) external onlyOwner {\n _setAccessControlManager(accessControlManager_);\n }\n\n /**\n * @notice Returns the address of the access control manager contract\n */\n function accessControlManager() external view returns (IAccessControlManagerV8) {\n return _accessControlManager;\n }\n\n /**\n * @dev Internal function to set address of AccessControlManager\n * @param accessControlManager_ The new address of the AccessControlManager\n */\n function _setAccessControlManager(address accessControlManager_) internal {\n require(address(accessControlManager_) != address(0), \"invalid acess control manager address\");\n address oldAccessControlManager = address(_accessControlManager);\n _accessControlManager = IAccessControlManagerV8(accessControlManager_);\n emit NewAccessControlManager(oldAccessControlManager, accessControlManager_);\n }\n\n /**\n * @notice Reverts if the call is not allowed by AccessControlManager\n * @param signature Method signature\n */\n function _checkAccessAllowed(string memory signature) internal view {\n bool isAllowedToCall = _accessControlManager.isAllowedToCall(msg.sender, signature);\n\n if (!isAllowedToCall) {\n revert Unauthorized(msg.sender, address(this), signature);\n }\n }\n}\n" + }, + "@venusprotocol/governance-contracts/contracts/Governance/IAccessControlManagerV8.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity ^0.8.25;\n\nimport \"@openzeppelin/contracts/access/IAccessControl.sol\";\n\n/**\n * @title IAccessControlManagerV8\n * @author Venus\n * @notice Interface implemented by the `AccessControlManagerV8` contract.\n */\ninterface IAccessControlManagerV8 is IAccessControl {\n function giveCallPermission(address contractAddress, string calldata functionSig, address accountToPermit) external;\n\n function revokeCallPermission(\n address contractAddress,\n string calldata functionSig,\n address accountToRevoke\n ) external;\n\n function isAllowedToCall(address account, string calldata functionSig) external view returns (bool);\n\n function hasPermission(\n address account,\n address contractAddress,\n string calldata functionSig\n ) external view returns (bool);\n}\n" + }, + "@venusprotocol/oracle/contracts/interfaces/IDeviationBoundedOracle.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\n\ninterface IDeviationBoundedOracle {\n // --- Enums ---\n\n /// @notice Identifies whether a price bound is a minimum or maximum\n enum PriceBoundType {\n MIN,\n MAX\n }\n\n /// @notice Identifies which keeper action a single syncPriceBoundsAndProtections item performs\n enum KeeperAction {\n SetMinPrice,\n SetMaxPrice,\n ExitProtectionMode\n }\n\n // --- Structs ---\n\n /// @notice Per-asset protection state tracking the min/max price window\n struct MarketProtectionState {\n /// @notice Lowest price observed in the current window (packed with maxPrice in one slot)\n uint128 minPrice;\n /// @notice Highest price observed in the current window\n uint128 maxPrice;\n /// @notice Whether protected price is currently being used\n bool currentlyUsingProtectedPrice;\n /// @notice Whether this market is whitelisted for bounded pricing\n bool isBoundedPricingEnabled;\n /// @notice Timestamp of the last protection trigger — reset on every trigger\n uint64 lastProtectionTriggeredAt;\n /// @notice Minimum time protection stays active after last trigger\n uint64 cooldownPeriod;\n /// @notice The underlying asset address, used to verify initialization\n address asset;\n /// @notice Entry deviation threshold (mantissa, e.g. 0.1667e18 = 16.67%); packed with resetThreshold\n uint128 triggerThreshold;\n /// @notice Exit threshold (mantissa); window must converge below this for protection to be disabled\n uint128 resetThreshold;\n /// @notice Whether transient caching of the bounded (collateral, debt) pair is enabled for this asset\n bool cachingEnabled;\n }\n\n /// @notice One item in an syncPriceBoundsAndProtections payload\n /// @dev `value` is interpreted per-action: the new bound price for SetMinPrice / SetMaxPrice, ignored for ExitProtectionMode\n struct KeeperActionItem {\n address asset;\n KeeperAction action;\n uint256 value;\n }\n\n /// @notice One item in a setTokenConfigs payload\n struct TokenConfigInput {\n /// @notice The underlying asset address\n address asset;\n /// @notice Minimum time protection stays active after the last trigger (seconds)\n uint64 cooldownPeriod;\n /// @notice Entry deviation threshold (mantissa). Must be between 5% and 50%.\n uint256 triggerThreshold;\n /// @notice Exit deviation threshold (mantissa). Must be non-zero and below triggerThreshold.\n uint256 resetThreshold;\n /// @notice Whether to enable bounded pricing immediately upon initialization\n bool enableBoundedPricing;\n /// @notice Whether transient caching of the bounded (collateral, debt) pair is enabled for this asset\n bool enableCaching;\n }\n\n // --- Events ---\n\n /// @notice Emitted when protection is initialized for an asset\n event ProtectionInitialized(\n address indexed asset,\n uint128 minPrice,\n uint128 maxPrice,\n uint64 cooldownPeriod,\n uint256 triggerThreshold\n );\n\n /// @notice Emitted when protection mode is triggered for an asset\n event ProtectionTriggered(address indexed asset, uint256 spotPrice, uint128 minPrice, uint128 maxPrice);\n\n /// @notice Emitted when protection mode is disabled for an asset\n event ProtectionModeExited(address indexed asset);\n\n /// @notice Emitted when the keeper updates the minimum price for an asset\n event MinPriceUpdated(address indexed asset, uint128 oldMin, uint128 newMin);\n\n /// @notice Emitted when the keeper updates the maximum price for an asset\n event MaxPriceUpdated(address indexed asset, uint128 oldMax, uint128 newMax);\n\n /// @notice Emitted when the entry threshold is updated for an asset\n event TriggerThresholdSet(address indexed asset, uint256 oldThreshold, uint256 newThreshold);\n\n /// @notice Emitted when the exit threshold is updated for an asset\n event ResetThresholdSet(address indexed asset, uint256 oldExitThreshold, uint256 newExitThreshold);\n\n /// @notice Emitted when the cooldown period is updated for an asset\n event CooldownPeriodSet(address indexed asset, uint64 oldCooldown, uint64 newCooldown);\n\n /// @notice Emitted when an asset's whitelist status changes\n event BoundedPricingWhitelistUpdated(address indexed asset, bool whitelisted);\n\n /// @notice Emitted when the per-asset transient caching flag is toggled\n event CachingEnabledUpdated(address indexed asset, bool oldEnabled, bool newEnabled);\n\n // --- Errors ---\n\n /// @notice Thrown when trying to use or update protection for an asset that has not been initialized\n error MarketNotInitialized(address asset);\n\n /// @notice Thrown when trying to initialize an already initialized market\n error MarketAlreadyInitialized(address asset);\n\n /// @notice Thrown when trying to disable protection that is not active\n error ProtectedPriceInactive(address asset);\n\n /// @notice Thrown when trying to disable protection before cooldown has elapsed\n error CooldownNotElapsed(address asset, uint64 lastProtectionTriggeredAt, uint64 cooldownPeriod);\n\n /// @notice Thrown when trying to disable protection before price range has converged\n error PriceRangeNotConverged(address asset, uint256 currentRangeRatio, uint256 resetThreshold);\n\n /// @notice Thrown when keeper tries to set minPrice above current spot\n error InvalidMinPrice(address asset, uint128 newMin, uint256 currentSpot);\n\n /// @notice Thrown when keeper tries to set maxPrice below current spot\n error InvalidMaxPrice(address asset, uint128 newMax, uint256 currentSpot);\n\n /// @notice Thrown when threshold is set below the minimum allowed value\n error ThresholdBelowMinimum(uint256 threshold, uint256 minimum);\n\n /// @notice Thrown when threshold is set above the maximum allowed value\n error ThresholdAboveMaximum(uint256 threshold, uint256 maximum);\n\n /// @notice Thrown when a price exceeds uint128 max\n error PriceExceedsUint128(uint256 price);\n\n /// @notice Thrown when a zero price is provided where a non-zero price is required\n error ZeroPriceNotAllowed();\n\n /// @notice Thrown when trying to initialize protection for VAI\n error VAINotAllowed();\n\n /// @notice Thrown when trying to disable bounded pricing for an asset while protection is active\n error ProtectedPriceActive(address asset);\n\n /// @notice Thrown when the lengths of the arrays are not equal\n error InvalidArrayLength();\n\n /// @notice Thrown when the exit threshold is set at or above the trigger threshold\n error InvalidResetThreshold(uint256 resetThreshold);\n\n /// @notice Thrown when an syncPriceBoundsAndProtections item carries an unsupported action enum value\n error InvalidKeeperAction(uint8 action);\n\n // --- Non-view price functions (update window + trigger protection) ---\n\n /**\n * @notice Gets the bounded collateral price for a given vToken, updating protection state\n * @param vToken vToken address\n * @return collateralPrice The bounded collateral price\n * @custom:error PriceExceedsUint128 if the spot price overflows uint128\n * @custom:event MinPriceUpdated if a new window minimum is recorded\n * @custom:event MaxPriceUpdated if a new window maximum is recorded\n * @custom:event ProtectionTriggered if the spot price deviates beyond the threshold\n */\n function getBoundedCollateralPrice(address vToken) external returns (uint256 collateralPrice);\n\n /**\n * @notice Gets the bounded debt price for a given vToken, updating protection state\n * @param vToken vToken address\n * @return debtPrice The bounded debt price\n * @custom:error PriceExceedsUint128 if the spot price overflows uint128\n * @custom:event MinPriceUpdated if a new window minimum is recorded\n * @custom:event MaxPriceUpdated if a new window maximum is recorded\n * @custom:event ProtectionTriggered if the spot price deviates beyond the threshold\n */\n function getBoundedDebtPrice(address vToken) external returns (uint256 debtPrice);\n\n /**\n * @notice Gets both the bounded collateral and debt prices for a given vToken, updating protection state\n * @param vToken vToken address\n * @return collateralPrice The bounded collateral price\n * @return debtPrice The bounded debt price\n * @custom:error PriceExceedsUint128 if the spot price overflows uint128\n * @custom:event MinPriceUpdated if a new window minimum is recorded\n * @custom:event MaxPriceUpdated if a new window maximum is recorded\n * @custom:event ProtectionTriggered if the spot price deviates beyond the threshold\n */\n function getBoundedPrices(address vToken) external returns (uint256 collateralPrice, uint256 debtPrice);\n\n // --- State update (call before view price reads to populate transient cache) ---\n\n /**\n * @notice Updates the protection state for a given vToken, caching the resolved collateral and debt prices\n * @dev Called by PolicyFacet before liquidity calculations so subsequent view price\n * reads in the same transaction are served from transient storage. The transient\n * cache is only populated when the asset's `cachingEnabled` flag is `true`.\n * @param vToken vToken address\n * @custom:error PriceExceedsUint128 if the spot price overflows uint128\n * @custom:event MinPriceUpdated if a new window minimum is recorded\n * @custom:event MaxPriceUpdated if a new window maximum is recorded\n * @custom:event ProtectionTriggered if the spot price deviates beyond the threshold\n */\n function updateProtectionState(address vToken) external;\n\n // --- View price functions (read stored/cached state only) ---\n\n /**\n * @notice Gets the bounded collateral price for a given vToken (view variant)\n * @dev Reads from transient cache first when the asset's `cachingEnabled` flag is `true`;\n * falls back to ResilientOracle on cache miss or when caching is disabled.\n * @param vToken vToken address\n * @return price The bounded collateral price\n * @custom:error PriceExceedsUint128 if the spot price overflows uint128 (cache miss path only)\n */\n function getBoundedCollateralPriceView(address vToken) external view returns (uint256 price);\n\n /**\n * @notice Gets the bounded debt price for a given vToken (view variant)\n * @dev Reads from transient cache first when the asset's `cachingEnabled` flag is `true`;\n * falls back to ResilientOracle on cache miss or when caching is disabled.\n * @param vToken vToken address\n * @return price The bounded debt price\n * @custom:error PriceExceedsUint128 if the spot price overflows uint128 (cache miss path only)\n */\n function getBoundedDebtPriceView(address vToken) external view returns (uint256 price);\n\n /**\n * @notice Gets both the bounded collateral and debt prices for a given vToken (view variant)\n * @dev Reads from transient cache first when the asset's `cachingEnabled` flag is `true`;\n * falls back to ResilientOracle on cache miss or when caching is disabled.\n * @param vToken vToken address\n * @return collateralPrice The bounded collateral price\n * @return debtPrice The bounded debt price\n * @custom:error PriceExceedsUint128 if the spot price overflows uint128 (cache miss path only)\n */\n function getBoundedPricesView(address vToken) external view returns (uint256 collateralPrice, uint256 debtPrice);\n\n // --- Keeper functions ---\n\n /**\n * @notice Updates the minimum price in the rolling window for a given asset\n * @param asset The underlying asset address\n * @param newMin The new minimum price; must be at or below the current spot and below maxPrice\n * @custom:access Only authorized keeper addresses\n * @custom:error ZeroPriceNotAllowed if newMin is zero\n * @custom:error MarketNotInitialized if the asset has not been initialized\n * @custom:error InvalidMinPrice if newMin exceeds the current spot or is at or above maxPrice\n * @custom:event MinPriceUpdated\n */\n function updateMinPrice(address asset, uint128 newMin) external;\n\n /**\n * @notice Updates the maximum price in the rolling window for a given asset\n * @param asset The underlying asset address\n * @param newMax The new maximum price; must be at or above the current spot and above minPrice\n * @custom:access Only authorized keeper addresses\n * @custom:error ZeroPriceNotAllowed if newMax is zero\n * @custom:error MarketNotInitialized if the asset has not been initialized\n * @custom:error InvalidMaxPrice if newMax is below the current spot or is at or below minPrice\n * @custom:event MaxPriceUpdated\n */\n function updateMaxPrice(address asset, uint128 newMax) external;\n\n /**\n * @notice Exits protection mode for a given asset once conditions are met\n * @param asset The underlying asset address\n * @custom:access Only authorized monitor/keeper addresses\n * @custom:error ProtectedPriceInactive if protection is not currently active\n * @custom:error CooldownNotElapsed if the cooldown period has not elapsed since the last trigger\n * @custom:error PriceRangeNotConverged if the window range is still above the exit threshold\n * @custom:event ProtectionModeExited\n */\n function exitProtectionMode(address asset) external;\n\n /**\n * @notice Dispatches a batch of keeper-only actions (set min, set max, or exit protection) under a single ACM check\n * @dev Each item is processed in array order; any item revert rolls back the whole batch.\n * `value` is interpreted as the new bound price for SetMinPrice / SetMaxPrice and ignored for ExitProtectionMode.\n * Empty `actions` is a no-op success.\n * @param actions The list of keeper actions to apply\n * @custom:access Only authorized keeper addresses\n * @custom:error InvalidKeeperAction if an item carries an unsupported action enum value\n * @custom:error PriceExceedsUint128 if a SetMin/SetMax item value overflows uint128\n * @custom:error ZeroPriceNotAllowed if a SetMin/SetMax item value is zero\n * @custom:error MarketNotInitialized if any referenced asset has not been initialized\n * @custom:error InvalidMinPrice if a SetMinPrice item violates the spot/maxPrice constraints\n * @custom:error InvalidMaxPrice if a SetMaxPrice item violates the spot/minPrice constraints\n * @custom:error ProtectedPriceInactive if an ExitProtectionMode item targets an asset whose protection is not active\n * @custom:error CooldownNotElapsed if an ExitProtectionMode item is submitted before cooldown elapsed\n * @custom:error PriceRangeNotConverged if an ExitProtectionMode item is submitted before window convergence\n * @custom:event MinPriceUpdated, MaxPriceUpdated, ProtectionModeExited\n */\n function syncPriceBoundsAndProtections(KeeperActionItem[] calldata actions) external;\n\n // --- Admin functions (governance-gated) ---\n\n /**\n * @notice Initializes protection parameters for a new asset\n * @dev Seeds the initial min/max window from the current ResilientOracle spot price,\n * confirming the oracle is live for this asset before it is listed.\n * @param tokenConfig_ Token config input for the asset\n * @custom:access Only Governance\n * @custom:error ZeroAddressNotAllowed if asset is the zero address\n * @custom:error ZeroValueNotAllowed if cooldownPeriod, triggerThreshold, or resetThreshold is zero\n * @custom:error MarketAlreadyInitialized if the asset has already been initialized\n * @custom:error ThresholdBelowMinimum if triggerThreshold is below 5%\n * @custom:error ThresholdAboveMaximum if triggerThreshold is above 50%\n * @custom:error InvalidResetThreshold if resetThreshold is at or above triggerThreshold\n * @custom:error VAINotAllowed if asset is the VAI token\n * @custom:error PriceExceedsUint128 if the spot price overflows uint128\n * @custom:event ProtectionInitialized\n * @custom:event BoundedPricingWhitelistUpdated\n */\n function setTokenConfig(TokenConfigInput calldata tokenConfig_) external;\n\n /**\n * @notice Batch-initializes protection parameters for multiple assets in a single transaction\n * @param tokenConfigs_ Array of token config inputs, one per asset\n * @custom:access Only Governance\n * @custom:error InvalidArrayLength if the input array is empty\n * @custom:error ZeroAddressNotAllowed if any asset is the zero address\n * @custom:error ZeroValueNotAllowed if any cooldownPeriod, triggerThreshold, or resetThreshold is zero\n * @custom:error MarketAlreadyInitialized if any asset has already been initialized\n * @custom:error ThresholdBelowMinimum if any triggerThreshold is below 5%\n * @custom:error ThresholdAboveMaximum if any triggerThreshold is above 50%\n * @custom:error InvalidResetThreshold if any resetThreshold is at or above its triggerThreshold\n * @custom:error VAINotAllowed if any asset is the VAI token\n * @custom:error PriceExceedsUint128 if the spot price for any asset overflows uint128\n * @custom:event ProtectionInitialized for each asset\n * @custom:event BoundedPricingWhitelistUpdated for each asset\n */\n function setTokenConfigs(TokenConfigInput[] calldata tokenConfigs_) external;\n\n /**\n * @notice Sets the cooldown period for an asset\n * @param asset The underlying asset address\n * @param newCooldown The new cooldown period in seconds; must be non-zero\n * @custom:access Only Governance\n * @custom:error MarketNotInitialized if the asset has not been initialized\n * @custom:event CooldownPeriodSet\n */\n function setCooldownPeriod(address asset, uint64 newCooldown) external;\n\n /**\n * @notice Sets the trigger and reset thresholds for an asset\n * @param asset The underlying asset address\n * @param newTriggerThreshold The new trigger threshold (mantissa). Must be between 5% and 50% and above the reset threshold.\n * @param newResetThreshold The new reset threshold (mantissa). Must be non-zero and below the trigger threshold.\n * @custom:access Only Governance\n * @custom:error MarketNotInitialized if the asset has not been initialized\n * @custom:error ThresholdBelowMinimum if newTriggerThreshold is below 5%\n * @custom:error ThresholdAboveMaximum if newTriggerThreshold is above 50%\n * @custom:error InvalidResetThreshold if newResetThreshold is at or above newTriggerThreshold\n * @custom:event TriggerThresholdSet if the trigger threshold changed\n * @custom:event ResetThresholdSet if the reset threshold changed\n */\n function setThresholds(address asset, uint256 newTriggerThreshold, uint256 newResetThreshold) external;\n\n /**\n * @notice Sets whether bounded pricing is enabled for an asset\n * @param asset The underlying asset address\n * @param enabled Whether bounded pricing should be enabled for the asset\n * @custom:access Only Governance\n * @custom:error MarketNotInitialized if the asset has not been initialized\n * @custom:error ProtectedPriceActive if trying to disable an asset while protection is active\n * @custom:event BoundedPricingWhitelistUpdated\n */\n function setAssetBoundedPricingEnabled(address asset, bool enabled) external;\n\n /**\n * @notice Toggles transient caching of the bounded (collateral, debt) pair for an asset\n * @dev When disabled, each view/non-view price call recomputes bounded prices from the\n * live spot instead of reading or writing the transient slots. The initial value is\n * set via the `enableCaching` argument of `setTokenConfig`.\n * @param asset The underlying asset address\n * @param enabled Whether transient caching is enabled for this asset\n * @custom:access Only Governance\n * @custom:error MarketNotInitialized if the asset has not been initialized\n * @custom:event CachingEnabledUpdated\n */\n function setCachingEnabled(address asset, bool enabled) external;\n\n // --- View helpers ---\n\n /**\n * @notice Returns the full protection state for an asset\n * @param asset The underlying asset address\n * @return minPrice Lowest price observed in the current window\n * @return maxPrice Highest price observed in the current window\n * @return currentlyUsingProtectedPrice Whether protected price is currently active\n * @return isBoundedPricingEnabled Whether the asset is whitelisted for bounded pricing\n * @return lastProtectionTriggeredAt Timestamp of the last protection trigger\n * @return cooldownPeriod Minimum time protection stays active after last trigger\n * @return assetAddr The underlying asset address stored in the struct\n * @return triggerThreshold Entry deviation threshold (mantissa) that activates protection\n * @return resetThreshold Exit deviation threshold (mantissa) below which protection can be disabled\n * @return cachingEnabled Whether transient caching of the bounded pair is enabled for the asset\n */\n function assetProtectionConfig(\n address asset\n )\n external\n view\n returns (\n uint128 minPrice,\n uint128 maxPrice,\n bool currentlyUsingProtectedPrice,\n bool isBoundedPricingEnabled,\n uint64 lastProtectionTriggeredAt,\n uint64 cooldownPeriod,\n address assetAddr,\n uint128 triggerThreshold,\n uint128 resetThreshold,\n bool cachingEnabled\n );\n\n /**\n * @notice Checks if an asset is whitelisted for bounded pricing\n * @param asset The underlying asset address\n * @return True if the asset is whitelisted\n */\n function isBoundedPricingEnabled(address asset) external view returns (bool);\n\n /**\n * @notice Checks if the asset is currently using the protected (bounded) price\n * @param asset The underlying asset address\n * @return True if the asset is currently using the protected price instead of spot\n */\n function currentlyUsingProtectedPrice(address asset) external view returns (bool);\n\n /**\n * @notice Checks if protection can be exited for a given asset\n * @param asset The underlying asset address\n * @return True if both the cooldown has elapsed and the price range has converged below the exit threshold\n */\n function canExitProtection(address asset) external view returns (bool);\n\n /**\n * @notice Returns the initialized asset at the given index (auto-generated array getter)\n * @param index Array index\n * @return The asset address at the given index\n */\n function allAssets(uint256 index) external view returns (address);\n\n /**\n * @notice Returns all currently whitelisted asset addresses\n * @return result Array of whitelisted asset addresses\n */\n function getAllBoundedPricingEnabledAssets() external view returns (address[] memory result);\n\n /**\n * @notice Returns all asset addresses that have ever been initialized\n * @return Array of all initialized asset addresses\n */\n function getInitializedAssets() external view returns (address[] memory);\n\n /**\n * @notice Batch-checks which assets' on-chain min/max have drifted beyond the keeper deadband\n * @param assets Array of asset addresses to check\n * @param proposedMins Keeper's proposed window minimum prices\n * @param proposedMaxs Keeper's proposed window maximum prices\n * @return needsMinUpdate Whether minPrice drift exceeds the deadband for each asset\n * @return needsMaxUpdate Whether maxPrice drift exceeds the deadband for each asset\n * @custom:error InvalidArrayLength if the input array lengths do not match\n */\n function checkAndGetWindowDrift(\n address[] calldata assets,\n uint128[] calldata proposedMins,\n uint128[] calldata proposedMaxs\n ) external view returns (bool[] memory needsMinUpdate, bool[] memory needsMaxUpdate);\n}\n" + }, + "@venusprotocol/oracle/contracts/interfaces/OracleInterface.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity ^0.8.25;\n\ninterface OracleInterface {\n function getPrice(address asset) external view returns (uint256);\n}\n\ninterface ResilientOracleInterface is OracleInterface {\n function updatePrice(address vToken) external;\n\n function updateAssetPrice(address asset) external;\n\n function getUnderlyingPrice(address vToken) external view returns (uint256);\n}\n\ninterface BoundValidatorInterface {\n function validatePriceWithAnchorPrice(\n address asset,\n uint256 reporterPrice,\n uint256 anchorPrice\n ) external view returns (bool);\n}\n" + }, + "@venusprotocol/protocol-reserve/contracts/Interfaces/IProtocolShareReserve.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity ^0.8.25;\n\ninterface IProtocolShareReserve {\n /// @notice it represents the type of vToken income\n enum IncomeType {\n SPREAD,\n LIQUIDATION,\n ERC4626_WRAPPER_REWARDS,\n FLASHLOAN\n }\n\n function updateAssetsState(\n address comptroller,\n address asset,\n IncomeType incomeType\n ) external;\n}\n" + }, + "@venusprotocol/solidity-utilities/contracts/constants.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity ^0.8.25;\n\n/// @dev Base unit for computations, usually used in scaling (multiplications, divisions)\nuint256 constant EXP_SCALE = 1e18;\n\n/// @dev A unit (literal one) in EXP_SCALE, usually used in additions/subtractions\nuint256 constant MANTISSA_ONE = EXP_SCALE;\n\n/// @dev The approximate number of seconds per year\nuint256 constant SECONDS_PER_YEAR = 31_536_000;\n" + }, + "@venusprotocol/solidity-utilities/contracts/TimeManagerV8.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\n\nimport { SECONDS_PER_YEAR } from \"./constants.sol\";\n\nabstract contract TimeManagerV8 {\n /// @notice Stores blocksPerYear if isTimeBased is true else secondsPerYear is stored\n /// @custom:oz-upgrades-unsafe-allow state-variable-immutable\n uint256 public immutable blocksOrSecondsPerYear;\n\n /// @notice Acknowledges if a contract is time based or not\n /// @custom:oz-upgrades-unsafe-allow state-variable-immutable\n bool public immutable isTimeBased;\n\n /// @notice Stores the current block timestamp or block number depending on isTimeBased\n /// @custom:oz-upgrades-unsafe-allow state-variable-immutable\n function() view returns (uint256) private immutable _getCurrentSlot;\n\n /**\n * @dev This empty reserved space is put in place to allow future versions to add new\n * variables without shifting down storage in the inheritance chain\n * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\n */\n uint256[48] private __gap;\n\n /// @notice Thrown when blocks per year is invalid\n error InvalidBlocksPerYear();\n\n /// @notice Thrown when time based but blocks per year is provided\n error InvalidTimeBasedConfiguration();\n\n /**\n * @param timeBased_ A boolean indicating whether the contract is based on time or block\n * If timeBased is true than blocksPerYear_ param is ignored as blocksOrSecondsPerYear is set to SECONDS_PER_YEAR\n * @param blocksPerYear_ The number of blocks per year\n * @custom:error InvalidBlocksPerYear is thrown if blocksPerYear entered is zero and timeBased is false\n * @custom:error InvalidTimeBasedConfiguration is thrown if blocksPerYear entered is non zero and timeBased is true\n * @custom:oz-upgrades-unsafe-allow constructor\n */\n constructor(bool timeBased_, uint256 blocksPerYear_) {\n if (!timeBased_ && blocksPerYear_ == 0) {\n revert InvalidBlocksPerYear();\n }\n\n if (timeBased_ && blocksPerYear_ != 0) {\n revert InvalidTimeBasedConfiguration();\n }\n\n isTimeBased = timeBased_;\n blocksOrSecondsPerYear = timeBased_ ? SECONDS_PER_YEAR : blocksPerYear_;\n _getCurrentSlot = timeBased_ ? _getBlockTimestamp : _getBlockNumber;\n }\n\n /**\n * @dev Function to simply retrieve block number or block timestamp\n * @return Current block number or block timestamp\n */\n function getBlockNumberOrTimestamp() public view virtual returns (uint256) {\n return _getCurrentSlot();\n }\n\n /**\n * @dev Returns the current timestamp in seconds\n * @return The current timestamp\n */\n function _getBlockTimestamp() private view returns (uint256) {\n return block.timestamp;\n }\n\n /**\n * @dev Returns the current block number\n * @return The current block number\n */\n function _getBlockNumber() private view returns (uint256) {\n return block.number;\n }\n}\n" + }, + "@venusprotocol/venus-protocol/contracts/Tokens/Prime/Interfaces/IPrime.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity ^0.8.25;\n\nimport { PrimeStorageV1 } from \"../PrimeStorage.sol\";\n\n/**\n * @title IPrime\n * @author Venus\n * @notice Interface for Prime Token\n */\ninterface IPrime {\n struct APRInfo {\n // supply APR of the user in BPS\n uint256 supplyAPR;\n // borrow APR of the user in BPS\n uint256 borrowAPR;\n // total score of the market\n uint256 totalScore;\n // score of the user\n uint256 userScore;\n // capped XVS balance of the user\n uint256 xvsBalanceForScore;\n // capital of the user\n uint256 capital;\n // capped supply of the user\n uint256 cappedSupply;\n // capped borrow of the user\n uint256 cappedBorrow;\n // capped supply of user in USD\n uint256 supplyCapUSD;\n // capped borrow of user in USD\n uint256 borrowCapUSD;\n }\n\n struct Capital {\n // capital of the user\n uint256 capital;\n // capped supply of the user\n uint256 cappedSupply;\n // capped borrow of the user\n uint256 cappedBorrow;\n // capped supply of user in USD\n uint256 supplyCapUSD;\n // capped borrow of user in USD\n uint256 borrowCapUSD;\n }\n\n /**\n * @notice Returns boosted pending interest accrued for a user for all markets\n * @param user the account for which to get the accrued interests\n * @return pendingRewards the number of underlying tokens accrued by the user for all markets\n */\n function getPendingRewards(address user) external returns (PrimeStorageV1.PendingReward[] memory pendingRewards);\n\n /**\n * @notice Update total score of multiple users and market\n * @param users accounts for which we need to update score\n */\n function updateScores(address[] memory users) external;\n\n /**\n * @notice Update value of alpha\n * @param _alphaNumerator numerator of alpha. If alpha is 0.5 then numerator is 1\n * @param _alphaDenominator denominator of alpha. If alpha is 0.5 then denominator is 2\n */\n function updateAlpha(uint128 _alphaNumerator, uint128 _alphaDenominator) external;\n\n /**\n * @notice Update multipliers for a market\n * @param market address of the market vToken\n * @param supplyMultiplier new supply multiplier for the market, scaled by 1e18\n * @param borrowMultiplier new borrow multiplier for the market, scaled by 1e18\n */\n function updateMultipliers(address market, uint256 supplyMultiplier, uint256 borrowMultiplier) external;\n\n /**\n * @notice Add a market to prime program\n * @param comptroller address of the comptroller\n * @param market address of the market vToken\n * @param supplyMultiplier the multiplier for supply cap. It should be converted to 1e18\n * @param borrowMultiplier the multiplier for borrow cap. It should be converted to 1e18\n */\n function addMarket(\n address comptroller,\n address market,\n uint256 supplyMultiplier,\n uint256 borrowMultiplier\n ) external;\n\n /**\n * @notice Set limits for total tokens that can be minted\n * @param _irrevocableLimit total number of irrevocable tokens that can be minted\n * @param _revocableLimit total number of revocable tokens that can be minted\n */\n function setLimit(uint256 _irrevocableLimit, uint256 _revocableLimit) external;\n\n /**\n * @notice Directly issue prime tokens to users\n * @param isIrrevocable are the tokens being issued\n * @param users list of address to issue tokens to\n */\n function issue(bool isIrrevocable, address[] calldata users) external;\n\n /**\n * @notice Executed by XVSVault whenever user's XVSVault balance changes\n * @param user the account address whose balance was updated\n */\n function xvsUpdated(address user) external;\n\n /**\n * @notice accrues interest and updates score for an user for a specific market\n * @param user the account address for which to accrue interest and update score\n * @param market the market for which to accrue interest and update score\n */\n function accrueInterestAndUpdateScore(address user, address market) external;\n\n /**\n * @notice For claiming prime token when staking period is completed\n */\n function claim() external;\n\n /**\n * @notice For burning any prime token\n * @param user the account address for which the prime token will be burned\n */\n function burn(address user) external;\n\n /**\n * @notice To pause or unpause claiming of interest\n */\n function togglePause() external;\n\n /**\n * @notice For user to claim boosted yield\n * @param vToken the market for which claim the accrued interest\n * @return amount the amount of tokens transferred to the user\n */\n function claimInterest(address vToken) external returns (uint256);\n\n /**\n * @notice For user to claim boosted yield\n * @param vToken the market for which claim the accrued interest\n * @param user the user for which to claim the accrued interest\n * @return amount the amount of tokens transferred to the user\n */\n function claimInterest(address vToken, address user) external returns (uint256);\n\n /**\n * @notice Distributes income from market since last distribution\n * @param vToken the market for which to distribute the income\n */\n function accrueInterest(address vToken) external;\n\n /**\n * @notice Returns boosted interest accrued for a user\n * @param vToken the market for which to fetch the accrued interest\n * @param user the account for which to get the accrued interest\n * @return interestAccrued the number of underlying tokens accrued by the user since the last accrual\n */\n function getInterestAccrued(address vToken, address user) external returns (uint256);\n\n /**\n * @notice Retrieves an array of all available markets\n * @return an array of addresses representing all available markets\n */\n function getAllMarkets() external view returns (address[] memory);\n\n /**\n * @notice fetch the numbers of seconds remaining for staking period to complete\n * @param user the account address for which we are checking the remaining time\n * @return timeRemaining the number of seconds the user needs to wait to claim prime token\n */\n function claimTimeRemaining(address user) external view returns (uint256);\n\n /**\n * @notice Returns supply and borrow APR for user for a given market\n * @param market the market for which to fetch the APR\n * @param user the account for which to get the APR\n * @return aprInfo APR information for the user for the given market\n */\n function calculateAPR(address market, address user) external view returns (APRInfo memory aprInfo);\n\n /**\n * @notice Returns supply and borrow APR for estimated supply, borrow and XVS staked\n * @param market the market for which to fetch the APR\n * @param user the account for which to get the APR\n * @param borrow hypothetical borrow amount\n * @param supply hypothetical supply amount\n * @param xvsStaked hypothetical staked XVS amount\n * @return aprInfo APR information for the user for the given market\n */\n function estimateAPR(\n address market,\n address user,\n uint256 borrow,\n uint256 supply,\n uint256 xvsStaked\n ) external view returns (APRInfo memory aprInfo);\n\n /**\n * @notice the total income that's going to be distributed in a year to prime token holders\n * @param vToken the market for which to fetch the total income that's going to distributed in a year\n * @return amount the total income\n */\n function incomeDistributionYearly(address vToken) external view returns (uint256 amount);\n\n /**\n * @notice Returns if user is a prime holder\n * @return isPrimeHolder true if user is a prime holder\n */\n function isUserPrimeHolder(address user) external view returns (bool);\n\n /**\n * @notice Set the limit for the loops can iterate to avoid the DOS\n * @param loopsLimit Number of loops limit\n */\n function setMaxLoopsLimit(uint256 loopsLimit) external;\n\n /**\n * @notice Update staked at timestamp for multiple users\n * @param users accounts for which we need to update staked at timestamp\n * @param timestamps new staked at timestamp for the users\n */\n function setStakedAt(address[] calldata users, uint256[] calldata timestamps) external;\n}\n" + }, + "@venusprotocol/venus-protocol/contracts/Tokens/Prime/PrimeStorage.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\n\nimport { ResilientOracleInterface } from \"@venusprotocol/oracle/contracts/interfaces/OracleInterface.sol\";\n\n/**\n * @title PrimeStorageV1\n * @author Venus\n * @notice Storage for Prime Token\n */\ncontract PrimeStorageV1 {\n struct Token {\n bool exists;\n bool isIrrevocable;\n }\n\n struct Market {\n uint256 supplyMultiplier;\n uint256 borrowMultiplier;\n uint256 rewardIndex;\n uint256 sumOfMembersScore;\n bool exists;\n }\n\n struct Interest {\n uint256 accrued;\n uint256 score;\n uint256 rewardIndex;\n }\n\n struct PendingReward {\n address vToken;\n address rewardToken;\n uint256 amount;\n }\n\n /// @notice Base unit for computations, usually used in scaling (multiplications, divisions)\n uint256 internal constant EXP_SCALE = 1e18;\n\n /// @notice maximum BPS = 100%\n uint256 internal constant MAXIMUM_BPS = 1e4;\n\n /// @notice Mapping to get prime token's metadata\n mapping(address => Token) public tokens;\n\n /// @notice Tracks total irrevocable tokens minted\n uint256 public totalIrrevocable;\n\n /// @notice Tracks total revocable tokens minted\n uint256 public totalRevocable;\n\n /// @notice Indicates maximum revocable tokens that can be minted\n uint256 public revocableLimit;\n\n /// @notice Indicates maximum irrevocable tokens that can be minted\n uint256 public irrevocableLimit;\n\n /// @notice Tracks when prime token eligible users started staking for claiming prime token\n mapping(address => uint256) public stakedAt;\n\n /// @notice vToken to market configuration\n mapping(address => Market) public markets;\n\n /// @notice vToken to user to user index\n mapping(address => mapping(address => Interest)) public interests;\n\n /// @notice A list of boosted markets\n address[] internal _allMarkets;\n\n /// @notice numerator of alpha. Ex: if alpha is 0.5 then this will be 1\n uint128 public alphaNumerator;\n\n /// @notice denominator of alpha. Ex: if alpha is 0.5 then this will be 2\n uint128 public alphaDenominator;\n\n /// @notice address of XVS vault\n address public xvsVault;\n\n /// @notice address of XVS vault reward token\n address public xvsVaultRewardToken;\n\n /// @notice address of XVS vault pool id\n uint256 public xvsVaultPoolId;\n\n /// @notice mapping to check if a account's score was updated in the round\n mapping(uint256 => mapping(address => bool)) public isScoreUpdated;\n\n /// @notice unique id for next round\n uint256 public nextScoreUpdateRoundId;\n\n /// @notice total number of accounts whose score needs to be updated\n uint256 public totalScoreUpdatesRequired;\n\n /// @notice total number of accounts whose score is yet to be updated\n uint256 public pendingScoreUpdates;\n\n /// @notice mapping used to find if an asset is part of prime markets\n mapping(address => address) public vTokenForAsset;\n\n /// @notice Address of core pool comptroller contract\n address internal corePoolComptroller;\n\n /// @notice unreleased income from PLP that's already distributed to prime holders\n /// @dev mapping of asset address => amount\n mapping(address => uint256) public unreleasedPLPIncome;\n\n /// @notice The address of PLP contract\n address public primeLiquidityProvider;\n\n /// @notice The address of ResilientOracle contract\n ResilientOracleInterface public oracle;\n\n /// @notice The address of PoolRegistry contract\n address public poolRegistry;\n\n /// @dev This empty reserved space is put in place to allow future versions to add new\n /// variables without shifting down storage in the inheritance chain.\n uint256[26] private __gap;\n}\n" + }, + "contracts/Comptroller.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\n\nimport { Ownable2StepUpgradeable } from \"@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol\";\nimport { ResilientOracleInterface } from \"@venusprotocol/oracle/contracts/interfaces/OracleInterface.sol\";\nimport { AccessControlledV8 } from \"@venusprotocol/governance-contracts/contracts/Governance/AccessControlledV8.sol\";\nimport { IPrime } from \"@venusprotocol/venus-protocol/contracts/Tokens/Prime/Interfaces/IPrime.sol\";\n\nimport { ComptrollerInterface, Action } from \"./ComptrollerInterface.sol\";\nimport { ComptrollerStorage } from \"./ComptrollerStorage.sol\";\nimport { ExponentialNoError } from \"./ExponentialNoError.sol\";\nimport { VToken } from \"./VToken.sol\";\nimport { RewardsDistributor } from \"./Rewards/RewardsDistributor.sol\";\nimport { MaxLoopsLimitHelper } from \"./MaxLoopsLimitHelper.sol\";\nimport { ensureNonzeroAddress } from \"./lib/validators.sol\";\n\n/**\n * @title Comptroller\n * @author Venus\n * @notice The Comptroller is designed to provide checks for all minting, redeeming, transferring, borrowing, lending, repaying, liquidating,\n * and seizing done by the `vToken` contract. Each pool has one `Comptroller` checking these interactions across markets. When a user interacts\n * with a given market by one of these main actions, a call is made to a corresponding hook in the associated `Comptroller`, which either allows\n * or reverts the transaction. These hooks also update supply and borrow rewards as they are called. The comptroller holds the logic for assessing\n * liquidity snapshots of an account via the collateral factor and liquidation threshold. This check determines the collateral needed for a borrow,\n * as well as how much of a borrow may be liquidated. A user may borrow a portion of their collateral with the maximum amount determined by the\n * markets collateral factor. However, if their borrowed amount exceeds an amount calculated using the market’s corresponding liquidation threshold,\n * the borrow is eligible for liquidation.\n *\n * The `Comptroller` also includes two functions `liquidateAccount()` and `healAccount()`, which are meant to handle accounts that do not exceed\n * the `minLiquidatableCollateral` for the `Comptroller`:\n *\n * - `healAccount()`: This function is called to seize all of a given user’s collateral, requiring the `msg.sender` repay a certain percentage\n * of the debt calculated by `collateral/(borrows*liquidationIncentive)`. The function can only be called if the calculated percentage does not exceed\n * 100%, because otherwise no `badDebt` would be created and `liquidateAccount()` should be used instead. The difference in the actual amount of debt\n * and debt paid off is recorded as `badDebt` for each market, which can then be auctioned off for the risk reserves of the associated pool.\n * - `liquidateAccount()`: This function can only be called if the collateral seized will cover all borrows of an account, as well as the liquidation\n * incentive. Otherwise, the pool will incur bad debt, in which case the function `healAccount()` should be used instead. This function skips the logic\n * verifying that the repay amount does not exceed the close factor.\n */\ncontract Comptroller is\n Ownable2StepUpgradeable,\n AccessControlledV8,\n ComptrollerStorage,\n ComptrollerInterface,\n ExponentialNoError,\n MaxLoopsLimitHelper\n{\n // PoolRegistry, immutable to save on gas\n /// @custom:oz-upgrades-unsafe-allow state-variable-immutable\n address public immutable poolRegistry;\n\n /// @notice Emitted when an account enters a market\n event MarketEntered(VToken indexed vToken, address indexed account);\n\n /// @notice Emitted when an account exits a market\n event MarketExited(VToken indexed vToken, address indexed account);\n\n /// @notice Emitted when close factor is changed by admin\n event NewCloseFactor(uint256 oldCloseFactorMantissa, uint256 newCloseFactorMantissa);\n\n /// @notice Emitted when a collateral factor is changed by admin\n event NewCollateralFactor(VToken vToken, uint256 oldCollateralFactorMantissa, uint256 newCollateralFactorMantissa);\n\n /// @notice Emitted when liquidation threshold is changed by admin\n event NewLiquidationThreshold(\n VToken vToken,\n uint256 oldLiquidationThresholdMantissa,\n uint256 newLiquidationThresholdMantissa\n );\n\n /// @notice Emitted when liquidation incentive is changed by admin\n event NewLiquidationIncentive(uint256 oldLiquidationIncentiveMantissa, uint256 newLiquidationIncentiveMantissa);\n\n /// @notice Emitted when price oracle is changed\n event NewPriceOracle(ResilientOracleInterface oldPriceOracle, ResilientOracleInterface newPriceOracle);\n\n /// @notice Emitted when an action is paused on a market\n event ActionPausedMarket(VToken vToken, Action action, bool pauseState);\n\n /// @notice Emitted when borrow cap for a vToken is changed\n event NewBorrowCap(VToken indexed vToken, uint256 newBorrowCap);\n\n /// @notice Emitted when the collateral threshold (in USD) for non-batch liquidations is changed\n event NewMinLiquidatableCollateral(uint256 oldMinLiquidatableCollateral, uint256 newMinLiquidatableCollateral);\n\n /// @notice Emitted when supply cap for a vToken is changed\n event NewSupplyCap(VToken indexed vToken, uint256 newSupplyCap);\n\n /// @notice Emitted when a rewards distributor is added\n event NewRewardsDistributor(address indexed rewardsDistributor, address indexed rewardToken);\n\n /// @notice Emitted when a market is supported\n event MarketSupported(VToken vToken);\n\n /// @notice Emitted when prime token contract address is changed\n event NewPrimeToken(IPrime oldPrimeToken, IPrime newPrimeToken);\n\n /// @notice Emitted when forced liquidation is enabled or disabled for a market\n event IsForcedLiquidationEnabledUpdated(address indexed vToken, bool enable);\n\n /// @notice Emitted when a market is unlisted\n event MarketUnlisted(address indexed vToken);\n /// @notice Emitted when the borrowing or redeeming delegate rights are updated for an account\n event DelegateUpdated(address indexed approver, address indexed delegate, bool approved);\n\n /// @notice Thrown when collateral factor exceeds the upper bound\n error InvalidCollateralFactor();\n\n /// @notice Thrown when liquidation threshold exceeds the collateral factor\n error InvalidLiquidationThreshold();\n\n /// @notice Thrown when the action is only available to specific sender, but the real sender was different\n error UnexpectedSender(address expectedSender, address actualSender);\n\n /// @notice Thrown when the oracle returns an invalid price for some asset\n error PriceError(address vToken);\n\n /// @notice Thrown if VToken unexpectedly returned a nonzero error code while trying to get account snapshot\n error SnapshotError(address vToken, address user);\n\n /// @notice Thrown when the market is not listed\n error MarketNotListed(address market);\n\n /// @notice Thrown when a market has an unexpected comptroller\n error ComptrollerMismatch();\n\n /// @notice Thrown when user is not member of market\n error MarketNotCollateral(address vToken, address user);\n\n /// @notice Thrown when borrow action is not paused\n error BorrowActionNotPaused();\n\n /// @notice Thrown when mint action is not paused\n error MintActionNotPaused();\n\n /// @notice Thrown when redeem action is not paused\n error RedeemActionNotPaused();\n\n /// @notice Thrown when repay action is not paused\n error RepayActionNotPaused();\n\n /// @notice Thrown when seize action is not paused\n error SeizeActionNotPaused();\n\n /// @notice Thrown when exit market action is not paused\n error ExitMarketActionNotPaused();\n\n /// @notice Thrown when transfer action is not paused\n error TransferActionNotPaused();\n\n /// @notice Thrown when enter market action is not paused\n error EnterMarketActionNotPaused();\n\n /// @notice Thrown when liquidate action is not paused\n error LiquidateActionNotPaused();\n\n /// @notice Thrown when borrow cap is not zero\n error BorrowCapIsNotZero();\n\n /// @notice Thrown when supply cap is not zero\n error SupplyCapIsNotZero();\n\n /// @notice Thrown when collateral factor is not zero\n error CollateralFactorIsNotZero();\n\n /**\n * @notice Thrown during the liquidation if user's total collateral amount is lower than\n * a predefined threshold. In this case only batch liquidations (either liquidateAccount\n * or healAccount) are available.\n */\n error MinimalCollateralViolated(uint256 expectedGreaterThan, uint256 actual);\n error CollateralExceedsThreshold(uint256 expectedLessThanOrEqualTo, uint256 actual);\n error InsufficientCollateral(uint256 collateralToSeize, uint256 availableCollateral);\n\n /// @notice Thrown when the account doesn't have enough liquidity to redeem or borrow\n error InsufficientLiquidity();\n\n /// @notice Thrown when trying to liquidate a healthy account\n error InsufficientShortfall();\n\n /// @notice Thrown when trying to repay more than allowed by close factor\n error TooMuchRepay();\n\n /// @notice Thrown if the user is trying to exit a market in which they have an outstanding debt\n error NonzeroBorrowBalance();\n\n /// @notice Thrown when trying to perform an action that is paused\n error ActionPaused(address market, Action action);\n\n /// @notice Thrown when trying to add a market that is already listed\n error MarketAlreadyListed(address market);\n\n /// @notice Thrown if the supply cap is exceeded\n error SupplyCapExceeded(address market, uint256 cap);\n\n /// @notice Thrown if the borrow cap is exceeded\n error BorrowCapExceeded(address market, uint256 cap);\n\n /// @notice Thrown if delegate approval status is already set to the requested value\n error DelegationStatusUnchanged();\n\n /// @param poolRegistry_ Pool registry address\n /// @custom:oz-upgrades-unsafe-allow constructor\n /// @custom:error ZeroAddressNotAllowed is thrown when pool registry address is zero\n constructor(address poolRegistry_) {\n ensureNonzeroAddress(poolRegistry_);\n\n poolRegistry = poolRegistry_;\n _disableInitializers();\n }\n\n /**\n * @param loopLimit Limit for the loops can iterate to avoid the DOS\n * @param accessControlManager Access control manager contract address\n */\n function initialize(uint256 loopLimit, address accessControlManager) external initializer {\n __Ownable2Step_init();\n __AccessControlled_init_unchained(accessControlManager);\n\n _setMaxLoopsLimit(loopLimit);\n }\n\n /**\n * @notice Add assets to be included in account liquidity calculation; enabling them to be used as collateral\n * @param vTokens The list of addresses of the vToken markets to be enabled\n * @return errors An array of NO_ERROR for compatibility with Venus core tooling\n * @custom:event MarketEntered is emitted for each market on success\n * @custom:error ActionPaused error is thrown if entering any of the markets is paused\n * @custom:error MarketNotListed error is thrown if any of the markets is not listed\n * @custom:access Not restricted\n */\n function enterMarkets(address[] memory vTokens) external override returns (uint256[] memory) {\n uint256 len = vTokens.length;\n\n uint256[] memory results = new uint256[](len);\n for (uint256 i; i < len; ++i) {\n VToken vToken = VToken(vTokens[i]);\n\n _addToMarket(vToken, msg.sender);\n results[i] = NO_ERROR;\n }\n\n return results;\n }\n\n /**\n * @notice Unlist a market by setting isListed to false\n * @dev Checks if all actions are paused, borrow/supply caps is set to 0 and collateral factor is to 0.\n * @param market The address of the market (token) to unlist\n * @return uint256 Always NO_ERROR for compatibility with Venus core tooling\n * @custom:event MarketUnlisted is emitted on success\n * @custom:error MarketNotListed error is thrown when the market is not listed\n * @custom:error BorrowActionNotPaused error is thrown if borrow action is not paused\n * @custom:error MintActionNotPaused error is thrown if mint action is not paused\n * @custom:error RedeemActionNotPaused error is thrown if redeem action is not paused\n * @custom:error RepayActionNotPaused error is thrown if repay action is not paused\n * @custom:error EnterMarketActionNotPaused error is thrown if enter market action is not paused\n * @custom:error LiquidateActionNotPaused error is thrown if liquidate action is not paused\n * @custom:error BorrowCapIsNotZero error is thrown if borrow cap is not zero\n * @custom:error SupplyCapIsNotZero error is thrown if supply cap is not zero\n * @custom:error CollateralFactorIsNotZero error is thrown if collateral factor is not zero\n */\n function unlistMarket(address market) external returns (uint256) {\n _checkAccessAllowed(\"unlistMarket(address)\");\n\n Market storage _market = markets[market];\n\n if (!_market.isListed) {\n revert MarketNotListed(market);\n }\n\n if (!actionPaused(market, Action.BORROW)) {\n revert BorrowActionNotPaused();\n }\n\n if (!actionPaused(market, Action.MINT)) {\n revert MintActionNotPaused();\n }\n\n if (!actionPaused(market, Action.REDEEM)) {\n revert RedeemActionNotPaused();\n }\n\n if (!actionPaused(market, Action.REPAY)) {\n revert RepayActionNotPaused();\n }\n\n if (!actionPaused(market, Action.SEIZE)) {\n revert SeizeActionNotPaused();\n }\n\n if (!actionPaused(market, Action.ENTER_MARKET)) {\n revert EnterMarketActionNotPaused();\n }\n\n if (!actionPaused(market, Action.LIQUIDATE)) {\n revert LiquidateActionNotPaused();\n }\n\n if (!actionPaused(market, Action.TRANSFER)) {\n revert TransferActionNotPaused();\n }\n\n if (!actionPaused(market, Action.EXIT_MARKET)) {\n revert ExitMarketActionNotPaused();\n }\n\n if (borrowCaps[market] != 0) {\n revert BorrowCapIsNotZero();\n }\n\n if (supplyCaps[market] != 0) {\n revert SupplyCapIsNotZero();\n }\n\n if (_market.collateralFactorMantissa != 0) {\n revert CollateralFactorIsNotZero();\n }\n\n _market.isListed = false;\n emit MarketUnlisted(market);\n\n return NO_ERROR;\n }\n\n /**\n * @notice Grants or revokes the borrowing or redeeming delegate rights to / from an account\n * If allowed, the delegate will be able to borrow funds on behalf of the sender\n * Upon a delegated borrow, the delegate will receive the funds, and the borrower\n * will see the debt on their account\n * Upon a delegated redeem, the delegate will receive the redeemed amount and the approver\n * will see a deduction in his vToken balance\n * @param delegate The address to update the rights for\n * @param approved Whether to grant (true) or revoke (false) the borrowing or redeeming rights\n * @custom:event DelegateUpdated emits on success\n * @custom:error ZeroAddressNotAllowed is thrown when delegate address is zero\n * @custom:error DelegationStatusUnchanged is thrown if approval status is already set to the requested value\n * @custom:access Not restricted\n */\n function updateDelegate(address delegate, bool approved) external {\n ensureNonzeroAddress(delegate);\n if (approvedDelegates[msg.sender][delegate] == approved) {\n revert DelegationStatusUnchanged();\n }\n\n approvedDelegates[msg.sender][delegate] = approved;\n emit DelegateUpdated(msg.sender, delegate, approved);\n }\n\n /**\n * @notice Removes asset from sender's account liquidity calculation; disabling them as collateral\n * @dev Sender must not have an outstanding borrow balance in the asset,\n * or be providing necessary collateral for an outstanding borrow.\n * @param vTokenAddress The address of the asset to be removed\n * @return error Always NO_ERROR for compatibility with Venus core tooling\n * @custom:event MarketExited is emitted on success\n * @custom:error ActionPaused error is thrown if exiting the market is paused\n * @custom:error NonzeroBorrowBalance error is thrown if the user has an outstanding borrow in this market\n * @custom:error MarketNotListed error is thrown when the market is not listed\n * @custom:error InsufficientLiquidity error is thrown if exiting the market would lead to user's insolvency\n * @custom:error SnapshotError is thrown if some vToken fails to return the account's supply and borrows\n * @custom:error PriceError is thrown if the oracle returns an incorrect price for some asset\n * @custom:access Not restricted\n */\n function exitMarket(address vTokenAddress) external override returns (uint256) {\n _checkActionPauseState(vTokenAddress, Action.EXIT_MARKET);\n VToken vToken = VToken(vTokenAddress);\n /* Get sender tokensHeld and amountOwed underlying from the vToken */\n (uint256 tokensHeld, uint256 amountOwed, ) = _safeGetAccountSnapshot(vToken, msg.sender);\n\n /* Fail if the sender has a borrow balance */\n if (amountOwed != 0) {\n revert NonzeroBorrowBalance();\n }\n\n /* Fail if the sender is not permitted to redeem all of their tokens */\n _checkRedeemAllowed(vTokenAddress, msg.sender, tokensHeld);\n\n Market storage marketToExit = markets[address(vToken)];\n\n /* Return true if the sender is not already ‘in’ the market */\n if (!marketToExit.accountMembership[msg.sender]) {\n return NO_ERROR;\n }\n\n /* Set vToken account membership to false */\n delete marketToExit.accountMembership[msg.sender];\n\n /* Delete vToken from the account’s list of assets */\n // load into memory for faster iteration\n VToken[] memory userAssetList = accountAssets[msg.sender];\n uint256 len = userAssetList.length;\n\n uint256 assetIndex = len;\n for (uint256 i; i < len; ++i) {\n if (userAssetList[i] == vToken) {\n assetIndex = i;\n break;\n }\n }\n\n // We *must* have found the asset in the list or our redundant data structure is broken\n assert(assetIndex < len);\n\n // copy last item in list to location of item to be removed, reduce length by 1\n VToken[] storage storedList = accountAssets[msg.sender];\n storedList[assetIndex] = storedList[storedList.length - 1];\n storedList.pop();\n\n emit MarketExited(vToken, msg.sender);\n\n return NO_ERROR;\n }\n\n /*** Policy Hooks ***/\n\n /**\n * @notice Checks if the account should be allowed to mint tokens in the given market\n * @param vToken The market to verify the mint against\n * @param minter The account which would get the minted tokens\n * @param mintAmount The amount of underlying being supplied to the market in exchange for tokens\n * @custom:error ActionPaused error is thrown if supplying to this market is paused\n * @custom:error MarketNotListed error is thrown when the market is not listed\n * @custom:error SupplyCapExceeded error is thrown if the total supply exceeds the cap after minting\n * @custom:access Not restricted\n */\n function preMintHook(address vToken, address minter, uint256 mintAmount) external override {\n _checkActionPauseState(vToken, Action.MINT);\n\n if (!markets[vToken].isListed) {\n revert MarketNotListed(address(vToken));\n }\n\n uint256 supplyCap = supplyCaps[vToken];\n // Skipping the cap check for uncapped coins to save some gas\n if (supplyCap != type(uint256).max) {\n uint256 vTokenSupply = VToken(vToken).totalSupply();\n Exp memory exchangeRate = Exp({ mantissa: VToken(vToken).exchangeRateStored() });\n uint256 nextTotalSupply = mul_ScalarTruncateAddUInt(exchangeRate, vTokenSupply, mintAmount);\n if (nextTotalSupply > supplyCap) {\n revert SupplyCapExceeded(vToken, supplyCap);\n }\n }\n\n // Keep the flywheel moving\n uint256 rewardDistributorsCount = rewardsDistributors.length;\n\n for (uint256 i; i < rewardDistributorsCount; ++i) {\n RewardsDistributor rewardsDistributor = rewardsDistributors[i];\n rewardsDistributor.updateRewardTokenSupplyIndex(vToken);\n rewardsDistributor.distributeSupplierRewardToken(vToken, minter);\n }\n }\n\n /**\n * @notice Validates mint, accrues interest and updates score in prime. Reverts on rejection. May emit logs.\n * @param vToken Asset being minted\n * @param minter The address minting the tokens\n * @param actualMintAmount The amount of the underlying asset being minted\n * @param mintTokens The number of tokens being minted\n */\n // solhint-disable-next-line no-unused-vars\n function mintVerify(address vToken, address minter, uint256 actualMintAmount, uint256 mintTokens) external {\n if (address(prime) != address(0)) {\n prime.accrueInterestAndUpdateScore(minter, vToken);\n }\n }\n\n /**\n * @notice Checks if the account should be allowed to redeem tokens in the given market\n * @param vToken The market to verify the redeem against\n * @param redeemer The account which would redeem the tokens\n * @param redeemTokens The number of vTokens to exchange for the underlying asset in the market\n * @custom:error ActionPaused error is thrown if withdrawals are paused in this market\n * @custom:error MarketNotListed error is thrown when the market is not listed\n * @custom:error InsufficientLiquidity error is thrown if the withdrawal would lead to user's insolvency\n * @custom:error SnapshotError is thrown if some vToken fails to return the account's supply and borrows\n * @custom:error PriceError is thrown if the oracle returns an incorrect price for some asset\n * @custom:access Not restricted\n */\n function preRedeemHook(address vToken, address redeemer, uint256 redeemTokens) external override {\n _checkActionPauseState(vToken, Action.REDEEM);\n\n _checkRedeemAllowed(vToken, redeemer, redeemTokens);\n\n // Keep the flywheel moving\n uint256 rewardDistributorsCount = rewardsDistributors.length;\n\n for (uint256 i; i < rewardDistributorsCount; ++i) {\n RewardsDistributor rewardsDistributor = rewardsDistributors[i];\n rewardsDistributor.updateRewardTokenSupplyIndex(vToken);\n rewardsDistributor.distributeSupplierRewardToken(vToken, redeemer);\n }\n }\n\n /**\n * @notice Validates redeem, accrues interest and updates score in prime. Reverts on rejection. May emit logs.\n * @param vToken Asset being redeemed\n * @param redeemer The address redeeming the tokens\n * @param redeemAmount The amount of the underlying asset being redeemed\n * @param redeemTokens The number of tokens being redeemed\n */\n function redeemVerify(address vToken, address redeemer, uint256 redeemAmount, uint256 redeemTokens) external {\n if (address(prime) != address(0)) {\n prime.accrueInterestAndUpdateScore(redeemer, vToken);\n }\n }\n\n /**\n * @notice Validates repayBorrow, accrues interest and updates score in prime. Reverts on rejection. May emit logs.\n * @param vToken Asset being repaid\n * @param payer The address repaying the borrow\n * @param borrower The address of the borrower\n * @param actualRepayAmount The amount of underlying being repaid\n */\n function repayBorrowVerify(\n address vToken,\n address payer, // solhint-disable-line no-unused-vars\n address borrower,\n uint256 actualRepayAmount, // solhint-disable-line no-unused-vars\n uint256 borrowerIndex // solhint-disable-line no-unused-vars\n ) external {\n if (address(prime) != address(0)) {\n prime.accrueInterestAndUpdateScore(borrower, vToken);\n }\n }\n\n /**\n * @notice Validates liquidateBorrow, accrues interest and updates score in prime. Reverts on rejection. May emit logs.\n * @param vTokenBorrowed Asset which was borrowed by the borrower\n * @param vTokenCollateral Asset which was used as collateral and will be seized\n * @param liquidator The address repaying the borrow and seizing the collateral\n * @param borrower The address of the borrower\n * @param actualRepayAmount The amount of underlying being repaid\n * @param seizeTokens The amount of collateral token that will be seized\n */\n function liquidateBorrowVerify(\n address vTokenBorrowed,\n address vTokenCollateral, // solhint-disable-line no-unused-vars\n address liquidator,\n address borrower,\n uint256 actualRepayAmount, // solhint-disable-line no-unused-vars\n uint256 seizeTokens // solhint-disable-line no-unused-vars\n ) external {\n if (address(prime) != address(0)) {\n prime.accrueInterestAndUpdateScore(borrower, vTokenBorrowed);\n prime.accrueInterestAndUpdateScore(liquidator, vTokenBorrowed);\n }\n }\n\n /**\n * @notice Validates seize, accrues interest and updates score in prime. Reverts on rejection. May emit logs.\n * @param vTokenCollateral Asset which was used as collateral and will be seized\n * @param vTokenBorrowed Asset which was borrowed by the borrower\n * @param liquidator The address repaying the borrow and seizing the collateral\n * @param borrower The address of the borrower\n * @param seizeTokens The number of collateral tokens to seize\n */\n function seizeVerify(\n address vTokenCollateral,\n address vTokenBorrowed, // solhint-disable-line no-unused-vars\n address liquidator,\n address borrower,\n uint256 seizeTokens // solhint-disable-line no-unused-vars\n ) external {\n if (address(prime) != address(0)) {\n prime.accrueInterestAndUpdateScore(borrower, vTokenCollateral);\n prime.accrueInterestAndUpdateScore(liquidator, vTokenCollateral);\n }\n }\n\n /**\n * @notice Validates transfer, accrues interest and updates score in prime. Reverts on rejection. May emit logs.\n * @param vToken Asset being transferred\n * @param src The account which sources the tokens\n * @param dst The account which receives the tokens\n * @param transferTokens The number of vTokens to transfer\n */\n // solhint-disable-next-line no-unused-vars\n function transferVerify(address vToken, address src, address dst, uint256 transferTokens) external {\n if (address(prime) != address(0)) {\n prime.accrueInterestAndUpdateScore(src, vToken);\n prime.accrueInterestAndUpdateScore(dst, vToken);\n }\n }\n\n /**\n * @notice Checks if the account should be allowed to borrow the underlying asset of the given market\n * @param vToken The market to verify the borrow against\n * @param borrower The account which would borrow the asset\n * @param borrowAmount The amount of underlying the account would borrow\n * @custom:error ActionPaused error is thrown if borrowing is paused in this market\n * @custom:error MarketNotListed error is thrown when the market is not listed\n * @custom:error InsufficientLiquidity error is thrown if there is not enough collateral to borrow\n * @custom:error BorrowCapExceeded is thrown if the borrow cap will be exceeded should this borrow succeed\n * @custom:error SnapshotError is thrown if some vToken fails to return the account's supply and borrows\n * @custom:error PriceError is thrown if the oracle returns an incorrect price for some asset\n * @custom:access Not restricted if vToken is enabled as collateral, otherwise only vToken\n */\n /// disable-eslint\n function preBorrowHook(address vToken, address borrower, uint256 borrowAmount) external override {\n _checkActionPauseState(vToken, Action.BORROW);\n\n if (!markets[vToken].isListed) {\n revert MarketNotListed(address(vToken));\n }\n\n if (!markets[vToken].accountMembership[borrower]) {\n // only vTokens may call borrowAllowed if borrower not in market\n _checkSenderIs(vToken);\n\n // attempt to add borrower to the market or revert\n _addToMarket(VToken(msg.sender), borrower);\n }\n\n // Update the prices of tokens\n updatePrices(borrower);\n\n if (oracle.getUnderlyingPrice(vToken) == 0) {\n revert PriceError(address(vToken));\n }\n\n uint256 borrowCap = borrowCaps[vToken];\n // Skipping the cap check for uncapped coins to save some gas\n if (borrowCap != type(uint256).max) {\n uint256 totalBorrows = VToken(vToken).totalBorrows();\n uint256 badDebt = VToken(vToken).badDebt();\n uint256 nextTotalBorrows = totalBorrows + borrowAmount + badDebt;\n if (nextTotalBorrows > borrowCap) {\n revert BorrowCapExceeded(vToken, borrowCap);\n }\n }\n\n AccountLiquiditySnapshot memory snapshot = _getHypotheticalLiquiditySnapshot(\n borrower,\n VToken(vToken),\n 0,\n borrowAmount,\n _getCollateralFactor\n );\n\n if (snapshot.shortfall > 0) {\n revert InsufficientLiquidity();\n }\n\n Exp memory borrowIndex = Exp({ mantissa: VToken(vToken).borrowIndex() });\n\n // Keep the flywheel moving\n uint256 rewardDistributorsCount = rewardsDistributors.length;\n\n for (uint256 i; i < rewardDistributorsCount; ++i) {\n RewardsDistributor rewardsDistributor = rewardsDistributors[i];\n rewardsDistributor.updateRewardTokenBorrowIndex(vToken, borrowIndex);\n rewardsDistributor.distributeBorrowerRewardToken(vToken, borrower, borrowIndex);\n }\n }\n\n /**\n * @notice Validates borrow, accrues interest and updates score in prime. Reverts on rejection. May emit logs.\n * @param vToken Asset whose underlying is being borrowed\n * @param borrower The address borrowing the underlying\n * @param borrowAmount The amount of the underlying asset requested to borrow\n */\n // solhint-disable-next-line no-unused-vars\n function borrowVerify(address vToken, address borrower, uint256 borrowAmount) external {\n if (address(prime) != address(0)) {\n prime.accrueInterestAndUpdateScore(borrower, vToken);\n }\n }\n\n /**\n * @notice Checks if the account should be allowed to repay a borrow in the given market\n * @param vToken The market to verify the repay against\n * @param borrower The account which would borrowed the asset\n * @custom:error ActionPaused error is thrown if repayments are paused in this market\n * @custom:error MarketNotListed error is thrown when the market is not listed\n * @custom:access Not restricted\n */\n function preRepayHook(address vToken, address borrower) external override {\n _checkActionPauseState(vToken, Action.REPAY);\n\n oracle.updatePrice(vToken);\n\n if (!markets[vToken].isListed) {\n revert MarketNotListed(address(vToken));\n }\n\n // Keep the flywheel moving\n uint256 rewardDistributorsCount = rewardsDistributors.length;\n\n for (uint256 i; i < rewardDistributorsCount; ++i) {\n Exp memory borrowIndex = Exp({ mantissa: VToken(vToken).borrowIndex() });\n RewardsDistributor rewardsDistributor = rewardsDistributors[i];\n rewardsDistributor.updateRewardTokenBorrowIndex(vToken, borrowIndex);\n rewardsDistributor.distributeBorrowerRewardToken(vToken, borrower, borrowIndex);\n }\n }\n\n /**\n * @notice Checks if the liquidation should be allowed to occur\n * @param vTokenBorrowed Asset which was borrowed by the borrower\n * @param vTokenCollateral Asset which was used as collateral and will be seized\n * @param borrower The address of the borrower\n * @param repayAmount The amount of underlying being repaid\n * @param skipLiquidityCheck Allows the borrow to be liquidated regardless of the account liquidity\n * @custom:error ActionPaused error is thrown if liquidations are paused in this market\n * @custom:error MarketNotListed error is thrown if either collateral or borrowed token is not listed\n * @custom:error TooMuchRepay error is thrown if the liquidator is trying to repay more than allowed by close factor\n * @custom:error MinimalCollateralViolated is thrown if the users' total collateral is lower than the threshold for non-batch liquidations\n * @custom:error InsufficientShortfall is thrown when trying to liquidate a healthy account\n * @custom:error SnapshotError is thrown if some vToken fails to return the account's supply and borrows\n * @custom:error PriceError is thrown if the oracle returns an incorrect price for some asset\n */\n function preLiquidateHook(\n address vTokenBorrowed,\n address vTokenCollateral,\n address borrower,\n uint256 repayAmount,\n bool skipLiquidityCheck\n ) external override {\n // Pause Action.LIQUIDATE on BORROWED TOKEN to prevent liquidating it.\n // If we want to pause liquidating to vTokenCollateral, we should pause\n // Action.SEIZE on it\n _checkActionPauseState(vTokenBorrowed, Action.LIQUIDATE);\n\n // Update the prices of tokens\n updatePrices(borrower);\n\n if (!markets[vTokenBorrowed].isListed) {\n revert MarketNotListed(address(vTokenBorrowed));\n }\n if (!markets[vTokenCollateral].isListed) {\n revert MarketNotListed(address(vTokenCollateral));\n }\n\n uint256 borrowBalance = VToken(vTokenBorrowed).borrowBalanceStored(borrower);\n\n /* Allow accounts to be liquidated if it is a forced liquidation */\n if (skipLiquidityCheck || isForcedLiquidationEnabled[vTokenBorrowed]) {\n if (repayAmount > borrowBalance) {\n revert TooMuchRepay();\n }\n return;\n }\n\n /* The borrower must have shortfall and collateral > threshold in order to be liquidatable */\n AccountLiquiditySnapshot memory snapshot = _getCurrentLiquiditySnapshot(borrower, _getLiquidationThreshold);\n\n if (snapshot.totalCollateral <= minLiquidatableCollateral) {\n /* The liquidator should use either liquidateAccount or healAccount */\n revert MinimalCollateralViolated(minLiquidatableCollateral, snapshot.totalCollateral);\n }\n\n if (snapshot.shortfall == 0) {\n revert InsufficientShortfall();\n }\n\n /* The liquidator may not repay more than what is allowed by the closeFactor */\n uint256 maxClose = mul_ScalarTruncate(Exp({ mantissa: closeFactorMantissa }), borrowBalance);\n if (repayAmount > maxClose) {\n revert TooMuchRepay();\n }\n }\n\n /**\n * @notice Checks if the seizing of assets should be allowed to occur\n * @param vTokenCollateral Asset which was used as collateral and will be seized\n * @param seizerContract Contract that tries to seize the asset (either borrowed vToken or Comptroller)\n * @param liquidator The address repaying the borrow and seizing the collateral\n * @param borrower The address of the borrower\n * @custom:error ActionPaused error is thrown if seizing this type of collateral is paused\n * @custom:error MarketNotListed error is thrown if either collateral or borrowed token is not listed\n * @custom:error ComptrollerMismatch error is when seizer contract or seized asset belong to different pools\n * @custom:access Not restricted\n */\n function preSeizeHook(\n address vTokenCollateral,\n address seizerContract,\n address liquidator,\n address borrower\n ) external override {\n // Pause Action.SEIZE on COLLATERAL to prevent seizing it.\n // If we want to pause liquidating vTokenBorrowed, we should pause\n // Action.LIQUIDATE on it\n _checkActionPauseState(vTokenCollateral, Action.SEIZE);\n\n Market storage market = markets[vTokenCollateral];\n\n if (!market.isListed) {\n revert MarketNotListed(vTokenCollateral);\n }\n\n if (seizerContract == address(this)) {\n // If Comptroller is the seizer, just check if collateral's comptroller\n // is equal to the current address\n if (address(VToken(vTokenCollateral).comptroller()) != address(this)) {\n revert ComptrollerMismatch();\n }\n } else {\n // If the seizer is not the Comptroller, check that the seizer is a\n // listed market, and that the markets' comptrollers match\n if (!markets[seizerContract].isListed) {\n revert MarketNotListed(seizerContract);\n }\n if (VToken(vTokenCollateral).comptroller() != VToken(seizerContract).comptroller()) {\n revert ComptrollerMismatch();\n }\n }\n\n if (!market.accountMembership[borrower]) {\n revert MarketNotCollateral(vTokenCollateral, borrower);\n }\n\n // Keep the flywheel moving\n uint256 rewardDistributorsCount = rewardsDistributors.length;\n\n for (uint256 i; i < rewardDistributorsCount; ++i) {\n RewardsDistributor rewardsDistributor = rewardsDistributors[i];\n rewardsDistributor.updateRewardTokenSupplyIndex(vTokenCollateral);\n rewardsDistributor.distributeSupplierRewardToken(vTokenCollateral, borrower);\n rewardsDistributor.distributeSupplierRewardToken(vTokenCollateral, liquidator);\n }\n }\n\n /**\n * @notice Checks if the account should be allowed to transfer tokens in the given market\n * @param vToken The market to verify the transfer against\n * @param src The account which sources the tokens\n * @param dst The account which receives the tokens\n * @param transferTokens The number of vTokens to transfer\n * @custom:error ActionPaused error is thrown if withdrawals are paused in this market\n * @custom:error MarketNotListed error is thrown when the market is not listed\n * @custom:error InsufficientLiquidity error is thrown if the withdrawal would lead to user's insolvency\n * @custom:error SnapshotError is thrown if some vToken fails to return the account's supply and borrows\n * @custom:error PriceError is thrown if the oracle returns an incorrect price for some asset\n * @custom:access Not restricted\n */\n function preTransferHook(address vToken, address src, address dst, uint256 transferTokens) external override {\n _checkActionPauseState(vToken, Action.TRANSFER);\n\n // Currently the only consideration is whether or not\n // the src is allowed to redeem this many tokens\n _checkRedeemAllowed(vToken, src, transferTokens);\n\n // Keep the flywheel moving\n uint256 rewardDistributorsCount = rewardsDistributors.length;\n\n for (uint256 i; i < rewardDistributorsCount; ++i) {\n RewardsDistributor rewardsDistributor = rewardsDistributors[i];\n rewardsDistributor.updateRewardTokenSupplyIndex(vToken);\n rewardsDistributor.distributeSupplierRewardToken(vToken, src);\n rewardsDistributor.distributeSupplierRewardToken(vToken, dst);\n }\n }\n\n /*** Pool-level operations ***/\n\n /**\n * @notice Seizes all the remaining collateral, makes msg.sender repay the existing\n * borrows, and treats the rest of the debt as bad debt (for each market).\n * The sender has to repay a certain percentage of the debt, computed as\n * collateral / (borrows * liquidationIncentive).\n * @param user account to heal\n * @custom:error CollateralExceedsThreshold error is thrown when the collateral is too big for healing\n * @custom:error SnapshotError is thrown if some vToken fails to return the account's supply and borrows\n * @custom:error PriceError is thrown if the oracle returns an incorrect price for some asset\n * @custom:access Not restricted\n */\n function healAccount(address user) external {\n VToken[] memory userAssets = getAssetsIn(user);\n uint256 userAssetsCount = userAssets.length;\n\n address liquidator = msg.sender;\n {\n ResilientOracleInterface oracle_ = oracle;\n // We need all user's markets to be fresh for the computations to be correct\n for (uint256 i; i < userAssetsCount; ++i) {\n userAssets[i].accrueInterest();\n oracle_.updatePrice(address(userAssets[i]));\n }\n }\n\n AccountLiquiditySnapshot memory snapshot = _getCurrentLiquiditySnapshot(user, _getLiquidationThreshold);\n\n if (snapshot.totalCollateral > minLiquidatableCollateral) {\n revert CollateralExceedsThreshold(minLiquidatableCollateral, snapshot.totalCollateral);\n }\n\n if (snapshot.shortfall == 0) {\n revert InsufficientShortfall();\n }\n\n // percentage = collateral / (borrows * liquidation incentive)\n Exp memory collateral = Exp({ mantissa: snapshot.totalCollateral });\n Exp memory scaledBorrows = mul_(\n Exp({ mantissa: snapshot.borrows }),\n Exp({ mantissa: liquidationIncentiveMantissa })\n );\n\n Exp memory percentage = div_(collateral, scaledBorrows);\n if (lessThanExp(Exp({ mantissa: MANTISSA_ONE }), percentage)) {\n revert CollateralExceedsThreshold(scaledBorrows.mantissa, collateral.mantissa);\n }\n\n for (uint256 i; i < userAssetsCount; ++i) {\n VToken market = userAssets[i];\n\n (uint256 tokens, uint256 borrowBalance, ) = _safeGetAccountSnapshot(market, user);\n uint256 repaymentAmount = mul_ScalarTruncate(percentage, borrowBalance);\n\n // Seize the entire collateral\n if (tokens != 0) {\n market.seize(liquidator, user, tokens);\n }\n // Repay a certain percentage of the borrow, forgive the rest\n if (borrowBalance != 0) {\n market.healBorrow(liquidator, user, repaymentAmount);\n }\n }\n }\n\n /**\n * @notice Liquidates all borrows of the borrower. Callable only if the collateral is less than\n * a predefined threshold, and the account collateral can be seized to cover all borrows. If\n * the collateral is higher than the threshold, use regular liquidations. If the collateral is\n * below the threshold, and the account is insolvent, use healAccount.\n * @param borrower the borrower address\n * @param orders an array of liquidation orders\n * @custom:error CollateralExceedsThreshold error is thrown when the collateral is too big for a batch liquidation\n * @custom:error InsufficientCollateral error is thrown when there is not enough collateral to cover the debt\n * @custom:error SnapshotError is thrown if some vToken fails to return the account's supply and borrows\n * @custom:error PriceError is thrown if the oracle returns an incorrect price for some asset\n * @custom:access Not restricted\n */\n function liquidateAccount(address borrower, LiquidationOrder[] calldata orders) external {\n // We will accrue interest and update the oracle prices later during the liquidation\n\n AccountLiquiditySnapshot memory snapshot = _getCurrentLiquiditySnapshot(borrower, _getLiquidationThreshold);\n\n if (snapshot.totalCollateral > minLiquidatableCollateral) {\n // You should use the regular vToken.liquidateBorrow(...) call\n revert CollateralExceedsThreshold(minLiquidatableCollateral, snapshot.totalCollateral);\n }\n\n uint256 collateralToSeize = mul_ScalarTruncate(\n Exp({ mantissa: liquidationIncentiveMantissa }),\n snapshot.borrows\n );\n if (collateralToSeize >= snapshot.totalCollateral) {\n // There is not enough collateral to seize. Use healBorrow to repay some part of the borrow\n // and record bad debt.\n revert InsufficientCollateral(collateralToSeize, snapshot.totalCollateral);\n }\n\n if (snapshot.shortfall == 0) {\n revert InsufficientShortfall();\n }\n\n uint256 ordersCount = orders.length;\n\n _ensureMaxLoops(ordersCount / 2);\n\n for (uint256 i; i < ordersCount; ++i) {\n if (!markets[address(orders[i].vTokenBorrowed)].isListed) {\n revert MarketNotListed(address(orders[i].vTokenBorrowed));\n }\n if (!markets[address(orders[i].vTokenCollateral)].isListed) {\n revert MarketNotListed(address(orders[i].vTokenCollateral));\n }\n\n LiquidationOrder calldata order = orders[i];\n order.vTokenBorrowed.forceLiquidateBorrow(\n msg.sender,\n borrower,\n order.repayAmount,\n order.vTokenCollateral,\n true\n );\n }\n\n VToken[] memory borrowMarkets = getAssetsIn(borrower);\n uint256 marketsCount = borrowMarkets.length;\n\n for (uint256 i; i < marketsCount; ++i) {\n (, uint256 borrowBalance, ) = _safeGetAccountSnapshot(borrowMarkets[i], borrower);\n require(borrowBalance == 0, \"Nonzero borrow balance after liquidation\");\n }\n }\n\n /**\n * @notice Sets the closeFactor to use when liquidating borrows\n * @param newCloseFactorMantissa New close factor, scaled by 1e18\n * @custom:event Emits NewCloseFactor on success\n * @custom:access Controlled by AccessControlManager\n */\n function setCloseFactor(uint256 newCloseFactorMantissa) external {\n _checkAccessAllowed(\"setCloseFactor(uint256)\");\n require(MAX_CLOSE_FACTOR_MANTISSA >= newCloseFactorMantissa, \"Close factor greater than maximum close factor\");\n require(MIN_CLOSE_FACTOR_MANTISSA <= newCloseFactorMantissa, \"Close factor smaller than minimum close factor\");\n\n uint256 oldCloseFactorMantissa = closeFactorMantissa;\n closeFactorMantissa = newCloseFactorMantissa;\n emit NewCloseFactor(oldCloseFactorMantissa, newCloseFactorMantissa);\n }\n\n /**\n * @notice Sets the collateralFactor for a market\n * @dev This function is restricted by the AccessControlManager\n * @param vToken The market to set the factor on\n * @param newCollateralFactorMantissa The new collateral factor, scaled by 1e18\n * @param newLiquidationThresholdMantissa The new liquidation threshold, scaled by 1e18\n * @custom:event Emits NewCollateralFactor when collateral factor is updated\n * and NewLiquidationThreshold when liquidation threshold is updated\n * @custom:error MarketNotListed error is thrown when the market is not listed\n * @custom:error InvalidCollateralFactor error is thrown when collateral factor is too high\n * @custom:error InvalidLiquidationThreshold error is thrown when liquidation threshold is lower than collateral factor\n * @custom:error PriceError is thrown when the oracle returns an invalid price for the asset\n * @custom:access Controlled by AccessControlManager\n */\n function setCollateralFactor(\n VToken vToken,\n uint256 newCollateralFactorMantissa,\n uint256 newLiquidationThresholdMantissa\n ) external {\n _checkAccessAllowed(\"setCollateralFactor(address,uint256,uint256)\");\n\n // Verify market is listed\n Market storage market = markets[address(vToken)];\n if (!market.isListed) {\n revert MarketNotListed(address(vToken));\n }\n\n // Check collateral factor <= 0.9\n if (newCollateralFactorMantissa > MAX_COLLATERAL_FACTOR_MANTISSA) {\n revert InvalidCollateralFactor();\n }\n\n // Ensure that liquidation threshold <= 1\n if (newLiquidationThresholdMantissa > MANTISSA_ONE) {\n revert InvalidLiquidationThreshold();\n }\n\n // Ensure that liquidation threshold >= CF\n if (newLiquidationThresholdMantissa < newCollateralFactorMantissa) {\n revert InvalidLiquidationThreshold();\n }\n\n // If collateral factor != 0, fail if price == 0\n if (newCollateralFactorMantissa != 0 && oracle.getUnderlyingPrice(address(vToken)) == 0) {\n revert PriceError(address(vToken));\n }\n\n uint256 oldCollateralFactorMantissa = market.collateralFactorMantissa;\n if (newCollateralFactorMantissa != oldCollateralFactorMantissa) {\n market.collateralFactorMantissa = newCollateralFactorMantissa;\n emit NewCollateralFactor(vToken, oldCollateralFactorMantissa, newCollateralFactorMantissa);\n }\n\n uint256 oldLiquidationThresholdMantissa = market.liquidationThresholdMantissa;\n if (newLiquidationThresholdMantissa != oldLiquidationThresholdMantissa) {\n market.liquidationThresholdMantissa = newLiquidationThresholdMantissa;\n emit NewLiquidationThreshold(vToken, oldLiquidationThresholdMantissa, newLiquidationThresholdMantissa);\n }\n }\n\n /**\n * @notice Sets liquidationIncentive\n * @dev This function is restricted by the AccessControlManager\n * @param newLiquidationIncentiveMantissa New liquidationIncentive scaled by 1e18\n * @custom:event Emits NewLiquidationIncentive on success\n * @custom:access Controlled by AccessControlManager\n */\n function setLiquidationIncentive(uint256 newLiquidationIncentiveMantissa) external {\n require(newLiquidationIncentiveMantissa >= MANTISSA_ONE, \"liquidation incentive should be greater than 1e18\");\n\n _checkAccessAllowed(\"setLiquidationIncentive(uint256)\");\n\n // Save current value for use in log\n uint256 oldLiquidationIncentiveMantissa = liquidationIncentiveMantissa;\n\n // Set liquidation incentive to new incentive\n liquidationIncentiveMantissa = newLiquidationIncentiveMantissa;\n\n // Emit event with old incentive, new incentive\n emit NewLiquidationIncentive(oldLiquidationIncentiveMantissa, newLiquidationIncentiveMantissa);\n }\n\n /**\n * @notice Add the market to the markets mapping and set it as listed\n * @dev Only callable by the PoolRegistry\n * @param vToken The address of the market (token) to list\n * @custom:error MarketAlreadyListed is thrown if the market is already listed in this pool\n * @custom:access Only PoolRegistry\n */\n function supportMarket(VToken vToken) external {\n _checkSenderIs(poolRegistry);\n\n if (markets[address(vToken)].isListed) {\n revert MarketAlreadyListed(address(vToken));\n }\n\n require(vToken.isVToken(), \"Comptroller: Invalid vToken\"); // Sanity check to make sure its really a VToken\n\n Market storage newMarket = markets[address(vToken)];\n newMarket.isListed = true;\n newMarket.collateralFactorMantissa = 0;\n newMarket.liquidationThresholdMantissa = 0;\n\n _addMarket(address(vToken));\n\n uint256 rewardDistributorsCount = rewardsDistributors.length;\n\n for (uint256 i; i < rewardDistributorsCount; ++i) {\n rewardsDistributors[i].initializeMarket(address(vToken));\n }\n\n emit MarketSupported(vToken);\n }\n\n /**\n * @notice Set the given borrow caps for the given vToken markets. Borrowing that brings total borrows to or above borrow cap will revert.\n * @dev This function is restricted by the AccessControlManager\n * @dev A borrow cap of type(uint256).max corresponds to unlimited borrowing.\n * @dev Borrow caps smaller than the current total borrows are accepted. This way, new borrows will not be allowed\n until the total borrows amount goes below the new borrow cap\n * @param vTokens The addresses of the markets (tokens) to change the borrow caps for\n * @param newBorrowCaps The new borrow cap values in underlying to be set. A value of type(uint256).max corresponds to unlimited borrowing.\n * @custom:access Controlled by AccessControlManager\n */\n function setMarketBorrowCaps(VToken[] calldata vTokens, uint256[] calldata newBorrowCaps) external {\n _checkAccessAllowed(\"setMarketBorrowCaps(address[],uint256[])\");\n\n uint256 numMarkets = vTokens.length;\n uint256 numBorrowCaps = newBorrowCaps.length;\n\n require(numMarkets != 0 && numMarkets == numBorrowCaps, \"invalid input\");\n\n _ensureMaxLoops(numMarkets);\n\n for (uint256 i; i < numMarkets; ++i) {\n borrowCaps[address(vTokens[i])] = newBorrowCaps[i];\n emit NewBorrowCap(vTokens[i], newBorrowCaps[i]);\n }\n }\n\n /**\n * @notice Set the given supply caps for the given vToken markets. Supply that brings total Supply to or above supply cap will revert.\n * @dev This function is restricted by the AccessControlManager\n * @dev A supply cap of type(uint256).max corresponds to unlimited supply.\n * @dev Supply caps smaller than the current total supplies are accepted. This way, new supplies will not be allowed\n until the total supplies amount goes below the new supply cap\n * @param vTokens The addresses of the markets (tokens) to change the supply caps for\n * @param newSupplyCaps The new supply cap values in underlying to be set. A value of type(uint256).max corresponds to unlimited supply.\n * @custom:access Controlled by AccessControlManager\n */\n function setMarketSupplyCaps(VToken[] calldata vTokens, uint256[] calldata newSupplyCaps) external {\n _checkAccessAllowed(\"setMarketSupplyCaps(address[],uint256[])\");\n uint256 vTokensCount = vTokens.length;\n\n require(vTokensCount != 0, \"invalid number of markets\");\n require(vTokensCount == newSupplyCaps.length, \"invalid number of markets\");\n\n _ensureMaxLoops(vTokensCount);\n\n for (uint256 i; i < vTokensCount; ++i) {\n supplyCaps[address(vTokens[i])] = newSupplyCaps[i];\n emit NewSupplyCap(vTokens[i], newSupplyCaps[i]);\n }\n }\n\n /**\n * @notice Pause/unpause specified actions\n * @dev This function is restricted by the AccessControlManager\n * @param marketsList Markets to pause/unpause the actions on\n * @param actionsList List of action ids to pause/unpause\n * @param paused The new paused state (true=paused, false=unpaused)\n * @custom:access Controlled by AccessControlManager\n */\n function setActionsPaused(VToken[] calldata marketsList, Action[] calldata actionsList, bool paused) external {\n _checkAccessAllowed(\"setActionsPaused(address[],uint256[],bool)\");\n\n uint256 marketsCount = marketsList.length;\n uint256 actionsCount = actionsList.length;\n\n _ensureMaxLoops(marketsCount * actionsCount);\n\n for (uint256 marketIdx; marketIdx < marketsCount; ++marketIdx) {\n for (uint256 actionIdx; actionIdx < actionsCount; ++actionIdx) {\n _setActionPaused(address(marketsList[marketIdx]), actionsList[actionIdx], paused);\n }\n }\n }\n\n /**\n * @notice Set the given collateral threshold for non-batch liquidations. Regular liquidations\n * will fail if the collateral amount is less than this threshold. Liquidators should use batch\n * operations like liquidateAccount or healAccount.\n * @dev This function is restricted by the AccessControlManager\n * @param newMinLiquidatableCollateral The new min liquidatable collateral (in USD).\n * @custom:access Controlled by AccessControlManager\n */\n function setMinLiquidatableCollateral(uint256 newMinLiquidatableCollateral) external {\n _checkAccessAllowed(\"setMinLiquidatableCollateral(uint256)\");\n\n uint256 oldMinLiquidatableCollateral = minLiquidatableCollateral;\n minLiquidatableCollateral = newMinLiquidatableCollateral;\n emit NewMinLiquidatableCollateral(oldMinLiquidatableCollateral, newMinLiquidatableCollateral);\n }\n\n /**\n * @notice Add a new RewardsDistributor and initialize it with all markets. We can add several RewardsDistributor\n * contracts with the same rewardToken, and there could be overlaping among them considering the last reward slot (block or second)\n * @dev Only callable by the admin\n * @param _rewardsDistributor Address of the RewardDistributor contract to add\n * @custom:access Only Governance\n * @custom:event Emits NewRewardsDistributor with distributor address\n */\n function addRewardsDistributor(RewardsDistributor _rewardsDistributor) external onlyOwner {\n require(!rewardsDistributorExists[address(_rewardsDistributor)], \"already exists\");\n\n uint256 rewardsDistributorsLen = rewardsDistributors.length;\n _ensureMaxLoops(rewardsDistributorsLen + 1);\n\n rewardsDistributors.push(_rewardsDistributor);\n rewardsDistributorExists[address(_rewardsDistributor)] = true;\n\n uint256 marketsCount = allMarkets.length;\n\n for (uint256 i; i < marketsCount; ++i) {\n _rewardsDistributor.initializeMarket(address(allMarkets[i]));\n }\n\n emit NewRewardsDistributor(address(_rewardsDistributor), address(_rewardsDistributor.rewardToken()));\n }\n\n /**\n * @notice Sets a new price oracle for the Comptroller\n * @dev Only callable by the admin\n * @param newOracle Address of the new price oracle to set\n * @custom:event Emits NewPriceOracle on success\n * @custom:error ZeroAddressNotAllowed is thrown when the new oracle address is zero\n */\n function setPriceOracle(ResilientOracleInterface newOracle) external onlyOwner {\n ensureNonzeroAddress(address(newOracle));\n\n ResilientOracleInterface oldOracle = oracle;\n oracle = newOracle;\n emit NewPriceOracle(oldOracle, newOracle);\n }\n\n /**\n * @notice Set the for loop iteration limit to avoid DOS\n * @param limit Limit for the max loops can execute at a time\n */\n function setMaxLoopsLimit(uint256 limit) external onlyOwner {\n _setMaxLoopsLimit(limit);\n }\n\n /**\n * @notice Sets the prime token contract for the comptroller\n * @param _prime Address of the Prime contract\n */\n function setPrimeToken(IPrime _prime) external onlyOwner {\n ensureNonzeroAddress(address(_prime));\n\n emit NewPrimeToken(prime, _prime);\n prime = _prime;\n }\n\n /**\n * @notice Enables forced liquidations for a market. If forced liquidation is enabled,\n * borrows in the market may be liquidated regardless of the account liquidity\n * @param vTokenBorrowed Borrowed vToken\n * @param enable Whether to enable forced liquidations\n */\n function setForcedLiquidation(address vTokenBorrowed, bool enable) external {\n _checkAccessAllowed(\"setForcedLiquidation(address,bool)\");\n ensureNonzeroAddress(vTokenBorrowed);\n\n if (!markets[vTokenBorrowed].isListed) {\n revert MarketNotListed(vTokenBorrowed);\n }\n\n isForcedLiquidationEnabled[vTokenBorrowed] = enable;\n emit IsForcedLiquidationEnabledUpdated(vTokenBorrowed, enable);\n }\n\n /**\n * @notice Determine the current account liquidity with respect to liquidation threshold requirements\n * @dev The interface of this function is intentionally kept compatible with Compound and Venus Core\n * @param account The account get liquidity for\n * @return error Always NO_ERROR for compatibility with Venus core tooling\n * @return liquidity Account liquidity in excess of liquidation threshold requirements,\n * @return shortfall Account shortfall below liquidation threshold requirements\n */\n function getAccountLiquidity(\n address account\n ) external view returns (uint256 error, uint256 liquidity, uint256 shortfall) {\n AccountLiquiditySnapshot memory snapshot = _getCurrentLiquiditySnapshot(account, _getLiquidationThreshold);\n return (NO_ERROR, snapshot.liquidity, snapshot.shortfall);\n }\n\n /**\n * @notice Determine the current account liquidity with respect to collateral requirements\n * @dev The interface of this function is intentionally kept compatible with Compound and Venus Core\n * @param account The account get liquidity for\n * @return error Always NO_ERROR for compatibility with Venus core tooling\n * @return liquidity Account liquidity in excess of collateral requirements,\n * @return shortfall Account shortfall below collateral requirements\n */\n function getBorrowingPower(\n address account\n ) external view returns (uint256 error, uint256 liquidity, uint256 shortfall) {\n AccountLiquiditySnapshot memory snapshot = _getCurrentLiquiditySnapshot(account, _getCollateralFactor);\n return (NO_ERROR, snapshot.liquidity, snapshot.shortfall);\n }\n\n /**\n * @notice Determine what the account liquidity would be if the given amounts were redeemed/borrowed\n * @dev The interface of this function is intentionally kept compatible with Compound and Venus Core\n * @param vTokenModify The market to hypothetically redeem/borrow in\n * @param account The account to determine liquidity for\n * @param redeemTokens The number of tokens to hypothetically redeem\n * @param borrowAmount The amount of underlying to hypothetically borrow\n * @return error Always NO_ERROR for compatibility with Venus core tooling\n * @return liquidity Hypothetical account liquidity in excess of collateral requirements,\n * @return shortfall Hypothetical account shortfall below collateral requirements\n */\n function getHypotheticalAccountLiquidity(\n address account,\n address vTokenModify,\n uint256 redeemTokens,\n uint256 borrowAmount\n ) external view returns (uint256 error, uint256 liquidity, uint256 shortfall) {\n AccountLiquiditySnapshot memory snapshot = _getHypotheticalLiquiditySnapshot(\n account,\n VToken(vTokenModify),\n redeemTokens,\n borrowAmount,\n _getCollateralFactor\n );\n return (NO_ERROR, snapshot.liquidity, snapshot.shortfall);\n }\n\n /**\n * @notice Return all of the markets\n * @dev The automatic getter may be used to access an individual market.\n * @return markets The list of market addresses\n */\n function getAllMarkets() external view override returns (VToken[] memory) {\n return allMarkets;\n }\n\n /**\n * @notice Check if a market is marked as listed (active)\n * @param vToken vToken Address for the market to check\n * @return listed True if listed otherwise false\n */\n function isMarketListed(VToken vToken) external view returns (bool) {\n return markets[address(vToken)].isListed;\n }\n\n /*** Assets You Are In ***/\n\n /**\n * @notice Returns whether the given account is entered in a given market\n * @param account The address of the account to check\n * @param vToken The vToken to check\n * @return True if the account is in the market specified, otherwise false.\n */\n function checkMembership(address account, VToken vToken) external view returns (bool) {\n return markets[address(vToken)].accountMembership[account];\n }\n\n /**\n * @notice Calculate number of tokens of collateral asset to seize given an underlying amount\n * @dev Used in liquidation (called in vToken.liquidateBorrowFresh)\n * @param vTokenBorrowed The address of the borrowed vToken\n * @param vTokenCollateral The address of the collateral vToken\n * @param actualRepayAmount The amount of vTokenBorrowed underlying to convert into vTokenCollateral tokens\n * @return error Always NO_ERROR for compatibility with Venus core tooling\n * @return tokensToSeize Number of vTokenCollateral tokens to be seized in a liquidation\n * @custom:error PriceError if the oracle returns an invalid price\n */\n function liquidateCalculateSeizeTokens(\n address vTokenBorrowed,\n address vTokenCollateral,\n uint256 actualRepayAmount\n ) external view override returns (uint256 error, uint256 tokensToSeize) {\n /* Read oracle prices for borrowed and collateral markets */\n uint256 priceBorrowedMantissa = _safeGetUnderlyingPrice(VToken(vTokenBorrowed));\n uint256 priceCollateralMantissa = _safeGetUnderlyingPrice(VToken(vTokenCollateral));\n\n /*\n * Get the exchange rate and calculate the number of collateral tokens to seize:\n * seizeAmount = actualRepayAmount * liquidationIncentive * priceBorrowed / priceCollateral\n * seizeTokens = seizeAmount / exchangeRate\n * = actualRepayAmount * (liquidationIncentive * priceBorrowed) / (priceCollateral * exchangeRate)\n */\n uint256 exchangeRateMantissa = VToken(vTokenCollateral).exchangeRateStored(); // Note: reverts on error\n uint256 seizeTokens;\n Exp memory numerator;\n Exp memory denominator;\n Exp memory ratio;\n\n numerator = mul_(Exp({ mantissa: liquidationIncentiveMantissa }), Exp({ mantissa: priceBorrowedMantissa }));\n denominator = mul_(Exp({ mantissa: priceCollateralMantissa }), Exp({ mantissa: exchangeRateMantissa }));\n ratio = div_(numerator, denominator);\n\n seizeTokens = mul_ScalarTruncate(ratio, actualRepayAmount);\n\n return (NO_ERROR, seizeTokens);\n }\n\n /**\n * @notice Returns reward speed given a vToken\n * @param vToken The vToken to get the reward speeds for\n * @return rewardSpeeds Array of total supply and borrow speeds and reward token for all reward distributors\n */\n function getRewardsByMarket(address vToken) external view returns (RewardSpeeds[] memory rewardSpeeds) {\n uint256 rewardsDistributorsLength = rewardsDistributors.length;\n rewardSpeeds = new RewardSpeeds[](rewardsDistributorsLength);\n for (uint256 i; i < rewardsDistributorsLength; ++i) {\n RewardsDistributor rewardsDistributor = rewardsDistributors[i];\n address rewardToken = address(rewardsDistributor.rewardToken());\n rewardSpeeds[i] = RewardSpeeds({\n rewardToken: rewardToken,\n supplySpeed: rewardsDistributor.rewardTokenSupplySpeeds(vToken),\n borrowSpeed: rewardsDistributor.rewardTokenBorrowSpeeds(vToken)\n });\n }\n return rewardSpeeds;\n }\n\n /**\n * @notice Return all reward distributors for this pool\n * @return Array of RewardDistributor addresses\n */\n function getRewardDistributors() external view returns (RewardsDistributor[] memory) {\n return rewardsDistributors;\n }\n\n /**\n * @notice A marker method that returns true for a valid Comptroller contract\n * @return Always true\n */\n function isComptroller() external pure override returns (bool) {\n return true;\n }\n\n /**\n * @notice Update the prices of all the tokens associated with the provided account\n * @param account Address of the account to get associated tokens with\n */\n function updatePrices(address account) public {\n VToken[] memory vTokens = getAssetsIn(account);\n uint256 vTokensCount = vTokens.length;\n\n ResilientOracleInterface oracle_ = oracle;\n\n for (uint256 i; i < vTokensCount; ++i) {\n oracle_.updatePrice(address(vTokens[i]));\n }\n }\n\n /**\n * @notice Checks if a certain action is paused on a market\n * @param market vToken address\n * @param action Action to check\n * @return paused True if the action is paused otherwise false\n */\n function actionPaused(address market, Action action) public view returns (bool) {\n return _actionPaused[market][action];\n }\n\n /**\n * @notice Returns the assets an account has entered\n * @param account The address of the account to pull assets for\n * @return A list with the assets the account has entered\n */\n function getAssetsIn(address account) public view returns (VToken[] memory) {\n uint256 len;\n VToken[] memory _accountAssets = accountAssets[account];\n uint256 _accountAssetsLength = _accountAssets.length;\n\n VToken[] memory assetsIn = new VToken[](_accountAssetsLength);\n\n for (uint256 i; i < _accountAssetsLength; ++i) {\n Market storage market = markets[address(_accountAssets[i])];\n if (market.isListed) {\n assetsIn[len] = _accountAssets[i];\n ++len;\n }\n }\n\n assembly {\n mstore(assetsIn, len)\n }\n\n return assetsIn;\n }\n\n /**\n * @notice Add the market to the borrower's \"assets in\" for liquidity calculations\n * @param vToken The market to enter\n * @param borrower The address of the account to modify\n */\n function _addToMarket(VToken vToken, address borrower) internal {\n _checkActionPauseState(address(vToken), Action.ENTER_MARKET);\n Market storage marketToJoin = markets[address(vToken)];\n\n if (!marketToJoin.isListed) {\n revert MarketNotListed(address(vToken));\n }\n\n if (marketToJoin.accountMembership[borrower]) {\n // already joined\n return;\n }\n\n // survived the gauntlet, add to list\n // NOTE: we store these somewhat redundantly as a significant optimization\n // this avoids having to iterate through the list for the most common use cases\n // that is, only when we need to perform liquidity checks\n // and not whenever we want to check if an account is in a particular market\n marketToJoin.accountMembership[borrower] = true;\n accountAssets[borrower].push(vToken);\n\n emit MarketEntered(vToken, borrower);\n }\n\n /**\n * @notice Internal function to validate that a market hasn't already been added\n * and if it hasn't adds it\n * @param vToken The market to support\n */\n function _addMarket(address vToken) internal {\n uint256 marketsCount = allMarkets.length;\n\n for (uint256 i; i < marketsCount; ++i) {\n if (allMarkets[i] == VToken(vToken)) {\n revert MarketAlreadyListed(vToken);\n }\n }\n allMarkets.push(VToken(vToken));\n marketsCount = allMarkets.length;\n _ensureMaxLoops(marketsCount);\n }\n\n /**\n * @dev Pause/unpause an action on a market\n * @param market Market to pause/unpause the action on\n * @param action Action id to pause/unpause\n * @param paused The new paused state (true=paused, false=unpaused)\n */\n function _setActionPaused(address market, Action action, bool paused) internal {\n require(markets[market].isListed, \"cannot pause a market that is not listed\");\n _actionPaused[market][action] = paused;\n emit ActionPausedMarket(VToken(market), action, paused);\n }\n\n /**\n * @dev Internal function to check that vTokens can be safely redeemed for the underlying asset.\n * @param vToken Address of the vTokens to redeem\n * @param redeemer Account redeeming the tokens\n * @param redeemTokens The number of tokens to redeem\n */\n function _checkRedeemAllowed(address vToken, address redeemer, uint256 redeemTokens) internal {\n Market storage market = markets[vToken];\n\n if (!market.isListed) {\n revert MarketNotListed(address(vToken));\n }\n\n /* If the redeemer is not 'in' the market, then we can bypass the liquidity check */\n if (!market.accountMembership[redeemer]) {\n return;\n }\n\n // Update the prices of tokens\n updatePrices(redeemer);\n\n /* Otherwise, perform a hypothetical liquidity check to guard against shortfall */\n AccountLiquiditySnapshot memory snapshot = _getHypotheticalLiquiditySnapshot(\n redeemer,\n VToken(vToken),\n redeemTokens,\n 0,\n _getCollateralFactor\n );\n if (snapshot.shortfall > 0) {\n revert InsufficientLiquidity();\n }\n }\n\n /**\n * @notice Get the total collateral, weighted collateral, borrow balance, liquidity, shortfall\n * @param account The account to get the snapshot for\n * @param weight The function to compute the weight of the collateral – either collateral factor or\n * liquidation threshold. Accepts the address of the vToken and returns the weight as Exp.\n * @dev Note that we calculate the exchangeRateStored for each collateral vToken using stored data,\n * without calculating accumulated interest.\n * @return snapshot Account liquidity snapshot\n */\n function _getCurrentLiquiditySnapshot(\n address account,\n function(VToken) internal view returns (Exp memory) weight\n ) internal view returns (AccountLiquiditySnapshot memory snapshot) {\n return _getHypotheticalLiquiditySnapshot(account, VToken(address(0)), 0, 0, weight);\n }\n\n /**\n * @notice Determine what the supply/borrow balances would be if the given amounts were redeemed/borrowed\n * @param vTokenModify The market to hypothetically redeem/borrow in\n * @param account The account to determine liquidity for\n * @param redeemTokens The number of tokens to hypothetically redeem\n * @param borrowAmount The amount of underlying to hypothetically borrow\n * @param weight The function to compute the weight of the collateral – either collateral factor or\n liquidation threshold. Accepts the address of the VToken and returns the weight\n * @dev Note that we calculate the exchangeRateStored for each collateral vToken using stored data,\n * without calculating accumulated interest.\n * @return snapshot Account liquidity snapshot\n */\n function _getHypotheticalLiquiditySnapshot(\n address account,\n VToken vTokenModify,\n uint256 redeemTokens,\n uint256 borrowAmount,\n function(VToken) internal view returns (Exp memory) weight\n ) internal view returns (AccountLiquiditySnapshot memory snapshot) {\n // For each asset the account is in\n VToken[] memory assets = getAssetsIn(account);\n uint256 assetsCount = assets.length;\n\n for (uint256 i; i < assetsCount; ++i) {\n VToken asset = assets[i];\n\n // Read the balances and exchange rate from the vToken\n (uint256 vTokenBalance, uint256 borrowBalance, uint256 exchangeRateMantissa) = _safeGetAccountSnapshot(\n asset,\n account\n );\n\n // Get the normalized price of the asset\n Exp memory oraclePrice = Exp({ mantissa: _safeGetUnderlyingPrice(asset) });\n\n // Pre-compute conversion factors from vTokens -> usd\n Exp memory vTokenPrice = mul_(Exp({ mantissa: exchangeRateMantissa }), oraclePrice);\n Exp memory weightedVTokenPrice = mul_(weight(asset), vTokenPrice);\n\n // weightedCollateral += weightedVTokenPrice * vTokenBalance\n snapshot.weightedCollateral = mul_ScalarTruncateAddUInt(\n weightedVTokenPrice,\n vTokenBalance,\n snapshot.weightedCollateral\n );\n\n // totalCollateral += vTokenPrice * vTokenBalance\n snapshot.totalCollateral = mul_ScalarTruncateAddUInt(vTokenPrice, vTokenBalance, snapshot.totalCollateral);\n\n // borrows += oraclePrice * borrowBalance\n snapshot.borrows = mul_ScalarTruncateAddUInt(oraclePrice, borrowBalance, snapshot.borrows);\n\n // Calculate effects of interacting with vTokenModify\n if (asset == vTokenModify) {\n // redeem effect\n // effects += tokensToDenom * redeemTokens\n snapshot.effects = mul_ScalarTruncateAddUInt(weightedVTokenPrice, redeemTokens, snapshot.effects);\n\n // borrow effect\n // effects += oraclePrice * borrowAmount\n snapshot.effects = mul_ScalarTruncateAddUInt(oraclePrice, borrowAmount, snapshot.effects);\n }\n }\n\n uint256 borrowPlusEffects = snapshot.borrows + snapshot.effects;\n // These are safe, as the underflow condition is checked first\n unchecked {\n if (snapshot.weightedCollateral > borrowPlusEffects) {\n snapshot.liquidity = snapshot.weightedCollateral - borrowPlusEffects;\n snapshot.shortfall = 0;\n } else {\n snapshot.liquidity = 0;\n snapshot.shortfall = borrowPlusEffects - snapshot.weightedCollateral;\n }\n }\n\n return snapshot;\n }\n\n /**\n * @dev Retrieves price from oracle for an asset and checks it is nonzero\n * @param asset Address for asset to query price\n * @return Underlying price\n */\n function _safeGetUnderlyingPrice(VToken asset) internal view returns (uint256) {\n uint256 oraclePriceMantissa = oracle.getUnderlyingPrice(address(asset));\n if (oraclePriceMantissa == 0) {\n revert PriceError(address(asset));\n }\n return oraclePriceMantissa;\n }\n\n /**\n * @dev Return collateral factor for a market\n * @param asset Address for asset\n * @return Collateral factor as exponential\n */\n function _getCollateralFactor(VToken asset) internal view returns (Exp memory) {\n return Exp({ mantissa: markets[address(asset)].collateralFactorMantissa });\n }\n\n /**\n * @dev Retrieves liquidation threshold for a market as an exponential\n * @param asset Address for asset to liquidation threshold\n * @return Liquidation threshold as exponential\n */\n function _getLiquidationThreshold(VToken asset) internal view returns (Exp memory) {\n return Exp({ mantissa: markets[address(asset)].liquidationThresholdMantissa });\n }\n\n /**\n * @dev Returns supply and borrow balances of user in vToken, reverts on failure\n * @param vToken Market to query\n * @param user Account address\n * @return vTokenBalance Balance of vTokens, the same as vToken.balanceOf(user)\n * @return borrowBalance Borrowed amount, including the interest\n * @return exchangeRateMantissa Stored exchange rate\n */\n function _safeGetAccountSnapshot(\n VToken vToken,\n address user\n ) internal view returns (uint256 vTokenBalance, uint256 borrowBalance, uint256 exchangeRateMantissa) {\n uint256 err;\n (err, vTokenBalance, borrowBalance, exchangeRateMantissa) = vToken.getAccountSnapshot(user);\n if (err != 0) {\n revert SnapshotError(address(vToken), user);\n }\n return (vTokenBalance, borrowBalance, exchangeRateMantissa);\n }\n\n /// @notice Reverts if the call is not from expectedSender\n /// @param expectedSender Expected transaction sender\n function _checkSenderIs(address expectedSender) internal view {\n if (msg.sender != expectedSender) {\n revert UnexpectedSender(expectedSender, msg.sender);\n }\n }\n\n /// @notice Reverts if a certain action is paused on a market\n /// @param market Market to check\n /// @param action Action to check\n function _checkActionPauseState(address market, Action action) private view {\n if (actionPaused(market, action)) {\n revert ActionPaused(market, action);\n }\n }\n}\n" + }, + "contracts/ComptrollerInterface.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity ^0.8.25;\n\nimport { ResilientOracleInterface } from \"@venusprotocol/oracle/contracts/interfaces/OracleInterface.sol\";\n\nimport { VToken } from \"./VToken.sol\";\nimport { RewardsDistributor } from \"./Rewards/RewardsDistributor.sol\";\n\nenum Action {\n MINT,\n REDEEM,\n BORROW,\n REPAY,\n SEIZE,\n LIQUIDATE,\n TRANSFER,\n ENTER_MARKET,\n EXIT_MARKET\n}\n\n/**\n * @title ComptrollerInterface\n * @author Venus\n * @notice Interface implemented by the `Comptroller` contract.\n */\ninterface ComptrollerInterface {\n /*** Assets You Are In ***/\n\n function enterMarkets(address[] calldata vTokens) external returns (uint256[] memory);\n\n function exitMarket(address vToken) external returns (uint256);\n\n /*** Policy Hooks ***/\n\n function preMintHook(address vToken, address minter, uint256 mintAmount) external;\n\n function preRedeemHook(address vToken, address redeemer, uint256 redeemTokens) external;\n\n function preBorrowHook(address vToken, address borrower, uint256 borrowAmount) external;\n\n function preRepayHook(address vToken, address borrower) external;\n\n function preLiquidateHook(\n address vTokenBorrowed,\n address vTokenCollateral,\n address borrower,\n uint256 repayAmount,\n bool skipLiquidityCheck\n ) external;\n\n function preSeizeHook(\n address vTokenCollateral,\n address vTokenBorrowed,\n address liquidator,\n address borrower\n ) external;\n\n function borrowVerify(address vToken, address borrower, uint borrowAmount) external;\n\n function mintVerify(address vToken, address minter, uint mintAmount, uint mintTokens) external;\n\n function redeemVerify(address vToken, address redeemer, uint redeemAmount, uint redeemTokens) external;\n\n function repayBorrowVerify(\n address vToken,\n address payer,\n address borrower,\n uint repayAmount,\n uint borrowerIndex\n ) external;\n\n function liquidateBorrowVerify(\n address vTokenBorrowed,\n address vTokenCollateral,\n address liquidator,\n address borrower,\n uint repayAmount,\n uint seizeTokens\n ) external;\n\n function seizeVerify(\n address vTokenCollateral,\n address vTokenBorrowed,\n address liquidator,\n address borrower,\n uint seizeTokens\n ) external;\n\n function transferVerify(address vToken, address src, address dst, uint transferTokens) external;\n\n function preTransferHook(address vToken, address src, address dst, uint256 transferTokens) external;\n\n function isComptroller() external view returns (bool);\n\n /*** Liquidity/Liquidation Calculations ***/\n\n function liquidateCalculateSeizeTokens(\n address vTokenBorrowed,\n address vTokenCollateral,\n uint256 repayAmount\n ) external view returns (uint256, uint256);\n\n function getAllMarkets() external view returns (VToken[] memory);\n\n function actionPaused(address market, Action action) external view returns (bool);\n}\n\n/**\n * @title ComptrollerViewInterface\n * @author Venus\n * @notice Interface implemented by the `Comptroller` contract, including only some util view functions.\n */\ninterface ComptrollerViewInterface {\n function markets(address) external view returns (bool, uint256);\n\n function oracle() external view returns (ResilientOracleInterface);\n\n function getAssetsIn(address) external view returns (VToken[] memory);\n\n function closeFactorMantissa() external view returns (uint256);\n\n function liquidationIncentiveMantissa() external view returns (uint256);\n\n function minLiquidatableCollateral() external view returns (uint256);\n\n function getRewardDistributors() external view returns (RewardsDistributor[] memory);\n\n function getAllMarkets() external view returns (VToken[] memory);\n\n function borrowCaps(address) external view returns (uint256);\n\n function supplyCaps(address) external view returns (uint256);\n\n function approvedDelegates(address user, address delegate) external view returns (bool);\n}\n" + }, + "contracts/ComptrollerStorage.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\n\nimport { ResilientOracleInterface } from \"@venusprotocol/oracle/contracts/interfaces/OracleInterface.sol\";\n\nimport { VToken } from \"./VToken.sol\";\nimport { RewardsDistributor } from \"./Rewards/RewardsDistributor.sol\";\nimport { IPrime } from \"@venusprotocol/venus-protocol/contracts/Tokens/Prime/Interfaces/IPrime.sol\";\nimport { Action } from \"./ComptrollerInterface.sol\";\n\n/**\n * @title ComptrollerStorage\n * @author Venus\n * @notice Storage layout for the `Comptroller` contract.\n */\ncontract ComptrollerStorage {\n struct LiquidationOrder {\n VToken vTokenCollateral;\n VToken vTokenBorrowed;\n uint256 repayAmount;\n }\n\n struct AccountLiquiditySnapshot {\n uint256 totalCollateral;\n uint256 weightedCollateral;\n uint256 borrows;\n uint256 effects;\n uint256 liquidity;\n uint256 shortfall;\n }\n\n struct RewardSpeeds {\n address rewardToken;\n uint256 supplySpeed;\n uint256 borrowSpeed;\n }\n\n struct Market {\n // Whether or not this market is listed\n bool isListed;\n // Multiplier representing the most one can borrow against their collateral in this market.\n // For instance, 0.9 to allow borrowing 90% of collateral value.\n // Must be between 0 and 1, and stored as a mantissa.\n uint256 collateralFactorMantissa;\n // Multiplier representing the collateralization after which the borrow is eligible\n // for liquidation. For instance, 0.8 liquidate when the borrow is 80% of collateral\n // value. Must be between 0 and collateral factor, stored as a mantissa.\n uint256 liquidationThresholdMantissa;\n // Per-market mapping of \"accounts in this asset\"\n mapping(address => bool) accountMembership;\n }\n\n /**\n * @notice Oracle which gives the price of any given asset\n */\n ResilientOracleInterface public oracle;\n\n /**\n * @notice Multiplier used to calculate the maximum repayAmount when liquidating a borrow\n */\n uint256 public closeFactorMantissa;\n\n /**\n * @notice Multiplier representing the discount on collateral that a liquidator receives\n */\n uint256 public liquidationIncentiveMantissa;\n\n /**\n * @notice Per-account mapping of \"assets you are in\"\n */\n mapping(address => VToken[]) public accountAssets;\n\n /**\n * @notice Official mapping of vTokens -> Market metadata\n * @dev Used e.g. to determine if a market is supported\n */\n mapping(address => Market) public markets;\n\n /// @notice A list of all markets\n VToken[] public allMarkets;\n\n /// @notice Borrow caps enforced by borrowAllowed for each vToken address. Defaults to zero which restricts borrowing.\n mapping(address => uint256) public borrowCaps;\n\n /// @notice Minimal collateral required for regular (non-batch) liquidations\n uint256 public minLiquidatableCollateral;\n\n /// @notice Supply caps enforced by mintAllowed for each vToken address. Defaults to zero which corresponds to minting not allowed\n mapping(address => uint256) public supplyCaps;\n\n /// @notice True if a certain action is paused on a certain market\n mapping(address => mapping(Action => bool)) internal _actionPaused;\n\n // List of Reward Distributors added\n RewardsDistributor[] internal rewardsDistributors;\n\n // Used to check if rewards distributor is added\n mapping(address => bool) internal rewardsDistributorExists;\n\n /// @notice Flag indicating whether forced liquidation enabled for a market\n mapping(address => bool) public isForcedLiquidationEnabled;\n\n uint256 internal constant NO_ERROR = 0;\n\n // closeFactorMantissa must be strictly greater than this value\n uint256 internal constant MIN_CLOSE_FACTOR_MANTISSA = 0.05e18; // 0.05\n\n // closeFactorMantissa must not exceed this value\n uint256 internal constant MAX_CLOSE_FACTOR_MANTISSA = 0.9e18; // 0.9\n\n // No collateralFactorMantissa may exceed this value\n uint256 internal constant MAX_COLLATERAL_FACTOR_MANTISSA = 0.95e18; // 0.95\n\n /// Prime token address\n IPrime public prime;\n\n /// @notice Whether the delegate is allowed to borrow or redeem on behalf of the user\n //mapping(address user => mapping (address delegate => bool approved)) public approvedDelegates;\n mapping(address => mapping(address => bool)) public approvedDelegates;\n\n /**\n * @dev This empty reserved space is put in place to allow future versions to add new\n * variables without shifting down storage in the inheritance chain.\n * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\n */\n uint256[47] private __gap;\n}\n" + }, + "contracts/ErrorReporter.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\n\n/**\n * @title TokenErrorReporter\n * @author Venus\n * @notice Errors that can be thrown by the `VToken` contract.\n */\ncontract TokenErrorReporter {\n uint256 public constant NO_ERROR = 0; // support legacy return codes\n\n error TransferNotAllowed();\n\n error MintFreshnessCheck();\n\n error RedeemFreshnessCheck();\n error RedeemTransferOutNotPossible();\n\n error BorrowFreshnessCheck();\n error BorrowCashNotAvailable();\n error DelegateNotApproved();\n\n error RepayBorrowFreshnessCheck();\n\n error HealBorrowUnauthorized();\n error ForceLiquidateBorrowUnauthorized();\n\n error LiquidateFreshnessCheck();\n error LiquidateCollateralFreshnessCheck();\n error LiquidateAccrueCollateralInterestFailed(uint256 errorCode);\n error LiquidateLiquidatorIsBorrower();\n error LiquidateCloseAmountIsZero();\n error LiquidateCloseAmountIsUintMax();\n\n error LiquidateSeizeLiquidatorIsBorrower();\n\n error ProtocolSeizeShareTooBig();\n\n error SetReserveFactorFreshCheck();\n error SetReserveFactorBoundsCheck();\n\n error AddReservesFactorFreshCheck(uint256 actualAddAmount);\n\n error ReduceReservesFreshCheck();\n error ReduceReservesCashNotAvailable();\n error ReduceReservesCashValidation();\n\n error SetInterestRateModelFreshCheck();\n}\n" + }, + "contracts/ExponentialNoError.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\n\nimport { EXP_SCALE as EXP_SCALE_, MANTISSA_ONE as MANTISSA_ONE_ } from \"./lib/constants.sol\";\n\n/**\n * @title Exponential module for storing fixed-precision decimals\n * @author Compound\n * @notice Exp is a struct which stores decimals with a fixed precision of 18 decimal places.\n * Thus, if we wanted to store the 5.1, mantissa would store 5.1e18. That is:\n * `Exp({mantissa: 5100000000000000000})`.\n */\ncontract ExponentialNoError {\n struct Exp {\n uint256 mantissa;\n }\n\n struct Double {\n uint256 mantissa;\n }\n\n uint256 internal constant EXP_SCALE = EXP_SCALE_;\n uint256 internal constant DOUBLE_SCALE = 1e36;\n uint256 internal constant HALF_EXP_SCALE = EXP_SCALE / 2;\n uint256 internal constant MANTISSA_ONE = MANTISSA_ONE_;\n\n /**\n * @dev Truncates the given exp to a whole number value.\n * For example, truncate(Exp{mantissa: 15 * EXP_SCALE}) = 15\n */\n function truncate(Exp memory exp) internal pure returns (uint256) {\n // Note: We are not using careful math here as we're performing a division that cannot fail\n return exp.mantissa / EXP_SCALE;\n }\n\n /**\n * @dev Multiply an Exp by a scalar, then truncate to return an unsigned integer.\n */\n // solhint-disable-next-line func-name-mixedcase\n function mul_ScalarTruncate(Exp memory a, uint256 scalar) internal pure returns (uint256) {\n Exp memory product = mul_(a, scalar);\n return truncate(product);\n }\n\n /**\n * @dev Multiply an Exp by a scalar, truncate, then add an to an unsigned integer, returning an unsigned integer.\n */\n // solhint-disable-next-line func-name-mixedcase\n function mul_ScalarTruncateAddUInt(Exp memory a, uint256 scalar, uint256 addend) internal pure returns (uint256) {\n Exp memory product = mul_(a, scalar);\n return add_(truncate(product), addend);\n }\n\n /**\n * @dev Checks if first Exp is less than second Exp.\n */\n function lessThanExp(Exp memory left, Exp memory right) internal pure returns (bool) {\n return left.mantissa < right.mantissa;\n }\n\n function safe224(uint256 n, string memory errorMessage) internal pure returns (uint224) {\n require(n <= type(uint224).max, errorMessage);\n return uint224(n);\n }\n\n function safe32(uint256 n, string memory errorMessage) internal pure returns (uint32) {\n require(n <= type(uint32).max, errorMessage);\n return uint32(n);\n }\n\n function add_(Exp memory a, Exp memory b) internal pure returns (Exp memory) {\n return Exp({ mantissa: add_(a.mantissa, b.mantissa) });\n }\n\n function add_(Double memory a, Double memory b) internal pure returns (Double memory) {\n return Double({ mantissa: add_(a.mantissa, b.mantissa) });\n }\n\n function add_(uint256 a, uint256 b) internal pure returns (uint256) {\n return a + b;\n }\n\n function sub_(Exp memory a, Exp memory b) internal pure returns (Exp memory) {\n return Exp({ mantissa: sub_(a.mantissa, b.mantissa) });\n }\n\n function sub_(Double memory a, Double memory b) internal pure returns (Double memory) {\n return Double({ mantissa: sub_(a.mantissa, b.mantissa) });\n }\n\n function sub_(uint256 a, uint256 b) internal pure returns (uint256) {\n return a - b;\n }\n\n function mul_(Exp memory a, Exp memory b) internal pure returns (Exp memory) {\n return Exp({ mantissa: mul_(a.mantissa, b.mantissa) / EXP_SCALE });\n }\n\n function mul_(Exp memory a, uint256 b) internal pure returns (Exp memory) {\n return Exp({ mantissa: mul_(a.mantissa, b) });\n }\n\n function mul_(uint256 a, Exp memory b) internal pure returns (uint256) {\n return mul_(a, b.mantissa) / EXP_SCALE;\n }\n\n function mul_(Double memory a, Double memory b) internal pure returns (Double memory) {\n return Double({ mantissa: mul_(a.mantissa, b.mantissa) / DOUBLE_SCALE });\n }\n\n function mul_(Double memory a, uint256 b) internal pure returns (Double memory) {\n return Double({ mantissa: mul_(a.mantissa, b) });\n }\n\n function mul_(uint256 a, Double memory b) internal pure returns (uint256) {\n return mul_(a, b.mantissa) / DOUBLE_SCALE;\n }\n\n function mul_(uint256 a, uint256 b) internal pure returns (uint256) {\n return a * b;\n }\n\n function div_(Exp memory a, Exp memory b) internal pure returns (Exp memory) {\n return Exp({ mantissa: div_(mul_(a.mantissa, EXP_SCALE), b.mantissa) });\n }\n\n function div_(Exp memory a, uint256 b) internal pure returns (Exp memory) {\n return Exp({ mantissa: div_(a.mantissa, b) });\n }\n\n function div_(uint256 a, Exp memory b) internal pure returns (uint256) {\n return div_(mul_(a, EXP_SCALE), b.mantissa);\n }\n\n function div_(Double memory a, Double memory b) internal pure returns (Double memory) {\n return Double({ mantissa: div_(mul_(a.mantissa, DOUBLE_SCALE), b.mantissa) });\n }\n\n function div_(Double memory a, uint256 b) internal pure returns (Double memory) {\n return Double({ mantissa: div_(a.mantissa, b) });\n }\n\n function div_(uint256 a, Double memory b) internal pure returns (uint256) {\n return div_(mul_(a, DOUBLE_SCALE), b.mantissa);\n }\n\n function div_(uint256 a, uint256 b) internal pure returns (uint256) {\n return a / b;\n }\n\n function fraction(uint256 a, uint256 b) internal pure returns (Double memory) {\n return Double({ mantissa: div_(mul_(a, DOUBLE_SCALE), b) });\n }\n}\n" + }, + "contracts/InterestRateModel.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\n\n/**\n * @title Compound's InterestRateModel Interface\n * @author Compound\n */\nabstract contract InterestRateModel {\n /**\n * @notice Calculates the current borrow interest rate per slot (block or second)\n * @param cash The total amount of cash the market has\n * @param borrows The total amount of borrows the market has outstanding\n * @param reserves The total amount of reserves the market has\n * @param badDebt The amount of badDebt in the market\n * @return The borrow rate percentage per slot (block or second) as a mantissa (scaled by EXP_SCALE)\n */\n function getBorrowRate(\n uint256 cash,\n uint256 borrows,\n uint256 reserves,\n uint256 badDebt\n ) external view virtual returns (uint256);\n\n /**\n * @notice Calculates the current supply interest rate per slot (block or second)\n * @param cash The total amount of cash the market has\n * @param borrows The total amount of borrows the market has outstanding\n * @param reserves The total amount of reserves the market has\n * @param reserveFactorMantissa The current reserve factor the market has\n * @param badDebt The amount of badDebt in the market\n * @return The supply rate percentage per slot (block or second) as a mantissa (scaled by EXP_SCALE)\n */\n function getSupplyRate(\n uint256 cash,\n uint256 borrows,\n uint256 reserves,\n uint256 reserveFactorMantissa,\n uint256 badDebt\n ) external view virtual returns (uint256);\n\n /**\n * @notice Indicator that this is an InterestRateModel contract (for inspection)\n * @return Always true\n */\n function isInterestRateModel() external pure virtual returns (bool) {\n return true;\n }\n}\n" + }, + "contracts/lib/constants.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity ^0.8.25;\n\n/// @dev The approximate number of seconds per year\nuint256 constant SECONDS_PER_YEAR = 31_536_000;\n\n/// @dev Base unit for computations, usually used in scaling (multiplications, divisions)\nuint256 constant EXP_SCALE = 1e18;\n\n/// @dev A unit (literal one) in EXP_SCALE, usually used in additions/subtractions\nuint256 constant MANTISSA_ONE = EXP_SCALE;\n" + }, + "contracts/lib/validators.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\n\n/// @notice Thrown if the supplied address is a zero address where it is not allowed\nerror ZeroAddressNotAllowed();\n\n/// @notice Checks if the provided address is nonzero, reverts otherwise\n/// @param address_ Address to check\n/// @custom:error ZeroAddressNotAllowed is thrown if the provided address is a zero address\nfunction ensureNonzeroAddress(address address_) pure {\n if (address_ == address(0)) {\n revert ZeroAddressNotAllowed();\n }\n}\n" + }, + "contracts/MaxLoopsLimitHelper.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\n\n/**\n * @title MaxLoopsLimitHelper\n * @author Venus\n * @notice Abstract contract used to avoid collection with too many items that would generate gas errors and DoS.\n */\nabstract contract MaxLoopsLimitHelper {\n // Limit for the loops to avoid the DOS\n uint256 public maxLoopsLimit;\n\n /**\n * @dev This empty reserved space is put in place to allow future versions to add new\n * variables without shifting down storage in the inheritance chain.\n * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\n */\n uint256[49] private __gap;\n\n /// @notice Emitted when max loops limit is set\n event MaxLoopsLimitUpdated(uint256 oldMaxLoopsLimit, uint256 newmaxLoopsLimit);\n\n /// @notice Thrown an error on maxLoopsLimit exceeds for any loop\n error MaxLoopsLimitExceeded(uint256 loopsLimit, uint256 requiredLoops);\n\n /**\n * @notice Set the limit for the loops can iterate to avoid the DOS\n * @param limit Limit for the max loops can execute at a time\n */\n function _setMaxLoopsLimit(uint256 limit) internal {\n require(limit > maxLoopsLimit, \"Comptroller: Invalid maxLoopsLimit\");\n\n uint256 oldMaxLoopsLimit = maxLoopsLimit;\n maxLoopsLimit = limit;\n\n emit MaxLoopsLimitUpdated(oldMaxLoopsLimit, limit);\n }\n\n /**\n * @notice Compare the maxLoopsLimit with number of the times loop iterate\n * @param len Length of the loops iterate\n * @custom:error MaxLoopsLimitExceeded error is thrown when loops length exceeds maxLoopsLimit\n */\n function _ensureMaxLoops(uint256 len) internal view {\n if (len > maxLoopsLimit) {\n revert MaxLoopsLimitExceeded(maxLoopsLimit, len);\n }\n }\n}\n" + }, + "contracts/Rewards/RewardsDistributor.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\n\nimport { Ownable2StepUpgradeable } from \"@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol\";\nimport { IERC20Upgradeable } from \"@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol\";\nimport { SafeERC20Upgradeable } from \"@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol\";\nimport { AccessControlledV8 } from \"@venusprotocol/governance-contracts/contracts/Governance/AccessControlledV8.sol\";\nimport { TimeManagerV8 } from \"@venusprotocol/solidity-utilities/contracts/TimeManagerV8.sol\";\n\nimport { ExponentialNoError } from \"../ExponentialNoError.sol\";\nimport { VToken } from \"../VToken.sol\";\nimport { Comptroller } from \"../Comptroller.sol\";\nimport { MaxLoopsLimitHelper } from \"../MaxLoopsLimitHelper.sol\";\nimport { RewardsDistributorStorage } from \"./RewardsDistributorStorage.sol\";\n\n/**\n * @title `RewardsDistributor`\n * @author Venus\n * @notice Contract used to configure, track and distribute rewards to users based on their actions (borrows and supplies) in the protocol.\n * Users can receive additional rewards through a `RewardsDistributor`. Each `RewardsDistributor` proxy is initialized with a specific reward\n * token and `Comptroller`, which can then distribute the reward token to users that supply or borrow in the associated pool.\n * Authorized users can set the reward token borrow and supply speeds for each market in the pool. This sets a fixed amount of reward\n * token to be released each slot (block or second) for borrowers and suppliers, which is distributed based on a user’s percentage of the borrows or supplies\n * respectively. The owner can also set up reward distributions to contributor addresses (distinct from suppliers and borrowers) by setting\n * their contributor reward token speed, which similarly allocates a fixed amount of reward token per slot (block or second).\n *\n * The owner has the ability to transfer any amount of reward tokens held by the contract to any other address. Rewards are not distributed\n * automatically and must be claimed by a user calling `claimRewardToken()`. Users should be aware that it is up to the owner and other centralized\n * entities to ensure that the `RewardsDistributor` holds enough tokens to distribute the accumulated rewards of users and contributors.\n */\ncontract RewardsDistributor is\n ExponentialNoError,\n Ownable2StepUpgradeable,\n AccessControlledV8,\n MaxLoopsLimitHelper,\n RewardsDistributorStorage,\n TimeManagerV8\n{\n using SafeERC20Upgradeable for IERC20Upgradeable;\n\n /// @notice The initial REWARD TOKEN index for a market\n uint224 public constant INITIAL_INDEX = 1e36;\n\n /// @notice Emitted when REWARD TOKEN is distributed to a supplier\n event DistributedSupplierRewardToken(\n VToken indexed vToken,\n address indexed supplier,\n uint256 rewardTokenDelta,\n uint256 rewardTokenTotal,\n uint256 rewardTokenSupplyIndex\n );\n\n /// @notice Emitted when REWARD TOKEN is distributed to a borrower\n event DistributedBorrowerRewardToken(\n VToken indexed vToken,\n address indexed borrower,\n uint256 rewardTokenDelta,\n uint256 rewardTokenTotal,\n uint256 rewardTokenBorrowIndex\n );\n\n /// @notice Emitted when a new supply-side REWARD TOKEN speed is calculated for a market\n event RewardTokenSupplySpeedUpdated(VToken indexed vToken, uint256 newSpeed);\n\n /// @notice Emitted when a new borrow-side REWARD TOKEN speed is calculated for a market\n event RewardTokenBorrowSpeedUpdated(VToken indexed vToken, uint256 newSpeed);\n\n /// @notice Emitted when REWARD TOKEN is granted by admin\n event RewardTokenGranted(address indexed recipient, uint256 amount);\n\n /// @notice Emitted when a new REWARD TOKEN speed is set for a contributor\n event ContributorRewardTokenSpeedUpdated(address indexed contributor, uint256 newSpeed);\n\n /// @notice Emitted when a market is initialized\n event MarketInitialized(address indexed vToken);\n\n /// @notice Emitted when a reward token supply index is updated\n event RewardTokenSupplyIndexUpdated(address indexed vToken);\n\n /// @notice Emitted when a reward token borrow index is updated\n event RewardTokenBorrowIndexUpdated(address indexed vToken, Exp marketBorrowIndex);\n\n /// @notice Emitted when a reward for contributor is updated\n event ContributorRewardsUpdated(address indexed contributor, uint256 rewardAccrued);\n\n /// @notice Emitted when a reward token last rewarding block for supply is updated\n event SupplyLastRewardingBlockUpdated(address indexed vToken, uint32 newBlock);\n\n /// @notice Emitted when a reward token last rewarding block for borrow is updated\n event BorrowLastRewardingBlockUpdated(address indexed vToken, uint32 newBlock);\n\n /// @notice Emitted when a reward token last rewarding timestamp for supply is updated\n event SupplyLastRewardingBlockTimestampUpdated(address indexed vToken, uint256 newTimestamp);\n\n /// @notice Emitted when a reward token last rewarding timestamp for borrow is updated\n event BorrowLastRewardingBlockTimestampUpdated(address indexed vToken, uint256 newTimestamp);\n\n modifier onlyComptroller() {\n require(address(comptroller) == msg.sender, \"Only comptroller can call this function\");\n _;\n }\n\n /**\n * @param timeBased_ A boolean indicating whether the contract is based on time or block.\n * @param blocksPerYear_ The number of blocks per year\n * @custom:oz-upgrades-unsafe-allow constructor\n */\n constructor(bool timeBased_, uint256 blocksPerYear_) TimeManagerV8(timeBased_, blocksPerYear_) {\n // Note that the contract is upgradeable. Use initialize() or reinitializers\n // to set the state variables.\n _disableInitializers();\n }\n\n /**\n * @notice RewardsDistributor initializer\n * @dev Initializes the deployer to owner\n * @param comptroller_ Comptroller to attach the reward distributor to\n * @param rewardToken_ Reward token to distribute\n * @param loopsLimit_ Maximum number of iterations for the loops in this contract\n * @param accessControlManager_ AccessControlManager contract address\n */\n function initialize(\n Comptroller comptroller_,\n IERC20Upgradeable rewardToken_,\n uint256 loopsLimit_,\n address accessControlManager_\n ) external initializer {\n comptroller = comptroller_;\n rewardToken = rewardToken_;\n __Ownable2Step_init();\n __AccessControlled_init_unchained(accessControlManager_);\n\n _setMaxLoopsLimit(loopsLimit_);\n }\n\n /**\n * @notice Initializes the market state for a specific vToken\n * @param vToken The address of the vToken to be initialized\n * @custom:event MarketInitialized emits on success\n * @custom:access Only Comptroller\n */\n function initializeMarket(address vToken) external onlyComptroller {\n uint256 blockNumberOrTimestamp = getBlockNumberOrTimestamp();\n\n isTimeBased\n ? _initializeMarketTimestampBased(vToken, blockNumberOrTimestamp)\n : _initializeMarketBlockBased(vToken, safe32(blockNumberOrTimestamp, \"block number exceeds 32 bits\"));\n\n emit MarketInitialized(vToken);\n }\n\n /*** Reward Token Distribution ***/\n\n /**\n * @notice Calculate reward token accrued by a borrower and possibly transfer it to them\n * Borrowers will begin to accrue after the first interaction with the protocol.\n * @dev This function should only be called when the user has a borrow position in the market\n * (e.g. Comptroller.preBorrowHook, and Comptroller.preRepayHook)\n * We avoid an external call to check if they are in the market to save gas because this function is called in many places\n * @param vToken The market in which the borrower is interacting\n * @param borrower The address of the borrower to distribute REWARD TOKEN to\n * @param marketBorrowIndex The current global borrow index of vToken\n */\n function distributeBorrowerRewardToken(\n address vToken,\n address borrower,\n Exp memory marketBorrowIndex\n ) external onlyComptroller {\n _distributeBorrowerRewardToken(vToken, borrower, marketBorrowIndex);\n }\n\n function updateRewardTokenSupplyIndex(address vToken) external onlyComptroller {\n _updateRewardTokenSupplyIndex(vToken);\n }\n\n /**\n * @notice Transfer REWARD TOKEN to the recipient\n * @dev Note: If there is not enough REWARD TOKEN, we do not perform the transfer all\n * @param recipient The address of the recipient to transfer REWARD TOKEN to\n * @param amount The amount of REWARD TOKEN to (possibly) transfer\n */\n function grantRewardToken(address recipient, uint256 amount) external onlyOwner {\n uint256 amountLeft = _grantRewardToken(recipient, amount);\n require(amountLeft == 0, \"insufficient rewardToken for grant\");\n emit RewardTokenGranted(recipient, amount);\n }\n\n function updateRewardTokenBorrowIndex(address vToken, Exp memory marketBorrowIndex) external onlyComptroller {\n _updateRewardTokenBorrowIndex(vToken, marketBorrowIndex);\n }\n\n /**\n * @notice Set REWARD TOKEN borrow and supply speeds for the specified markets\n * @param vTokens The markets whose REWARD TOKEN speed to update\n * @param supplySpeeds New supply-side REWARD TOKEN speed for the corresponding market\n * @param borrowSpeeds New borrow-side REWARD TOKEN speed for the corresponding market\n */\n function setRewardTokenSpeeds(\n VToken[] memory vTokens,\n uint256[] memory supplySpeeds,\n uint256[] memory borrowSpeeds\n ) external {\n _checkAccessAllowed(\"setRewardTokenSpeeds(address[],uint256[],uint256[])\");\n uint256 numTokens = vTokens.length;\n require(numTokens == supplySpeeds.length && numTokens == borrowSpeeds.length, \"invalid setRewardTokenSpeeds\");\n\n for (uint256 i; i < numTokens; ++i) {\n _setRewardTokenSpeed(vTokens[i], supplySpeeds[i], borrowSpeeds[i]);\n }\n }\n\n /**\n * @notice Set REWARD TOKEN last rewarding block for the specified markets, used when contract is block based\n * @param vTokens The markets whose REWARD TOKEN last rewarding block to update\n * @param supplyLastRewardingBlocks New supply-side REWARD TOKEN last rewarding block for the corresponding market\n * @param borrowLastRewardingBlocks New borrow-side REWARD TOKEN last rewarding block for the corresponding market\n */\n function setLastRewardingBlocks(\n VToken[] calldata vTokens,\n uint32[] calldata supplyLastRewardingBlocks,\n uint32[] calldata borrowLastRewardingBlocks\n ) external {\n _checkAccessAllowed(\"setLastRewardingBlocks(address[],uint32[],uint32[])\");\n require(!isTimeBased, \"Block-based operation only\");\n\n uint256 numTokens = vTokens.length;\n require(\n numTokens == supplyLastRewardingBlocks.length && numTokens == borrowLastRewardingBlocks.length,\n \"RewardsDistributor::setLastRewardingBlocks invalid input\"\n );\n\n for (uint256 i; i < numTokens; ) {\n _setLastRewardingBlock(vTokens[i], supplyLastRewardingBlocks[i], borrowLastRewardingBlocks[i]);\n unchecked {\n ++i;\n }\n }\n }\n\n /**\n * @notice Set REWARD TOKEN last rewarding block timestamp for the specified markets, used when contract is time based\n * @param vTokens The markets whose REWARD TOKEN last rewarding block to update\n * @param supplyLastRewardingBlockTimestamps New supply-side REWARD TOKEN last rewarding block timestamp for the corresponding market\n * @param borrowLastRewardingBlockTimestamps New borrow-side REWARD TOKEN last rewarding block timestamp for the corresponding market\n */\n function setLastRewardingBlockTimestamps(\n VToken[] calldata vTokens,\n uint256[] calldata supplyLastRewardingBlockTimestamps,\n uint256[] calldata borrowLastRewardingBlockTimestamps\n ) external {\n _checkAccessAllowed(\"setLastRewardingBlockTimestamps(address[],uint256[],uint256[])\");\n require(isTimeBased, \"Time-based operation only\");\n\n uint256 numTokens = vTokens.length;\n require(\n numTokens == supplyLastRewardingBlockTimestamps.length &&\n numTokens == borrowLastRewardingBlockTimestamps.length,\n \"RewardsDistributor::setLastRewardingBlockTimestamps invalid input\"\n );\n\n for (uint256 i; i < numTokens; ) {\n _setLastRewardingBlockTimestamp(\n vTokens[i],\n supplyLastRewardingBlockTimestamps[i],\n borrowLastRewardingBlockTimestamps[i]\n );\n unchecked {\n ++i;\n }\n }\n }\n\n /**\n * @notice Set REWARD TOKEN speed for a single contributor\n * @param contributor The contributor whose REWARD TOKEN speed to update\n * @param rewardTokenSpeed New REWARD TOKEN speed for contributor\n */\n function setContributorRewardTokenSpeed(address contributor, uint256 rewardTokenSpeed) external onlyOwner {\n // note that REWARD TOKEN speed could be set to 0 to halt liquidity rewards for a contributor\n updateContributorRewards(contributor);\n if (rewardTokenSpeed == 0) {\n // release storage\n delete lastContributorBlock[contributor];\n } else {\n lastContributorBlock[contributor] = getBlockNumberOrTimestamp();\n }\n rewardTokenContributorSpeeds[contributor] = rewardTokenSpeed;\n\n emit ContributorRewardTokenSpeedUpdated(contributor, rewardTokenSpeed);\n }\n\n function distributeSupplierRewardToken(address vToken, address supplier) external onlyComptroller {\n _distributeSupplierRewardToken(vToken, supplier);\n }\n\n /**\n * @notice Claim all the rewardToken accrued by holder in all markets\n * @param holder The address to claim REWARD TOKEN for\n */\n function claimRewardToken(address holder) external {\n return claimRewardToken(holder, comptroller.getAllMarkets());\n }\n\n /**\n * @notice Set the limit for the loops can iterate to avoid the DOS\n * @param limit Limit for the max loops can execute at a time\n */\n function setMaxLoopsLimit(uint256 limit) external onlyOwner {\n _setMaxLoopsLimit(limit);\n }\n\n /**\n * @notice Calculate additional accrued REWARD TOKEN for a contributor since last accrual\n * @param contributor The address to calculate contributor rewards for\n */\n function updateContributorRewards(address contributor) public {\n uint256 rewardTokenSpeed = rewardTokenContributorSpeeds[contributor];\n uint256 blockNumberOrTimestamp = getBlockNumberOrTimestamp();\n uint256 deltaBlocksOrTimestamp = sub_(blockNumberOrTimestamp, lastContributorBlock[contributor]);\n if (deltaBlocksOrTimestamp > 0 && rewardTokenSpeed > 0) {\n uint256 newAccrued = mul_(deltaBlocksOrTimestamp, rewardTokenSpeed);\n uint256 contributorAccrued = add_(rewardTokenAccrued[contributor], newAccrued);\n\n rewardTokenAccrued[contributor] = contributorAccrued;\n lastContributorBlock[contributor] = blockNumberOrTimestamp;\n\n emit ContributorRewardsUpdated(contributor, rewardTokenAccrued[contributor]);\n }\n }\n\n /**\n * @notice Claim all the rewardToken accrued by holder in the specified markets\n * @param holder The address to claim REWARD TOKEN for\n * @param vTokens The list of markets to claim REWARD TOKEN in\n */\n function claimRewardToken(address holder, VToken[] memory vTokens) public {\n uint256 vTokensCount = vTokens.length;\n\n _ensureMaxLoops(vTokensCount);\n\n for (uint256 i; i < vTokensCount; ++i) {\n VToken vToken = vTokens[i];\n require(comptroller.isMarketListed(vToken), \"market must be listed\");\n Exp memory borrowIndex = Exp({ mantissa: vToken.borrowIndex() });\n _updateRewardTokenBorrowIndex(address(vToken), borrowIndex);\n _distributeBorrowerRewardToken(address(vToken), holder, borrowIndex);\n _updateRewardTokenSupplyIndex(address(vToken));\n _distributeSupplierRewardToken(address(vToken), holder);\n }\n rewardTokenAccrued[holder] = _grantRewardToken(holder, rewardTokenAccrued[holder]);\n }\n\n /**\n * @notice Set REWARD TOKEN last rewarding block for a single market.\n * @param vToken market's whose reward token last rewarding block to be updated\n * @param supplyLastRewardingBlock New supply-side REWARD TOKEN last rewarding block for market\n * @param borrowLastRewardingBlock New borrow-side REWARD TOKEN last rewarding block for market\n */\n function _setLastRewardingBlock(\n VToken vToken,\n uint32 supplyLastRewardingBlock,\n uint32 borrowLastRewardingBlock\n ) internal {\n require(comptroller.isMarketListed(vToken), \"rewardToken market is not listed\");\n\n uint256 blockNumber = getBlockNumberOrTimestamp();\n\n require(supplyLastRewardingBlock > blockNumber, \"setting last rewarding block in the past is not allowed\");\n require(borrowLastRewardingBlock > blockNumber, \"setting last rewarding block in the past is not allowed\");\n\n uint32 currentSupplyLastRewardingBlock = rewardTokenSupplyState[address(vToken)].lastRewardingBlock;\n uint32 currentBorrowLastRewardingBlock = rewardTokenBorrowState[address(vToken)].lastRewardingBlock;\n\n require(\n currentSupplyLastRewardingBlock == 0 || currentSupplyLastRewardingBlock > blockNumber,\n \"this RewardsDistributor is already locked\"\n );\n require(\n currentBorrowLastRewardingBlock == 0 || currentBorrowLastRewardingBlock > blockNumber,\n \"this RewardsDistributor is already locked\"\n );\n\n if (currentSupplyLastRewardingBlock != supplyLastRewardingBlock) {\n rewardTokenSupplyState[address(vToken)].lastRewardingBlock = supplyLastRewardingBlock;\n emit SupplyLastRewardingBlockUpdated(address(vToken), supplyLastRewardingBlock);\n }\n\n if (currentBorrowLastRewardingBlock != borrowLastRewardingBlock) {\n rewardTokenBorrowState[address(vToken)].lastRewardingBlock = borrowLastRewardingBlock;\n emit BorrowLastRewardingBlockUpdated(address(vToken), borrowLastRewardingBlock);\n }\n }\n\n /**\n * @notice Set REWARD TOKEN last rewarding timestamp for a single market.\n * @param vToken market's whose reward token last rewarding timestamp to be updated\n * @param supplyLastRewardingBlockTimestamp New supply-side REWARD TOKEN last rewarding timestamp for market\n * @param borrowLastRewardingBlockTimestamp New borrow-side REWARD TOKEN last rewarding timestamp for market\n */\n function _setLastRewardingBlockTimestamp(\n VToken vToken,\n uint256 supplyLastRewardingBlockTimestamp,\n uint256 borrowLastRewardingBlockTimestamp\n ) internal {\n require(comptroller.isMarketListed(vToken), \"rewardToken market is not listed\");\n\n uint256 blockTimestamp = getBlockNumberOrTimestamp();\n\n require(\n supplyLastRewardingBlockTimestamp > blockTimestamp,\n \"setting last rewarding timestamp in the past is not allowed\"\n );\n require(\n borrowLastRewardingBlockTimestamp > blockTimestamp,\n \"setting last rewarding timestamp in the past is not allowed\"\n );\n\n uint256 currentSupplyLastRewardingBlockTimestamp = rewardTokenSupplyStateTimeBased[address(vToken)]\n .lastRewardingTimestamp;\n uint256 currentBorrowLastRewardingBlockTimestamp = rewardTokenBorrowStateTimeBased[address(vToken)]\n .lastRewardingTimestamp;\n\n require(\n currentSupplyLastRewardingBlockTimestamp == 0 || currentSupplyLastRewardingBlockTimestamp > blockTimestamp,\n \"this RewardsDistributor is already locked\"\n );\n require(\n currentBorrowLastRewardingBlockTimestamp == 0 || currentBorrowLastRewardingBlockTimestamp > blockTimestamp,\n \"this RewardsDistributor is already locked\"\n );\n\n if (currentSupplyLastRewardingBlockTimestamp != supplyLastRewardingBlockTimestamp) {\n rewardTokenSupplyStateTimeBased[address(vToken)].lastRewardingTimestamp = supplyLastRewardingBlockTimestamp;\n emit SupplyLastRewardingBlockTimestampUpdated(address(vToken), supplyLastRewardingBlockTimestamp);\n }\n\n if (currentBorrowLastRewardingBlockTimestamp != borrowLastRewardingBlockTimestamp) {\n rewardTokenBorrowStateTimeBased[address(vToken)].lastRewardingTimestamp = borrowLastRewardingBlockTimestamp;\n emit BorrowLastRewardingBlockTimestampUpdated(address(vToken), borrowLastRewardingBlockTimestamp);\n }\n }\n\n /**\n * @notice Set REWARD TOKEN speed for a single market.\n * @param vToken market's whose reward token rate to be updated\n * @param supplySpeed New supply-side REWARD TOKEN speed for market\n * @param borrowSpeed New borrow-side REWARD TOKEN speed for market\n */\n function _setRewardTokenSpeed(VToken vToken, uint256 supplySpeed, uint256 borrowSpeed) internal {\n require(comptroller.isMarketListed(vToken), \"rewardToken market is not listed\");\n\n if (rewardTokenSupplySpeeds[address(vToken)] != supplySpeed) {\n // Supply speed updated so let's update supply state to ensure that\n // 1. REWARD TOKEN accrued properly for the old speed, and\n // 2. REWARD TOKEN accrued at the new speed starts after this block.\n _updateRewardTokenSupplyIndex(address(vToken));\n\n // Update speed and emit event\n rewardTokenSupplySpeeds[address(vToken)] = supplySpeed;\n emit RewardTokenSupplySpeedUpdated(vToken, supplySpeed);\n }\n\n if (rewardTokenBorrowSpeeds[address(vToken)] != borrowSpeed) {\n // Borrow speed updated so let's update borrow state to ensure that\n // 1. REWARD TOKEN accrued properly for the old speed, and\n // 2. REWARD TOKEN accrued at the new speed starts after this block.\n Exp memory borrowIndex = Exp({ mantissa: vToken.borrowIndex() });\n _updateRewardTokenBorrowIndex(address(vToken), borrowIndex);\n\n // Update speed and emit event\n rewardTokenBorrowSpeeds[address(vToken)] = borrowSpeed;\n emit RewardTokenBorrowSpeedUpdated(vToken, borrowSpeed);\n }\n }\n\n /**\n * @notice Calculate REWARD TOKEN accrued by a supplier and possibly transfer it to them.\n * @param vToken The market in which the supplier is interacting\n * @param supplier The address of the supplier to distribute REWARD TOKEN to\n */\n function _distributeSupplierRewardToken(address vToken, address supplier) internal {\n RewardToken storage supplyState = rewardTokenSupplyState[vToken];\n TimeBasedRewardToken storage supplyStateTimeBased = rewardTokenSupplyStateTimeBased[vToken];\n\n uint256 supplyIndex = isTimeBased ? supplyStateTimeBased.index : supplyState.index;\n uint256 supplierIndex = rewardTokenSupplierIndex[vToken][supplier];\n\n // Update supplier's index to the current index since we are distributing accrued REWARD TOKEN\n rewardTokenSupplierIndex[vToken][supplier] = supplyIndex;\n\n if (supplierIndex == 0 && supplyIndex >= INITIAL_INDEX) {\n // Covers the case where users supplied tokens before the market's supply state index was set.\n // Rewards the user with REWARD TOKEN accrued from the start of when supplier rewards were first\n // set for the market.\n supplierIndex = INITIAL_INDEX;\n }\n\n // Calculate change in the cumulative sum of the REWARD TOKEN per vToken accrued\n Double memory deltaIndex = Double({ mantissa: sub_(supplyIndex, supplierIndex) });\n\n uint256 supplierTokens = VToken(vToken).balanceOf(supplier);\n\n // Calculate REWARD TOKEN accrued: vTokenAmount * accruedPerVToken\n uint256 supplierDelta = mul_(supplierTokens, deltaIndex);\n\n uint256 supplierAccrued = add_(rewardTokenAccrued[supplier], supplierDelta);\n rewardTokenAccrued[supplier] = supplierAccrued;\n\n emit DistributedSupplierRewardToken(VToken(vToken), supplier, supplierDelta, supplierAccrued, supplyIndex);\n }\n\n /**\n * @notice Calculate reward token accrued by a borrower and possibly transfer it to them.\n * @param vToken The market in which the borrower is interacting\n * @param borrower The address of the borrower to distribute REWARD TOKEN to\n * @param marketBorrowIndex The current global borrow index of vToken\n */\n function _distributeBorrowerRewardToken(address vToken, address borrower, Exp memory marketBorrowIndex) internal {\n RewardToken storage borrowState = rewardTokenBorrowState[vToken];\n TimeBasedRewardToken storage borrowStateTimeBased = rewardTokenBorrowStateTimeBased[vToken];\n\n uint256 borrowIndex = isTimeBased ? borrowStateTimeBased.index : borrowState.index;\n uint256 borrowerIndex = rewardTokenBorrowerIndex[vToken][borrower];\n\n // Update borrowers's index to the current index since we are distributing accrued REWARD TOKEN\n rewardTokenBorrowerIndex[vToken][borrower] = borrowIndex;\n\n if (borrowerIndex == 0 && borrowIndex >= INITIAL_INDEX) {\n // Covers the case where users borrowed tokens before the market's borrow state index was set.\n // Rewards the user with REWARD TOKEN accrued from the start of when borrower rewards were first\n // set for the market.\n borrowerIndex = INITIAL_INDEX;\n }\n\n // Calculate change in the cumulative sum of the REWARD TOKEN per borrowed unit accrued\n Double memory deltaIndex = Double({ mantissa: sub_(borrowIndex, borrowerIndex) });\n\n uint256 borrowerAmount = div_(VToken(vToken).borrowBalanceStored(borrower), marketBorrowIndex);\n\n // Calculate REWARD TOKEN accrued: vTokenAmount * accruedPerBorrowedUnit\n if (borrowerAmount != 0) {\n uint256 borrowerDelta = mul_(borrowerAmount, deltaIndex);\n\n uint256 borrowerAccrued = add_(rewardTokenAccrued[borrower], borrowerDelta);\n rewardTokenAccrued[borrower] = borrowerAccrued;\n\n emit DistributedBorrowerRewardToken(VToken(vToken), borrower, borrowerDelta, borrowerAccrued, borrowIndex);\n }\n }\n\n /**\n * @notice Transfer REWARD TOKEN to the user.\n * @dev Note: If there is not enough REWARD TOKEN, we do not perform the transfer all.\n * @param user The address of the user to transfer REWARD TOKEN to\n * @param amount The amount of REWARD TOKEN to (possibly) transfer\n * @return The amount of REWARD TOKEN which was NOT transferred to the user\n */\n function _grantRewardToken(address user, uint256 amount) internal returns (uint256) {\n uint256 rewardTokenRemaining = rewardToken.balanceOf(address(this));\n if (amount > 0 && amount <= rewardTokenRemaining) {\n rewardToken.safeTransfer(user, amount);\n return 0;\n }\n return amount;\n }\n\n /**\n * @notice Accrue REWARD TOKEN to the market by updating the supply index\n * @param vToken The market whose supply index to update\n * @dev Index is a cumulative sum of the REWARD TOKEN per vToken accrued\n */\n function _updateRewardTokenSupplyIndex(address vToken) internal {\n RewardToken storage supplyState = rewardTokenSupplyState[vToken];\n TimeBasedRewardToken storage supplyStateTimeBased = rewardTokenSupplyStateTimeBased[vToken];\n\n uint256 supplySpeed = rewardTokenSupplySpeeds[vToken];\n uint256 blockNumberOrTimestamp = getBlockNumberOrTimestamp();\n\n if (!isTimeBased) {\n safe32(blockNumberOrTimestamp, \"block number exceeds 32 bits\");\n }\n\n uint256 lastRewardingBlockOrTimestamp = isTimeBased\n ? supplyStateTimeBased.lastRewardingTimestamp\n : uint256(supplyState.lastRewardingBlock);\n\n if (lastRewardingBlockOrTimestamp > 0 && blockNumberOrTimestamp > lastRewardingBlockOrTimestamp) {\n blockNumberOrTimestamp = lastRewardingBlockOrTimestamp;\n }\n\n uint256 deltaBlocksOrTimestamp = sub_(\n blockNumberOrTimestamp,\n (isTimeBased ? supplyStateTimeBased.timestamp : uint256(supplyState.block))\n );\n if (deltaBlocksOrTimestamp > 0 && supplySpeed > 0) {\n uint256 supplyTokens = VToken(vToken).totalSupply();\n uint256 accruedSinceUpdate = mul_(deltaBlocksOrTimestamp, supplySpeed);\n Double memory ratio = supplyTokens > 0\n ? fraction(accruedSinceUpdate, supplyTokens)\n : Double({ mantissa: 0 });\n uint224 supplyIndex = isTimeBased ? supplyStateTimeBased.index : supplyState.index;\n uint224 index = safe224(\n add_(Double({ mantissa: supplyIndex }), ratio).mantissa,\n \"new index exceeds 224 bits\"\n );\n\n if (isTimeBased) {\n supplyStateTimeBased.index = index;\n supplyStateTimeBased.timestamp = blockNumberOrTimestamp;\n } else {\n supplyState.index = index;\n supplyState.block = uint32(blockNumberOrTimestamp);\n }\n } else if (deltaBlocksOrTimestamp > 0) {\n isTimeBased ? supplyStateTimeBased.timestamp = blockNumberOrTimestamp : supplyState.block = uint32(\n blockNumberOrTimestamp\n );\n }\n\n emit RewardTokenSupplyIndexUpdated(vToken);\n }\n\n /**\n * @notice Accrue REWARD TOKEN to the market by updating the borrow index\n * @param vToken The market whose borrow index to update\n * @param marketBorrowIndex The current global borrow index of vToken\n * @dev Index is a cumulative sum of the REWARD TOKEN per vToken accrued\n */\n function _updateRewardTokenBorrowIndex(address vToken, Exp memory marketBorrowIndex) internal {\n RewardToken storage borrowState = rewardTokenBorrowState[vToken];\n TimeBasedRewardToken storage borrowStateTimeBased = rewardTokenBorrowStateTimeBased[vToken];\n\n uint256 borrowSpeed = rewardTokenBorrowSpeeds[vToken];\n uint256 blockNumberOrTimestamp = getBlockNumberOrTimestamp();\n\n if (!isTimeBased) {\n safe32(blockNumberOrTimestamp, \"block number exceeds 32 bits\");\n }\n\n uint256 lastRewardingBlockOrTimestamp = isTimeBased\n ? borrowStateTimeBased.lastRewardingTimestamp\n : uint256(borrowState.lastRewardingBlock);\n\n if (lastRewardingBlockOrTimestamp > 0 && blockNumberOrTimestamp > lastRewardingBlockOrTimestamp) {\n blockNumberOrTimestamp = lastRewardingBlockOrTimestamp;\n }\n\n uint256 deltaBlocksOrTimestamp = sub_(\n blockNumberOrTimestamp,\n (isTimeBased ? borrowStateTimeBased.timestamp : uint256(borrowState.block))\n );\n if (deltaBlocksOrTimestamp > 0 && borrowSpeed > 0) {\n uint256 borrowAmount = div_(VToken(vToken).totalBorrows(), marketBorrowIndex);\n uint256 accruedSinceUpdate = mul_(deltaBlocksOrTimestamp, borrowSpeed);\n Double memory ratio = borrowAmount > 0\n ? fraction(accruedSinceUpdate, borrowAmount)\n : Double({ mantissa: 0 });\n uint224 borrowIndex = isTimeBased ? borrowStateTimeBased.index : borrowState.index;\n uint224 index = safe224(\n add_(Double({ mantissa: borrowIndex }), ratio).mantissa,\n \"new index exceeds 224 bits\"\n );\n\n if (isTimeBased) {\n borrowStateTimeBased.index = index;\n borrowStateTimeBased.timestamp = blockNumberOrTimestamp;\n } else {\n borrowState.index = index;\n borrowState.block = uint32(blockNumberOrTimestamp);\n }\n } else if (deltaBlocksOrTimestamp > 0) {\n if (isTimeBased) {\n borrowStateTimeBased.timestamp = blockNumberOrTimestamp;\n } else {\n borrowState.block = uint32(blockNumberOrTimestamp);\n }\n }\n\n emit RewardTokenBorrowIndexUpdated(vToken, marketBorrowIndex);\n }\n\n /**\n * @notice Initializes the market state for a specific vToken called when contract is block-based\n * @param vToken The address of the vToken to be initialized\n * @param blockNumber current block number\n */\n function _initializeMarketBlockBased(address vToken, uint32 blockNumber) internal {\n RewardToken storage supplyState = rewardTokenSupplyState[vToken];\n RewardToken storage borrowState = rewardTokenBorrowState[vToken];\n\n /*\n * Update market state indices\n */\n if (supplyState.index == 0) {\n // Initialize supply state index with default value\n supplyState.index = INITIAL_INDEX;\n }\n\n if (borrowState.index == 0) {\n // Initialize borrow state index with default value\n borrowState.index = INITIAL_INDEX;\n }\n\n /*\n * Update market state block numbers\n */\n supplyState.block = borrowState.block = blockNumber;\n }\n\n /**\n * @notice Initializes the market state for a specific vToken called when contract is time-based\n * @param vToken The address of the vToken to be initialized\n * @param blockTimestamp current block timestamp\n */\n function _initializeMarketTimestampBased(address vToken, uint256 blockTimestamp) internal {\n TimeBasedRewardToken storage supplyState = rewardTokenSupplyStateTimeBased[vToken];\n TimeBasedRewardToken storage borrowState = rewardTokenBorrowStateTimeBased[vToken];\n\n /*\n * Update market state indices\n */\n if (supplyState.index == 0) {\n // Initialize supply state index with default value\n supplyState.index = INITIAL_INDEX;\n }\n\n if (borrowState.index == 0) {\n // Initialize borrow state index with default value\n borrowState.index = INITIAL_INDEX;\n }\n\n /*\n * Update market state block timestamp\n */\n supplyState.timestamp = borrowState.timestamp = blockTimestamp;\n }\n}\n" + }, + "contracts/Rewards/RewardsDistributorStorage.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\n\nimport { IERC20Upgradeable } from \"@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol\";\n\nimport { Comptroller } from \"../Comptroller.sol\";\n\n/**\n * @title RewardsDistributorStorage\n * @author Venus\n * @dev Storage for RewardsDistributor\n */\ncontract RewardsDistributorStorage {\n struct RewardToken {\n // The market's last updated rewardTokenBorrowIndex or rewardTokenSupplyIndex\n uint224 index;\n // The block number the index was last updated at\n uint32 block;\n // The block number at which to stop rewards\n uint32 lastRewardingBlock;\n }\n\n struct TimeBasedRewardToken {\n // The market's last updated rewardTokenBorrowIndex or rewardTokenSupplyIndex\n uint224 index;\n // The block timestamp the index was last updated at\n uint256 timestamp;\n // The block timestamp at which to stop rewards\n uint256 lastRewardingTimestamp;\n }\n\n /// @notice The REWARD TOKEN market supply state for each market\n mapping(address => RewardToken) public rewardTokenSupplyState;\n\n /// @notice The REWARD TOKEN borrow index for each market for each supplier as of the last time they accrued REWARD TOKEN\n mapping(address => mapping(address => uint256)) public rewardTokenSupplierIndex;\n\n /// @notice The REWARD TOKEN accrued but not yet transferred to each user\n mapping(address => uint256) public rewardTokenAccrued;\n\n /// @notice The rate at which rewardToken is distributed to the corresponding borrow market per slot (block or second)\n mapping(address => uint256) public rewardTokenBorrowSpeeds;\n\n /// @notice The rate at which rewardToken is distributed to the corresponding supply market per slot (block or second)\n mapping(address => uint256) public rewardTokenSupplySpeeds;\n\n /// @notice The REWARD TOKEN market borrow state for each market\n mapping(address => RewardToken) public rewardTokenBorrowState;\n\n /// @notice The portion of REWARD TOKEN that each contributor receives per slot (block or second)\n mapping(address => uint256) public rewardTokenContributorSpeeds;\n\n /// @notice Last slot (block or second) at which a contributor's REWARD TOKEN rewards have been allocated\n mapping(address => uint256) public lastContributorBlock;\n\n /// @notice The REWARD TOKEN borrow index for each market for each borrower as of the last time they accrued REWARD TOKEN\n mapping(address => mapping(address => uint256)) public rewardTokenBorrowerIndex;\n\n Comptroller internal comptroller;\n\n IERC20Upgradeable public rewardToken;\n\n /// @notice The REWARD TOKEN market supply state for each market\n mapping(address => TimeBasedRewardToken) public rewardTokenSupplyStateTimeBased;\n\n /// @notice The REWARD TOKEN market borrow state for each market\n mapping(address => TimeBasedRewardToken) public rewardTokenBorrowStateTimeBased;\n\n /**\n * @dev This empty reserved space is put in place to allow future versions to add new\n * variables without shifting down storage in the inheritance chain\n * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\n */\n uint256[37] private __gap;\n}\n" + }, + "contracts/Spoke/SpokeComptroller.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\n\nimport { Ownable2StepUpgradeable } from \"@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol\";\nimport { ResilientOracleInterface } from \"@venusprotocol/oracle/contracts/interfaces/OracleInterface.sol\";\nimport { IDeviationBoundedOracle } from \"@venusprotocol/oracle/contracts/interfaces/IDeviationBoundedOracle.sol\";\nimport { AccessControlledV8 } from \"@venusprotocol/governance-contracts/contracts/Governance/AccessControlledV8.sol\";\n\nimport { Action } from \"../ComptrollerInterface.sol\";\nimport { SpokeComptrollerInterface } from \"./SpokeComptrollerInterface.sol\";\nimport { SpokeComptrollerStorage } from \"./SpokeComptrollerStorage.sol\";\nimport { ExponentialNoError } from \"../ExponentialNoError.sol\";\nimport { VToken } from \"../VToken.sol\";\nimport { RewardsDistributor } from \"../Rewards/RewardsDistributor.sol\";\nimport { MaxLoopsLimitHelper } from \"../MaxLoopsLimitHelper.sol\";\nimport { ensureNonzeroAddress } from \"../lib/validators.sol\";\n\n/// @notice Which per-market risk parameter weights an account's collateral in a liquidity snapshot. Internal to the\n/// implementation: no event, error or external function exposes it, so it is deliberately not part of\n/// `SpokeComptrollerInterface`.\nenum WeightFunction {\n USE_COLLATERAL_FACTOR,\n USE_LIQUIDATION_THRESHOLD\n}\n\n/**\n * @title SpokeComptroller\n * @author Venus\n * @notice The `SpokeComptroller` provides checks for all minting, redeeming, transferring, borrowing, repaying,\n * liquidating and seizing done by the `vToken` contract. It is the comptroller of a single pool and checks those\n * interactions across every market in it: when a user interacts with a market by one of these actions, the market\n * calls a corresponding hook here, which either allows or reverts the transaction. These hooks also update supply and\n * borrow rewards as they are called. The comptroller holds the logic for assessing liquidity snapshots of an account\n * via the collateral factor and liquidation threshold. This check determines the collateral needed for a borrow, as\n * well as how much of a borrow may be liquidated. A user may borrow a portion of their collateral with the maximum\n * amount determined by the market's collateral factor, applied to a collateral value the deviation-bounded oracle\n * caps against the asset's recent price window. However, if their borrowed amount exceeds an amount calculated using\n * the market's corresponding liquidation threshold, the borrow is eligible for liquidation. Liquidations themselves\n * are priced at spot.\n *\n * The `SpokeComptroller` also includes two functions `liquidateAccount()` and `healAccount()`, which are meant to\n * handle accounts that do not exceed the `minLiquidatableCollateral` for the `SpokeComptroller`:\n *\n * - `healAccount()`: This function is called to seize all of a given user's collateral, requiring the `msg.sender`\n * repay a certain percentage of the debt calculated by `maxClearableDebt/borrows`, where `maxClearableDebt` is the\n * sum over the user's collateral markets of `collateralValue/liquidationIncentive`, each market taken at its own\n * incentive. The function can only be called if the calculated percentage does not exceed 100%, because otherwise no\n * `badDebt` would be created and `liquidateAccount()` should be used instead. The difference in the actual amount of\n * debt and debt paid off is recorded as `badDebt` for each market, which can then be auctioned off for the risk\n * reserves of the pool.\n * - `liquidateAccount()`: This function can only be called if the collateral seized will cover all borrows of an\n * account, as well as the liquidation incentive of each collateral market, which is the same condition stated as\n * `borrows < maxClearableDebt`. Otherwise, the pool will incur bad debt, in which case the function `healAccount()`\n * should be used instead. This function skips the logic verifying that the repay amount does not exceed the close\n * factor.\n *\n * The two conditions are complements, so every account below `minLiquidatableCollateral` is served by exactly one of\n * them.\n *\n * @dev Fork of `Comptroller` (`contracts/Comptroller.sol`). It is a separate implementation because `Comptroller` is\n * the one every other pool in this repo shares, here and on other chains, so policy that applies only to a spoke pool\n * does not belong in it: bounded collateral pricing, the supply and liquidation allowlists, and per-market liquidation\n * incentives. Nothing keeps the two in sync automatically: a change to `Comptroller` has to be reviewed and\n * re-applied here by hand, and `diff contracts/Comptroller.sol contracts/Spoke/SpokeComptroller.sol` shows what this\n * fork currently changes.\n */\ncontract SpokeComptroller is\n Ownable2StepUpgradeable,\n AccessControlledV8,\n SpokeComptrollerStorage,\n SpokeComptrollerInterface,\n ExponentialNoError,\n MaxLoopsLimitHelper\n{\n // PoolRegistry, immutable to save on gas\n /// @custom:oz-upgrades-unsafe-allow state-variable-immutable\n address public immutable poolRegistry;\n\n /// @param poolRegistry_ Pool registry address\n /// @custom:oz-upgrades-unsafe-allow constructor\n /// @custom:error ZeroAddressNotAllowed is thrown when pool registry address is zero\n constructor(address poolRegistry_) {\n ensureNonzeroAddress(poolRegistry_);\n\n poolRegistry = poolRegistry_;\n _disableInitializers();\n }\n\n /**\n * @param loopLimit Limit for the loops can iterate to avoid the DOS\n * @param accessControlManager Access control manager contract address\n */\n function initialize(uint256 loopLimit, address accessControlManager) external initializer {\n __Ownable2Step_init();\n __AccessControlled_init_unchained(accessControlManager);\n\n _setMaxLoopsLimit(loopLimit);\n }\n\n /**\n * @notice Add assets to be included in account liquidity calculation; enabling them to be used as collateral\n * @param vTokens The list of addresses of the vToken markets to be enabled\n * @return errors An array of NO_ERROR for compatibility with Venus core tooling\n * @custom:event MarketEntered is emitted for each market on success\n * @custom:error ActionPaused error is thrown if entering any of the markets is paused\n * @custom:error MarketNotListed error is thrown if any of the markets is not listed\n * @custom:access Not restricted\n */\n function enterMarkets(address[] memory vTokens) external override returns (uint256[] memory) {\n uint256 len = vTokens.length;\n\n uint256[] memory results = new uint256[](len);\n for (uint256 i; i < len; ++i) {\n VToken vToken = VToken(vTokens[i]);\n\n _addToMarket(vToken, msg.sender);\n results[i] = NO_ERROR;\n }\n\n return results;\n }\n\n /**\n * @notice Add an asset to another account's liquidity calculation, enabling it as collateral for that account\n * @dev `enterMarkets` reads `msg.sender`, so a router supplying through `VToken.mintBehalf` would enter itself\n * rather than the supplier. This enters the supplier, so both steps fit in one user transaction. Grant the\n * permission only to a router that passes its own caller as `account`.\n * @param vToken The address of the vToken market to be enabled\n * @param account The account to enable the market for\n * @custom:event MarketEntered is emitted on success\n * @custom:error ActionPaused error is thrown if entering the market is paused\n * @custom:error MarketNotListed error is thrown if the market is not listed\n * @custom:error ZeroAddressNotAllowed is thrown when the account address is zero\n * @custom:access Controlled by AccessControlManager\n */\n function enterMarketBehalf(address vToken, address account) external {\n _checkAccessAllowed(\"enterMarketBehalf(address,address)\");\n ensureNonzeroAddress(account);\n\n _addToMarket(VToken(vToken), account);\n }\n\n /**\n * @notice Unlist a market by setting isListed to false\n * @dev Checks if all actions are paused, borrow/supply caps is set to 0 and collateral factor is to 0.\n * @param market The address of the market (token) to unlist\n * @return uint256 Always NO_ERROR for compatibility with Venus core tooling\n * @custom:event MarketUnlisted is emitted on success\n * @custom:error MarketNotListed error is thrown when the market is not listed\n * @custom:error BorrowActionNotPaused error is thrown if borrow action is not paused\n * @custom:error MintActionNotPaused error is thrown if mint action is not paused\n * @custom:error RedeemActionNotPaused error is thrown if redeem action is not paused\n * @custom:error RepayActionNotPaused error is thrown if repay action is not paused\n * @custom:error EnterMarketActionNotPaused error is thrown if enter market action is not paused\n * @custom:error LiquidateActionNotPaused error is thrown if liquidate action is not paused\n * @custom:error BorrowCapIsNotZero error is thrown if borrow cap is not zero\n * @custom:error SupplyCapIsNotZero error is thrown if supply cap is not zero\n * @custom:error CollateralFactorIsNotZero error is thrown if collateral factor is not zero\n */\n function unlistMarket(address market) external returns (uint256) {\n _checkAccessAllowed(\"unlistMarket(address)\");\n\n Market storage _market = markets[market];\n\n if (!_market.isListed) {\n revert MarketNotListed(market);\n }\n\n if (!actionPaused(market, Action.BORROW)) {\n revert BorrowActionNotPaused();\n }\n\n if (!actionPaused(market, Action.MINT)) {\n revert MintActionNotPaused();\n }\n\n if (!actionPaused(market, Action.REDEEM)) {\n revert RedeemActionNotPaused();\n }\n\n if (!actionPaused(market, Action.REPAY)) {\n revert RepayActionNotPaused();\n }\n\n if (!actionPaused(market, Action.SEIZE)) {\n revert SeizeActionNotPaused();\n }\n\n if (!actionPaused(market, Action.ENTER_MARKET)) {\n revert EnterMarketActionNotPaused();\n }\n\n if (!actionPaused(market, Action.LIQUIDATE)) {\n revert LiquidateActionNotPaused();\n }\n\n if (!actionPaused(market, Action.TRANSFER)) {\n revert TransferActionNotPaused();\n }\n\n if (!actionPaused(market, Action.EXIT_MARKET)) {\n revert ExitMarketActionNotPaused();\n }\n\n if (borrowCaps[market] != 0) {\n revert BorrowCapIsNotZero();\n }\n\n if (supplyCaps[market] != 0) {\n revert SupplyCapIsNotZero();\n }\n\n if (_market.collateralFactorMantissa != 0) {\n revert CollateralFactorIsNotZero();\n }\n\n _market.isListed = false;\n emit MarketUnlisted(market);\n\n return NO_ERROR;\n }\n\n /**\n * @notice Grants or revokes the borrowing or redeeming delegate rights to / from an account\n * If allowed, the delegate will be able to borrow funds on behalf of the sender\n * Upon a delegated borrow, the delegate will receive the funds, and the borrower\n * will see the debt on their account\n * Upon a delegated redeem, the delegate will receive the redeemed amount and the approver\n * will see a deduction in his vToken balance\n * @param delegate The address to update the rights for\n * @param approved Whether to grant (true) or revoke (false) the borrowing or redeeming rights\n * @custom:event DelegateUpdated emits on success\n * @custom:error ZeroAddressNotAllowed is thrown when delegate address is zero\n * @custom:error DelegationStatusUnchanged is thrown if approval status is already set to the requested value\n * @custom:access Not restricted\n */\n function updateDelegate(address delegate, bool approved) external {\n ensureNonzeroAddress(delegate);\n if (approvedDelegates[msg.sender][delegate] == approved) {\n revert DelegationStatusUnchanged();\n }\n\n approvedDelegates[msg.sender][delegate] = approved;\n emit DelegateUpdated(msg.sender, delegate, approved);\n }\n\n /**\n * @notice Removes asset from sender's account liquidity calculation; disabling them as collateral\n * @dev Sender must not have an outstanding borrow balance in the asset,\n * or be providing necessary collateral for an outstanding borrow.\n * @param vTokenAddress The address of the asset to be removed\n * @return error Always NO_ERROR for compatibility with Venus core tooling\n * @custom:event MarketExited is emitted on success\n * @custom:error ActionPaused error is thrown if exiting the market is paused\n * @custom:error NonzeroBorrowBalance error is thrown if the user has an outstanding borrow in this market\n * @custom:error MarketNotListed error is thrown when the market is not listed\n * @custom:error InsufficientLiquidity error is thrown if exiting the market would lead to user's insolvency\n * @custom:error SnapshotError is thrown if some vToken fails to return the account's supply and borrows\n * @custom:error PriceError is thrown if the oracle returns an incorrect price for some asset\n * @custom:access Not restricted\n */\n function exitMarket(address vTokenAddress) external override returns (uint256) {\n _checkActionPauseState(vTokenAddress, Action.EXIT_MARKET);\n VToken vToken = VToken(vTokenAddress);\n /* Get sender tokensHeld and amountOwed underlying from the vToken */\n (uint256 tokensHeld, uint256 amountOwed, ) = _safeGetAccountSnapshot(vToken, msg.sender);\n\n /* Fail if the sender has a borrow balance */\n if (amountOwed != 0) {\n revert NonzeroBorrowBalance();\n }\n\n /* Fail if the sender is not permitted to redeem all of their tokens */\n _checkRedeemAllowed(vTokenAddress, msg.sender, tokensHeld);\n\n Market storage marketToExit = markets[address(vToken)];\n\n /* Return true if the sender is not already ‘in’ the market */\n if (!marketToExit.accountMembership[msg.sender]) {\n return NO_ERROR;\n }\n\n /* Set vToken account membership to false */\n delete marketToExit.accountMembership[msg.sender];\n\n /* Delete vToken from the account’s list of assets */\n // load into memory for faster iteration\n VToken[] memory userAssetList = accountAssets[msg.sender];\n uint256 len = userAssetList.length;\n\n uint256 assetIndex = len;\n for (uint256 i; i < len; ++i) {\n if (userAssetList[i] == vToken) {\n assetIndex = i;\n break;\n }\n }\n\n // We *must* have found the asset in the list or our redundant data structure is broken\n assert(assetIndex < len);\n\n // copy last item in list to location of item to be removed, reduce length by 1\n VToken[] storage storedList = accountAssets[msg.sender];\n storedList[assetIndex] = storedList[storedList.length - 1];\n storedList.pop();\n\n emit MarketExited(vToken, msg.sender);\n\n return NO_ERROR;\n }\n\n /*** Policy Hooks ***/\n\n /**\n * @notice Checks if the account should be allowed to mint tokens in the given market\n * @param vToken The market to verify the mint against\n * @param minter The account which would get the minted tokens\n * @param mintAmount The amount of underlying being supplied to the market in exchange for tokens\n * @custom:error ActionPaused error is thrown if supplying to this market is paused\n * @custom:error MarketNotListed error is thrown when the market is not listed\n * @custom:error SupplyNotAllowed error is thrown if the market's supply allowlist is enabled and the minter is\n * not on it\n * @custom:error SupplyCapExceeded error is thrown if the total supply exceeds the cap after minting\n * @custom:access Not restricted\n */\n function preMintHook(address vToken, address minter, uint256 mintAmount) external override {\n _checkActionPauseState(vToken, Action.MINT);\n\n if (!markets[vToken].isListed) {\n revert MarketNotListed(address(vToken));\n }\n\n // `minter` is the account credited with the newly minted vTokens, not necessarily the account paying for\n // them: `mintBehalf` lets a third party fund a mint attributed to someone else. Metering the recipient is\n // what bounds the market's supply, so the payer is deliberately not checked.\n if (isSupplyAllowlistEnabled[vToken] && !isAllowedSupplier[vToken][minter]) {\n revert SupplyNotAllowed(vToken, minter);\n }\n\n uint256 supplyCap = supplyCaps[vToken];\n // Skipping the cap check for uncapped coins to save some gas\n if (supplyCap != type(uint256).max) {\n uint256 vTokenSupply = VToken(vToken).totalSupply();\n Exp memory exchangeRate = Exp({ mantissa: VToken(vToken).exchangeRateStored() });\n uint256 nextTotalSupply = mul_ScalarTruncateAddUInt(exchangeRate, vTokenSupply, mintAmount);\n if (nextTotalSupply > supplyCap) {\n revert SupplyCapExceeded(vToken, supplyCap);\n }\n }\n\n // Keep the flywheel moving\n uint256 rewardDistributorsCount = rewardsDistributors.length;\n\n for (uint256 i; i < rewardDistributorsCount; ++i) {\n RewardsDistributor rewardsDistributor = rewardsDistributors[i];\n rewardsDistributor.updateRewardTokenSupplyIndex(vToken);\n rewardsDistributor.distributeSupplierRewardToken(vToken, minter);\n }\n }\n\n /**\n * @notice Checks if the account should be allowed to redeem tokens in the given market\n * @param vToken The market to verify the redeem against\n * @param redeemer The account which would redeem the tokens\n * @param redeemTokens The number of vTokens to exchange for the underlying asset in the market\n * @custom:error ActionPaused error is thrown if withdrawals are paused in this market\n * @custom:error MarketNotListed error is thrown when the market is not listed\n * @custom:error InsufficientLiquidity error is thrown if the withdrawal would lead to user's insolvency\n * @custom:error SnapshotError is thrown if some vToken fails to return the account's supply and borrows\n * @custom:error PriceError is thrown if the oracle returns an incorrect price for some asset\n * @custom:access Not restricted\n */\n function preRedeemHook(address vToken, address redeemer, uint256 redeemTokens) external override {\n _checkActionPauseState(vToken, Action.REDEEM);\n\n _checkRedeemAllowed(vToken, redeemer, redeemTokens);\n\n // Keep the flywheel moving\n uint256 rewardDistributorsCount = rewardsDistributors.length;\n\n for (uint256 i; i < rewardDistributorsCount; ++i) {\n RewardsDistributor rewardsDistributor = rewardsDistributors[i];\n rewardsDistributor.updateRewardTokenSupplyIndex(vToken);\n rewardsDistributor.distributeSupplierRewardToken(vToken, redeemer);\n }\n }\n\n /**\n * @notice Checks if the account should be allowed to borrow the underlying asset of the given market\n * @param vToken The market to verify the borrow against\n * @param borrower The account which would borrow the asset\n * @param borrowAmount The amount of underlying the account would borrow\n * @custom:error ActionPaused error is thrown if borrowing is paused in this market\n * @custom:error MarketNotListed error is thrown when the market is not listed\n * @custom:error InsufficientLiquidity error is thrown if there is not enough collateral to borrow\n * @custom:error BorrowCapExceeded is thrown if the borrow cap will be exceeded should this borrow succeed\n * @custom:error SnapshotError is thrown if some vToken fails to return the account's supply and borrows\n * @custom:error PriceError is thrown if the oracle returns an incorrect price for some asset\n * @custom:access Not restricted if vToken is enabled as collateral, otherwise only vToken\n */\n /// disable-eslint\n function preBorrowHook(address vToken, address borrower, uint256 borrowAmount) external override {\n _checkActionPauseState(vToken, Action.BORROW);\n\n if (!markets[vToken].isListed) {\n revert MarketNotListed(address(vToken));\n }\n\n if (!markets[vToken].accountMembership[borrower]) {\n // only vTokens may call borrowAllowed if borrower not in market\n _checkSenderIs(vToken);\n\n // attempt to add borrower to the market or revert\n _addToMarket(VToken(msg.sender), borrower);\n }\n\n // Update the prices of tokens. Resolved once here, after the membership change above, and reused for both\n // updates.\n VToken[] memory borrowerAssets = getAssetsIn(borrower);\n _updatePrices(borrowerAssets);\n _updateProtectionStates(borrowerAssets);\n\n if (oracle.getUnderlyingPrice(vToken) == 0) {\n revert PriceError(address(vToken));\n }\n\n uint256 borrowCap = borrowCaps[vToken];\n // Skipping the cap check for uncapped coins to save some gas\n if (borrowCap != type(uint256).max) {\n uint256 totalBorrows = VToken(vToken).totalBorrows();\n uint256 badDebt = VToken(vToken).badDebt();\n uint256 nextTotalBorrows = totalBorrows + borrowAmount + badDebt;\n if (nextTotalBorrows > borrowCap) {\n revert BorrowCapExceeded(vToken, borrowCap);\n }\n }\n\n AccountLiquiditySnapshot memory snapshot = _getHypotheticalLiquiditySnapshot(\n borrower,\n VToken(vToken),\n 0,\n borrowAmount,\n WeightFunction.USE_COLLATERAL_FACTOR\n );\n\n if (snapshot.shortfall > 0) {\n revert InsufficientLiquidity();\n }\n\n Exp memory borrowIndex = Exp({ mantissa: VToken(vToken).borrowIndex() });\n\n // Keep the flywheel moving\n uint256 rewardDistributorsCount = rewardsDistributors.length;\n\n for (uint256 i; i < rewardDistributorsCount; ++i) {\n RewardsDistributor rewardsDistributor = rewardsDistributors[i];\n rewardsDistributor.updateRewardTokenBorrowIndex(vToken, borrowIndex);\n rewardsDistributor.distributeBorrowerRewardToken(vToken, borrower, borrowIndex);\n }\n }\n\n /**\n * @notice Checks if the account should be allowed to repay a borrow in the given market\n * @param vToken The market to verify the repay against\n * @param borrower The account which would borrowed the asset\n * @custom:error ActionPaused error is thrown if repayments are paused in this market\n * @custom:error MarketNotListed error is thrown when the market is not listed\n * @custom:access Not restricted\n */\n function preRepayHook(address vToken, address borrower) external override {\n _checkActionPauseState(vToken, Action.REPAY);\n\n oracle.updatePrice(vToken);\n\n if (!markets[vToken].isListed) {\n revert MarketNotListed(address(vToken));\n }\n\n // Keep the flywheel moving\n uint256 rewardDistributorsCount = rewardsDistributors.length;\n\n for (uint256 i; i < rewardDistributorsCount; ++i) {\n Exp memory borrowIndex = Exp({ mantissa: VToken(vToken).borrowIndex() });\n RewardsDistributor rewardsDistributor = rewardsDistributors[i];\n rewardsDistributor.updateRewardTokenBorrowIndex(vToken, borrowIndex);\n rewardsDistributor.distributeBorrowerRewardToken(vToken, borrower, borrowIndex);\n }\n }\n\n /**\n * @notice Checks if the liquidation should be allowed to occur\n * @param vTokenBorrowed Asset which was borrowed by the borrower\n * @param vTokenCollateral Asset which was used as collateral and will be seized\n * @param borrower The address of the borrower\n * @param repayAmount The amount of underlying being repaid\n * @param skipLiquidityCheck Allows the borrow to be liquidated regardless of the account liquidity\n * @custom:error ActionPaused error is thrown if liquidations are paused in this market\n * @custom:error MarketNotListed error is thrown if either collateral or borrowed token is not listed\n * @custom:error TooMuchRepay error is thrown if the liquidator is trying to repay more than allowed by close factor\n * @custom:error MinimalCollateralViolated is thrown if the users' total collateral is lower than the threshold for non-batch liquidations\n * @custom:error InsufficientShortfall is thrown when trying to liquidate a healthy account\n * @custom:error SnapshotError is thrown if some vToken fails to return the account's supply and borrows\n * @custom:error PriceError is thrown if the oracle returns an incorrect price for some asset\n */\n function preLiquidateHook(\n address vTokenBorrowed,\n address vTokenCollateral,\n address borrower,\n uint256 repayAmount,\n bool skipLiquidityCheck\n ) external override {\n // Pause Action.LIQUIDATE on BORROWED TOKEN to prevent liquidating it.\n // If we want to pause liquidating to vTokenCollateral, we should pause\n // Action.SEIZE on it\n _checkActionPauseState(vTokenBorrowed, Action.LIQUIDATE);\n\n // Update the prices of tokens\n updatePrices(borrower);\n\n if (!markets[vTokenBorrowed].isListed) {\n revert MarketNotListed(address(vTokenBorrowed));\n }\n if (!markets[vTokenCollateral].isListed) {\n revert MarketNotListed(address(vTokenCollateral));\n }\n\n uint256 borrowBalance = VToken(vTokenBorrowed).borrowBalanceStored(borrower);\n\n /* Allow accounts to be liquidated if it is a forced liquidation */\n if (skipLiquidityCheck || isForcedLiquidationEnabled[vTokenBorrowed]) {\n if (repayAmount > borrowBalance) {\n revert TooMuchRepay();\n }\n return;\n }\n\n /* The borrower must have shortfall and collateral > threshold in order to be liquidatable */\n AccountLiquiditySnapshot memory snapshot = _getCurrentLiquiditySnapshot(\n borrower,\n WeightFunction.USE_LIQUIDATION_THRESHOLD\n );\n\n if (snapshot.totalCollateral <= minLiquidatableCollateral) {\n /* The liquidator should use either liquidateAccount or healAccount */\n revert MinimalCollateralViolated(minLiquidatableCollateral, snapshot.totalCollateral);\n }\n\n if (snapshot.shortfall == 0) {\n revert InsufficientShortfall();\n }\n\n /* The liquidator may not repay more than what is allowed by the closeFactor */\n uint256 maxClose = mul_ScalarTruncate(Exp({ mantissa: closeFactorMantissa }), borrowBalance);\n if (repayAmount > maxClose) {\n revert TooMuchRepay();\n }\n }\n\n /**\n * @notice Checks if the seizing of assets should be allowed to occur\n * @param vTokenCollateral Asset which was used as collateral and will be seized\n * @param seizerContract Contract that tries to seize the asset (either borrowed vToken or Comptroller)\n * @param liquidator The address repaying the borrow and seizing the collateral\n * @param borrower The address of the borrower\n * @custom:error ActionPaused error is thrown if seizing this type of collateral is paused\n * @custom:error MarketNotListed error is thrown if either collateral or borrowed token is not listed\n * @custom:error ComptrollerMismatch error is when seizer contract or seized asset belong to different pools\n * @custom:error LiquidationNotAllowed is thrown if the liquidation allowlist is enabled and the liquidator is not\n * on it\n * @custom:access Not restricted while the liquidation allowlist is disabled, otherwise the liquidator has to be\n * on it\n */\n function preSeizeHook(\n address vTokenCollateral,\n address seizerContract,\n address liquidator,\n address borrower\n ) external override {\n // Pause Action.SEIZE on COLLATERAL to prevent seizing it.\n // If we want to pause liquidating vTokenBorrowed, we should pause\n // Action.LIQUIDATE on it\n _checkActionPauseState(vTokenCollateral, Action.SEIZE);\n\n Market storage market = markets[vTokenCollateral];\n\n if (!market.isListed) {\n revert MarketNotListed(vTokenCollateral);\n }\n\n if (seizerContract == address(this)) {\n // If Comptroller is the seizer, just check if collateral's comptroller\n // is equal to the current address\n if (address(VToken(vTokenCollateral).comptroller()) != address(this)) {\n revert ComptrollerMismatch();\n }\n } else {\n // If the seizer is not the Comptroller, check that the seizer is a\n // listed market, and that the markets' comptrollers match\n if (!markets[seizerContract].isListed) {\n revert MarketNotListed(seizerContract);\n }\n if (VToken(vTokenCollateral).comptroller() != VToken(seizerContract).comptroller()) {\n revert ComptrollerMismatch();\n }\n }\n\n if (!market.accountMembership[borrower]) {\n revert MarketNotCollateral(vTokenCollateral, borrower);\n }\n\n // Every seizure reaches this hook with the account that receives the collateral, whichever entry point it\n // came from, so this is the check that actually enforces the allowlist.\n _checkLiquidatorAllowlisted(liquidator);\n\n // Keep the flywheel moving\n uint256 rewardDistributorsCount = rewardsDistributors.length;\n\n for (uint256 i; i < rewardDistributorsCount; ++i) {\n RewardsDistributor rewardsDistributor = rewardsDistributors[i];\n rewardsDistributor.updateRewardTokenSupplyIndex(vTokenCollateral);\n rewardsDistributor.distributeSupplierRewardToken(vTokenCollateral, borrower);\n rewardsDistributor.distributeSupplierRewardToken(vTokenCollateral, liquidator);\n }\n }\n\n /**\n * @notice Checks if the account should be allowed to transfer tokens in the given market\n * @param vToken The market to verify the transfer against\n * @param src The account which sources the tokens\n * @param dst The account which receives the tokens\n * @param transferTokens The number of vTokens to transfer\n * @custom:error ActionPaused error is thrown if withdrawals are paused in this market\n * @custom:error MarketNotListed error is thrown when the market is not listed\n * @custom:error InsufficientLiquidity error is thrown if the withdrawal would lead to user's insolvency\n * @custom:error SnapshotError is thrown if some vToken fails to return the account's supply and borrows\n * @custom:error PriceError is thrown if the oracle returns an incorrect price for some asset\n * @custom:access Not restricted\n */\n function preTransferHook(address vToken, address src, address dst, uint256 transferTokens) external override {\n _checkActionPauseState(vToken, Action.TRANSFER);\n\n // Currently the only consideration is whether or not\n // the src is allowed to redeem this many tokens\n _checkRedeemAllowed(vToken, src, transferTokens);\n\n // Keep the flywheel moving\n uint256 rewardDistributorsCount = rewardsDistributors.length;\n\n for (uint256 i; i < rewardDistributorsCount; ++i) {\n RewardsDistributor rewardsDistributor = rewardsDistributors[i];\n rewardsDistributor.updateRewardTokenSupplyIndex(vToken);\n rewardsDistributor.distributeSupplierRewardToken(vToken, src);\n rewardsDistributor.distributeSupplierRewardToken(vToken, dst);\n }\n }\n\n /*** Post-action Hooks ***/\n\n // The vToken calls one of the seven functions below as the last step of every successful mint, redeem, borrow,\n // repayment, liquidation, seizure and transfer. All of them are no-ops here, and none of them can be dropped:\n // `ComptrollerInterface` declares all seven, so omitting one leaves this contract abstract, and the vToken calls\n // each of them with a plain external call against a comptroller that has no fallback, so a missing function would\n // revert the operation it belongs to. They are unrestricted, which is harmless because they do nothing, and their\n // parameters are unnamed because the bodies are empty.\n // solhint-disable no-empty-blocks\n\n /// @notice Called by the vToken once a mint has succeeded. No-op, see the note above.\n function mintVerify(address, address, uint256, uint256) external {}\n\n /// @notice Called by the vToken once a redeem has succeeded. No-op, see the note above.\n function redeemVerify(address, address, uint256, uint256) external {}\n\n /// @notice Called by the vToken once a borrow has succeeded. No-op, see the note above.\n function borrowVerify(address, address, uint256) external {}\n\n /// @notice Called by the vToken once a repayment has succeeded. No-op, see the note above.\n function repayBorrowVerify(address, address, address, uint256, uint256) external {}\n\n /// @notice Called by the vToken once a liquidation has succeeded. No-op, see the note above.\n function liquidateBorrowVerify(address, address, address, address, uint256, uint256) external {}\n\n /// @notice Called by the vToken once a seizure has succeeded. No-op, see the note above.\n function seizeVerify(address, address, address, address, uint256) external {}\n\n /// @notice Called by the vToken once a transfer has succeeded. No-op, see the note above.\n function transferVerify(address, address, address, uint256) external {}\n\n // solhint-enable no-empty-blocks\n\n /*** Pool-level operations ***/\n\n /**\n * @notice Seizes all the remaining collateral, makes msg.sender repay the existing\n * borrows, and treats the rest of the debt as bad debt (for each market).\n * The sender has to repay a certain percentage of the debt, computed as `maxClearableDebt / borrows`: see the\n * note on `AccountLiquiditySnapshot.maxClearableDebt`.\n * @param user account to heal\n * @custom:error LiquidationNotAllowed is thrown if the liquidation allowlist is enabled and the caller is not on it\n * @custom:error CollateralExceedsThreshold error is thrown when the collateral is too big for healing\n * @custom:error CollateralCoversDebt is thrown when the collateral can clear the whole debt, which leaves nothing\n * to heal\n * @custom:error SnapshotError is thrown if some vToken fails to return the account's supply and borrows\n * @custom:error PriceError is thrown if the oracle returns an incorrect price for some asset\n * @custom:access Not restricted while the liquidation allowlist is disabled, otherwise restricted to the accounts\n * on it\n */\n function healAccount(address user) external {\n // Checked here as well as in `preSeizeHook`, because a borrower holding no vTokens at all takes the branch\n // below that only calls `healBorrow`, which reaches no hook carrying the caller. Without this the whole\n // remaining principal could be moved into bad debt by anyone, at no cost.\n _checkLiquidatorAllowlisted(msg.sender);\n\n VToken[] memory userAssets = getAssetsIn(user);\n uint256 userAssetsCount = userAssets.length;\n\n address liquidator = msg.sender;\n\n // We need all user's markets to be fresh for the computations to be correct\n _refreshMarkets(userAssets);\n\n AccountLiquiditySnapshot memory snapshot = _getCurrentLiquiditySnapshot(\n user,\n WeightFunction.USE_LIQUIDATION_THRESHOLD\n );\n\n if (snapshot.totalCollateral > minLiquidatableCollateral) {\n revert CollateralExceedsThreshold(minLiquidatableCollateral, snapshot.totalCollateral);\n }\n\n if (snapshot.shortfall == 0) {\n revert InsufficientShortfall();\n }\n\n // The collateral covers the whole debt at every market's own incentive, so healing would forgive nothing and\n // the account should go through `liquidateAccount` instead.\n if (snapshot.maxClearableDebt > snapshot.borrows) {\n revert CollateralCoversDebt(snapshot.borrows, snapshot.maxClearableDebt);\n }\n\n // percentage = maxClearableDebt / borrows. One blended share applies to every borrow, so what the caller\n // pays in total is the sum over the collateral markets of each market's value at its own liquidation\n // incentive. The discount is exact in aggregate; it is not attributed per piece of collateral.\n Exp memory percentage = div_(Exp({ mantissa: snapshot.maxClearableDebt }), Exp({ mantissa: snapshot.borrows }));\n\n for (uint256 i; i < userAssetsCount; ++i) {\n VToken market = userAssets[i];\n\n (uint256 tokens, uint256 borrowBalance, ) = _safeGetAccountSnapshot(market, user);\n uint256 repaymentAmount = mul_ScalarTruncate(percentage, borrowBalance);\n\n // Seize the entire collateral\n if (tokens != 0) {\n market.seize(liquidator, user, tokens);\n }\n // Repay a certain percentage of the borrow, forgive the rest\n if (borrowBalance != 0) {\n market.healBorrow(liquidator, user, repaymentAmount);\n }\n }\n }\n\n /**\n * @notice Liquidates all borrows of the borrower. Callable only if the collateral is less than\n * a predefined threshold, and the account collateral can be seized to cover all borrows. If\n * the collateral is higher than the threshold, use regular liquidations. If the collateral is\n * below the threshold, and the account is insolvent, use healAccount.\n * @param borrower the borrower address\n * @param orders an array of liquidation orders\n * @custom:error LiquidationNotAllowed is thrown by `preSeizeHook`, while seizing for the first order, if the\n * liquidation allowlist is enabled and the caller is not on it\n * @custom:error CollateralExceedsThreshold error is thrown when the collateral is too big for a batch liquidation\n * @custom:error DebtExceedsClearableAmount is thrown when the collateral cannot clear the whole debt, which\n * means the account has to go through `healAccount`\n * @custom:error NonzeroBorrowBalanceAfterLiquidation is thrown if the orders do not clear every borrow\n * @custom:error SnapshotError is thrown if some vToken fails to return the account's supply and borrows\n * @custom:error PriceError is thrown if the oracle returns an incorrect price for some asset\n * @custom:access Not restricted while the liquidation allowlist is disabled, otherwise restricted to the accounts\n * on it\n */\n function liquidateAccount(address borrower, LiquidationOrder[] calldata orders) external {\n // No entry check on the liquidation allowlist here, unlike `healAccount`: every order ends in a seizure and\n // `preSeizeHook` carries the caller, so the allowlist is enforced there.\n\n VToken[] memory borrowerAssets = getAssetsIn(borrower);\n\n // We need all borrower's markets to be fresh for the computations to be correct. The orders below run with\n // the liquidity check skipped, so this snapshot is the only thing standing between a solvent account and a\n // full liquidation, and it decides on the same state the orders will execute against.\n _refreshMarkets(borrowerAssets);\n\n AccountLiquiditySnapshot memory snapshot = _getCurrentLiquiditySnapshot(\n borrower,\n WeightFunction.USE_LIQUIDATION_THRESHOLD\n );\n\n if (snapshot.totalCollateral > minLiquidatableCollateral) {\n // You should use the regular vToken.liquidateBorrow(...) call\n revert CollateralExceedsThreshold(minLiquidatableCollateral, snapshot.totalCollateral);\n }\n\n if (snapshot.borrows >= snapshot.maxClearableDebt) {\n // There is not enough collateral to seize. Use healAccount to repay some part of the borrow\n // and record bad debt.\n revert DebtExceedsClearableAmount(snapshot.borrows, snapshot.maxClearableDebt);\n }\n\n if (snapshot.shortfall == 0) {\n revert InsufficientShortfall();\n }\n\n uint256 ordersCount = orders.length;\n\n _ensureMaxLoops(ordersCount / 2);\n\n for (uint256 i; i < ordersCount; ++i) {\n if (!markets[address(orders[i].vTokenBorrowed)].isListed) {\n revert MarketNotListed(address(orders[i].vTokenBorrowed));\n }\n if (!markets[address(orders[i].vTokenCollateral)].isListed) {\n revert MarketNotListed(address(orders[i].vTokenCollateral));\n }\n\n LiquidationOrder calldata order = orders[i];\n order.vTokenBorrowed.forceLiquidateBorrow(\n msg.sender,\n borrower,\n order.repayAmount,\n order.vTokenCollateral,\n true\n );\n }\n\n uint256 marketsCount = borrowerAssets.length;\n\n for (uint256 i; i < marketsCount; ++i) {\n (, uint256 borrowBalance, ) = _safeGetAccountSnapshot(borrowerAssets[i], borrower);\n if (borrowBalance != 0) {\n revert NonzeroBorrowBalanceAfterLiquidation();\n }\n }\n }\n\n /**\n * @notice Sets the closeFactor to use when liquidating borrows\n * @param newCloseFactorMantissa New close factor, scaled by 1e18\n * @custom:event Emits NewCloseFactor on success\n * @custom:error InvalidCloseFactor is thrown if the new close factor is outside the allowed bounds\n * @custom:access Controlled by AccessControlManager\n */\n function setCloseFactor(uint256 newCloseFactorMantissa) external {\n _checkAccessAllowed(\"setCloseFactor(uint256)\");\n if (newCloseFactorMantissa > MAX_CLOSE_FACTOR_MANTISSA) {\n revert InvalidCloseFactor();\n }\n\n if (newCloseFactorMantissa < MIN_CLOSE_FACTOR_MANTISSA) {\n revert InvalidCloseFactor();\n }\n\n uint256 oldCloseFactorMantissa = closeFactorMantissa;\n closeFactorMantissa = newCloseFactorMantissa;\n emit NewCloseFactor(oldCloseFactorMantissa, newCloseFactorMantissa);\n }\n\n /**\n * @notice Sets the collateralFactor for a market\n * @dev This function is restricted by the AccessControlManager\n * @param vToken The market to set the factor on\n * @param newCollateralFactorMantissa The new collateral factor, scaled by 1e18\n * @param newLiquidationThresholdMantissa The new liquidation threshold, scaled by 1e18\n * @custom:event Emits NewCollateralFactor when collateral factor is updated\n * and NewLiquidationThreshold when liquidation threshold is updated\n * @custom:error MarketNotListed error is thrown when the market is not listed\n * @custom:error InvalidCollateralFactor error is thrown when collateral factor is too high\n * @custom:error InvalidLiquidationThreshold error is thrown when liquidation threshold is lower than collateral factor\n * @custom:error PriceError is thrown when the oracle returns an invalid price for the asset\n * @custom:access Controlled by AccessControlManager\n */\n function setCollateralFactor(\n VToken vToken,\n uint256 newCollateralFactorMantissa,\n uint256 newLiquidationThresholdMantissa\n ) external {\n _checkAccessAllowed(\"setCollateralFactor(address,uint256,uint256)\");\n\n // Verify market is listed\n Market storage market = markets[address(vToken)];\n if (!market.isListed) {\n revert MarketNotListed(address(vToken));\n }\n\n // Check collateral factor <= 0.9\n if (newCollateralFactorMantissa > MAX_COLLATERAL_FACTOR_MANTISSA) {\n revert InvalidCollateralFactor();\n }\n\n // Ensure that liquidation threshold <= 1\n if (newLiquidationThresholdMantissa > MANTISSA_ONE) {\n revert InvalidLiquidationThreshold();\n }\n\n // Ensure that liquidation threshold >= CF\n if (newLiquidationThresholdMantissa < newCollateralFactorMantissa) {\n revert InvalidLiquidationThreshold();\n }\n\n // If collateral factor != 0, fail if price == 0\n if (newCollateralFactorMantissa != 0 && oracle.getUnderlyingPrice(address(vToken)) == 0) {\n revert PriceError(address(vToken));\n }\n\n uint256 oldCollateralFactorMantissa = market.collateralFactorMantissa;\n if (newCollateralFactorMantissa != oldCollateralFactorMantissa) {\n market.collateralFactorMantissa = newCollateralFactorMantissa;\n emit NewCollateralFactor(vToken, oldCollateralFactorMantissa, newCollateralFactorMantissa);\n }\n\n uint256 oldLiquidationThresholdMantissa = market.liquidationThresholdMantissa;\n if (newLiquidationThresholdMantissa != oldLiquidationThresholdMantissa) {\n market.liquidationThresholdMantissa = newLiquidationThresholdMantissa;\n emit NewLiquidationThreshold(vToken, oldLiquidationThresholdMantissa, newLiquidationThresholdMantissa);\n }\n }\n\n /**\n * @notice Sets the liquidation incentive applied to any market that has no incentive of its own\n * @dev This function is restricted by the AccessControlManager\n * @dev `PoolRegistry.addPool` calls this while registering the pool, so the value clears the floor below by the\n * time any market can be listed.\n *\n * This value has to stay at or above `1e18 + protocolSeizeShareMantissa` of every market that has no incentive of\n * its own, or a liquidator of that market's collateral receives less than the debt it repaid. The shares of those\n * markets are only readable market by market, so what is enforced here is the floor for a market at the default\n * share: `MIN_POOL_LIQUIDATION_INCENTIVE_MANTISSA`. That covers a freshly listed market, which is the case\n * nothing else was watching, and it is where this fork parts with upstream `Comptroller` - which stops at 1e18\n * and would let a pool be registered one that pays every default-share market's liquidator less than it repaid.\n * A market whose share is raised above the default still needs an incentive of its own;\n * `setMarketLiquidationIncentive` bounds that from one side and `VToken.setProtocolSeizeShare` from the other.\n * @param newLiquidationIncentiveMantissa New liquidationIncentive scaled by 1e18\n * @custom:event Emits NewLiquidationIncentive on success\n * @custom:error InvalidLiquidationIncentive is thrown if the new incentive is below\n * `MIN_POOL_LIQUIDATION_INCENTIVE_MANTISSA`\n * @custom:access Controlled by AccessControlManager\n */\n function setLiquidationIncentive(uint256 newLiquidationIncentiveMantissa) external {\n _checkAccessAllowed(\"setLiquidationIncentive(uint256)\");\n\n // Upstream `Comptroller` stops at 1e18 and rejects with a revert string. Raised to the floor a default-share\n // market needs, and reduced to the custom error the per-market setter uses, so that the same condition\n // reports the same way from both.\n if (newLiquidationIncentiveMantissa < MIN_POOL_LIQUIDATION_INCENTIVE_MANTISSA) {\n revert InvalidLiquidationIncentive();\n }\n\n // Save current value for use in log\n uint256 oldLiquidationIncentiveMantissa = _poolLiquidationIncentiveMantissa;\n\n // Set liquidation incentive to new incentive\n _poolLiquidationIncentiveMantissa = newLiquidationIncentiveMantissa;\n\n // Emit event with old incentive, new incentive\n emit NewLiquidationIncentive(oldLiquidationIncentiveMantissa, newLiquidationIncentiveMantissa);\n }\n\n /**\n * @notice Add the market to the markets mapping and set it as listed\n * @dev Only callable by the PoolRegistry\n * @param vToken The address of the market (token) to list\n * @custom:error MarketAlreadyListed is thrown if the market is already listed in this pool\n * @custom:error InvalidVToken is thrown if the market does not identify itself as a VToken\n * @custom:access Only PoolRegistry\n */\n function supportMarket(VToken vToken) external {\n _checkSenderIs(poolRegistry);\n\n if (markets[address(vToken)].isListed) {\n revert MarketAlreadyListed(address(vToken));\n }\n\n // Sanity check to make sure its really a VToken\n if (!vToken.isVToken()) {\n revert InvalidVToken();\n }\n\n Market storage newMarket = markets[address(vToken)];\n newMarket.isListed = true;\n newMarket.collateralFactorMantissa = 0;\n newMarket.liquidationThresholdMantissa = 0;\n\n _addMarket(address(vToken));\n\n uint256 rewardDistributorsCount = rewardsDistributors.length;\n\n for (uint256 i; i < rewardDistributorsCount; ++i) {\n rewardsDistributors[i].initializeMarket(address(vToken));\n }\n\n emit MarketSupported(vToken);\n }\n\n /**\n * @notice Set the given borrow caps for the given vToken markets. Borrowing that brings total borrows to or above borrow cap will revert.\n * @dev This function is restricted by the AccessControlManager\n * @dev A borrow cap of type(uint256).max corresponds to unlimited borrowing.\n * @dev Borrow caps smaller than the current total borrows are accepted. This way, new borrows will not be allowed\n until the total borrows amount goes below the new borrow cap\n * @param vTokens The addresses of the markets (tokens) to change the borrow caps for\n * @param newBorrowCaps The new borrow cap values in underlying to be set. A value of type(uint256).max corresponds to unlimited borrowing.\n * @custom:error InvalidArrayLength is thrown if the arrays are empty or their lengths do not match\n * @custom:access Controlled by AccessControlManager\n */\n function setMarketBorrowCaps(VToken[] calldata vTokens, uint256[] calldata newBorrowCaps) external {\n _checkAccessAllowed(\"setMarketBorrowCaps(address[],uint256[])\");\n\n uint256 numMarkets = vTokens.length;\n uint256 numBorrowCaps = newBorrowCaps.length;\n\n if (numMarkets == 0 || numMarkets != numBorrowCaps) {\n revert InvalidArrayLength();\n }\n\n _ensureMaxLoops(numMarkets);\n\n for (uint256 i; i < numMarkets; ++i) {\n borrowCaps[address(vTokens[i])] = newBorrowCaps[i];\n emit NewBorrowCap(vTokens[i], newBorrowCaps[i]);\n }\n }\n\n /**\n * @notice Set the given supply caps for the given vToken markets. Supply that brings total Supply to or above supply cap will revert.\n * @dev This function is restricted by the AccessControlManager\n * @dev A supply cap of type(uint256).max corresponds to unlimited supply.\n * @dev Supply caps smaller than the current total supplies are accepted. This way, new supplies will not be allowed\n until the total supplies amount goes below the new supply cap\n * @param vTokens The addresses of the markets (tokens) to change the supply caps for\n * @param newSupplyCaps The new supply cap values in underlying to be set. A value of type(uint256).max corresponds to unlimited supply.\n * @custom:error InvalidArrayLength is thrown if the arrays are empty or their lengths do not match\n * @custom:access Controlled by AccessControlManager\n */\n function setMarketSupplyCaps(VToken[] calldata vTokens, uint256[] calldata newSupplyCaps) external {\n _checkAccessAllowed(\"setMarketSupplyCaps(address[],uint256[])\");\n uint256 vTokensCount = vTokens.length;\n\n if (vTokensCount == 0 || vTokensCount != newSupplyCaps.length) {\n revert InvalidArrayLength();\n }\n\n _ensureMaxLoops(vTokensCount);\n\n for (uint256 i; i < vTokensCount; ++i) {\n supplyCaps[address(vTokens[i])] = newSupplyCaps[i];\n emit NewSupplyCap(vTokens[i], newSupplyCaps[i]);\n }\n }\n\n /**\n * @notice Pause/unpause specified actions\n * @dev This function is restricted by the AccessControlManager\n * @param marketsList Markets to pause/unpause the actions on\n * @param actionsList List of action ids to pause/unpause\n * @param paused The new paused state (true=paused, false=unpaused)\n * @custom:error MarketNotListed is thrown if any of the markets is not listed\n * @custom:access Controlled by AccessControlManager\n */\n function setActionsPaused(VToken[] calldata marketsList, Action[] calldata actionsList, bool paused) external {\n _checkAccessAllowed(\"setActionsPaused(address[],uint256[],bool)\");\n\n uint256 marketsCount = marketsList.length;\n uint256 actionsCount = actionsList.length;\n\n _ensureMaxLoops(marketsCount * actionsCount);\n\n for (uint256 marketIdx; marketIdx < marketsCount; ++marketIdx) {\n for (uint256 actionIdx; actionIdx < actionsCount; ++actionIdx) {\n _setActionPaused(address(marketsList[marketIdx]), actionsList[actionIdx], paused);\n }\n }\n }\n\n /**\n * @notice Set the given collateral threshold for non-batch liquidations. Regular liquidations\n * will fail if the collateral amount is less than this threshold. Liquidators should use batch\n * operations like liquidateAccount or healAccount.\n * @dev This function is restricted by the AccessControlManager\n * @param newMinLiquidatableCollateral The new min liquidatable collateral (in USD).\n * @custom:access Controlled by AccessControlManager\n */\n function setMinLiquidatableCollateral(uint256 newMinLiquidatableCollateral) external {\n _checkAccessAllowed(\"setMinLiquidatableCollateral(uint256)\");\n\n uint256 oldMinLiquidatableCollateral = minLiquidatableCollateral;\n minLiquidatableCollateral = newMinLiquidatableCollateral;\n emit NewMinLiquidatableCollateral(oldMinLiquidatableCollateral, newMinLiquidatableCollateral);\n }\n\n /**\n * @notice Add a new RewardsDistributor and initialize it with all markets. We can add several RewardsDistributor\n * contracts with the same rewardToken, and there could be overlaping among them considering the last reward slot (block or second)\n * @dev Only callable by the admin\n * @param _rewardsDistributor Address of the RewardDistributor contract to add\n * @custom:access Only Governance\n * @custom:event Emits NewRewardsDistributor with distributor address\n * @custom:error RewardsDistributorAlreadyExists is thrown if this pool already has the given distributor\n */\n function addRewardsDistributor(RewardsDistributor _rewardsDistributor) external onlyOwner {\n if (rewardsDistributorExists[address(_rewardsDistributor)]) {\n revert RewardsDistributorAlreadyExists();\n }\n\n uint256 rewardsDistributorsLen = rewardsDistributors.length;\n _ensureMaxLoops(rewardsDistributorsLen + 1);\n\n rewardsDistributors.push(_rewardsDistributor);\n rewardsDistributorExists[address(_rewardsDistributor)] = true;\n\n uint256 marketsCount = allMarkets.length;\n\n for (uint256 i; i < marketsCount; ++i) {\n _rewardsDistributor.initializeMarket(address(allMarkets[i]));\n }\n\n emit NewRewardsDistributor(address(_rewardsDistributor), address(_rewardsDistributor.rewardToken()));\n }\n\n /**\n * @notice Sets a new price oracle for the Comptroller\n * @dev Only callable by the admin\n * @param newOracle Address of the new price oracle to set\n * @custom:event Emits NewPriceOracle on success\n * @custom:error ZeroAddressNotAllowed is thrown when the new oracle address is zero\n */\n function setPriceOracle(ResilientOracleInterface newOracle) external onlyOwner {\n ensureNonzeroAddress(address(newOracle));\n\n ResilientOracleInterface oldOracle = oracle;\n oracle = newOracle;\n emit NewPriceOracle(oldOracle, newOracle);\n }\n\n /**\n * @notice Sets a new deviation-bounded oracle for the Comptroller\n * @dev Only callable by the admin. Nothing falls back to spot when this is unset: both of the calls into the zero\n * address revert, so borrow and redeem fail closed rather than running unbounded, and this has to be set before\n * the pool serves either action. `_updateProtectionStates` is the one that fails first and it is the stronger of\n * the two guards, because solc emits an `extcodesize` existence check ahead of a call whose return data it does\n * not decode, which is exactly that call. `_safeGetUnderlyingPrices` gets no such check and instead relies on\n * the ABI decoder rejecting the empty return data.\n * @param newBoundedOracle Address of the new deviation-bounded oracle to set\n * @custom:event Emits NewDeviationBoundedOracle on success\n * @custom:error ZeroAddressNotAllowed is thrown when the new oracle address is zero\n */\n function setDeviationBoundedOracle(IDeviationBoundedOracle newBoundedOracle) external onlyOwner {\n ensureNonzeroAddress(address(newBoundedOracle));\n\n IDeviationBoundedOracle oldBoundedOracle = deviationBoundedOracle;\n deviationBoundedOracle = newBoundedOracle;\n emit NewDeviationBoundedOracle(oldBoundedOracle, newBoundedOracle);\n }\n\n /**\n * @notice Set the for loop iteration limit to avoid DOS\n * @param limit Limit for the max loops can execute at a time\n */\n function setMaxLoopsLimit(uint256 limit) external onlyOwner {\n _setMaxLoopsLimit(limit);\n }\n\n /**\n * @notice Enables forced liquidations for a market. If forced liquidation is enabled,\n * borrows in the market may be liquidated regardless of the account liquidity\n * @param vTokenBorrowed Borrowed vToken\n * @param enable Whether to enable forced liquidations\n */\n function setForcedLiquidation(address vTokenBorrowed, bool enable) external {\n _checkAccessAllowed(\"setForcedLiquidation(address,bool)\");\n ensureNonzeroAddress(vTokenBorrowed);\n\n if (!markets[vTokenBorrowed].isListed) {\n revert MarketNotListed(vTokenBorrowed);\n }\n\n isForcedLiquidationEnabled[vTokenBorrowed] = enable;\n emit IsForcedLiquidationEnabledUpdated(vTokenBorrowed, enable);\n }\n\n /**\n * @notice Sets the discount a liquidator receives on the collateral it seizes from a single market\n * @dev This function is restricted by the AccessControlManager\n * @dev Keyed on the collateral market, since that is what the discount prices. Setting it steers liquidators\n * toward one collateral over another, and it feeds the routing between `liquidateAccount` and `healAccount`\n * through `AccountLiquiditySnapshot.maxClearableDebt`.\n *\n * There is no way back to \"unset\" once a value is stored: `0` is the sentinel that means the pool-wide discount\n * applies, and it is rejected here so that a mistaken zero cannot silently move a market back onto the pool-wide\n * value. Pass that value explicitly to get the same effect.\n * @param vToken The collateral market to set the incentive for\n * @param newLiquidationIncentiveMantissa New incentive for this market, scaled by 1e18, at least\n * 1e18 + the market's `protocolSeizeShareMantissa`\n * @custom:event Emits NewMarketLiquidationIncentive on success\n * @custom:error MarketNotListed is thrown if the market is not listed\n * @custom:error InvalidLiquidationIncentive is thrown if the new incentive would leave the liquidator with less\n * collateral than the debt it repaid\n * @custom:access Controlled by AccessControlManager\n */\n function setMarketLiquidationIncentive(address vToken, uint256 newLiquidationIncentiveMantissa) external {\n _checkAccessAllowed(\"setMarketLiquidationIncentive(address,uint256)\");\n\n // Checked before reading from `vToken` below, so this never calls out to an arbitrary address\n if (!markets[vToken].isListed) {\n revert MarketNotListed(vToken);\n }\n\n // The incentive is a multiplier on the debt repaid, and `VToken._seize` hands the protocol\n // `protocolSeizeShareMantissa` of that debt out of the seized collateral, so an incentive below\n // `1e18 + protocolSeizeShareMantissa` pays the liquidator less collateral than it repaid and no one liquidates\n // this collateral. The 1e18 floor falls out of the same bound, since the seize share is never negative.\n // `VToken.setProtocolSeizeShare` holds the bound from the other side: it reads the incentive back through\n // `liquidationIncentiveMantissa()`, which answers for the calling market.\n if (newLiquidationIncentiveMantissa < MANTISSA_ONE + VToken(vToken).protocolSeizeShareMantissa()) {\n revert InvalidLiquidationIncentive();\n }\n\n uint256 oldLiquidationIncentiveMantissa = liquidationIncentives[vToken];\n liquidationIncentives[vToken] = newLiquidationIncentiveMantissa;\n emit NewMarketLiquidationIncentive(vToken, oldLiquidationIncentiveMantissa, newLiquidationIncentiveMantissa);\n }\n\n /**\n * @notice Restricts supplying to a market to the accounts on its supply allowlist, or lifts the restriction\n * @dev Enforced in `preMintHook`, so only supply is metered. Redeeming is never restricted, and an account\n * removed from the allowlist keeps the position it already holds and can still exit. Enabling it on a market that\n * is already serving supply cuts off every account that is not on the list, the seed supplier included.\n * @param vToken The market to change the setting for\n * @param enabled Whether the market should accept supply only from allowlisted accounts\n * @custom:event Emits SupplyAllowlistEnabledUpdated on success\n * @custom:error MarketNotListed is thrown if the market is not listed\n * @custom:access Controlled by AccessControlManager\n */\n function setSupplyAllowlistEnabled(address vToken, bool enabled) external {\n _checkAccessAllowed(\"setSupplyAllowlistEnabled(address,bool)\");\n\n if (!markets[vToken].isListed) {\n revert MarketNotListed(vToken);\n }\n\n isSupplyAllowlistEnabled[vToken] = enabled;\n emit SupplyAllowlistEnabledUpdated(vToken, enabled);\n }\n\n /**\n * @notice Adds an account to a market's supply allowlist or removes it\n * @dev Takes effect only while the market's supply allowlist is enabled. Setting an account to the value it\n * already holds is not an error, so a governance action that overlaps an earlier one still executes.\n * @param vToken The market whose allowlist to update\n * @param supplier The account to add or remove\n * @param allowed Whether the account should be allowed to supply\n * @custom:event Emits AllowedSupplierUpdated on success\n * @custom:error MarketNotListed is thrown if the market is not listed\n * @custom:error ZeroAddressNotAllowed is thrown if the account is the zero address\n * @custom:access Controlled by AccessControlManager\n */\n function setAllowedSupplier(address vToken, address supplier, bool allowed) external {\n _checkAccessAllowed(\"setAllowedSupplier(address,address,bool)\");\n ensureNonzeroAddress(supplier);\n\n if (!markets[vToken].isListed) {\n revert MarketNotListed(vToken);\n }\n\n isAllowedSupplier[vToken][supplier] = allowed;\n emit AllowedSupplierUpdated(vToken, supplier, allowed);\n }\n\n /**\n * @notice Restricts seizing collateral in this pool to the accounts on the liquidation allowlist, or lifts the\n * restriction\n * @dev Pool-wide rather than per market, for the reason given on `isLiquidationAllowlistEnabled`. Enabling this\n * also restricts `healAccount`, so any keeper relied on to record bad debt has to be allowlisted too.\n * @param enabled Whether seizing collateral should be restricted to allowlisted accounts\n * @custom:event Emits LiquidationAllowlistEnabledUpdated on success\n * @custom:access Controlled by AccessControlManager\n */\n function setLiquidationAllowlistEnabled(bool enabled) external {\n _checkAccessAllowed(\"setLiquidationAllowlistEnabled(bool)\");\n\n isLiquidationAllowlistEnabled = enabled;\n emit LiquidationAllowlistEnabledUpdated(enabled);\n }\n\n /**\n * @notice Adds an account to the pool's liquidation allowlist or removes it\n * @dev Takes effect only while the liquidation allowlist is enabled.\n * @param liquidator The account to add or remove\n * @param allowed Whether the account should be allowed to seize collateral\n * @custom:event Emits AllowedLiquidatorUpdated on success\n * @custom:error ZeroAddressNotAllowed is thrown if the account is the zero address\n * @custom:access Controlled by AccessControlManager\n */\n function setAllowedLiquidator(address liquidator, bool allowed) external {\n _checkAccessAllowed(\"setAllowedLiquidator(address,bool)\");\n ensureNonzeroAddress(liquidator);\n\n isAllowedLiquidator[liquidator] = allowed;\n emit AllowedLiquidatorUpdated(liquidator, allowed);\n }\n\n /**\n * @notice Determine the current account liquidity with respect to liquidation threshold requirements\n * @dev The interface of this function is intentionally kept compatible with Compound and Venus Core\n * @param account The account get liquidity for\n * @return error Always NO_ERROR for compatibility with Venus core tooling\n * @return liquidity Account liquidity in excess of liquidation threshold requirements,\n * @return shortfall Account shortfall below liquidation threshold requirements\n */\n function getAccountLiquidity(\n address account\n ) external view returns (uint256 error, uint256 liquidity, uint256 shortfall) {\n AccountLiquiditySnapshot memory snapshot = _getCurrentLiquiditySnapshot(\n account,\n WeightFunction.USE_LIQUIDATION_THRESHOLD\n );\n return (NO_ERROR, snapshot.liquidity, snapshot.shortfall);\n }\n\n /**\n * @notice Determine the current account liquidity with respect to collateral requirements\n * @dev The interface of this function is intentionally kept compatible with Compound and Venus Core\n * @param account The account get liquidity for\n * @return error Always NO_ERROR for compatibility with Venus core tooling\n * @return liquidity Account liquidity in excess of collateral requirements,\n * @return shortfall Account shortfall below collateral requirements\n */\n function getBorrowingPower(\n address account\n ) external view returns (uint256 error, uint256 liquidity, uint256 shortfall) {\n AccountLiquiditySnapshot memory snapshot = _getCurrentLiquiditySnapshot(\n account,\n WeightFunction.USE_COLLATERAL_FACTOR\n );\n return (NO_ERROR, snapshot.liquidity, snapshot.shortfall);\n }\n\n /**\n * @notice Determine what the account liquidity would be if the given amounts were redeemed/borrowed\n * @dev The interface of this function is intentionally kept compatible with Compound and Venus Core\n * @param vTokenModify The market to hypothetically redeem/borrow in\n * @param account The account to determine liquidity for\n * @param redeemTokens The number of tokens to hypothetically redeem\n * @param borrowAmount The amount of underlying to hypothetically borrow\n * @return error Always NO_ERROR for compatibility with Venus core tooling\n * @return liquidity Hypothetical account liquidity in excess of collateral requirements,\n * @return shortfall Hypothetical account shortfall below collateral requirements\n */\n function getHypotheticalAccountLiquidity(\n address account,\n address vTokenModify,\n uint256 redeemTokens,\n uint256 borrowAmount\n ) external view returns (uint256 error, uint256 liquidity, uint256 shortfall) {\n AccountLiquiditySnapshot memory snapshot = _getHypotheticalLiquiditySnapshot(\n account,\n VToken(vTokenModify),\n redeemTokens,\n borrowAmount,\n WeightFunction.USE_COLLATERAL_FACTOR\n );\n return (NO_ERROR, snapshot.liquidity, snapshot.shortfall);\n }\n\n /**\n * @notice Return all of the markets\n * @dev The automatic getter may be used to access an individual market.\n * @return markets The list of market addresses\n */\n function getAllMarkets() external view override returns (VToken[] memory) {\n return allMarkets;\n }\n\n /**\n * @notice Check if a market is marked as listed (active)\n * @param vToken vToken Address for the market to check\n * @return listed True if listed otherwise false\n */\n function isMarketListed(VToken vToken) external view returns (bool) {\n return markets[address(vToken)].isListed;\n }\n\n /**\n * @notice Returns the discount a liquidator receives on the collateral it seizes from the calling market\n * @dev Answers for `msg.sender` instead of taking the market as an argument, because this is the getter\n * `ComptrollerViewInterface` declares and `VToken` calls on itself: `_seize` divides the protocol seize share by\n * it, and `setProtocolSeizeShare` bounds that share against it. Both have to see the discount that prices the\n * calling market's own collateral, or the protocol takes more than its configured share of the repaid debt and the\n * liquidator is paid less than the market's configured discount.\n *\n * Any caller that is not a market of this pool, a lens or a liquidation bot, reads the pool-wide discount. Ask\n * about a specific market through `effectiveLiquidationIncentive`.\n * @return The discount that applies to the caller, scaled by 1e18\n */\n function liquidationIncentiveMantissa() external view returns (uint256) {\n return _liquidationIncentive(msg.sender);\n }\n\n /**\n * @notice Returns the discount a liquidator receives on the collateral it seizes from a market\n * @param vToken The collateral market to read the discount of\n * @return The market's own discount if it has one, otherwise the pool-wide discount, scaled by 1e18\n */\n function effectiveLiquidationIncentive(address vToken) external view returns (uint256) {\n return _liquidationIncentive(vToken);\n }\n\n /*** Assets You Are In ***/\n\n /**\n * @notice Returns whether the given account is entered in a given market\n * @param account The address of the account to check\n * @param vToken The vToken to check\n * @return True if the account is in the market specified, otherwise false.\n */\n function checkMembership(address account, VToken vToken) external view returns (bool) {\n return markets[address(vToken)].accountMembership[account];\n }\n\n /**\n * @notice Calculate number of tokens of collateral asset to seize given an underlying amount\n * @dev Used in liquidation (called in vToken.liquidateBorrowFresh)\n * @param vTokenBorrowed The address of the borrowed vToken\n * @param vTokenCollateral The address of the collateral vToken\n * @param actualRepayAmount The amount of vTokenBorrowed underlying to convert into vTokenCollateral tokens\n * @return error Always NO_ERROR for compatibility with Venus core tooling\n * @return tokensToSeize Number of vTokenCollateral tokens to be seized in a liquidation\n * @custom:error PriceError if the oracle returns an invalid price\n */\n function liquidateCalculateSeizeTokens(\n address vTokenBorrowed,\n address vTokenCollateral,\n uint256 actualRepayAmount\n ) external view override returns (uint256 error, uint256 tokensToSeize) {\n /* Read oracle prices for borrowed and collateral markets */\n uint256 priceBorrowedMantissa = _safeGetUnderlyingPrice(VToken(vTokenBorrowed));\n uint256 priceCollateralMantissa = _safeGetUnderlyingPrice(VToken(vTokenCollateral));\n\n /*\n * Get the exchange rate and calculate the number of collateral tokens to seize, where\n * `liquidationIncentive` is the collateral market's own discount if it has one and the pool-wide default\n * otherwise, the same value `VToken._seize` reads back through `liquidationIncentiveMantissa()`:\n * seizeAmount = actualRepayAmount * liquidationIncentive * priceBorrowed / priceCollateral\n * seizeTokens = seizeAmount / exchangeRate\n * = actualRepayAmount * (liquidationIncentive * priceBorrowed) / (priceCollateral * exchangeRate)\n */\n uint256 exchangeRateMantissa = VToken(vTokenCollateral).exchangeRateStored(); // Note: reverts on error\n uint256 seizeTokens;\n Exp memory numerator;\n Exp memory denominator;\n Exp memory ratio;\n\n numerator = mul_(\n Exp({ mantissa: _liquidationIncentive(vTokenCollateral) }),\n Exp({ mantissa: priceBorrowedMantissa })\n );\n denominator = mul_(Exp({ mantissa: priceCollateralMantissa }), Exp({ mantissa: exchangeRateMantissa }));\n ratio = div_(numerator, denominator);\n\n seizeTokens = mul_ScalarTruncate(ratio, actualRepayAmount);\n\n return (NO_ERROR, seizeTokens);\n }\n\n /**\n * @notice Returns reward speed given a vToken\n * @param vToken The vToken to get the reward speeds for\n * @return rewardSpeeds Array of total supply and borrow speeds and reward token for all reward distributors\n */\n function getRewardsByMarket(address vToken) external view returns (RewardSpeeds[] memory rewardSpeeds) {\n uint256 rewardsDistributorsLength = rewardsDistributors.length;\n rewardSpeeds = new RewardSpeeds[](rewardsDistributorsLength);\n for (uint256 i; i < rewardsDistributorsLength; ++i) {\n RewardsDistributor rewardsDistributor = rewardsDistributors[i];\n address rewardToken = address(rewardsDistributor.rewardToken());\n rewardSpeeds[i] = RewardSpeeds({\n rewardToken: rewardToken,\n supplySpeed: rewardsDistributor.rewardTokenSupplySpeeds(vToken),\n borrowSpeed: rewardsDistributor.rewardTokenBorrowSpeeds(vToken)\n });\n }\n return rewardSpeeds;\n }\n\n /**\n * @notice Return all reward distributors for this pool\n * @return Array of RewardDistributor addresses\n */\n function getRewardDistributors() external view returns (RewardsDistributor[] memory) {\n return rewardsDistributors;\n }\n\n /**\n * @notice A marker method that returns true for a valid Comptroller contract\n * @return Always true\n */\n function isComptroller() external pure override returns (bool) {\n return true;\n }\n\n /**\n * @notice Update the prices of all the tokens associated with the provided account\n * @param account Address of the account to get associated tokens with\n */\n function updatePrices(address account) public {\n _updatePrices(getAssetsIn(account));\n }\n\n /**\n * @notice Checks if a certain action is paused on a market\n * @param market vToken address\n * @param action Action to check\n * @return paused True if the action is paused otherwise false\n */\n function actionPaused(address market, Action action) public view returns (bool) {\n return _actionPaused[market][action];\n }\n\n /**\n * @notice Returns the assets an account has entered\n * @param account The address of the account to pull assets for\n * @return A list with the assets the account has entered\n */\n function getAssetsIn(address account) public view returns (VToken[] memory) {\n uint256 len;\n VToken[] memory _accountAssets = accountAssets[account];\n uint256 _accountAssetsLength = _accountAssets.length;\n\n VToken[] memory assetsIn = new VToken[](_accountAssetsLength);\n\n for (uint256 i; i < _accountAssetsLength; ++i) {\n Market storage market = markets[address(_accountAssets[i])];\n if (market.isListed) {\n assetsIn[len] = _accountAssets[i];\n ++len;\n }\n }\n\n assembly {\n mstore(assetsIn, len)\n }\n\n return assetsIn;\n }\n\n /**\n * @notice Add the market to the borrower's \"assets in\" for liquidity calculations\n * @param vToken The market to enter\n * @param borrower The address of the account to modify\n */\n function _addToMarket(VToken vToken, address borrower) internal {\n _checkActionPauseState(address(vToken), Action.ENTER_MARKET);\n Market storage marketToJoin = markets[address(vToken)];\n\n if (!marketToJoin.isListed) {\n revert MarketNotListed(address(vToken));\n }\n\n if (marketToJoin.accountMembership[borrower]) {\n // already joined\n return;\n }\n\n // survived the gauntlet, add to list\n // NOTE: we store these somewhat redundantly as a significant optimization\n // this avoids having to iterate through the list for the most common use cases\n // that is, only when we need to perform liquidity checks\n // and not whenever we want to check if an account is in a particular market\n marketToJoin.accountMembership[borrower] = true;\n accountAssets[borrower].push(vToken);\n\n emit MarketEntered(vToken, borrower);\n }\n\n /**\n * @notice Internal function to validate that a market hasn't already been added\n * and if it hasn't adds it\n * @param vToken The market to support\n */\n function _addMarket(address vToken) internal {\n uint256 marketsCount = allMarkets.length;\n\n for (uint256 i; i < marketsCount; ++i) {\n if (allMarkets[i] == VToken(vToken)) {\n revert MarketAlreadyListed(vToken);\n }\n }\n allMarkets.push(VToken(vToken));\n marketsCount = allMarkets.length;\n _ensureMaxLoops(marketsCount);\n }\n\n /**\n * @dev Pause/unpause an action on a market\n * @param market Market to pause/unpause the action on\n * @param action Action id to pause/unpause\n * @param paused The new paused state (true=paused, false=unpaused)\n */\n function _setActionPaused(address market, Action action, bool paused) internal {\n if (!markets[market].isListed) {\n revert MarketNotListed(market);\n }\n _actionPaused[market][action] = paused;\n emit ActionPausedMarket(VToken(market), action, paused);\n }\n\n /**\n * @dev Accrues interest and pushes an oracle update for each of the given markets, so that a snapshot taken\n * afterwards values the account on live balances and prices. Used by the two batch liquidation entry points,\n * which decide on their snapshot and then execute with the per-order liquidity check skipped.\n * @param vTokens The markets to refresh, as returned by `getAssetsIn`\n */\n function _refreshMarkets(VToken[] memory vTokens) internal {\n uint256 vTokensCount = vTokens.length;\n\n for (uint256 i; i < vTokensCount; ++i) {\n vTokens[i].accrueInterest();\n }\n\n _updatePrices(vTokens);\n }\n\n /**\n * @dev Pushes an oracle update for each of the given markets. Takes the resolved market list rather than an\n * account, because the two callers that need protection state as well would otherwise walk `getAssetsIn` twice\n * for the same account in the same call.\n * @param vTokens The markets to update, as returned by `getAssetsIn`\n */\n function _updatePrices(VToken[] memory vTokens) internal {\n uint256 vTokensCount = vTokens.length;\n\n ResilientOracleInterface oracle_ = oracle;\n\n for (uint256 i; i < vTokensCount; ++i) {\n oracle_.updatePrice(address(vTokens[i]));\n }\n }\n\n /**\n * @dev Persists the deviation-bounded oracle's price window and protection state for each of the given markets.\n * Runs before the borrow and redeem liquidity checks, the ones weighted by the collateral factor, so that a\n * deviating print latches protection and starts its cooldown instead of evaporating once the price returns to\n * the window. The liquidation-threshold paths never call this, matching how they read spot prices: see\n * `_safeGetUnderlyingPrices`.\n * @param vTokens The markets to update, as returned by `getAssetsIn`\n */\n function _updateProtectionStates(VToken[] memory vTokens) internal {\n uint256 vTokensCount = vTokens.length;\n\n IDeviationBoundedOracle boundedOracle = deviationBoundedOracle;\n\n for (uint256 i; i < vTokensCount; ++i) {\n boundedOracle.updateProtectionState(address(vTokens[i]));\n }\n }\n\n /**\n * @dev Internal function to check that vTokens can be safely redeemed for the underlying asset.\n * @param vToken Address of the vTokens to redeem\n * @param redeemer Account redeeming the tokens\n * @param redeemTokens The number of tokens to redeem\n */\n function _checkRedeemAllowed(address vToken, address redeemer, uint256 redeemTokens) internal {\n Market storage market = markets[vToken];\n\n if (!market.isListed) {\n revert MarketNotListed(address(vToken));\n }\n\n /* If the redeemer is not 'in' the market, then we can bypass the liquidity check */\n if (!market.accountMembership[redeemer]) {\n return;\n }\n\n // Update the prices of tokens\n VToken[] memory redeemerAssets = getAssetsIn(redeemer);\n _updatePrices(redeemerAssets);\n _updateProtectionStates(redeemerAssets);\n\n /* Otherwise, perform a hypothetical liquidity check to guard against shortfall */\n AccountLiquiditySnapshot memory snapshot = _getHypotheticalLiquiditySnapshot(\n redeemer,\n VToken(vToken),\n redeemTokens,\n 0,\n WeightFunction.USE_COLLATERAL_FACTOR\n );\n if (snapshot.shortfall > 0) {\n revert InsufficientLiquidity();\n }\n }\n\n /**\n * @notice Get the total collateral, weighted collateral, borrow balance, liquidity, shortfall\n * @param account The account to get the snapshot for\n * @param weighting Which per-market risk parameter weights the collateral, either the collateral factor or\n * the liquidation threshold\n * @dev Note that we calculate the exchangeRateStored for each collateral vToken using stored data,\n * without calculating accumulated interest.\n * @return snapshot Account liquidity snapshot\n */\n function _getCurrentLiquiditySnapshot(\n address account,\n WeightFunction weighting\n ) internal view returns (AccountLiquiditySnapshot memory snapshot) {\n return _getHypotheticalLiquiditySnapshot(account, VToken(address(0)), 0, 0, weighting);\n }\n\n /**\n * @notice Determine what the supply/borrow balances would be if the given amounts were redeemed/borrowed\n * @param vTokenModify The market to hypothetically redeem/borrow in\n * @param account The account to determine liquidity for\n * @param redeemTokens The number of tokens to hypothetically redeem\n * @param borrowAmount The amount of underlying to hypothetically borrow\n * @param weighting Which per-market risk parameter weights the collateral, either the collateral factor or\n * the liquidation threshold\n * @dev Note that we calculate the exchangeRateStored for each collateral vToken using stored data,\n * without calculating accumulated interest.\n * @return snapshot Account liquidity snapshot\n */\n function _getHypotheticalLiquiditySnapshot(\n address account,\n VToken vTokenModify,\n uint256 redeemTokens,\n uint256 borrowAmount,\n WeightFunction weighting\n ) internal view returns (AccountLiquiditySnapshot memory snapshot) {\n // For each asset the account is in\n VToken[] memory assets = getAssetsIn(account);\n uint256 assetsCount = assets.length;\n\n for (uint256 i; i < assetsCount; ++i) {\n VToken asset = assets[i];\n (Exp memory weightedVTokenPrice, Exp memory debtPrice) = _accumulateMarketPosition(\n snapshot,\n asset,\n account,\n weighting\n );\n\n // Calculate effects of interacting with vTokenModify\n if (asset == vTokenModify) {\n // redeem effect\n // effects += tokensToDenom * redeemTokens\n snapshot.effects = mul_ScalarTruncateAddUInt(weightedVTokenPrice, redeemTokens, snapshot.effects);\n\n // borrow effect\n // effects += debtPrice * borrowAmount\n snapshot.effects = mul_ScalarTruncateAddUInt(debtPrice, borrowAmount, snapshot.effects);\n }\n }\n\n uint256 borrowPlusEffects = snapshot.borrows + snapshot.effects;\n // These are safe, as the underflow condition is checked first\n unchecked {\n if (snapshot.weightedCollateral > borrowPlusEffects) {\n snapshot.liquidity = snapshot.weightedCollateral - borrowPlusEffects;\n snapshot.shortfall = 0;\n } else {\n snapshot.liquidity = 0;\n snapshot.shortfall = borrowPlusEffects - snapshot.weightedCollateral;\n }\n }\n\n return snapshot;\n }\n\n /**\n * @dev Adds one market's collateral and debt to an account's liquidity snapshot, and returns the two prices the\n * caller needs to apply a hypothetical redeem or borrow in that market. Split out of\n * `_getHypotheticalLiquiditySnapshot` because holding this market's prices and the loop's own variables in one\n * frame exceeds the stack the legacy code generator can address, and this repo does not enable `viaIR`.\n * `snapshot` is a memory reference, so the accumulated fields are updated in place.\n * @param snapshot The snapshot to accumulate into\n * @param asset The market to value\n * @param account The account whose position in the market is being valued\n * @param weighting Which per-market risk parameter weights the collateral\n * @return weightedVTokenPrice Value of one vToken after the risk weight, which prices a hypothetical redeem\n * @return debtPrice Price valuing the market's debt, which prices a hypothetical borrow\n */\n function _accumulateMarketPosition(\n AccountLiquiditySnapshot memory snapshot,\n VToken asset,\n address account,\n WeightFunction weighting\n ) internal view returns (Exp memory weightedVTokenPrice, Exp memory debtPrice) {\n // Read the balances and exchange rate from the vToken\n (uint256 vTokenBalance, uint256 borrowBalance, uint256 exchangeRateMantissa) = _safeGetAccountSnapshot(\n asset,\n account\n );\n\n // Get the normalized prices that value this market's collateral and debt\n Exp memory collateralPrice;\n (collateralPrice, debtPrice) = _safeGetUnderlyingPrices(asset, weighting);\n\n // Pre-compute conversion factors from vTokens -> usd\n Exp memory vTokenPrice = mul_(Exp({ mantissa: exchangeRateMantissa }), collateralPrice);\n weightedVTokenPrice = mul_(_getWeightingFactor(asset, weighting), vTokenPrice);\n\n // weightedCollateral += weightedVTokenPrice * vTokenBalance\n snapshot.weightedCollateral = mul_ScalarTruncateAddUInt(\n weightedVTokenPrice,\n vTokenBalance,\n snapshot.weightedCollateral\n );\n\n // totalCollateral += vTokenPrice * vTokenBalance\n snapshot.totalCollateral = mul_ScalarTruncateAddUInt(vTokenPrice, vTokenBalance, snapshot.totalCollateral);\n\n // maxClearableDebt += (vTokenPrice * vTokenBalance) / liquidationIncentive, at this market's own incentive.\n // Only the liquidation-threshold weighting gives this field a meaning and only that weighting's callers read\n // it, so a collateral-factor snapshot would read the incentive and divide only to discard the result: see the\n // note on `AccountLiquiditySnapshot.maxClearableDebt`. Also skipped when the account holds none of this\n // market, since the term would be zero and a borrower is a member of every market it borrows from, including\n // ones it holds no collateral in. The repeated product costs nothing, the optimizer shares it with the line\n // above.\n if (weighting == WeightFunction.USE_LIQUIDATION_THRESHOLD && vTokenBalance != 0) {\n snapshot.maxClearableDebt += div_(\n mul_ScalarTruncate(vTokenPrice, vTokenBalance),\n Exp({ mantissa: _liquidationIncentive(address(asset)) })\n );\n }\n\n // borrows += debtPrice * borrowBalance\n snapshot.borrows = mul_ScalarTruncateAddUInt(debtPrice, borrowBalance, snapshot.borrows);\n }\n\n /**\n * @dev Retrieves price from oracle for an asset and checks it is nonzero\n * @param asset Address for asset to query price\n * @return Underlying price\n */\n function _safeGetUnderlyingPrice(VToken asset) internal view returns (uint256) {\n uint256 oraclePriceMantissa = oracle.getUnderlyingPrice(address(asset));\n if (oraclePriceMantissa == 0) {\n revert PriceError(address(asset));\n }\n return oraclePriceMantissa;\n }\n\n /**\n * @dev Retrieves the two prices that value a market's collateral and its debt, and checks they are nonzero.\n * Under the collateral factor both come from `deviationBoundedOracle`: while protection is active for the asset\n * it values collateral at the low end of the asset's recent price window and debt at the high end, and it returns\n * spot on both legs otherwise, including for an asset it holds no configuration for. A deviating print can\n * therefore only ever shrink an account's borrowing capacity, never inflate it. Under the liquidation threshold\n * both legs are spot, because those snapshots route an unhealthy account between `liquidateAccount` and\n * `healAccount` and set how much of its debt healing repays, which has to track the live price.\n * @param asset Address for asset to query prices for\n * @param weighting Which risk parameter weights the position being valued\n * @return collateralPrice Price valuing the collateral held in the market\n * @return debtPrice Price valuing the debt owed to the market\n */\n function _safeGetUnderlyingPrices(\n VToken asset,\n WeightFunction weighting\n ) internal view returns (Exp memory collateralPrice, Exp memory debtPrice) {\n if (weighting == WeightFunction.USE_LIQUIDATION_THRESHOLD) {\n uint256 spotPriceMantissa = _safeGetUnderlyingPrice(asset);\n return (Exp({ mantissa: spotPriceMantissa }), Exp({ mantissa: spotPriceMantissa }));\n }\n\n (uint256 collateralPriceMantissa, uint256 debtPriceMantissa) = deviationBoundedOracle.getBoundedPricesView(\n address(asset)\n );\n if (collateralPriceMantissa == 0 || debtPriceMantissa == 0) {\n revert PriceError(address(asset));\n }\n return (Exp({ mantissa: collateralPriceMantissa }), Exp({ mantissa: debtPriceMantissa }));\n }\n\n /**\n * @dev Returns the risk parameter that weights a market's collateral in a liquidity snapshot\n * @param asset Address for asset whose parameter to read\n * @param weighting Which of the two parameters to read\n * @return The market's collateral factor or liquidation threshold, as an exponential\n */\n function _getWeightingFactor(VToken asset, WeightFunction weighting) internal view returns (Exp memory) {\n Market storage market = markets[address(asset)];\n return\n Exp({\n mantissa: weighting == WeightFunction.USE_COLLATERAL_FACTOR\n ? market.collateralFactorMantissa\n : market.liquidationThresholdMantissa\n });\n }\n\n /**\n * @dev Returns the liquidation incentive that applies when a market's collateral is seized\n * @param vTokenCollateral Market whose collateral would be seized\n * @return The market's own incentive, or the pool-wide one if the market has none. Never zero for a listed\n * market, so callers may divide by it: see the note on `liquidationIncentives`. An unlisted market still reads\n * back whatever incentive it was last given, which no seizure can reach, since every path that divides by this\n * is gated on the market being listed.\n */\n function _liquidationIncentive(address vTokenCollateral) internal view returns (uint256) {\n uint256 incentive = liquidationIncentives[vTokenCollateral];\n return incentive != 0 ? incentive : _poolLiquidationIncentiveMantissa;\n }\n\n /**\n * @dev Returns supply and borrow balances of user in vToken, reverts on failure\n * @param vToken Market to query\n * @param user Account address\n * @return vTokenBalance Balance of vTokens, the same as vToken.balanceOf(user)\n * @return borrowBalance Borrowed amount, including the interest\n * @return exchangeRateMantissa Stored exchange rate\n */\n function _safeGetAccountSnapshot(\n VToken vToken,\n address user\n ) internal view returns (uint256 vTokenBalance, uint256 borrowBalance, uint256 exchangeRateMantissa) {\n uint256 err;\n (err, vTokenBalance, borrowBalance, exchangeRateMantissa) = vToken.getAccountSnapshot(user);\n if (err != 0) {\n revert SnapshotError(address(vToken), user);\n }\n return (vTokenBalance, borrowBalance, exchangeRateMantissa);\n }\n\n /// @notice Reverts if the call is not from expectedSender\n /// @param expectedSender Expected transaction sender\n function _checkSenderIs(address expectedSender) internal view {\n if (msg.sender != expectedSender) {\n revert UnexpectedSender(expectedSender, msg.sender);\n }\n }\n\n /// @notice Reverts if a certain action is paused on a market\n /// @param market Market to check\n /// @param action Action to check\n function _checkActionPauseState(address market, Action action) private view {\n if (actionPaused(market, action)) {\n revert ActionPaused(market, action);\n }\n }\n\n /// @notice Reverts if the liquidation allowlist is enabled and the given account is not on it\n /// @param liquidator Account that would receive the seized collateral\n function _checkLiquidatorAllowlisted(address liquidator) private view {\n if (isLiquidationAllowlistEnabled && !isAllowedLiquidator[liquidator]) {\n revert LiquidationNotAllowed(liquidator);\n }\n }\n}\n" + }, + "contracts/Spoke/SpokeComptrollerInterface.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity ^0.8.25;\n\nimport { ResilientOracleInterface } from \"@venusprotocol/oracle/contracts/interfaces/OracleInterface.sol\";\nimport { IDeviationBoundedOracle } from \"@venusprotocol/oracle/contracts/interfaces/IDeviationBoundedOracle.sol\";\n\nimport { ComptrollerInterface, Action } from \"../ComptrollerInterface.sol\";\nimport { VToken } from \"../VToken.sol\";\n\n/**\n * @title SpokeComptrollerInterface\n * @author Venus\n * @notice Interface implemented by the `SpokeComptroller` contract. It declares the events and errors that make up the\n * contract's observable surface, so integrators and off-chain consumers can decode them without depending on the\n * implementation.\n * @dev The getters this fork adds are declared separately, in `SpokeComptrollerViewInterface` below. They cannot live\n * here: `SpokeComptroller` implements this interface, and it serves those getters from public variables declared in\n * `SpokeComptrollerStorage`, which is a sibling base rather than a derived one. Solidity will not let a public\n * variable in one base satisfy a function declared in another, so declaring them here would force\n * `SpokeComptrollerStorage` to inherit this interface and mark six variables `override` - noise in the file that has\n * to be diffed by hand against `ComptrollerStorage`, for no gain over a standalone view interface.\n */\ninterface SpokeComptrollerInterface is ComptrollerInterface {\n /// @notice Emitted when an account enters a market\n event MarketEntered(VToken indexed vToken, address indexed account);\n\n /// @notice Emitted when an account exits a market\n event MarketExited(VToken indexed vToken, address indexed account);\n\n /// @notice Emitted when close factor is changed by admin\n event NewCloseFactor(uint256 oldCloseFactorMantissa, uint256 newCloseFactorMantissa);\n\n /// @notice Emitted when a collateral factor is changed by admin\n event NewCollateralFactor(VToken vToken, uint256 oldCollateralFactorMantissa, uint256 newCollateralFactorMantissa);\n\n /// @notice Emitted when liquidation threshold is changed by admin\n event NewLiquidationThreshold(\n VToken vToken,\n uint256 oldLiquidationThresholdMantissa,\n uint256 newLiquidationThresholdMantissa\n );\n\n /// @notice Emitted when liquidation incentive is changed by admin\n event NewLiquidationIncentive(uint256 oldLiquidationIncentiveMantissa, uint256 newLiquidationIncentiveMantissa);\n\n /// @notice Emitted when the liquidation incentive of a single market is changed by admin\n event NewMarketLiquidationIncentive(\n address indexed vToken,\n uint256 oldLiquidationIncentiveMantissa,\n uint256 newLiquidationIncentiveMantissa\n );\n\n /// @notice Emitted when price oracle is changed\n event NewPriceOracle(ResilientOracleInterface oldPriceOracle, ResilientOracleInterface newPriceOracle);\n\n /// @notice Emitted when the deviation-bounded oracle is changed\n event NewDeviationBoundedOracle(IDeviationBoundedOracle oldBoundedOracle, IDeviationBoundedOracle newBoundedOracle);\n\n /// @notice Emitted when an action is paused on a market\n event ActionPausedMarket(VToken vToken, Action action, bool pauseState);\n\n /// @notice Emitted when borrow cap for a vToken is changed\n event NewBorrowCap(VToken indexed vToken, uint256 newBorrowCap);\n\n /// @notice Emitted when the collateral threshold (in USD) for non-batch liquidations is changed\n event NewMinLiquidatableCollateral(uint256 oldMinLiquidatableCollateral, uint256 newMinLiquidatableCollateral);\n\n /// @notice Emitted when supply cap for a vToken is changed\n event NewSupplyCap(VToken indexed vToken, uint256 newSupplyCap);\n\n /// @notice Emitted when a rewards distributor is added\n event NewRewardsDistributor(address indexed rewardsDistributor, address indexed rewardToken);\n\n /// @notice Emitted when a market is supported\n event MarketSupported(VToken vToken);\n\n /// @notice Emitted when forced liquidation is enabled or disabled for a market\n event IsForcedLiquidationEnabledUpdated(address indexed vToken, bool enable);\n\n /// @notice Emitted when a market is unlisted\n event MarketUnlisted(address indexed vToken);\n\n /// @notice Emitted when the borrowing or redeeming delegate rights are updated for an account\n event DelegateUpdated(address indexed approver, address indexed delegate, bool approved);\n\n /// @notice Emitted when a market's supply allowlist is enabled or disabled\n event SupplyAllowlistEnabledUpdated(address indexed vToken, bool enabled);\n\n /// @notice Emitted when an account is added to or removed from a market's supply allowlist\n event AllowedSupplierUpdated(address indexed vToken, address indexed supplier, bool allowed);\n\n /// @notice Emitted when the pool's liquidation allowlist is enabled or disabled\n event LiquidationAllowlistEnabledUpdated(bool enabled);\n\n /// @notice Emitted when an account is added to or removed from the pool's liquidation allowlist\n event AllowedLiquidatorUpdated(address indexed liquidator, bool allowed);\n\n /// @notice Thrown when the close factor is outside the bounds set by `MIN_CLOSE_FACTOR_MANTISSA` and\n /// `MAX_CLOSE_FACTOR_MANTISSA`\n error InvalidCloseFactor();\n\n /// @notice Thrown when collateral factor exceeds the upper bound\n error InvalidCollateralFactor();\n\n /// @notice Thrown when liquidation threshold exceeds the collateral factor\n error InvalidLiquidationThreshold();\n\n /// @notice Thrown when a liquidation incentive is low enough that a liquidator would seize less value than it\n /// repaid: below `1e18 + protocolSeizeShareMantissa` for a single market, below\n /// `MIN_POOL_LIQUIDATION_INCENTIVE_MANTISSA` for the pool-wide fallback\n error InvalidLiquidationIncentive();\n\n /// @notice Thrown when the action is only available to specific sender, but the real sender was different\n error UnexpectedSender(address expectedSender, address actualSender);\n\n /// @notice Thrown when the oracle returns an invalid price for some asset\n error PriceError(address vToken);\n\n /// @notice Thrown if VToken unexpectedly returned a nonzero error code while trying to get account snapshot\n error SnapshotError(address vToken, address user);\n\n /// @notice Thrown when the market is not listed\n error MarketNotListed(address market);\n\n /// @notice Thrown when a market has an unexpected comptroller\n error ComptrollerMismatch();\n\n /// @notice Thrown when user is not member of market\n error MarketNotCollateral(address vToken, address user);\n\n /// @notice Thrown when borrow action is not paused\n error BorrowActionNotPaused();\n\n /// @notice Thrown when mint action is not paused\n error MintActionNotPaused();\n\n /// @notice Thrown when redeem action is not paused\n error RedeemActionNotPaused();\n\n /// @notice Thrown when repay action is not paused\n error RepayActionNotPaused();\n\n /// @notice Thrown when seize action is not paused\n error SeizeActionNotPaused();\n\n /// @notice Thrown when exit market action is not paused\n error ExitMarketActionNotPaused();\n\n /// @notice Thrown when transfer action is not paused\n error TransferActionNotPaused();\n\n /// @notice Thrown when enter market action is not paused\n error EnterMarketActionNotPaused();\n\n /// @notice Thrown when liquidate action is not paused\n error LiquidateActionNotPaused();\n\n /// @notice Thrown when borrow cap is not zero\n error BorrowCapIsNotZero();\n\n /// @notice Thrown when supply cap is not zero\n error SupplyCapIsNotZero();\n\n /// @notice Thrown when collateral factor is not zero\n error CollateralFactorIsNotZero();\n\n /**\n * @notice Thrown during the liquidation if user's total collateral amount is lower than\n * a predefined threshold. In this case only batch liquidations (either liquidateAccount\n * or healAccount) are available.\n */\n error MinimalCollateralViolated(uint256 expectedGreaterThan, uint256 actual);\n\n /**\n * @notice Thrown by the batch operations when the account's total collateral is above\n * `minLiquidatableCollateral`, which means it is large enough for a regular `VToken.liquidateBorrow`\n * @dev Same name, arguments and meaning as the shared `Comptroller`, so a decoder written against either one\n * reads this correctly.\n */\n error CollateralExceedsThreshold(uint256 expectedLessThanOrEqualTo, uint256 actual);\n\n /// @notice Thrown by `healAccount` when the collateral can clear the whole debt at each market's own liquidation\n /// incentive, so healing would forgive nothing and `liquidateAccount` has to be used instead\n error CollateralCoversDebt(uint256 borrows, uint256 maxClearableDebt);\n\n /// @notice Thrown by `liquidateAccount` when the debt is too large for the collateral to clear, so `healAccount`\n /// has to be used instead. Reports the debt and the largest debt the collateral could have cleared.\n /// @dev Deliberately not the shared `Comptroller`'s `InsufficientCollateral`: both arguments here are debt values\n /// rather than collateral values, and reusing that name would leave the two versions sharing one selector.\n error DebtExceedsClearableAmount(uint256 borrows, uint256 maxClearableDebt);\n\n /// @notice Thrown when the account doesn't have enough liquidity to redeem or borrow\n error InsufficientLiquidity();\n\n /// @notice Thrown when trying to liquidate a healthy account\n error InsufficientShortfall();\n\n /// @notice Thrown if the liquidation allowlist is enabled and the account liquidating is not on it\n error LiquidationNotAllowed(address liquidator);\n\n /// @notice Thrown when trying to repay more than allowed by close factor\n error TooMuchRepay();\n\n /// @notice Thrown if the user is trying to exit a market in which they have an outstanding debt\n error NonzeroBorrowBalance();\n\n /// @notice Thrown if a debt remains in any of the borrower's markets once every liquidation order has been\n /// executed, which means the orders passed to `liquidateAccount` did not cover the whole position\n error NonzeroBorrowBalanceAfterLiquidation();\n\n /// @notice Thrown when trying to perform an action that is paused\n error ActionPaused(address market, Action action);\n\n /// @notice Thrown when trying to add a market that is already listed\n error MarketAlreadyListed(address market);\n\n /// @notice Thrown when the market being listed does not identify itself as a VToken\n error InvalidVToken();\n\n /// @notice Thrown when an array argument is empty, or when two array arguments have different lengths\n error InvalidArrayLength();\n\n /// @notice Thrown if the supply cap is exceeded\n error SupplyCapExceeded(address market, uint256 cap);\n\n /// @notice Thrown if the account being credited with the minted vTokens is not on the market's supply allowlist\n error SupplyNotAllowed(address market, address supplier);\n\n /// @notice Thrown if the borrow cap is exceeded\n error BorrowCapExceeded(address market, uint256 cap);\n\n /// @notice Thrown if delegate approval status is already set to the requested value\n error DelegationStatusUnchanged();\n\n /// @notice Thrown when adding a rewards distributor that this pool already has\n error RewardsDistributorAlreadyExists();\n}\n\n/**\n * @title SpokeComptrollerViewInterface\n * @author Venus\n * @notice The getters `SpokeComptroller` adds on top of the pooled `Comptroller`, for integrators that read this pool\n * rather than implement it - the Liquidity Hub's spoke adapter, lenses, keepers and VIP tooling.\n * @dev Standalone and not implemented by `SpokeComptroller`, matching how `ComptrollerViewInterface` exposes the\n * shared pool's getters.\n *\n * Scope is the supply side: what an integrator that funds this pool has to read before and while it supplies, plus\n * the two allowlists and the per-market discount that only exist on this fork. `supplyCaps` and `actionPaused` are\n * repeated from `ComptrollerViewInterface` and `ComptrollerInterface` rather than inherited, so that a consumer of a\n * spoke pool needs one import and not three. `actionPaused` also has to be repeated on its own terms: the shared\n * declaration types its second argument as the `Action` enum, and a consumer that does not want this repo's enum in\n * its build needs the ABI-equivalent `uint8` form. The two share a selector, so they cannot both be declared in one\n * inheritance chain - which is the reason this interface inherits nothing.\n *\n * `markets` is repeated for a different reason: the shared declaration returns two of the three words the getter\n * actually produces, so a consumer reading it there silently loses `liquidationThresholdMantissa`. The form declared\n * here returns all three.\n *\n * Anything else the fork shares with the pooled `Comptroller` - `borrowCaps`, `oracle`, `closeFactorMantissa`,\n * `minLiquidatableCollateral` - is read through `ComptrollerViewInterface` as usual.\n */\ninterface SpokeComptrollerViewInterface {\n /**\n * @notice The listing state and both collateral weights of a market\n * @dev The auto-generated getter over the `markets` mapping. It returns three words, one per non-mapping member\n * of `Market`. `ComptrollerViewInterface` declares only the first two, so a caller reading the getter through\n * that declaration drops `liquidationThresholdMantissa` without any error: the extra word is simply trailing\n * return data the decoder ignores. Read it here to get all three.\n * @param vToken The market to query\n * @return isListed True once the market has been added to the pool\n * @return collateralFactorMantissa The most an account may borrow against this collateral, scaled by 1e18\n * @return liquidationThresholdMantissa The weighting past which the position becomes liquidatable, scaled by 1e18\n */\n function markets(\n address vToken\n ) external view returns (bool isListed, uint256 collateralFactorMantissa, uint256 liquidationThresholdMantissa);\n\n /**\n * @notice Whether a market may be liquidated while the borrower is still above the liquidation threshold\n * @dev Read in `preLiquidateHook`. While this is set the close factor no longer bounds the repayment, so a\n * position in the market can be closed in full in one call.\n * @param vToken The market to read the setting of\n * @return enabled True if this market allows liquidation without a shortfall\n */\n function isForcedLiquidationEnabled(address vToken) external view returns (bool enabled);\n\n /**\n * @notice The most underlying a market will hold before it stops accepting supply\n * @dev `type(uint256).max` disables the check. Zero is a real cap of zero rather than an \"unset\" sentinel:\n * `preMintHook` compares `nextTotalSupply > supplyCap`, so a market left at zero rejects every mint.\n * @param vToken The market to query\n * @return cap The market's supply cap, in underlying units\n */\n function supplyCaps(address vToken) external view returns (uint256 cap);\n\n /**\n * @notice Whether a market accepts supply only from the accounts on its supply allowlist\n * @dev Enforced in `preMintHook` alone. Redeeming is never restricted, and a market that has never had this\n * enabled accepts supply from anyone.\n * @param vToken The market to read the setting of\n * @return enabled True if the market's supply allowlist is armed\n */\n function isSupplyAllowlistEnabled(address vToken) external view returns (bool enabled);\n\n /**\n * @notice Whether an account may supply to a market while that market's supply allowlist is enabled\n * @dev The account this meters is the one CREDITED with the newly minted vTokens, not the one paying for them:\n * `mintBehalf` lets a third party fund a mint attributed to someone else, and metering the recipient is what\n * bounds the market's supply.\n * @param vToken The market whose allowlist to read\n * @param supplier The account to test\n * @return allowed True if `supplier` may be credited with this market's vTokens\n */\n function isAllowedSupplier(address vToken, address supplier) external view returns (bool allowed);\n\n /**\n * @notice Whether seizing collateral in this pool is restricted to the accounts on the liquidation allowlist\n * @dev Pool-wide rather than per market, because `healAccount` seizes across every market the borrower is in and\n * so cannot attribute a seizure to a single one of them.\n * @return enabled True if the pool's liquidation allowlist is armed\n */\n function isLiquidationAllowlistEnabled() external view returns (bool enabled);\n\n /**\n * @notice Whether an account may seize collateral in this pool while the liquidation allowlist is enabled\n * @param liquidator The account to test\n * @return allowed True if `liquidator` may seize collateral in this pool\n */\n function isAllowedLiquidator(address liquidator) external view returns (bool allowed);\n\n /**\n * @notice The discount a market has of its own, or zero when it takes the pool-wide one\n * @dev Zero is the \"unset\" sentinel rather than a real discount. Read `effectiveLiquidationIncentive` to get the\n * value that actually applies to a market.\n * @param vToken The collateral market to read the discount of\n * @return incentiveMantissa The market's own discount scaled by 1e18, or zero if it has none\n */\n function liquidationIncentives(address vToken) external view returns (uint256 incentiveMantissa);\n\n /**\n * @notice The discount that applies to a market: its own if it has one, otherwise the pool-wide value\n * @param vToken The collateral market to read the discount of\n * @return incentiveMantissa The discount that prices this market's collateral, scaled by 1e18\n */\n function effectiveLiquidationIncentive(address vToken) external view returns (uint256 incentiveMantissa);\n\n /**\n * @notice The oracle that bounds an asset's price against a recent window\n * @dev Read only where the collateral factor weights a position. The liquidation-threshold paths stay on the\n * `oracle`, because they route liquidations. Zero until governance sets it, and while it is zero borrowing and\n * redeeming fail closed.\n * @return boundedOracle The deviation-bounded oracle this pool prices borrowing capacity through\n */\n function deviationBoundedOracle() external view returns (IDeviationBoundedOracle boundedOracle);\n\n /**\n * @notice Whether an action is paused on a market\n * @dev Typed as `uint8` rather than `Action` so that a consumer can read this without importing the enum. The\n * encoding is identical; the deployed ordering is\n * `{ MINT, REDEEM, BORROW, REPAY, SEIZE, LIQUIDATE, TRANSFER, ENTER_MARKET, EXIT_MARKET }`.\n * @param market The market to query\n * @param action The action, as its position in `Action`\n * @return paused True if the action is paused on this market\n */\n function actionPaused(address market, uint8 action) external view returns (bool paused);\n}\n" + }, + "contracts/Spoke/SpokeComptrollerStorage.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\n\nimport { ResilientOracleInterface } from \"@venusprotocol/oracle/contracts/interfaces/OracleInterface.sol\";\nimport { IDeviationBoundedOracle } from \"@venusprotocol/oracle/contracts/interfaces/IDeviationBoundedOracle.sol\";\n\nimport { VToken } from \"../VToken.sol\";\nimport { RewardsDistributor } from \"../Rewards/RewardsDistributor.sol\";\nimport { Action } from \"../ComptrollerInterface.sol\";\n\n/**\n * @title SpokeComptrollerStorage\n * @author Venus\n * @notice Storage layout for the `SpokeComptroller` contract.\n * @dev Fork of `ComptrollerStorage` (`contracts/ComptrollerStorage.sol`), kept separate so the spoke layout and the\n * `AccountLiquiditySnapshot` struct can change without touching the shared implementation. Re-synced by hand when\n * `ComptrollerStorage` changes, like the implementation it accompanies.\n */\ncontract SpokeComptrollerStorage {\n struct LiquidationOrder {\n VToken vTokenCollateral;\n VToken vTokenBorrowed;\n uint256 repayAmount;\n }\n\n /// @dev `totalCollateral` and `maxClearableDebt` are only meaningful under\n /// `WeightFunction.USE_LIQUIDATION_THRESHOLD`, which is the weighting every caller that reads them passes. Under\n /// the collateral factor `totalCollateral` is derived from the deviation-bounded collateral price rather than spot,\n /// and `maxClearableDebt` is not accumulated at all, so it stays at zero. A new caller on that weighting must not\n /// start reading either one without deciding what it wants first: a zero `maxClearableDebt` puts `healAccount` on a\n /// repayment percentage of zero, which forgives the whole position as bad debt.\n struct AccountLiquiditySnapshot {\n uint256 totalCollateral;\n uint256 weightedCollateral;\n uint256 borrows;\n uint256 effects;\n uint256 liquidity;\n uint256 shortfall;\n // The largest borrow value the account's collateral can clear without leaving bad debt behind, computed as\n // the sum over the account's collateral markets of `collateralValue / liquidationIncentive`, each market at\n // its own incentive. Used to route an under-threshold account between `liquidateAccount` and `healAccount`.\n uint256 maxClearableDebt;\n }\n\n struct RewardSpeeds {\n address rewardToken;\n uint256 supplySpeed;\n uint256 borrowSpeed;\n }\n\n struct Market {\n // Whether or not this market is listed\n bool isListed;\n // Multiplier representing the most one can borrow against their collateral in this market.\n // For instance, 0.9 to allow borrowing 90% of collateral value.\n // Must be between 0 and 1, and stored as a mantissa.\n uint256 collateralFactorMantissa;\n // Multiplier representing the collateralization after which the borrow is eligible\n // for liquidation. For instance, 0.8 liquidate when the borrow is 80% of collateral\n // value. Must be between 0 and collateral factor, stored as a mantissa.\n uint256 liquidationThresholdMantissa;\n // Per-market mapping of \"accounts in this asset\"\n mapping(address => bool) accountMembership;\n }\n\n /**\n * @notice Oracle which gives the price of any given asset\n */\n ResilientOracleInterface public oracle;\n\n /**\n * @notice Multiplier used to calculate the maximum repayAmount when liquidating a borrow\n */\n uint256 public closeFactorMantissa;\n\n /**\n * @notice Multiplier representing the discount on collateral that a liquidator receives, applied to every market\n * that has no discount of its own\n * @dev Internal rather than public, because the `liquidationIncentiveMantissa()` getter has to resolve the\n * caller's market before answering: `VToken` reads that getter on itself and needs the discount that prices its\n * own collateral, not the pool-wide default. See `SpokeComptroller.liquidationIncentiveMantissa`.\n */\n uint256 internal _poolLiquidationIncentiveMantissa;\n\n /**\n * @notice Per-account mapping of \"assets you are in\"\n */\n mapping(address => VToken[]) public accountAssets;\n\n /**\n * @notice Official mapping of vTokens -> Market metadata\n * @dev Used e.g. to determine if a market is supported\n */\n mapping(address => Market) public markets;\n\n /// @notice A list of all markets\n VToken[] public allMarkets;\n\n /// @notice Borrow caps enforced by borrowAllowed for each vToken address. Defaults to zero which restricts borrowing.\n mapping(address => uint256) public borrowCaps;\n\n /// @notice Minimal collateral required for regular (non-batch) liquidations\n uint256 public minLiquidatableCollateral;\n\n /// @notice Supply caps enforced by mintAllowed for each vToken address. Defaults to zero which corresponds to minting not allowed\n mapping(address => uint256) public supplyCaps;\n\n /// @notice True if a certain action is paused on a certain market\n mapping(address => mapping(Action => bool)) internal _actionPaused;\n\n // List of Reward Distributors added\n RewardsDistributor[] internal rewardsDistributors;\n\n // Used to check if rewards distributor is added\n mapping(address => bool) internal rewardsDistributorExists;\n\n /// @notice Flag indicating whether forced liquidation enabled for a market\n mapping(address => bool) public isForcedLiquidationEnabled;\n\n uint256 internal constant NO_ERROR = 0;\n\n // closeFactorMantissa must be strictly greater than this value\n uint256 internal constant MIN_CLOSE_FACTOR_MANTISSA = 0.05e18; // 0.05\n\n // closeFactorMantissa must not exceed this value\n uint256 internal constant MAX_CLOSE_FACTOR_MANTISSA = 0.9e18; // 0.9\n\n // No collateralFactorMantissa may exceed this value\n uint256 internal constant MAX_COLLATERAL_FACTOR_MANTISSA = 0.95e18; // 0.95\n\n // No pool-wide liquidation incentive may fall below this value. It is `1e18 + VToken`'s\n // `DEFAULT_PROTOCOL_SEIZE_SHARE_MANTISSA`, restated here because that constant is internal to `VToken` and a\n // market's share is only readable once the market exists. A newly listed market carries the default share and no\n // incentive of its own, so this floor is what keeps its first liquidation from paying the liquidator less than it\n // repaid. It is a backstop for that case, not a full guarantee: a market whose share is later raised above the\n // default needs an incentive of its own, which `setMarketLiquidationIncentive` and `VToken.setProtocolSeizeShare`\n // bound from both sides.\n uint256 internal constant MIN_POOL_LIQUIDATION_INCENTIVE_MANTISSA = 1.05e18; // 1e18 + 0.05e18\n\n /// @notice Whether the delegate is allowed to borrow or redeem on behalf of the user\n //mapping(address user => mapping (address delegate => bool approved)) public approvedDelegates;\n mapping(address => mapping(address => bool)) public approvedDelegates;\n\n /// @notice Whether a market accepts supply only from the accounts on its supply allowlist. Keyed by market, and\n /// disabled by default, so a newly listed market accepts supply from anyone.\n mapping(address => bool) public isSupplyAllowlistEnabled;\n\n /// @notice The accounts a market accepts supply from while its supply allowlist is enabled. Keyed by market,\n /// then by account.\n mapping(address => mapping(address => bool)) public isAllowedSupplier;\n\n /// @notice Whether seizing collateral in this pool is restricted to the accounts on the liquidation allowlist.\n /// Disabled by default.\n /// @dev Pool-wide rather than per market, because `healAccount` seizes across every market the borrower is in and\n /// so cannot attribute a seizure to a single one of them.\n bool public isLiquidationAllowlistEnabled;\n\n /// @notice The accounts allowed to seize collateral in this pool while the liquidation allowlist is enabled\n mapping(address => bool) public isAllowedLiquidator;\n\n /// @notice Per-market discount a liquidator receives on the collateral it seizes, scaled by 1e18. Keyed by the\n /// collateral market, since that is what the discount prices.\n /// @dev Zero means no market value has been set, in which case `_poolLiquidationIncentiveMantissa` applies. That\n /// pool-wide value is always at least 1e18 for a listed market: `PoolRegistry.addMarket` refuses to add one to an\n /// unregistered pool, and registering a pool goes through `setLiquidationIncentive`, which enforces the floor.\n mapping(address => uint256) public liquidationIncentives;\n\n /// @notice Oracle that bounds an asset's price against a recent window, so a deviating print cannot inflate\n /// borrowing capacity. Read only where the collateral factor weights the position; the liquidation-threshold\n /// paths stay on `oracle`, because they route liquidations.\n IDeviationBoundedOracle public deviationBoundedOracle;\n\n /**\n * @dev This empty reserved space is put in place to allow future versions to add new\n * variables without shifting down storage in the inheritance chain.\n * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\n * The size is derived from the 47 slots `ComptrollerStorage` reserves: plus one for the Prime token slot, which\n * is unused here and is returned to the gap rather than left as a hole, minus the six slots the allowlists, the\n * per-market liquidation incentives and the deviation-bounded oracle above take. This contract therefore occupies\n * the same number of slots as the one it was forked from.\n *\n * That is not layout compatibility. Reclaiming the Prime slot moved every variable declared after it up by one,\n * so `approvedDelegates` here sits in the slot `ComptrollerStorage` gives to `prime`, and the two layouts cannot\n * be swapped under a live pool in either direction. They do not have to be: a spoke pool upgrades through its own\n * beacon. `tests/hardhat/Spoke/storageLayout.ts` pins the slot list, the divergence and this gap size.\n */\n uint256[42] private __gap;\n}\n" + }, + "contracts/VToken.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\n\nimport { Ownable2StepUpgradeable } from \"@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol\";\nimport { IERC20Upgradeable } from \"@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol\";\nimport { SafeERC20Upgradeable } from \"@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol\";\nimport { AccessControlledV8 } from \"@venusprotocol/governance-contracts/contracts/Governance/AccessControlledV8.sol\";\nimport { IProtocolShareReserve } from \"@venusprotocol/protocol-reserve/contracts/Interfaces/IProtocolShareReserve.sol\";\n\nimport { VTokenInterface } from \"./VTokenInterfaces.sol\";\nimport { ComptrollerInterface, ComptrollerViewInterface } from \"./ComptrollerInterface.sol\";\nimport { TokenErrorReporter } from \"./ErrorReporter.sol\";\nimport { InterestRateModel } from \"./InterestRateModel.sol\";\nimport { ExponentialNoError } from \"./ExponentialNoError.sol\";\nimport { TimeManagerV8 } from \"@venusprotocol/solidity-utilities/contracts/TimeManagerV8.sol\";\nimport { ensureNonzeroAddress } from \"./lib/validators.sol\";\n\n/**\n * @title VToken\n * @author Venus\n * @notice Each asset that is supported by a pool is integrated through an instance of the `VToken` contract. As outlined in the protocol overview,\n * each isolated pool creates its own `vToken` corresponding to an asset. Within a given pool, each included `vToken` is referred to as a market of\n * the pool. The main actions a user regularly interacts with in a market are:\n\n- mint/redeem of vTokens;\n- transfer of vTokens;\n- borrow/repay a loan on an underlying asset;\n- liquidate a borrow or liquidate/heal an account.\n\n * A user supplies the underlying asset to a pool by minting `vTokens`, where the corresponding `vToken` amount is determined by the `exchangeRate`.\n * The `exchangeRate` will change over time, dependent on a number of factors, some of which accrue interest. Additionally, once users have minted\n * `vToken` in a pool, they can borrow any asset in the isolated pool by using their `vToken` as collateral. In order to borrow an asset or use a `vToken`\n * as collateral, the user must be entered into each corresponding market (else, the `vToken` will not be considered collateral for a borrow). Note that\n * a user may borrow up to a portion of their collateral determined by the market’s collateral factor. However, if their borrowed amount exceeds an amount\n * calculated using the market’s corresponding liquidation threshold, the borrow is eligible for liquidation. When a user repays a borrow, they must also\n * pay off interest accrued on the borrow.\n * \n * The Venus protocol includes unique mechanisms for healing an account and liquidating an account. These actions are performed in the `Comptroller`\n * and consider all borrows and collateral for which a given account is entered within a market. These functions may only be called on an account with a\n * total collateral amount that is no larger than a universal `minLiquidatableCollateral` value, which is used for all markets within a `Comptroller`.\n * Both functions settle all of an account’s borrows, but `healAccount()` may add `badDebt` to a vToken. For more detail, see the description of\n * `healAccount()` and `liquidateAccount()` in the `Comptroller` summary section below.\n */\ncontract VToken is\n Ownable2StepUpgradeable,\n AccessControlledV8,\n VTokenInterface,\n ExponentialNoError,\n TokenErrorReporter,\n TimeManagerV8\n{\n using SafeERC20Upgradeable for IERC20Upgradeable;\n\n uint256 internal constant DEFAULT_PROTOCOL_SEIZE_SHARE_MANTISSA = 5e16; // 5%\n\n // Maximum fraction of interest that can be set aside for reserves\n uint256 internal constant MAX_RESERVE_FACTOR_MANTISSA = 1e18;\n\n // Maximum borrow rate that can ever be applied per slot(block or second)\n /// @custom:oz-upgrades-unsafe-allow state-variable-immutable\n uint256 internal immutable MAX_BORROW_RATE_MANTISSA;\n\n /**\n * Reentrancy Guard **\n */\n\n /**\n * @dev Prevents a contract from calling itself, directly or indirectly.\n */\n modifier nonReentrant() {\n require(_notEntered, \"re-entered\");\n _notEntered = false;\n _;\n _notEntered = true; // get a gas-refund post-Istanbul\n }\n\n /**\n * @param timeBased_ A boolean indicating whether the contract is based on time or block.\n * @param blocksPerYear_ The number of blocks per year\n * @param maxBorrowRateMantissa_ The maximum value of borrowing rate mantissa\n * @custom:oz-upgrades-unsafe-allow constructor\n */\n constructor(\n bool timeBased_,\n uint256 blocksPerYear_,\n uint256 maxBorrowRateMantissa_\n ) TimeManagerV8(timeBased_, blocksPerYear_) {\n // Note that the contract is upgradeable. Use initialize() or reinitializers\n // to set the state variables.\n require(maxBorrowRateMantissa_ <= 1e18, \"Max borrow rate must be <= 1e18\");\n\n MAX_BORROW_RATE_MANTISSA = maxBorrowRateMantissa_;\n _disableInitializers();\n }\n\n /**\n * @notice Construct a new money market\n * @param underlying_ The address of the underlying asset\n * @param comptroller_ The address of the Comptroller\n * @param interestRateModel_ The address of the interest rate model\n * @param initialExchangeRateMantissa_ The initial exchange rate, scaled by 1e18\n * @param name_ ERC-20 name of this token\n * @param symbol_ ERC-20 symbol of this token\n * @param decimals_ ERC-20 decimal precision of this token\n * @param admin_ Address of the administrator of this token\n * @param accessControlManager_ AccessControlManager contract address\n * @param riskManagement Addresses of risk & income related contracts\n * @param reserveFactorMantissa_ Percentage of borrow interest that goes to reserves (from 0 to 1e18)\n * @custom:error ZeroAddressNotAllowed is thrown when admin address is zero\n * @custom:error ZeroAddressNotAllowed is thrown when shortfall contract address is zero\n * @custom:error ZeroAddressNotAllowed is thrown when protocol share reserve address is zero\n */\n function initialize(\n address underlying_,\n ComptrollerInterface comptroller_,\n InterestRateModel interestRateModel_,\n uint256 initialExchangeRateMantissa_,\n string memory name_,\n string memory symbol_,\n uint8 decimals_,\n address admin_,\n address accessControlManager_,\n RiskManagementInit memory riskManagement,\n uint256 reserveFactorMantissa_\n ) external initializer {\n ensureNonzeroAddress(admin_);\n\n // Initialize the market\n _initialize(\n underlying_,\n comptroller_,\n interestRateModel_,\n initialExchangeRateMantissa_,\n name_,\n symbol_,\n decimals_,\n admin_,\n accessControlManager_,\n riskManagement,\n reserveFactorMantissa_\n );\n }\n\n /**\n * @notice Transfer `amount` tokens from `msg.sender` to `dst`\n * @param dst The address of the destination account\n * @param amount The number of tokens to transfer\n * @return success True if the transfer succeeded, reverts otherwise\n * @custom:event Emits Transfer event on success\n * @custom:error TransferNotAllowed is thrown if trying to transfer to self\n * @custom:access Not restricted\n */\n function transfer(address dst, uint256 amount) external override nonReentrant returns (bool) {\n _transferTokens(msg.sender, msg.sender, dst, amount);\n return true;\n }\n\n /**\n * @notice Transfer `amount` tokens from `src` to `dst`\n * @param src The address of the source account\n * @param dst The address of the destination account\n * @param amount The number of tokens to transfer\n * @return success True if the transfer succeeded, reverts otherwise\n * @custom:event Emits Transfer event on success\n * @custom:error TransferNotAllowed is thrown if trying to transfer to self\n * @custom:access Not restricted\n */\n function transferFrom(address src, address dst, uint256 amount) external override nonReentrant returns (bool) {\n _transferTokens(msg.sender, src, dst, amount);\n return true;\n }\n\n /**\n * @notice Approve `spender` to transfer up to `amount` from `src`\n * @dev This will overwrite the approval amount for `spender`\n * and is subject to issues noted [here](https://eips.ethereum.org/EIPS/eip-20#approve)\n * @param spender The address of the account which may transfer tokens\n * @param amount The number of tokens that are approved (uint256.max means infinite)\n * @return success Whether or not the approval succeeded\n * @custom:event Emits Approval event\n * @custom:access Not restricted\n * @custom:error ZeroAddressNotAllowed is thrown when spender address is zero\n */\n function approve(address spender, uint256 amount) external override returns (bool) {\n ensureNonzeroAddress(spender);\n\n address src = msg.sender;\n transferAllowances[src][spender] = amount;\n emit Approval(src, spender, amount);\n return true;\n }\n\n /**\n * @notice Increase approval for `spender`\n * @param spender The address of the account which may transfer tokens\n * @param addedValue The number of additional tokens spender can transfer\n * @return success Whether or not the approval succeeded\n * @custom:event Emits Approval event\n * @custom:access Not restricted\n * @custom:error ZeroAddressNotAllowed is thrown when spender address is zero\n */\n function increaseAllowance(address spender, uint256 addedValue) external override returns (bool) {\n ensureNonzeroAddress(spender);\n\n address src = msg.sender;\n uint256 newAllowance = transferAllowances[src][spender];\n newAllowance += addedValue;\n transferAllowances[src][spender] = newAllowance;\n\n emit Approval(src, spender, newAllowance);\n return true;\n }\n\n /**\n * @notice Decreases approval for `spender`\n * @param spender The address of the account which may transfer tokens\n * @param subtractedValue The number of tokens to remove from total approval\n * @return success Whether or not the approval succeeded\n * @custom:event Emits Approval event\n * @custom:access Not restricted\n * @custom:error ZeroAddressNotAllowed is thrown when spender address is zero\n */\n function decreaseAllowance(address spender, uint256 subtractedValue) external override returns (bool) {\n ensureNonzeroAddress(spender);\n\n address src = msg.sender;\n uint256 currentAllowance = transferAllowances[src][spender];\n require(currentAllowance >= subtractedValue, \"decreased allowance below zero\");\n unchecked {\n currentAllowance -= subtractedValue;\n }\n\n transferAllowances[src][spender] = currentAllowance;\n\n emit Approval(src, spender, currentAllowance);\n return true;\n }\n\n /**\n * @notice Get the underlying balance of the `owner`\n * @dev This also accrues interest in a transaction\n * @param owner The address of the account to query\n * @return amount The amount of underlying owned by `owner`\n */\n function balanceOfUnderlying(address owner) external override returns (uint256) {\n Exp memory exchangeRate = Exp({ mantissa: exchangeRateCurrent() });\n return mul_ScalarTruncate(exchangeRate, accountTokens[owner]);\n }\n\n /**\n * @notice Returns the current total borrows plus accrued interest\n * @return totalBorrows The total borrows with interest\n */\n function totalBorrowsCurrent() external override nonReentrant returns (uint256) {\n accrueInterest();\n return totalBorrows;\n }\n\n /**\n * @notice Accrue interest to updated borrowIndex and then calculate account's borrow balance using the updated borrowIndex\n * @param account The address whose balance should be calculated after updating borrowIndex\n * @return borrowBalance The calculated balance\n */\n function borrowBalanceCurrent(address account) external override nonReentrant returns (uint256) {\n accrueInterest();\n return _borrowBalanceStored(account);\n }\n\n /**\n * @notice Sender supplies assets into the market and receives vTokens in exchange\n * @dev Accrues interest whether or not the operation succeeds, unless reverted\n * @param mintAmount The amount of the underlying asset to supply\n * @return error Always NO_ERROR for compatibility with Venus core tooling\n * @custom:event Emits Mint and Transfer events; may emit AccrueInterest\n * @custom:access Not restricted\n */\n function mint(uint256 mintAmount) external override nonReentrant returns (uint256) {\n accrueInterest();\n\n _mintFresh(msg.sender, msg.sender, mintAmount);\n return NO_ERROR;\n }\n\n /**\n * @notice Sender calls on-behalf of minter. minter supplies assets into the market and receives vTokens in exchange\n * @dev Accrues interest whether or not the operation succeeds, unless reverted\n * @param minter User whom the supply will be attributed to\n * @param mintAmount The amount of the underlying asset to supply\n * @return error Always NO_ERROR for compatibility with Venus core tooling\n * @custom:event Emits Mint and Transfer events; may emit AccrueInterest\n * @custom:access Not restricted\n * @custom:error ZeroAddressNotAllowed is thrown when minter address is zero\n */\n function mintBehalf(address minter, uint256 mintAmount) external override nonReentrant returns (uint256) {\n ensureNonzeroAddress(minter);\n\n accrueInterest();\n\n _mintFresh(msg.sender, minter, mintAmount);\n return NO_ERROR;\n }\n\n /**\n * @notice Sender redeems vTokens in exchange for the underlying asset\n * @dev Accrues interest whether or not the operation succeeds, unless reverted\n * @param redeemTokens The number of vTokens to redeem into underlying\n * @return error Always NO_ERROR for compatibility with Venus core tooling\n * @custom:event Emits Redeem and Transfer events; may emit AccrueInterest\n * @custom:error RedeemTransferOutNotPossible is thrown when the protocol has insufficient cash\n * @custom:access Not restricted\n */\n function redeem(uint256 redeemTokens) external override nonReentrant returns (uint256) {\n accrueInterest();\n\n _redeemFresh(msg.sender, msg.sender, redeemTokens, 0);\n return NO_ERROR;\n }\n\n /**\n * @notice Sender redeems assets on behalf of some other address. This function is only available\n * for senders, explicitly marked as delegates of the supplier using `comptroller.updateDelegate`\n * @dev Accrues interest whether or not the operation succeeds, unless reverted\n * @param redeemer The user on behalf of whom to redeem\n * @param redeemTokens The number of vTokens to redeem into underlying\n * @return error Always NO_ERROR for compatibility with Venus core tooling\n * @custom:error InsufficientRedeemApproval is thrown when sender is not approved by the redeemer for the given amount\n * @custom:error RedeemTransferOutNotPossible is thrown when the protocol has insufficient cash\n * @custom:event Emits Redeem and Transfer events; may emit AccrueInterest\n * @custom:access Not restricted\n */\n function redeemBehalf(address redeemer, uint256 redeemTokens) external override nonReentrant returns (uint256) {\n _ensureSenderIsDelegateOf(redeemer);\n\n accrueInterest();\n\n _redeemFresh(redeemer, msg.sender, redeemTokens, 0);\n return NO_ERROR;\n }\n\n /**\n * @notice Sender redeems vTokens in exchange for a specified amount of underlying asset\n * @dev Accrues interest whether or not the operation succeeds, unless reverted\n * @param redeemAmount The amount of underlying to receive from redeeming vTokens\n * @return error Always NO_ERROR for compatibility with Venus core tooling\n */\n function redeemUnderlying(uint256 redeemAmount) external override nonReentrant returns (uint256) {\n accrueInterest();\n\n _redeemFresh(msg.sender, msg.sender, 0, redeemAmount);\n return NO_ERROR;\n }\n\n /**\n * @notice Sender redeems underlying assets on behalf of some other address. This function is only available\n * for senders, explicitly marked as delegates of the supplier using `comptroller.updateDelegate`\n * @dev Accrues interest whether or not the operation succeeds, unless reverted\n * @param redeemer, on behalf of whom to redeem\n * @param redeemAmount The amount of underlying to receive from redeeming vTokens\n * @return error Always NO_ERROR for compatibility with Venus core tooling\n * @custom:error InsufficientRedeemApproval is thrown when sender is not approved by the redeemer for the given amount\n * @custom:event Emits Redeem and Transfer events; may emit AccrueInterest\n * @custom:access Not restricted\n */\n function redeemUnderlyingBehalf(\n address redeemer,\n uint256 redeemAmount\n ) external override nonReentrant returns (uint256) {\n _ensureSenderIsDelegateOf(redeemer);\n\n accrueInterest();\n\n _redeemFresh(redeemer, msg.sender, 0, redeemAmount);\n return NO_ERROR;\n }\n\n /**\n * @notice Sender borrows assets from the protocol to their own address\n * @param borrowAmount The amount of the underlying asset to borrow\n * @return error Always NO_ERROR for compatibility with Venus core tooling\n * @custom:event Emits Borrow event; may emit AccrueInterest\n * @custom:error BorrowCashNotAvailable is thrown when the protocol has insufficient cash\n * @custom:access Not restricted\n */\n function borrow(uint256 borrowAmount) external override nonReentrant returns (uint256) {\n accrueInterest();\n\n _borrowFresh(msg.sender, msg.sender, borrowAmount);\n return NO_ERROR;\n }\n\n /**\n * @notice Sender borrows assets on behalf of some other address. This function is only available\n * for senders, explicitly marked as delegates of the borrower using `comptroller.updateDelegate`\n * @param borrower The borrower, on behalf of whom to borrow\n * @param borrowAmount The amount of the underlying asset to borrow\n * @return error Always NO_ERROR for compatibility with Venus core tooling\n * @custom:error DelegateNotApproved is thrown if caller is not approved delegate\n * @custom:error BorrowCashNotAvailable is thrown when the protocol has insufficient cash\n * @custom:event Emits Borrow event; may emit AccrueInterest\n * @custom:access Not restricted\n */\n function borrowBehalf(address borrower, uint256 borrowAmount) external override returns (uint256) {\n _ensureSenderIsDelegateOf(borrower);\n accrueInterest();\n\n _borrowFresh(borrower, msg.sender, borrowAmount);\n return NO_ERROR;\n }\n\n /**\n * @notice Sender repays their own borrow\n * @param repayAmount The amount to repay, or type(uint256).max for the full outstanding amount\n * @return error Always NO_ERROR for compatibility with Venus core tooling\n * @custom:event Emits RepayBorrow event; may emit AccrueInterest\n * @custom:access Not restricted\n */\n function repayBorrow(uint256 repayAmount) external override nonReentrant returns (uint256) {\n accrueInterest();\n\n _repayBorrowFresh(msg.sender, msg.sender, repayAmount);\n return NO_ERROR;\n }\n\n /**\n * @notice Sender repays a borrow belonging to borrower\n * @param borrower the account with the debt being payed off\n * @param repayAmount The amount to repay, or type(uint256).max for the full outstanding amount\n * @return error Always NO_ERROR for compatibility with Venus core tooling\n * @custom:event Emits RepayBorrow event; may emit AccrueInterest\n * @custom:access Not restricted\n */\n function repayBorrowBehalf(address borrower, uint256 repayAmount) external override nonReentrant returns (uint256) {\n accrueInterest();\n\n _repayBorrowFresh(msg.sender, borrower, repayAmount);\n return NO_ERROR;\n }\n\n /**\n * @notice The sender liquidates the borrowers collateral.\n * The collateral seized is transferred to the liquidator.\n * @param borrower The borrower of this vToken to be liquidated\n * @param repayAmount The amount of the underlying borrowed asset to repay\n * @param vTokenCollateral The market in which to seize collateral from the borrower\n * @return error Always NO_ERROR for compatibility with Venus core tooling\n * @custom:event Emits LiquidateBorrow event; may emit AccrueInterest\n * @custom:error LiquidateAccrueCollateralInterestFailed is thrown when it is not possible to accrue interest on the collateral vToken\n * @custom:error LiquidateCollateralFreshnessCheck is thrown when interest has not been accrued on the collateral vToken\n * @custom:error LiquidateLiquidatorIsBorrower is thrown when trying to liquidate self\n * @custom:error LiquidateCloseAmountIsZero is thrown when repayment amount is zero\n * @custom:error LiquidateCloseAmountIsUintMax is thrown when repayment amount is UINT_MAX\n * @custom:access Not restricted\n */\n function liquidateBorrow(\n address borrower,\n uint256 repayAmount,\n VTokenInterface vTokenCollateral\n ) external override returns (uint256) {\n _liquidateBorrow(msg.sender, borrower, repayAmount, vTokenCollateral, false);\n return NO_ERROR;\n }\n\n /**\n * @notice sets protocol share accumulated from liquidations\n * @dev must be equal or less than liquidation incentive - 1\n * @param newProtocolSeizeShareMantissa_ new protocol share mantissa\n * @custom:event Emits NewProtocolSeizeShare event on success\n * @custom:error Unauthorized error is thrown when the call is not authorized by AccessControlManager\n * @custom:error ProtocolSeizeShareTooBig is thrown when the new seize share is too high\n * @custom:access Controlled by AccessControlManager\n */\n function setProtocolSeizeShare(uint256 newProtocolSeizeShareMantissa_) external {\n _checkAccessAllowed(\"setProtocolSeizeShare(uint256)\");\n uint256 liquidationIncentive = ComptrollerViewInterface(address(comptroller)).liquidationIncentiveMantissa();\n if (newProtocolSeizeShareMantissa_ + MANTISSA_ONE > liquidationIncentive) {\n revert ProtocolSeizeShareTooBig();\n }\n\n uint256 oldProtocolSeizeShareMantissa = protocolSeizeShareMantissa;\n protocolSeizeShareMantissa = newProtocolSeizeShareMantissa_;\n emit NewProtocolSeizeShare(oldProtocolSeizeShareMantissa, newProtocolSeizeShareMantissa_);\n }\n\n /**\n * @notice accrues interest and sets a new reserve factor for the protocol using _setReserveFactorFresh\n * @dev Admin function to accrue interest and set a new reserve factor\n * @param newReserveFactorMantissa New reserve factor (from 0 to 1e18)\n * @custom:event Emits NewReserveFactor event; may emit AccrueInterest\n * @custom:error Unauthorized error is thrown when the call is not authorized by AccessControlManager\n * @custom:error SetReserveFactorBoundsCheck is thrown when the new reserve factor is too high\n * @custom:access Controlled by AccessControlManager\n */\n function setReserveFactor(uint256 newReserveFactorMantissa) external override nonReentrant {\n _checkAccessAllowed(\"setReserveFactor(uint256)\");\n\n accrueInterest();\n _setReserveFactorFresh(newReserveFactorMantissa);\n }\n\n /**\n * @notice Accrues interest and reduces reserves by transferring to the protocol reserve contract\n * @dev Gracefully return if reserves already reduced in accrueInterest\n * @param reduceAmount Amount of reduction to reserves\n * @custom:event Emits ReservesReduced event; may emit AccrueInterest\n * @custom:error ReduceReservesCashNotAvailable is thrown when the vToken does not have sufficient cash\n * @custom:error ReduceReservesCashValidation is thrown when trying to withdraw more cash than the reserves have\n * @custom:access Not restricted\n */\n function reduceReserves(uint256 reduceAmount) external override nonReentrant {\n accrueInterest();\n if (reduceReservesBlockNumber == getBlockNumberOrTimestamp()) return;\n _reduceReservesFresh(reduceAmount);\n }\n\n /**\n * @notice The sender adds to reserves.\n * @param addAmount The amount of underlying token to add as reserves\n * @custom:event Emits ReservesAdded event; may emit AccrueInterest\n * @custom:access Not restricted\n */\n function addReserves(uint256 addAmount) external override nonReentrant {\n accrueInterest();\n _addReservesFresh(addAmount);\n }\n\n /**\n * @notice accrues interest and updates the interest rate model using _setInterestRateModelFresh\n * @dev Admin function to accrue interest and update the interest rate model\n * @param newInterestRateModel the new interest rate model to use\n * @custom:event Emits NewMarketInterestRateModel event; may emit AccrueInterest\n * @custom:error Unauthorized error is thrown when the call is not authorized by AccessControlManager\n * @custom:access Controlled by AccessControlManager\n */\n function setInterestRateModel(InterestRateModel newInterestRateModel) external override {\n _checkAccessAllowed(\"setInterestRateModel(address)\");\n\n accrueInterest();\n _setInterestRateModelFresh(newInterestRateModel);\n }\n\n /**\n * @notice Repays a certain amount of debt, treats the rest of the borrow as bad debt, essentially\n * \"forgiving\" the borrower. Healing is a situation that should rarely happen. However, some pools\n * may list risky assets or be configured improperly – we want to still handle such cases gracefully.\n * We assume that Comptroller does the seizing, so this function is only available to Comptroller.\n * @dev This function does not call any Comptroller hooks (like \"healAllowed\"), because we assume\n * the Comptroller does all the necessary checks before calling this function.\n * @param payer account who repays the debt\n * @param borrower account to heal\n * @param repayAmount amount to repay\n * @custom:event Emits RepayBorrow, BadDebtIncreased events; may emit AccrueInterest\n * @custom:error HealBorrowUnauthorized is thrown when the request does not come from Comptroller\n * @custom:access Only Comptroller\n */\n function healBorrow(address payer, address borrower, uint256 repayAmount) external override nonReentrant {\n if (repayAmount != 0) {\n comptroller.preRepayHook(address(this), borrower);\n }\n\n if (msg.sender != address(comptroller)) {\n revert HealBorrowUnauthorized();\n }\n\n uint256 accountBorrowsPrev = _borrowBalanceStored(borrower);\n uint256 totalBorrowsNew = totalBorrows;\n\n uint256 actualRepayAmount;\n if (repayAmount != 0) {\n // _doTransferIn reverts if anything goes wrong, since we can't be sure if side effects occurred.\n // We violate checks-effects-interactions here to account for tokens that take transfer fees\n actualRepayAmount = _doTransferIn(payer, repayAmount);\n totalBorrowsNew = totalBorrowsNew - actualRepayAmount;\n emit RepayBorrow(\n payer,\n borrower,\n actualRepayAmount,\n accountBorrowsPrev - actualRepayAmount,\n totalBorrowsNew\n );\n }\n\n // The transaction will fail if trying to repay too much\n uint256 badDebtDelta = accountBorrowsPrev - actualRepayAmount;\n if (badDebtDelta != 0) {\n uint256 badDebtOld = badDebt;\n uint256 badDebtNew = badDebtOld + badDebtDelta;\n totalBorrowsNew = totalBorrowsNew - badDebtDelta;\n badDebt = badDebtNew;\n\n // We treat healing as \"repayment\", where vToken is the payer\n emit RepayBorrow(address(this), borrower, badDebtDelta, 0, totalBorrowsNew);\n emit BadDebtIncreased(borrower, badDebtDelta, badDebtOld, badDebtNew);\n }\n\n accountBorrows[borrower].principal = 0;\n accountBorrows[borrower].interestIndex = borrowIndex;\n totalBorrows = totalBorrowsNew;\n\n emit HealBorrow(payer, borrower, repayAmount);\n }\n\n /**\n * @notice The extended version of liquidations, callable only by Comptroller. May skip\n * the close factor check. The collateral seized is transferred to the liquidator.\n * @param liquidator The address repaying the borrow and seizing collateral\n * @param borrower The borrower of this vToken to be liquidated\n * @param repayAmount The amount of the underlying borrowed asset to repay\n * @param vTokenCollateral The market in which to seize collateral from the borrower\n * @param skipLiquidityCheck If set to true, allows to liquidate up to 100% of the borrow\n * regardless of the account liquidity\n * @custom:event Emits LiquidateBorrow event; may emit AccrueInterest\n * @custom:error ForceLiquidateBorrowUnauthorized is thrown when the request does not come from Comptroller\n * @custom:error LiquidateAccrueCollateralInterestFailed is thrown when it is not possible to accrue interest on the collateral vToken\n * @custom:error LiquidateCollateralFreshnessCheck is thrown when interest has not been accrued on the collateral vToken\n * @custom:error LiquidateLiquidatorIsBorrower is thrown when trying to liquidate self\n * @custom:error LiquidateCloseAmountIsZero is thrown when repayment amount is zero\n * @custom:error LiquidateCloseAmountIsUintMax is thrown when repayment amount is UINT_MAX\n * @custom:access Only Comptroller\n */\n function forceLiquidateBorrow(\n address liquidator,\n address borrower,\n uint256 repayAmount,\n VTokenInterface vTokenCollateral,\n bool skipLiquidityCheck\n ) external override {\n if (msg.sender != address(comptroller)) {\n revert ForceLiquidateBorrowUnauthorized();\n }\n _liquidateBorrow(liquidator, borrower, repayAmount, vTokenCollateral, skipLiquidityCheck);\n }\n\n /**\n * @notice Transfers collateral tokens (this market) to the liquidator.\n * @dev Will fail unless called by another vToken during the process of liquidation.\n * It's absolutely critical to use msg.sender as the borrowed vToken and not a parameter.\n * @param liquidator The account receiving seized collateral\n * @param borrower The account having collateral seized\n * @param seizeTokens The number of vTokens to seize\n * @custom:event Emits Transfer, ReservesAdded events\n * @custom:error LiquidateSeizeLiquidatorIsBorrower is thrown when trying to liquidate self\n * @custom:access Not restricted\n */\n function seize(address liquidator, address borrower, uint256 seizeTokens) external override nonReentrant {\n _seize(msg.sender, liquidator, borrower, seizeTokens);\n }\n\n /**\n * @notice Updates bad debt\n * @dev Called only when bad debt is recovered from auction. Updates internal cash balance.\n * @param recoveredAmount_ The amount of bad debt recovered\n * @custom:event Emits BadDebtRecovered event\n * @custom:access Only Shortfall contract\n */\n function badDebtRecovered(uint256 recoveredAmount_) external {\n require(msg.sender == shortfall, \"only shortfall contract can update bad debt\");\n require(recoveredAmount_ <= badDebt, \"more than bad debt recovered from auction\");\n\n uint256 badDebtOld = badDebt;\n uint256 badDebtNew = badDebtOld - recoveredAmount_;\n badDebt = badDebtNew;\n internalCash += recoveredAmount_;\n\n emit BadDebtRecovered(badDebtOld, badDebtNew);\n }\n\n /**\n * @notice Sets protocol share reserve contract address\n * @param protocolShareReserve_ The address of the protocol share reserve contract\n * @custom:error ZeroAddressNotAllowed is thrown when protocol share reserve address is zero\n * @custom:access Only Governance\n */\n function setProtocolShareReserve(address payable protocolShareReserve_) external onlyOwner {\n _setProtocolShareReserve(protocolShareReserve_);\n }\n\n /**\n * @notice Sets shortfall contract address\n * @param shortfall_ The address of the shortfall contract\n * @custom:error ZeroAddressNotAllowed is thrown when shortfall contract address is zero\n * @custom:access Only Governance\n */\n function setShortfallContract(address shortfall_) external onlyOwner {\n _setShortfallContract(shortfall_);\n }\n\n /**\n * @notice A public function to sweep accidental ERC-20 transfers to this contract. Tokens are sent to admin (timelock)\n * @param token The address of the ERC-20 token to sweep\n * @custom:access Only Governance\n */\n function sweepToken(IERC20Upgradeable token) external override {\n require(msg.sender == owner(), \"VToken::sweepToken: only admin can sweep tokens\");\n require(address(token) != underlying, \"VToken::sweepToken: can not sweep underlying token\");\n uint256 balance = token.balanceOf(address(this));\n token.safeTransfer(owner(), balance);\n\n emit SweepToken(address(token));\n }\n\n /**\n * @notice Sync internalCash with the actual underlying token balance\n * @dev Used for one-time migration after upgrade to initialize internalCash\n * @custom:event Emits CashSynced\n * @custom:access Controlled by AccessControlManager\n */\n function syncCash() external override nonReentrant {\n _checkAccessAllowed(\"syncCash()\");\n uint256 oldInternalCash = internalCash;\n uint256 actualCash = IERC20Upgradeable(underlying).balanceOf(address(this));\n internalCash = actualCash;\n emit CashSynced(oldInternalCash, actualCash);\n }\n\n /**\n * @notice A public function to set new threshold of slot(block or second) difference after which funds will be sent to the protocol share reserve\n * @param _newReduceReservesBlockOrTimestampDelta slot(block or second) difference value\n * @custom:access Only Governance\n */\n function setReduceReservesBlockDelta(uint256 _newReduceReservesBlockOrTimestampDelta) external {\n _checkAccessAllowed(\"setReduceReservesBlockDelta(uint256)\");\n require(_newReduceReservesBlockOrTimestampDelta > 0, \"Invalid Input\");\n emit NewReduceReservesBlockDelta(reduceReservesBlockDelta, _newReduceReservesBlockOrTimestampDelta);\n reduceReservesBlockDelta = _newReduceReservesBlockOrTimestampDelta;\n }\n\n /**\n * @notice Get the current allowance from `owner` for `spender`\n * @param owner The address of the account which owns the tokens to be spent\n * @param spender The address of the account which may transfer tokens\n * @return amount The number of tokens allowed to be spent (type(uint256).max means infinite)\n */\n function allowance(address owner, address spender) external view override returns (uint256) {\n return transferAllowances[owner][spender];\n }\n\n /**\n * @notice Get the token balance of the `owner`\n * @param owner The address of the account to query\n * @return amount The number of tokens owned by `owner`\n */\n function balanceOf(address owner) external view override returns (uint256) {\n return accountTokens[owner];\n }\n\n /**\n * @notice Get a snapshot of the account's balances, and the cached exchange rate\n * @dev This is used by comptroller to more efficiently perform liquidity checks.\n * @param account Address of the account to snapshot\n * @return error Always NO_ERROR for compatibility with Venus core tooling\n * @return vTokenBalance User's balance of vTokens\n * @return borrowBalance Amount owed in terms of underlying\n * @return exchangeRate Stored exchange rate\n */\n function getAccountSnapshot(\n address account\n )\n external\n view\n override\n returns (uint256 error, uint256 vTokenBalance, uint256 borrowBalance, uint256 exchangeRate)\n {\n return (NO_ERROR, accountTokens[account], _borrowBalanceStored(account), _exchangeRateStored());\n }\n\n /**\n * @notice Get the internally tracked cash balance of this vToken in the underlying asset\n * @return cash The quantity of underlying asset tracked internally by this contract\n */\n function getCash() external view override returns (uint256) {\n return _getCashPrior();\n }\n\n /**\n * @notice Returns the current per slot(block or second) borrow interest rate for this vToken\n * @return rate The borrow interest rate per slot(block or second), scaled by 1e18\n */\n function borrowRatePerBlock() external view override returns (uint256) {\n return interestRateModel.getBorrowRate(_getCashPrior(), totalBorrows, totalReserves, badDebt);\n }\n\n /**\n * @notice Returns the current per-slot(block or second) supply interest rate for this v\n * @return rate The supply interest rate per slot(block or second), scaled by 1e18\n */\n function supplyRatePerBlock() external view override returns (uint256) {\n return\n interestRateModel.getSupplyRate(\n _getCashPrior(),\n totalBorrows,\n totalReserves,\n reserveFactorMantissa,\n badDebt\n );\n }\n\n /**\n * @notice Return the borrow balance of account based on stored data\n * @param account The address whose balance should be calculated\n * @return borrowBalance The calculated balance\n */\n function borrowBalanceStored(address account) external view override returns (uint256) {\n return _borrowBalanceStored(account);\n }\n\n /**\n * @notice Calculates the exchange rate from the underlying to the VToken\n * @dev This function does not accrue interest before calculating the exchange rate\n * @return exchangeRate Calculated exchange rate scaled by 1e18\n */\n function exchangeRateStored() external view override returns (uint256) {\n return _exchangeRateStored();\n }\n\n /**\n * @notice Accrue interest then return the up-to-date exchange rate\n * @return exchangeRate Calculated exchange rate scaled by 1e18\n */\n function exchangeRateCurrent() public override nonReentrant returns (uint256) {\n accrueInterest();\n return _exchangeRateStored();\n }\n\n /**\n * @notice Applies accrued interest to total borrows and reserves\n * @dev This calculates interest accrued from the last checkpointed slot(block or second)\n * up to the current slot(block or second) and writes new checkpoint to storage and\n * reduce spread reserves to protocol share reserve\n * if currentSlot - reduceReservesBlockNumber >= slotDelta\n * @return Always NO_ERROR\n * @custom:event Emits AccrueInterest event on success\n * @custom:access Not restricted\n */\n function accrueInterest() public virtual override returns (uint256) {\n /* Remember the initial block number or timestamp */\n uint256 currentSlotNumber = getBlockNumberOrTimestamp();\n uint256 accrualSlotNumberPrior = accrualBlockNumber;\n\n /* Short-circuit accumulating 0 interest */\n if (accrualSlotNumberPrior == currentSlotNumber) {\n return NO_ERROR;\n }\n\n /* Read the previous values out of storage */\n uint256 cashPrior = _getCashPrior();\n uint256 borrowsPrior = totalBorrows;\n uint256 reservesPrior = totalReserves;\n uint256 borrowIndexPrior = borrowIndex;\n\n /* Calculate the current borrow interest rate */\n uint256 borrowRateMantissa = interestRateModel.getBorrowRate(cashPrior, borrowsPrior, reservesPrior, badDebt);\n require(borrowRateMantissa <= MAX_BORROW_RATE_MANTISSA, \"borrow rate is absurdly high\");\n\n /* Calculate the number of slots elapsed since the last accrual */\n uint256 slotDelta = currentSlotNumber - accrualSlotNumberPrior;\n\n /*\n * Calculate the interest accumulated into borrows and reserves and the new index:\n * simpleInterestFactor = borrowRate * slotDelta\n * interestAccumulated = simpleInterestFactor * totalBorrows\n * totalBorrowsNew = interestAccumulated + totalBorrows\n * totalReservesNew = interestAccumulated * reserveFactor + totalReserves\n * borrowIndexNew = simpleInterestFactor * borrowIndex + borrowIndex\n */\n\n Exp memory simpleInterestFactor = mul_(Exp({ mantissa: borrowRateMantissa }), slotDelta);\n uint256 interestAccumulated = mul_ScalarTruncate(simpleInterestFactor, borrowsPrior);\n uint256 totalBorrowsNew = interestAccumulated + borrowsPrior;\n uint256 totalReservesNew = mul_ScalarTruncateAddUInt(\n Exp({ mantissa: reserveFactorMantissa }),\n interestAccumulated,\n reservesPrior\n );\n uint256 borrowIndexNew = mul_ScalarTruncateAddUInt(simpleInterestFactor, borrowIndexPrior, borrowIndexPrior);\n\n /////////////////////////\n // EFFECTS & INTERACTIONS\n // (No safe failures beyond this point)\n\n /* We write the previously calculated values into storage */\n accrualBlockNumber = currentSlotNumber;\n borrowIndex = borrowIndexNew;\n totalBorrows = totalBorrowsNew;\n totalReserves = totalReservesNew;\n\n if (currentSlotNumber - reduceReservesBlockNumber >= reduceReservesBlockDelta) {\n reduceReservesBlockNumber = currentSlotNumber;\n if (cashPrior < totalReservesNew) {\n _reduceReservesFresh(cashPrior);\n } else {\n _reduceReservesFresh(totalReservesNew);\n }\n }\n\n /* We emit an AccrueInterest event */\n emit AccrueInterest(cashPrior, interestAccumulated, borrowIndexNew, totalBorrowsNew);\n\n return NO_ERROR;\n }\n\n /**\n * @notice User supplies assets into the market and receives vTokens in exchange\n * @dev Assumes interest has already been accrued up to the current block or timestamp\n * @param payer The address of the account which is sending the assets for supply\n * @param minter The address of the account which is supplying the assets\n * @param mintAmount The amount of the underlying asset to supply\n */\n function _mintFresh(address payer, address minter, uint256 mintAmount) internal {\n /* Fail if mint not allowed */\n comptroller.preMintHook(address(this), minter, mintAmount);\n\n /* Verify market's slot(block or second) number equals current slot(block or second) number */\n if (accrualBlockNumber != getBlockNumberOrTimestamp()) {\n revert MintFreshnessCheck();\n }\n\n Exp memory exchangeRate = Exp({ mantissa: _exchangeRateStored() });\n\n /////////////////////////\n // EFFECTS & INTERACTIONS\n // (No safe failures beyond this point)\n\n /*\n * We call `_doTransferIn` for the minter and the mintAmount.\n * `_doTransferIn` reverts if anything goes wrong, since we can't be sure if\n * side-effects occurred. The function returns the amount actually transferred,\n * in case of a fee. On success, the vToken holds an additional `actualMintAmount`\n * of cash.\n */\n uint256 actualMintAmount = _doTransferIn(payer, mintAmount);\n\n /*\n * We get the current exchange rate and calculate the number of vTokens to be minted:\n * mintTokens = actualMintAmount / exchangeRate\n */\n\n uint256 mintTokens = div_(actualMintAmount, exchangeRate);\n\n /*\n * We calculate the new total supply of vTokens and minter token balance, checking for overflow:\n * totalSupplyNew = totalSupply + mintTokens\n * accountTokensNew = accountTokens[minter] + mintTokens\n * And write them into storage\n */\n totalSupply = totalSupply + mintTokens;\n uint256 balanceAfter = accountTokens[minter] + mintTokens;\n accountTokens[minter] = balanceAfter;\n\n /* We emit a Mint event, and a Transfer event */\n emit Mint(minter, actualMintAmount, mintTokens, balanceAfter);\n emit Transfer(address(0), minter, mintTokens);\n\n /* We call the defense and prime accrue interest hook */\n comptroller.mintVerify(address(this), minter, actualMintAmount, mintTokens);\n }\n\n /**\n * @notice Redeemer redeems vTokens in exchange for the underlying assets, transferred to the receiver. Redeemer and receiver can be the same\n * address, or different addresses if the receiver was previously approved by the redeemer as a valid delegate (see Comptroller.updateDelegate)\n * @dev Assumes interest has already been accrued up to the current slot(block or second)\n * @param redeemer The address of the account which is redeeming the tokens\n * @param receiver The receiver of the underlying tokens\n * @param redeemTokensIn The number of vTokens to redeem into underlying (only one of redeemTokensIn or redeemAmountIn may be non-zero)\n * @param redeemAmountIn The number of underlying tokens to receive from redeeming vTokens (only one of redeemTokensIn or redeemAmountIn may be non-zero)\n */\n function _redeemFresh(address redeemer, address receiver, uint256 redeemTokensIn, uint256 redeemAmountIn) internal {\n require(redeemTokensIn == 0 || redeemAmountIn == 0, \"one of redeemTokensIn or redeemAmountIn must be zero\");\n\n /* Verify market's slot(block or second) number equals current slot(block or second) number */\n if (accrualBlockNumber != getBlockNumberOrTimestamp()) {\n revert RedeemFreshnessCheck();\n }\n\n /* exchangeRate = invoke Exchange Rate Stored() */\n Exp memory exchangeRate = Exp({ mantissa: _exchangeRateStored() });\n\n uint256 redeemTokens;\n uint256 redeemAmount;\n\n /* If redeemTokensIn > 0: */\n if (redeemTokensIn > 0) {\n /*\n * We calculate the exchange rate and the amount of underlying to be redeemed:\n * redeemTokens = redeemTokensIn\n */\n redeemTokens = redeemTokensIn;\n } else {\n /*\n * We get the current exchange rate and calculate the amount to be redeemed:\n * redeemTokens = redeemAmountIn / exchangeRate\n */\n redeemTokens = div_(redeemAmountIn, exchangeRate);\n\n uint256 _redeemAmount = mul_(redeemTokens, exchangeRate);\n if (_redeemAmount != 0 && _redeemAmount != redeemAmountIn) redeemTokens++; // round up\n }\n\n // redeemAmount = exchangeRate * redeemTokens\n redeemAmount = mul_ScalarTruncate(exchangeRate, redeemTokens);\n\n // Revert if amount is zero\n if (redeemAmount == 0) {\n revert(\"redeemAmount is zero\");\n }\n\n /* Fail if redeem not allowed */\n comptroller.preRedeemHook(address(this), redeemer, redeemTokens);\n\n /* Fail gracefully if protocol has insufficient cash */\n if (_getCashPrior() - totalReserves < redeemAmount) {\n revert RedeemTransferOutNotPossible();\n }\n\n /////////////////////////\n // EFFECTS & INTERACTIONS\n // (No safe failures beyond this point)\n\n /*\n * We write the previously calculated values into storage.\n * Note: Avoid token reentrancy attacks by writing reduced supply before external transfer.\n */\n totalSupply = totalSupply - redeemTokens;\n uint256 balanceAfter = accountTokens[redeemer] - redeemTokens;\n accountTokens[redeemer] = balanceAfter;\n\n /*\n * We invoke _doTransferOut for the receiver and the redeemAmount.\n * On success, the vToken has redeemAmount less of cash.\n * _doTransferOut reverts if anything goes wrong, since we can't be sure if side effects occurred.\n */\n _doTransferOut(receiver, redeemAmount);\n\n /* We emit a Transfer event, and a Redeem event */\n emit Transfer(redeemer, address(this), redeemTokens);\n emit Redeem(redeemer, redeemAmount, redeemTokens, balanceAfter);\n\n /* We call the defense and prime accrue interest hook */\n comptroller.redeemVerify(address(this), redeemer, redeemAmount, redeemTokens);\n }\n\n /**\n * @notice Users or their delegates borrow assets from the protocol\n * @param borrower User who borrows the assets\n * @param receiver The receiver of the tokens, if called by a delegate\n * @param borrowAmount The amount of the underlying asset to borrow\n */\n function _borrowFresh(address borrower, address receiver, uint256 borrowAmount) internal {\n /* Fail if borrow not allowed */\n comptroller.preBorrowHook(address(this), borrower, borrowAmount);\n\n /* Verify market's slot(block or second) number equals current slot(block or second) number */\n if (accrualBlockNumber != getBlockNumberOrTimestamp()) {\n revert BorrowFreshnessCheck();\n }\n\n /* Fail gracefully if protocol has insufficient underlying cash */\n if (_getCashPrior() - totalReserves < borrowAmount) {\n revert BorrowCashNotAvailable();\n }\n\n /*\n * We calculate the new borrower and total borrow balances, failing on overflow:\n * accountBorrowNew = accountBorrow + borrowAmount\n * totalBorrowsNew = totalBorrows + borrowAmount\n */\n uint256 accountBorrowsPrev = _borrowBalanceStored(borrower);\n uint256 accountBorrowsNew = accountBorrowsPrev + borrowAmount;\n uint256 totalBorrowsNew = totalBorrows + borrowAmount;\n\n /////////////////////////\n // EFFECTS & INTERACTIONS\n // (No safe failures beyond this point)\n\n /*\n * We write the previously calculated values into storage.\n * Note: Avoid token reentrancy attacks by writing increased borrow before external transfer.\n `*/\n accountBorrows[borrower].principal = accountBorrowsNew;\n accountBorrows[borrower].interestIndex = borrowIndex;\n totalBorrows = totalBorrowsNew;\n\n /*\n * We invoke _doTransferOut for the receiver and the borrowAmount.\n * On success, the vToken borrowAmount less of cash.\n * _doTransferOut reverts if anything goes wrong, since we can't be sure if side effects occurred.\n */\n _doTransferOut(receiver, borrowAmount);\n\n /* We emit a Borrow event */\n emit Borrow(borrower, borrowAmount, accountBorrowsNew, totalBorrowsNew);\n\n /* We call the defense and prime accrue interest hook */\n comptroller.borrowVerify(address(this), borrower, borrowAmount);\n }\n\n /**\n * @notice Borrows are repaid by another user (possibly the borrower).\n * @param payer the account paying off the borrow\n * @param borrower the account with the debt being payed off\n * @param repayAmount the amount of underlying tokens being returned, or type(uint256).max for the full outstanding amount\n * @return (uint) the actual repayment amount.\n */\n function _repayBorrowFresh(address payer, address borrower, uint256 repayAmount) internal returns (uint256) {\n /* Fail if repayBorrow not allowed */\n comptroller.preRepayHook(address(this), borrower);\n\n /* Verify market's slot(block or second) number equals current slot(block or second) number */\n if (accrualBlockNumber != getBlockNumberOrTimestamp()) {\n revert RepayBorrowFreshnessCheck();\n }\n\n /* We fetch the amount the borrower owes, with accumulated interest */\n uint256 accountBorrowsPrev = _borrowBalanceStored(borrower);\n\n uint256 repayAmountFinal = repayAmount >= accountBorrowsPrev ? accountBorrowsPrev : repayAmount;\n\n /////////////////////////\n // EFFECTS & INTERACTIONS\n // (No safe failures beyond this point)\n\n /*\n * We call _doTransferIn for the payer and the repayAmount\n * On success, the vToken holds an additional repayAmount of cash.\n * _doTransferIn reverts if anything goes wrong, since we can't be sure if side effects occurred.\n * it returns the amount actually transferred, in case of a fee.\n */\n uint256 actualRepayAmount = _doTransferIn(payer, repayAmountFinal);\n\n /*\n * We calculate the new borrower and total borrow balances, failing on underflow:\n * accountBorrowsNew = accountBorrows - actualRepayAmount\n * totalBorrowsNew = totalBorrows - actualRepayAmount\n */\n uint256 accountBorrowsNew = accountBorrowsPrev - actualRepayAmount;\n uint256 totalBorrowsNew = totalBorrows - actualRepayAmount;\n\n /* We write the previously calculated values into storage */\n accountBorrows[borrower].principal = accountBorrowsNew;\n accountBorrows[borrower].interestIndex = borrowIndex;\n totalBorrows = totalBorrowsNew;\n\n /* We emit a RepayBorrow event */\n emit RepayBorrow(payer, borrower, actualRepayAmount, accountBorrowsNew, totalBorrowsNew);\n\n /* We call the defense and prime accrue interest hook */\n comptroller.repayBorrowVerify(address(this), payer, borrower, actualRepayAmount, borrowIndex);\n\n return actualRepayAmount;\n }\n\n /**\n * @notice The sender liquidates the borrowers collateral.\n * The collateral seized is transferred to the liquidator.\n * @param liquidator The address repaying the borrow and seizing collateral\n * @param borrower The borrower of this vToken to be liquidated\n * @param repayAmount The amount of the underlying borrowed asset to repay\n * @param vTokenCollateral The market in which to seize collateral from the borrower\n * @param skipLiquidityCheck If set to true, allows to liquidate up to 100% of the borrow\n * regardless of the account liquidity\n */\n function _liquidateBorrow(\n address liquidator,\n address borrower,\n uint256 repayAmount,\n VTokenInterface vTokenCollateral,\n bool skipLiquidityCheck\n ) internal nonReentrant {\n accrueInterest();\n\n uint256 error = vTokenCollateral.accrueInterest();\n if (error != NO_ERROR) {\n // accrueInterest emits logs on errors, but we still want to log the fact that an attempted liquidation failed\n revert LiquidateAccrueCollateralInterestFailed(error);\n }\n\n _liquidateBorrowFresh(liquidator, borrower, repayAmount, vTokenCollateral, skipLiquidityCheck);\n }\n\n /**\n * @notice The liquidator liquidates the borrowers collateral.\n * The collateral seized is transferred to the liquidator.\n * @param liquidator The address repaying the borrow and seizing collateral\n * @param borrower The borrower of this vToken to be liquidated\n * @param repayAmount The amount of the underlying borrowed asset to repay\n * @param vTokenCollateral The market in which to seize collateral from the borrower\n * @param skipLiquidityCheck If set to true, allows to liquidate up to 100% of the borrow\n * regardless of the account liquidity\n */\n function _liquidateBorrowFresh(\n address liquidator,\n address borrower,\n uint256 repayAmount,\n VTokenInterface vTokenCollateral,\n bool skipLiquidityCheck\n ) internal {\n /* Fail if liquidate not allowed */\n comptroller.preLiquidateHook(\n address(this),\n address(vTokenCollateral),\n borrower,\n repayAmount,\n skipLiquidityCheck\n );\n\n /* Verify market's slot(block or second) number equals current slot(block or second) number */\n if (accrualBlockNumber != getBlockNumberOrTimestamp()) {\n revert LiquidateFreshnessCheck();\n }\n\n /* Verify vTokenCollateral market's slot(block or second) number equals current slot(block or second) number */\n if (vTokenCollateral.accrualBlockNumber() != getBlockNumberOrTimestamp()) {\n revert LiquidateCollateralFreshnessCheck();\n }\n\n /* Fail if borrower = liquidator */\n if (borrower == liquidator) {\n revert LiquidateLiquidatorIsBorrower();\n }\n\n /* Fail if repayAmount = 0 */\n if (repayAmount == 0) {\n revert LiquidateCloseAmountIsZero();\n }\n\n /* Fail if repayAmount = type(uint256).max */\n if (repayAmount == type(uint256).max) {\n revert LiquidateCloseAmountIsUintMax();\n }\n\n /* Fail if repayBorrow fails */\n uint256 actualRepayAmount = _repayBorrowFresh(liquidator, borrower, repayAmount);\n\n /////////////////////////\n // EFFECTS & INTERACTIONS\n // (No safe failures beyond this point)\n\n /* We calculate the number of collateral tokens that will be seized */\n (uint256 amountSeizeError, uint256 seizeTokens) = comptroller.liquidateCalculateSeizeTokens(\n address(this),\n address(vTokenCollateral),\n actualRepayAmount\n );\n require(amountSeizeError == NO_ERROR, \"LIQUIDATE_COMPTROLLER_CALCULATE_AMOUNT_SEIZE_FAILED\");\n\n /* Revert if borrower collateral token balance < seizeTokens */\n require(vTokenCollateral.balanceOf(borrower) >= seizeTokens, \"LIQUIDATE_SEIZE_TOO_MUCH\");\n\n // If this is also the collateral, call _seize internally to avoid re-entrancy, otherwise make an external call\n if (address(vTokenCollateral) == address(this)) {\n _seize(address(this), liquidator, borrower, seizeTokens);\n } else {\n vTokenCollateral.seize(liquidator, borrower, seizeTokens);\n }\n\n /* We emit a LiquidateBorrow event */\n emit LiquidateBorrow(liquidator, borrower, actualRepayAmount, address(vTokenCollateral), seizeTokens);\n\n /* We call the defense and prime accrue interest hook */\n comptroller.liquidateBorrowVerify(\n address(this),\n address(vTokenCollateral),\n liquidator,\n borrower,\n actualRepayAmount,\n seizeTokens\n );\n }\n\n /**\n * @notice Transfers collateral tokens (this market) to the liquidator.\n * @dev Called only during an in-kind liquidation, or by liquidateBorrow during the liquidation of another VToken.\n * It's absolutely critical to use msg.sender as the seizer vToken and not a parameter.\n * @param seizerContract The contract seizing the collateral (either borrowed vToken or Comptroller)\n * @param liquidator The account receiving seized collateral\n * @param borrower The account having collateral seized\n * @param seizeTokens The number of vTokens to seize\n */\n function _seize(address seizerContract, address liquidator, address borrower, uint256 seizeTokens) internal {\n /* Fail if seize not allowed */\n comptroller.preSeizeHook(address(this), seizerContract, liquidator, borrower);\n\n /* Fail if borrower = liquidator */\n if (borrower == liquidator) {\n revert LiquidateSeizeLiquidatorIsBorrower();\n }\n\n /*\n * We calculate the new borrower and liquidator token balances, failing on underflow/overflow:\n * borrowerTokensNew = accountTokens[borrower] - seizeTokens\n * liquidatorTokensNew = accountTokens[liquidator] + seizeTokens\n */\n uint256 liquidationIncentiveMantissa = ComptrollerViewInterface(address(comptroller))\n .liquidationIncentiveMantissa();\n uint256 numerator = mul_(seizeTokens, Exp({ mantissa: protocolSeizeShareMantissa }));\n uint256 protocolSeizeTokens = div_(numerator, Exp({ mantissa: liquidationIncentiveMantissa }));\n uint256 liquidatorSeizeTokens = seizeTokens - protocolSeizeTokens;\n Exp memory exchangeRate = Exp({ mantissa: _exchangeRateStored() });\n uint256 protocolSeizeAmount = mul_ScalarTruncate(exchangeRate, protocolSeizeTokens);\n\n /////////////////////////\n // EFFECTS & INTERACTIONS\n // (No safe failures beyond this point)\n\n /* We write the calculated values into storage */\n totalSupply = totalSupply - protocolSeizeTokens;\n accountTokens[borrower] = accountTokens[borrower] - seizeTokens;\n accountTokens[liquidator] = accountTokens[liquidator] + liquidatorSeizeTokens;\n\n // _doTransferOut reverts if anything goes wrong, since we can't be sure if side effects occurred.\n // Transferring an underlying asset to the protocolShareReserve contract to channel the funds for different use.\n _doTransferOut(protocolShareReserve, protocolSeizeAmount);\n\n // Update the pool asset's state in the protocol share reserve for the above transfer.\n IProtocolShareReserve(protocolShareReserve).updateAssetsState(\n address(comptroller),\n underlying,\n IProtocolShareReserve.IncomeType.LIQUIDATION\n );\n\n /* Emit a Transfer event */\n emit Transfer(borrower, liquidator, liquidatorSeizeTokens);\n emit ProtocolSeize(borrower, protocolShareReserve, protocolSeizeAmount);\n\n /* We call the defense and prime accrue interest hook */\n comptroller.seizeVerify(address(this), seizerContract, liquidator, borrower, seizeTokens);\n }\n\n function _setComptroller(ComptrollerInterface newComptroller) internal {\n ComptrollerInterface oldComptroller = comptroller;\n // Ensure invoke comptroller.isComptroller() returns true\n require(newComptroller.isComptroller(), \"marker method returned false\");\n\n // Set market's comptroller to newComptroller\n comptroller = newComptroller;\n\n // Emit NewComptroller(oldComptroller, newComptroller)\n emit NewComptroller(oldComptroller, newComptroller);\n }\n\n /**\n * @notice Sets a new reserve factor for the protocol (*requires fresh interest accrual)\n * @dev Admin function to set a new reserve factor\n * @param newReserveFactorMantissa New reserve factor (from 0 to 1e18)\n */\n function _setReserveFactorFresh(uint256 newReserveFactorMantissa) internal {\n // Verify market's slot(block or second) number equals current slot(block or second) number\n if (accrualBlockNumber != getBlockNumberOrTimestamp()) {\n revert SetReserveFactorFreshCheck();\n }\n\n // Check newReserveFactor ≤ maxReserveFactor\n if (newReserveFactorMantissa > MAX_RESERVE_FACTOR_MANTISSA) {\n revert SetReserveFactorBoundsCheck();\n }\n\n uint256 oldReserveFactorMantissa = reserveFactorMantissa;\n reserveFactorMantissa = newReserveFactorMantissa;\n\n emit NewReserveFactor(oldReserveFactorMantissa, newReserveFactorMantissa);\n }\n\n /**\n * @notice Add reserves by transferring from caller\n * @dev Requires fresh interest accrual\n * @param addAmount Amount of addition to reserves\n * @return actualAddAmount The actual amount added, excluding the potential token fees\n */\n function _addReservesFresh(uint256 addAmount) internal returns (uint256) {\n // totalReserves + actualAddAmount\n uint256 totalReservesNew;\n uint256 actualAddAmount;\n\n // We fail gracefully unless market's slot(block or second) number equals current slot(block or second) number\n if (accrualBlockNumber != getBlockNumberOrTimestamp()) {\n revert AddReservesFactorFreshCheck(actualAddAmount);\n }\n\n actualAddAmount = _doTransferIn(msg.sender, addAmount);\n totalReservesNew = totalReserves + actualAddAmount;\n totalReserves = totalReservesNew;\n emit ReservesAdded(msg.sender, actualAddAmount, totalReservesNew);\n\n return actualAddAmount;\n }\n\n /**\n * @notice Reduces reserves by transferring to the protocol reserve contract\n * @dev Requires fresh interest accrual\n * @param reduceAmount Amount of reduction to reserves\n */\n function _reduceReservesFresh(uint256 reduceAmount) internal {\n if (reduceAmount == 0) {\n return;\n }\n // totalReserves - reduceAmount\n uint256 totalReservesNew;\n\n // We fail gracefully unless market's slot(block or second) number equals current slot(block or second) number\n if (accrualBlockNumber != getBlockNumberOrTimestamp()) {\n revert ReduceReservesFreshCheck();\n }\n\n // Fail gracefully if protocol has insufficient underlying cash\n if (_getCashPrior() < reduceAmount) {\n revert ReduceReservesCashNotAvailable();\n }\n\n // Check reduceAmount ≤ reserves[n] (totalReserves)\n if (reduceAmount > totalReserves) {\n revert ReduceReservesCashValidation();\n }\n\n /////////////////////////\n // EFFECTS & INTERACTIONS\n // (No safe failures beyond this point)\n\n totalReservesNew = totalReserves - reduceAmount;\n\n // Store reserves[n+1] = reserves[n] - reduceAmount\n totalReserves = totalReservesNew;\n\n // _doTransferOut reverts if anything goes wrong, since we can't be sure if side effects occurred.\n // Transferring an underlying asset to the protocolShareReserve contract to channel the funds for different use.\n _doTransferOut(protocolShareReserve, reduceAmount);\n\n // Update the pool asset's state in the protocol share reserve for the above transfer.\n IProtocolShareReserve(protocolShareReserve).updateAssetsState(\n address(comptroller),\n underlying,\n IProtocolShareReserve.IncomeType.SPREAD\n );\n\n emit SpreadReservesReduced(protocolShareReserve, reduceAmount, totalReservesNew);\n }\n\n /**\n * @notice updates the interest rate model (*requires fresh interest accrual)\n * @dev Admin function to update the interest rate model\n * @param newInterestRateModel the new interest rate model to use\n */\n function _setInterestRateModelFresh(InterestRateModel newInterestRateModel) internal {\n // Used to store old model for use in the event that is emitted on success\n InterestRateModel oldInterestRateModel;\n\n // We fail gracefully unless market's slot(block or second) number equals current slot(block or second) number\n if (accrualBlockNumber != getBlockNumberOrTimestamp()) {\n revert SetInterestRateModelFreshCheck();\n }\n\n // Track the market's current interest rate model\n oldInterestRateModel = interestRateModel;\n\n // Ensure invoke newInterestRateModel.isInterestRateModel() returns true\n require(newInterestRateModel.isInterestRateModel(), \"marker method returned false\");\n\n // Set the interest rate model to newInterestRateModel\n interestRateModel = newInterestRateModel;\n\n // Emit NewMarketInterestRateModel(oldInterestRateModel, newInterestRateModel)\n emit NewMarketInterestRateModel(oldInterestRateModel, newInterestRateModel);\n }\n\n /**\n * Safe Token **\n */\n\n /**\n * @dev Similar to ERC-20 transfer, but handles tokens that have transfer fees.\n * This function returns the actual amount received,\n * which may be less than `amount` if there is a fee attached to the transfer.\n * @param from Sender of the underlying tokens\n * @param amount Amount of underlying to transfer\n * @return Actual amount received\n */\n function _doTransferIn(address from, uint256 amount) internal virtual returns (uint256) {\n IERC20Upgradeable token = IERC20Upgradeable(underlying);\n uint256 balanceBefore = token.balanceOf(address(this));\n token.safeTransferFrom(from, address(this), amount);\n uint256 balanceAfter = token.balanceOf(address(this));\n uint256 actualAmount = balanceAfter - balanceBefore;\n internalCash += actualAmount;\n // Return the amount that was *actually* transferred\n return actualAmount;\n }\n\n /**\n * @dev Just a regular ERC-20 transfer, reverts on failure\n * @param to Receiver of the underlying tokens\n * @param amount Amount of underlying to transfer\n */\n function _doTransferOut(address to, uint256 amount) internal virtual {\n IERC20Upgradeable token = IERC20Upgradeable(underlying);\n internalCash -= amount;\n token.safeTransfer(to, amount);\n }\n\n /**\n * @notice Transfer `tokens` tokens from `src` to `dst` by `spender`\n * @dev Called by both `transfer` and `transferFrom` internally\n * @param spender The address of the account performing the transfer\n * @param src The address of the source account\n * @param dst The address of the destination account\n * @param tokens The number of tokens to transfer\n */\n function _transferTokens(address spender, address src, address dst, uint256 tokens) internal {\n /* Fail if transfer not allowed */\n comptroller.preTransferHook(address(this), src, dst, tokens);\n\n /* Do not allow self-transfers */\n if (src == dst) {\n revert TransferNotAllowed();\n }\n\n /* Get the allowance, infinite for the account owner */\n uint256 startingAllowance;\n if (spender == src) {\n startingAllowance = type(uint256).max;\n } else {\n startingAllowance = transferAllowances[src][spender];\n }\n\n /* Do the calculations, checking for {under,over}flow */\n uint256 allowanceNew = startingAllowance - tokens;\n uint256 srcTokensNew = accountTokens[src] - tokens;\n uint256 dstTokensNew = accountTokens[dst] + tokens;\n\n /////////////////////////\n // EFFECTS & INTERACTIONS\n\n accountTokens[src] = srcTokensNew;\n accountTokens[dst] = dstTokensNew;\n\n /* Eat some of the allowance (if necessary) */\n if (startingAllowance != type(uint256).max) {\n transferAllowances[src][spender] = allowanceNew;\n }\n\n /* We emit a Transfer event */\n emit Transfer(src, dst, tokens);\n\n comptroller.transferVerify(address(this), src, dst, tokens);\n }\n\n /**\n * @notice Initialize the money market\n * @param underlying_ The address of the underlying asset\n * @param comptroller_ The address of the Comptroller\n * @param interestRateModel_ The address of the interest rate model\n * @param initialExchangeRateMantissa_ The initial exchange rate, scaled by 1e18\n * @param name_ ERC-20 name of this token\n * @param symbol_ ERC-20 symbol of this token\n * @param decimals_ ERC-20 decimal precision of this token\n * @param admin_ Address of the administrator of this token\n * @param accessControlManager_ AccessControlManager contract address\n * @param riskManagement Addresses of risk & income related contracts\n * @param reserveFactorMantissa_ Percentage of borrow interest that goes to reserves (from 0 to 1e18)\n */\n function _initialize(\n address underlying_,\n ComptrollerInterface comptroller_,\n InterestRateModel interestRateModel_,\n uint256 initialExchangeRateMantissa_,\n string memory name_,\n string memory symbol_,\n uint8 decimals_,\n address admin_,\n address accessControlManager_,\n RiskManagementInit memory riskManagement,\n uint256 reserveFactorMantissa_\n ) internal onlyInitializing {\n __Ownable2Step_init();\n __AccessControlled_init_unchained(accessControlManager_);\n require(accrualBlockNumber == 0 && borrowIndex == 0, \"market may only be initialized once\");\n\n // Set initial exchange rate\n initialExchangeRateMantissa = initialExchangeRateMantissa_;\n require(initialExchangeRateMantissa > 0, \"initial exchange rate must be greater than zero.\");\n\n _setComptroller(comptroller_);\n\n // Initialize slot(block or second) number and borrow index (slot(block or second) number mocks depend on comptroller being set)\n accrualBlockNumber = getBlockNumberOrTimestamp();\n borrowIndex = MANTISSA_ONE;\n\n // Set the interest rate model (depends on slot(block or second) number / borrow index)\n _setInterestRateModelFresh(interestRateModel_);\n\n _setReserveFactorFresh(reserveFactorMantissa_);\n\n name = name_;\n symbol = symbol_;\n decimals = decimals_;\n _setShortfallContract(riskManagement.shortfall);\n _setProtocolShareReserve(riskManagement.protocolShareReserve);\n protocolSeizeShareMantissa = DEFAULT_PROTOCOL_SEIZE_SHARE_MANTISSA;\n\n // Set underlying and sanity check it\n underlying = underlying_;\n IERC20Upgradeable(underlying).totalSupply();\n\n // The counter starts true to prevent changing it from zero to non-zero (i.e. smaller cost/refund)\n _notEntered = true;\n _transferOwnership(admin_);\n }\n\n function _setShortfallContract(address shortfall_) internal {\n ensureNonzeroAddress(shortfall_);\n address oldShortfall = shortfall;\n shortfall = shortfall_;\n emit NewShortfallContract(oldShortfall, shortfall_);\n }\n\n function _setProtocolShareReserve(address payable protocolShareReserve_) internal {\n ensureNonzeroAddress(protocolShareReserve_);\n address oldProtocolShareReserve = address(protocolShareReserve);\n protocolShareReserve = protocolShareReserve_;\n emit NewProtocolShareReserve(oldProtocolShareReserve, address(protocolShareReserve_));\n }\n\n function _ensureSenderIsDelegateOf(address user) internal view {\n if (!ComptrollerViewInterface(address(comptroller)).approvedDelegates(user, msg.sender)) {\n revert DelegateNotApproved();\n }\n }\n\n /**\n * @notice Gets the internally tracked cash balance of this market\n * @dev Returns the internalCash state variable, which is updated on transfers in/out\n * @return The quantity of underlying tokens tracked internally by this contract\n */\n function _getCashPrior() internal view virtual returns (uint256) {\n return internalCash;\n }\n\n /**\n * @notice Return the borrow balance of account based on stored data\n * @param account The address whose balance should be calculated\n * @return borrowBalance the calculated balance\n */\n function _borrowBalanceStored(address account) internal view returns (uint256) {\n /* Get borrowBalance and borrowIndex */\n BorrowSnapshot memory borrowSnapshot = accountBorrows[account];\n\n /* If borrowBalance = 0 then borrowIndex is likely also 0.\n * Rather than failing the calculation with a division by 0, we immediately return 0 in this case.\n */\n if (borrowSnapshot.principal == 0) {\n return 0;\n }\n\n /* Calculate new borrow balance using the interest index:\n * recentBorrowBalance = borrower.borrowBalance * market.borrowIndex / borrower.borrowIndex\n */\n uint256 principalTimesIndex = borrowSnapshot.principal * borrowIndex;\n\n return principalTimesIndex / borrowSnapshot.interestIndex;\n }\n\n /**\n * @notice Calculates the exchange rate from the underlying to the VToken\n * @dev This function does not accrue interest before calculating the exchange rate\n * @return exchangeRate Calculated exchange rate scaled by 1e18\n */\n function _exchangeRateStored() internal view virtual returns (uint256) {\n uint256 _totalSupply = totalSupply;\n if (_totalSupply == 0) {\n /*\n * If there are no tokens minted:\n * exchangeRate = initialExchangeRate\n */\n return initialExchangeRateMantissa;\n }\n /*\n * Otherwise:\n * exchangeRate = (totalCash + totalBorrows + badDebt - totalReserves) / totalSupply\n */\n uint256 totalCash = _getCashPrior();\n uint256 cashPlusBorrowsMinusReserves = totalCash + totalBorrows + badDebt - totalReserves;\n uint256 exchangeRate = (cashPlusBorrowsMinusReserves * EXP_SCALE) / _totalSupply;\n\n return exchangeRate;\n }\n}\n" + }, + "contracts/VTokenInterfaces.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\n\nimport { IERC20Upgradeable } from \"@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol\";\nimport { ResilientOracleInterface } from \"@venusprotocol/oracle/contracts/interfaces/OracleInterface.sol\";\n\nimport { ComptrollerInterface } from \"./ComptrollerInterface.sol\";\nimport { InterestRateModel } from \"./InterestRateModel.sol\";\n\n/**\n * @title VTokenStorage\n * @author Venus\n * @notice Storage layout used by the `VToken` contract\n */\n// solhint-disable-next-line max-states-count\ncontract VTokenStorage {\n /**\n * @notice Container for borrow balance information\n * @member principal Total balance (with accrued interest), after applying the most recent balance-changing action\n * @member interestIndex Global borrowIndex as of the most recent balance-changing action\n */\n struct BorrowSnapshot {\n uint256 principal;\n uint256 interestIndex;\n }\n\n /**\n * @dev Guard variable for re-entrancy checks\n */\n bool internal _notEntered;\n\n /**\n * @notice Underlying asset for this VToken\n */\n address public underlying;\n\n /**\n * @notice EIP-20 token name for this token\n */\n string public name;\n\n /**\n * @notice EIP-20 token symbol for this token\n */\n string public symbol;\n\n /**\n * @notice EIP-20 token decimals for this token\n */\n uint8 public decimals;\n\n /**\n * @notice Protocol share Reserve contract address\n */\n address payable public protocolShareReserve;\n\n /**\n * @notice Contract which oversees inter-vToken operations\n */\n ComptrollerInterface public comptroller;\n\n /**\n * @notice Model which tells what the current interest rate should be\n */\n InterestRateModel public interestRateModel;\n\n // Initial exchange rate used when minting the first VTokens (used when totalSupply = 0)\n uint256 internal initialExchangeRateMantissa;\n\n /**\n * @notice Fraction of interest currently set aside for reserves\n */\n uint256 public reserveFactorMantissa;\n\n /**\n * @notice Slot(block or second) number that interest was last accrued at\n */\n uint256 public accrualBlockNumber;\n\n /**\n * @notice Accumulator of the total earned interest rate since the opening of the market\n */\n uint256 public borrowIndex;\n\n /**\n * @notice Total amount of outstanding borrows of the underlying in this market\n */\n uint256 public totalBorrows;\n\n /**\n * @notice Total amount of reserves of the underlying held in this market\n */\n uint256 public totalReserves;\n\n /**\n * @notice Total number of tokens in circulation\n */\n uint256 public totalSupply;\n\n /**\n * @notice Total bad debt of the market\n */\n uint256 public badDebt;\n\n // Official record of token balances for each account\n mapping(address => uint256) internal accountTokens;\n\n // Approved token transfer amounts on behalf of others\n mapping(address => mapping(address => uint256)) internal transferAllowances;\n\n // Mapping of account addresses to outstanding borrow balances\n mapping(address => BorrowSnapshot) internal accountBorrows;\n\n /**\n * @notice Share of seized collateral that is added to reserves\n */\n uint256 public protocolSeizeShareMantissa;\n\n /**\n * @notice Storage of Shortfall contract address\n */\n address public shortfall;\n\n /**\n * @notice delta slot (block or second) after which reserves will be reduced\n */\n uint256 public reduceReservesBlockDelta;\n\n /**\n * @notice last slot (block or second) number at which reserves were reduced\n */\n uint256 public reduceReservesBlockNumber;\n\n /**\n * @notice Tracked internal cash balance, immune to direct token transfers (donation attacks)\n * @dev Updated via _doTransferIn(), _doTransferOut(), syncCash(), and badDebtRecovered(). Must be initialized via syncCash() after upgrade.\n */\n uint256 public internalCash;\n\n /**\n * @dev This empty reserved space is put in place to allow future versions to add new\n * variables without shifting down storage in the inheritance chain.\n * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\n */\n uint256[47] private __gap;\n}\n\n/**\n * @title VTokenInterface\n * @author Venus\n * @notice Interface implemented by the `VToken` contract\n */\nabstract contract VTokenInterface is VTokenStorage {\n struct RiskManagementInit {\n address shortfall;\n address payable protocolShareReserve;\n }\n\n /*** Market Events ***/\n\n /**\n * @notice Event emitted when interest is accrued\n */\n event AccrueInterest(uint256 cashPrior, uint256 interestAccumulated, uint256 borrowIndex, uint256 totalBorrows);\n\n /**\n * @notice Event emitted when tokens are minted\n */\n event Mint(address indexed minter, uint256 mintAmount, uint256 mintTokens, uint256 accountBalance);\n\n /**\n * @notice Event emitted when tokens are redeemed\n */\n event Redeem(address indexed redeemer, uint256 redeemAmount, uint256 redeemTokens, uint256 accountBalance);\n\n /**\n * @notice Event emitted when underlying is borrowed\n */\n event Borrow(address indexed borrower, uint256 borrowAmount, uint256 accountBorrows, uint256 totalBorrows);\n\n /**\n * @notice Event emitted when a borrow is repaid\n */\n event RepayBorrow(\n address indexed payer,\n address indexed borrower,\n uint256 repayAmount,\n uint256 accountBorrows,\n uint256 totalBorrows\n );\n\n /**\n * @notice Event emitted when bad debt is accumulated on a market\n * @param borrower borrower to \"forgive\"\n * @param badDebtDelta amount of new bad debt recorded\n * @param badDebtOld previous bad debt value\n * @param badDebtNew new bad debt value\n */\n event BadDebtIncreased(address indexed borrower, uint256 badDebtDelta, uint256 badDebtOld, uint256 badDebtNew);\n\n /**\n * @notice Event emitted when bad debt is recovered via an auction\n * @param badDebtOld previous bad debt value\n * @param badDebtNew new bad debt value\n */\n event BadDebtRecovered(uint256 badDebtOld, uint256 badDebtNew);\n\n /**\n * @notice Event emitted when a borrow is liquidated\n */\n event LiquidateBorrow(\n address indexed liquidator,\n address indexed borrower,\n uint256 repayAmount,\n address indexed vTokenCollateral,\n uint256 seizeTokens\n );\n\n /*** Admin Events ***/\n\n /**\n * @notice Event emitted when comptroller is changed\n */\n event NewComptroller(ComptrollerInterface indexed oldComptroller, ComptrollerInterface indexed newComptroller);\n\n /**\n * @notice Event emitted when shortfall contract address is changed\n */\n event NewShortfallContract(address indexed oldShortfall, address indexed newShortfall);\n\n /**\n * @notice Event emitted when protocol share reserve contract address is changed\n */\n event NewProtocolShareReserve(address indexed oldProtocolShareReserve, address indexed newProtocolShareReserve);\n\n /**\n * @notice Event emitted when interestRateModel is changed\n */\n event NewMarketInterestRateModel(\n InterestRateModel indexed oldInterestRateModel,\n InterestRateModel indexed newInterestRateModel\n );\n\n /**\n * @notice Event emitted when protocol seize share is changed\n */\n event NewProtocolSeizeShare(uint256 oldProtocolSeizeShareMantissa, uint256 newProtocolSeizeShareMantissa);\n\n /**\n * @notice Event emitted when the reserve factor is changed\n */\n event NewReserveFactor(uint256 oldReserveFactorMantissa, uint256 newReserveFactorMantissa);\n\n /**\n * @notice Event emitted when the reserves are added\n */\n event ReservesAdded(address indexed benefactor, uint256 addAmount, uint256 newTotalReserves);\n\n /**\n * @notice Event emitted when the spread reserves are reduced\n */\n event SpreadReservesReduced(address indexed protocolShareReserve, uint256 reduceAmount, uint256 newTotalReserves);\n\n /**\n * @notice EIP20 Transfer event\n */\n event Transfer(address indexed from, address indexed to, uint256 amount);\n\n /**\n * @notice EIP20 Approval event\n */\n event Approval(address indexed owner, address indexed spender, uint256 amount);\n\n /**\n * @notice Event emitted when healing the borrow\n */\n event HealBorrow(address indexed payer, address indexed borrower, uint256 repayAmount);\n\n /**\n * @notice Event emitted when tokens are swept\n */\n event SweepToken(address indexed token);\n\n /**\n * @notice Event emitted when reduce reserves slot (block or second) delta is changed\n */\n event NewReduceReservesBlockDelta(\n uint256 oldReduceReservesBlockOrTimestampDelta,\n uint256 newReduceReservesBlockOrTimestampDelta\n );\n\n /**\n * @notice Event emitted when liquidation reserves are reduced\n */\n event ProtocolSeize(address indexed from, address indexed to, uint256 amount);\n\n /**\n * @notice Event emitted when internalCash is synced with actual token balance\n */\n event CashSynced(uint256 oldInternalCash, uint256 newInternalCash);\n\n /*** User Interface ***/\n\n function mint(uint256 mintAmount) external virtual returns (uint256);\n\n function mintBehalf(address minter, uint256 mintAllowed) external virtual returns (uint256);\n\n function redeem(uint256 redeemTokens) external virtual returns (uint256);\n\n function redeemBehalf(address redeemer, uint256 redeemTokens) external virtual returns (uint256);\n\n function redeemUnderlying(uint256 redeemAmount) external virtual returns (uint256);\n\n function redeemUnderlyingBehalf(address redeemer, uint256 redeemAmount) external virtual returns (uint256);\n\n function borrow(uint256 borrowAmount) external virtual returns (uint256);\n\n function borrowBehalf(address borrwwer, uint256 borrowAmount) external virtual returns (uint256);\n\n function repayBorrow(uint256 repayAmount) external virtual returns (uint256);\n\n function repayBorrowBehalf(address borrower, uint256 repayAmount) external virtual returns (uint256);\n\n function liquidateBorrow(\n address borrower,\n uint256 repayAmount,\n VTokenInterface vTokenCollateral\n ) external virtual returns (uint256);\n\n function healBorrow(address payer, address borrower, uint256 repayAmount) external virtual;\n\n function forceLiquidateBorrow(\n address liquidator,\n address borrower,\n uint256 repayAmount,\n VTokenInterface vTokenCollateral,\n bool skipCloseFactorCheck\n ) external virtual;\n\n function seize(address liquidator, address borrower, uint256 seizeTokens) external virtual;\n\n function transfer(address dst, uint256 amount) external virtual returns (bool);\n\n function transferFrom(address src, address dst, uint256 amount) external virtual returns (bool);\n\n function accrueInterest() external virtual returns (uint256);\n\n function sweepToken(IERC20Upgradeable token) external virtual;\n\n /*** Admin Functions ***/\n\n function setReserveFactor(uint256 newReserveFactorMantissa) external virtual;\n\n function reduceReserves(uint256 reduceAmount) external virtual;\n\n function exchangeRateCurrent() external virtual returns (uint256);\n\n function borrowBalanceCurrent(address account) external virtual returns (uint256);\n\n function setInterestRateModel(InterestRateModel newInterestRateModel) external virtual;\n\n function addReserves(uint256 addAmount) external virtual;\n\n function syncCash() external virtual;\n\n function totalBorrowsCurrent() external virtual returns (uint256);\n\n function balanceOfUnderlying(address owner) external virtual returns (uint256);\n\n function approve(address spender, uint256 amount) external virtual returns (bool);\n\n function increaseAllowance(address spender, uint256 addedValue) external virtual returns (bool);\n\n function decreaseAllowance(address spender, uint256 subtractedValue) external virtual returns (bool);\n\n function allowance(address owner, address spender) external view virtual returns (uint256);\n\n function balanceOf(address owner) external view virtual returns (uint256);\n\n function getAccountSnapshot(address account) external view virtual returns (uint256, uint256, uint256, uint256);\n\n function borrowRatePerBlock() external view virtual returns (uint256);\n\n function supplyRatePerBlock() external view virtual returns (uint256);\n\n function borrowBalanceStored(address account) external view virtual returns (uint256);\n\n function exchangeRateStored() external view virtual returns (uint256);\n\n function getCash() external view virtual returns (uint256);\n\n /**\n * @notice Indicator that this is a VToken contract (for inspection)\n * @return Always true\n */\n function isVToken() external pure virtual returns (bool) {\n return true;\n }\n}\n" + } + }, + "settings": { + "optimizer": { + "enabled": true, + "runs": 30, + "details": { + "yul": true + } + }, + "evmVersion": "paris", + "outputSelection": { + "*": { + "*": ["storageLayout", "abi", "evm.bytecode", "evm.deployedBytecode", "evm.methodIdentifiers", "metadata"], + "": ["ast"] + } + } + } +} diff --git a/deployments/bsctestnet_addresses.json b/deployments/bsctestnet_addresses.json index 9c54bee97..bc303d541 100644 --- a/deployments/bsctestnet_addresses.json +++ b/deployments/bsctestnet_addresses.json @@ -44,6 +44,7 @@ "Comptroller_BTC": "0xfE87008bf29DeCACC09a75FaAc2d128367D46e7a", "Comptroller_DeFi": "0x23a73971A6B9f6580c048B9CB188869B2A2aA2aD", "Comptroller_GameFi": "0x1F4f0989C51f12DAcacD4025018176711f3Bf289", + "Comptroller_HubSpoke": "0x11960c84d6c4F2a978a12372721C3A6A88C78f4c", "Comptroller_LiquidStakedBNB": "0x596B11acAACF03217287939f88d63b51d3771704", "Comptroller_LiquidStakedETH": "0xC7859B809Ed5A2e98659ab5427D5B69e706aE26b", "Comptroller_Meme": "0x92e8E3C202093A495e98C10f9fcaa5Abe288F74A", @@ -187,6 +188,14 @@ "Shortfall": "0x503574a82fE2A9f968d355C8AAc1Ba0481859369", "Shortfall_Implementation": "0x84B09BC76ABA4c0FC45616AdD44017b678FBCA87", "Shortfall_Proxy": "0x503574a82fE2A9f968d355C8AAc1Ba0481859369", + "SpokeComptrollerBeacon": "0x076f3fb34C8937a62aD562c33249798367542d75", + "SpokeComptrollerImpl": "0x7F81dC61F3D75569A67155fb188171Aef173a52b", + "SpokePoolLens": "0xfbCBFF4ca8b2fFe3231b0c0BBEa25C79F4B0597c", + "SpokePoolRegistry": "0xeAA45288d804971e5a76f33559e629F5b2b1Cb8B", + "SpokePoolRegistry_Implementation": "0x266e65b6D81802F2BAFa0c07288A62891172DAD8", + "SpokePoolRegistry_Proxy": "0xeAA45288d804971e5a76f33559e629F5b2b1Cb8B", + "SpokeVTokenBeacon": "0xB9c3b5A6f13FD5BEF51eE8B62ED3EA384a9d1B45", + "SpokeVTokenImpl": "0x0A2399b0fC9A5921D0d843f2960589c1300Ac601", "SwapRouter_BTC": "0x98C108285E389A5a12EdDa585b70b30C786AFb86", "SwapRouter_DeFi": "0x89Bc8dFe0Af08b60ec285071d133FCdfa9B3C08e", "SwapRouter_GameFi": "0x5D254Bc7c7f2670395B9E0716C21249083D41a4f", @@ -215,6 +224,7 @@ "VToken_vRACA_GameFi": "0x1958035231E125830bA5d17D168cEa07Bb42184a", "VToken_vTRX_Tron": "0x410286c43a525E1DCC7468a9B091C111C8324cd1", "VToken_vTWT_DeFi": "0x4C94e67d239aD585275Fdd3246Ab82c8a2668564", + "VToken_vUSDC_HubSpoke": "0xD05514217FD359659aE7da7740e79C11947eBB32", "VToken_vUSDD_DeFi": "0xa109DE0abaeefC521Ec29D89eA42E64F37A6882E", "VToken_vUSDD_GameFi": "0xdeDf3B2bcF25d0023115fd71a0F8221C91C92B1a", "VToken_vUSDD_LiquidStakedBNB": "0xD5b20708d8f0FcA52cb609938D0594C4e32E5DaD", @@ -223,6 +233,7 @@ "VToken_vUSDD_Tron": "0xD804F74fe21290d213c46610ab171f7c2EeEBDE7", "VToken_vUSDT_DeFi": "0x80CC30811e362aC9aB857C3d7875CbcCc0b65750", "VToken_vUSDT_GameFi": "0x0bFE4e0B8A2a096A27e5B18b078d25be57C08634", + "VToken_vUSDT_HubSpoke": "0xC88bAF0bA49a98F15A00182752f6d10bd3932F6a", "VToken_vUSDT_LiquidStakedBNB": "0x2197d02cC9cd1ad51317A0a85A656a0c82383A7c", "VToken_vUSDT_Meme": "0x3AF2bE7AbEF0f840b196D99d79F4B803a5dB14a1", "VToken_vUSDT_StableCoins": "0x3338988d0beb4419Acb8fE624218754053362D06", diff --git a/hardhat.config.ts b/hardhat.config.ts index 780a81c39..7a93fe559 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -267,6 +267,33 @@ const config: HardhatUserConfig = { }, }, ], + overrides: { + // SpokeComptroller does not fit under EIP-170 at the default 200 runs: it compiles to 24,806 bytes, 230 over + // the limit. Optimizing it for size instead brings it to 24,337 and costs +1,901 gas on a borrow and +1,825 on + // a redeem (measured over four markets), i.e. under 1.5%. 30 is the knee of that curve: 200 -> 100 buys 264 + // bytes, 100 -> 30 another 205, and 30 -> 1 only 81 more for a further +1,232 gas. Should this contract ever + // need materially more room than the ~240 bytes this leaves, move the liquidity snapshot into an external + // library rather than lowering runs again: a DELEGATECALL costs 2,600 gas for the cold account access alone, + // which is worse than this setting, but it frees kilobytes instead of bytes. + "contracts/Spoke/SpokeComptroller.sol": { + version: "0.8.25", + settings: { + optimizer: { + enabled: true, + runs: 30, + details: { + yul: !process.env.CI, + }, + }, + evmVersion: "paris", + outputSelection: { + "*": { + "*": ["storageLayout"], + }, + }, + }, + }, + }, }, networks: { hardhat: { @@ -278,6 +305,13 @@ const config: HardhatUserConfig = { hardforkHistory: { berlin: 0, london: 13000000, + // BSC activates its hardforks on a timestamp, which `hardforkHistory` cannot express, so these are the + // first blocks at or after each activation time in `bnb-chain/bsc` `params/config.go`: ShanghaiTime + // 1705996800 and CancunTime 1718863500. Without them a fork of a recent block runs the London EVM and + // every live contract compiled for a later target - the ResilientOracle and the DeviationBoundedOracle + // among them - reverts with "invalid opcode" on PUSH0 or TSTORE. + shanghai: 35490444, + cancun: 39769787, }, }, 8453: { diff --git a/helpers/deploymentConfig.ts b/helpers/deploymentConfig.ts index 2adbf52b1..3f5409f56 100644 --- a/helpers/deploymentConfig.ts +++ b/helpers/deploymentConfig.ts @@ -770,6 +770,14 @@ export const globalConfig: NetworkConfig = { tokenAddress: "0xA11c8D9DC9b66E209Ef60F0C8D969D3CD988782c", faucetInitialLiquidity: false, }, + { + isMock: false, + name: "USD Coin", + symbol: "USDC", + decimals: 6, + tokenAddress: "0x16227D60f7a0e586C66B005219dfc887D13C9531", + faucetInitialLiquidity: false, + }, { isMock: false, name: "lisUSD", diff --git a/helpers/deploymentUtils.ts b/helpers/deploymentUtils.ts index 9acff4c04..139fca36f 100644 --- a/helpers/deploymentUtils.ts +++ b/helpers/deploymentUtils.ts @@ -1,4 +1,5 @@ import { deployments, ethers, getNamedAccounts } from "hardhat"; +import { DeployResult } from "hardhat-deploy/dist/types"; import { HardhatRuntimeEnvironment } from "hardhat/types"; import { Comptroller, ERC20, MockToken } from "../typechain"; @@ -133,3 +134,78 @@ export const skipMainnets = () => async (hre: HardhatRuntimeEnvironment) => { const isMainnet = hre.network.live && !hre.network.tags["testnet"]; return isMainnet; }; + +// Addresses reach the deploy scripts in three casings: the `@venusprotocol/*-deployments` packages record some of them +// all-lowercase, hardhat-deploy records its own checksummed, and everything read back from the chain is checksummed by +// ethers. Comparing them as strings therefore rejects addresses that are equal. On bscmainnet the governance package +// records the access control manager lowercase, so a pre-handover check refused the very address the proxy had just +// been initialized with, and the run stopped before either ownership transfer. Compare parsed addresses, never strings. +export const sameAddress = (a: string, b: string): boolean => ethers.utils.getAddress(a) === ethers.utils.getAddress(b); + +// Verification reaches an external explorer API, so a failure here must not abort a deployment that already succeeded on +// chain. Re-run the script to retry. +export const verifyDeployment = async ( + hre: HardhatRuntimeEnvironment, + name: string, + deployment: DeployResult, + constructorArguments: unknown[], +): Promise => { + if (!hre.network.live || !deployment.newlyDeployed) { + return; + } + + console.log(`Verifying ${name}...`); + try { + await hre.run("verify:verify", { address: deployment.address, constructorArguments }); + console.log(`${name} verified successfully`); + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + // The plugin words this several ways depending on which explorer answered, "Already Verified" from one and + // "has already been verified on the block explorer" from another, so match on the part they share. + if (message.toLowerCase().includes("already verified")) { + console.log(`${name} already verified`); + } else { + console.error(`${name} verification failed: ${message}`); + } + } +}; + +// A proxy deployment is two contracts on the explorer: the implementation, which carries the source every reader wants, +// and the proxy, whose constructor arguments name that implementation and the admin. Verifying one leaves the other +// unreadable, so verify both off the single `DeployResult` hardhat-deploy returns for the pair. +export const verifyProxyDeployment = async ( + hre: HardhatRuntimeEnvironment, + name: string, + deployment: DeployResult, +): Promise => { + if (deployment.implementation) { + await verifyDeployment(hre, `${name} implementation`, { ...deployment, address: deployment.implementation }, []); + } + await verifyDeployment(hre, `${name} proxy`, deployment, deployment.args ?? []); +}; + +// A live RPC behind a load balancer can answer a read from a node that has not yet applied the transaction that was +// just mined, so a value read straight after a write can be the pre-write one. Both shapes of that showed up on a BSC +// testnet run: an `Ownable` beacon still naming the deployer after a successful `transferOwnership`, and an +// `Ownable2Step` registry reporting a zero pending owner after a successful nomination. Reading once and believing it +// is what turns that into a misleading log line, or worse aborts a correct deployment on a check that throws. +// +// Retry until the read matches what the transaction should have produced. A genuine misconfiguration reads the same +// wrong value on every attempt and still fails, only later; a stale read catches up within a block or two. +export const readBackUntil = async ( + read: () => Promise, + matches: (value: T) => boolean, + attempts = 5, + delayMs = 3000, +): Promise => { + let value = await read(); + for (let attempt = 1; attempt < attempts && !matches(value); attempt += 1) { + await new Promise(resolve => setTimeout(resolve, delayMs)); + value = await read(); + } + return value; +}; + +// `readBackUntil` for the common case, an address the caller already knows the expected value of. +export const readBackAddress = (read: () => Promise, expected: string): Promise => + readBackUntil(read, value => sameAddress(value, expected)); diff --git a/helpers/spokeDeploymentConfig.ts b/helpers/spokeDeploymentConfig.ts new file mode 100644 index 000000000..e1334ff31 --- /dev/null +++ b/helpers/spokeDeploymentConfig.ts @@ -0,0 +1,121 @@ +import { InterestRateModels, PoolConfig, preconfiguredAddresses } from "./deploymentConfig"; +import { convertToUnit } from "./utils"; + +// Market configuration for the hub-funded spoke pool, deliberately kept out of `deploymentConfig.ts`'s `globalConfig`. +// `008-deploy-comptrollers.ts` and `009-deploy-vtokens.ts` iterate every pool in that file, so a spoke entry there would +// be stood up behind the shared `ComptrollerBeacon` and `VTokenBeacon` and would claim these deployment names first. +// It is the same separation `024-deploy-spoke-pool-registry.ts` argues for on the registry side. +// +// The shape is the isolated pools' `PoolConfig`, so the parameters the listing VIP needs at `addPool` and `addMarket` +// time sit here next to the ones `028-deploy-spoke-vtokens.ts` reads. Spoke-only market state, the per-market +// liquidation threshold and incentive, the supply allowlist and forced liquidation, is set on `SpokeComptroller` after +// `addMarket` and is not deployment input. + +// Names the pool in every per-pool deployment record: `Comptroller_HubSpoke` from `025-deploy-spoke-comptroller.ts`, +// and the `_HubSpoke` suffix each vToken symbol below carries. Deployment naming across the spoke scripts follows one +// rule: pool-wide singletons take a `Spoke` prefix (`SpokePoolRegistry`, `SpokeComptrollerImpl`, `SpokeVTokenBeacon`, +// `SpokePoolLens`), per-pool instances take this id as a suffix, exactly as the isolated pools distinguish +// `Comptroller_Stablecoins` from `VToken_vUSDT_Stablecoins`. +export const SPOKE_POOL_ID = "HubSpoke"; + +// Risk parameters mirror the isolated pools' Stablecoins pool on the same network. The spoke pool restricts who may +// supply, borrow and liquidate; it does not take more risk per market, so there is no reason for the curve, the +// collateral factor or the caps to differ from the isolated stablecoin markets they sit beside. +export const spokePoolConfig: Record = { + hardhat: { + id: SPOKE_POOL_ID, + name: "Hub-funded spoke", + closeFactor: convertToUnit("0.5", 18), + liquidationIncentive: convertToUnit("1.1", 18), + minLiquidatableCollateral: convertToUnit("100", 18), + vtokens: [ + { + name: "Venus USDT (Hub-funded spoke)", + asset: "USDT", + symbol: `vUSDT_${SPOKE_POOL_ID}`, + rateModel: InterestRateModels.JumpRate.toString(), + baseRatePerYear: convertToUnit("0", 18), + multiplierPerYear: convertToUnit("0.1", 18), + jumpMultiplierPerYear: convertToUnit("2.5", 18), + kink_: convertToUnit("0.8", 18), + collateralFactor: convertToUnit("0.8", 18), + liquidationThreshold: convertToUnit("0.88", 18), + reserveFactor: convertToUnit("0.1", 18), + initialSupply: convertToUnit(10_000, 18), + supplyCap: convertToUnit(1_000_000, 18), + borrowCap: convertToUnit(400_000, 18), + vTokenReceiver: "account:deployer", + reduceReservesBlockDelta: "100", + }, + { + name: "Venus USDD (Hub-funded spoke)", + asset: "USDD", + symbol: `vUSDD_${SPOKE_POOL_ID}`, + rateModel: InterestRateModels.JumpRate.toString(), + baseRatePerYear: convertToUnit("0", 18), + multiplierPerYear: convertToUnit("0.1", 18), + jumpMultiplierPerYear: convertToUnit("2.5", 18), + kink_: convertToUnit("0.8", 18), + collateralFactor: convertToUnit("0.8", 18), + liquidationThreshold: convertToUnit("0.88", 18), + reserveFactor: convertToUnit("0.1", 18), + initialSupply: convertToUnit(10_000, 18), + supplyCap: convertToUnit(1_000_000, 18), + borrowCap: convertToUnit(400_000, 18), + vTokenReceiver: "account:deployer", + reduceReservesBlockDelta: "100", + }, + ], + }, + bsctestnet: { + id: SPOKE_POOL_ID, + name: "Hub-funded spoke", + closeFactor: convertToUnit("0.5", 18), + liquidationIncentive: convertToUnit("1.1", 18), + minLiquidatableCollateral: convertToUnit("100", 18), + vtokens: [ + { + name: "Venus USDT (Hub-funded spoke)", + asset: "USDT", + symbol: `vUSDT_${SPOKE_POOL_ID}`, + rateModel: InterestRateModels.JumpRate.toString(), + baseRatePerYear: convertToUnit("0", 18), + multiplierPerYear: convertToUnit("0.1", 18), + jumpMultiplierPerYear: convertToUnit("2.5", 18), + kink_: convertToUnit("0.8", 18), + collateralFactor: convertToUnit("0.8", 18), + liquidationThreshold: convertToUnit("0.88", 18), + reserveFactor: convertToUnit("0.1", 18), + // Both testnet stablecoins carry 6 decimals, so the amounts below are 1e6 scaled, as the isolated Stablecoins + // pool has them on this network. + initialSupply: convertToUnit(10_000, 6), + supplyCap: convertToUnit(1_000_000, 6), + borrowCap: convertToUnit(400_000, 6), + vTokenReceiver: preconfiguredAddresses.bsctestnet.VTreasury, + reduceReservesBlockDelta: "100", + }, + { + name: "Venus USDC (Hub-funded spoke)", + asset: "USDC", + symbol: `vUSDC_${SPOKE_POOL_ID}`, + rateModel: InterestRateModels.JumpRate.toString(), + baseRatePerYear: convertToUnit("0", 18), + multiplierPerYear: convertToUnit("0.1", 18), + jumpMultiplierPerYear: convertToUnit("2.5", 18), + kink_: convertToUnit("0.8", 18), + collateralFactor: convertToUnit("0.8", 18), + liquidationThreshold: convertToUnit("0.88", 18), + reserveFactor: convertToUnit("0.1", 18), + initialSupply: convertToUnit(10_000, 6), + supplyCap: convertToUnit(1_000_000, 6), + borrowCap: convertToUnit(400_000, 6), + vTokenReceiver: preconfiguredAddresses.bsctestnet.VTreasury, + reduceReservesBlockDelta: "100", + }, + ], + }, +}; + +// A network with no entry has no spoke pool, which is the normal case: the deploy script logs and returns rather than +// failing, so `--tags HubSpoke` stays runnable everywhere. +export const getSpokePoolConfig = (networkName: string): PoolConfig | undefined => spokePoolConfig[networkName]; diff --git a/package.json b/package.json index ed7a648db..beb19da41 100644 --- a/package.json +++ b/package.json @@ -80,7 +80,7 @@ "@typescript-eslint/eslint-plugin": "^5.27.1", "@typescript-eslint/parser": "^5.27.1", "@venusprotocol/governance-contracts": "2.13.0", - "@venusprotocol/oracle": "2.14.0", + "@venusprotocol/oracle": "2.15.0", "@venusprotocol/protocol-reserve": "3.4.0", "@venusprotocol/venus-protocol": "10.0.0", "bignumber.js": "9.0.0", diff --git a/tests/hardhat/Fork/HubSpoke/adapterGuards.ts b/tests/hardhat/Fork/HubSpoke/adapterGuards.ts new file mode 100644 index 000000000..e44701cfc --- /dev/null +++ b/tests/hardhat/Fork/HubSpoke/adapterGuards.ts @@ -0,0 +1,428 @@ +import { FakeContract, smock } from "@defi-wonderland/smock"; +import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; +import chai from "chai"; +import { BigNumber, Contract, constants } from "ethers"; +import { parseUnits } from "ethers/lib/utils"; +import { ethers } from "hardhat"; + +import { AdapterSpokeV1, ISpokeComptroller, IVTokenIsolated, MockToken } from "../../../../typechain"; +import { setForkBlock } from "../utils"; +import { bscmainnet } from "./constants"; +import { BLOCK_NUMBER, HUB_ABI, MAX_UINT128, deployYieldGroup, grant } from "./fixture"; + +const { expect } = chai; +chai.use(smock.matchers); + +/// Compound's mantissa scale, the unit every exchange rate and rate mantissa here is expressed in. +const EXP = parseUnits("1", 18); + +const FORK = process.env.FORK === "true"; +const FORKED_NETWORK = process.env.FORKED_NETWORK || "bscmainnet"; + +/** + * The half of `AdapterSpokeV1` a live spoke market cannot drive. + * + * Every other file in this directory funds a real market through the real Hub, and between them they + * exercise each of the adapter's ten members. What none of them can reach is the adapter's defensive + * half. An isolated-pools `VToken` always returns `NO_ERROR` and reverts on failure, so the three + * `VToken*Failed` branches never fire against one. No spoke pool lists a fee-on-transfer underlying, + * so `VTokenUnderfilled` never fires. No live market carries a holder balance against a zero total + * supply, holds reserves above its entire backing, or reports a zero exchange rate. Those branches + * exist precisely because the adapter does not own the contracts it reads, and a fork suite is + * structurally unable to put a contract it does not own into a state it refuses to enter. + * + * So the MARKET is faked here and nothing else is. The `YieldGroup` is the real one, minted from the + * implementation deployed on this chain, reached through its own `depositResource` / + * `withdrawResource` entry points so the adapter arrives by the same delegatecall the Hub uses. The + * adapter is the real one. The fakes are built from `IVTokenIsolated` and `ISpokeComptroller`, the + * two interfaces the adapter compiles against, and `interfaces.ts` pins both to the deployed + * contracts selector for selector - so a fake cannot drift into answering a surface the live pool + * does not have. + */ +if (FORK && FORKED_NETWORK === "bscmainnet") { + describe("HubSpoke: the adapter guards a live market cannot trip", () => { + /// A plausible rate for an 18-decimal market listed at the usual 1e28 seed, so one vToken is worth + /// far more than one unit of underlying and the sub-one-vToken guards have a real threshold. + const RATE_SEEDED = parseUnits("2", 28); + /// `ceil(RATE_SEEDED / 1e18)` - the smallest deposit that mints a whole vToken at that rate. + const ONE_VTOKEN_UNIT = RATE_SEEDED.add(EXP).sub(1).div(EXP); + const ACTION_MINT = 0; + const ACTION_REDEEM = 1; + + let deployer: SignerWithAddress; + let timelock: SignerWithAddress; + let acm: Contract; + + let asset: MockToken; + let hub: FakeContract; + let yieldGroup: Contract; + let hubSigner: SignerWithAddress; + let adapter: AdapterSpokeV1; + let market: FakeContract; + let comptroller: FakeContract; + + // Rebuilt for every test rather than snapshot-restored. A fake's configured behaviour lives in + // JavaScript, not in the EVM, so a value one test sets on a fake survives a chain rewind and + // silently becomes the next test's starting state. + beforeEach(async () => { + await setForkBlock(BLOCK_NUMBER); + [deployer] = await ethers.getSigners(); + timelock = await impersonate(bscmainnet.NORMAL_TIMELOCK); + acm = await ethers.getContractAt("AccessControlManager", bscmainnet.ACM); + + asset = (await ( + await ethers.getContractFactory("MockToken", deployer) + ).deploy("Spoke Asset", "SPOKE", 18)) as MockToken; + + hub = await smock.fake(HUB_ABI); + hub.asset.returns(asset.address); + hubSigner = await impersonate(hub.address); + + yieldGroup = await deployYieldGroup(deployer, hub.address, asset.address); + adapter = (await (await ethers.getContractFactory("AdapterSpokeV1", deployer)).deploy()) as AdapterSpokeV1; + + market = await smock.fake("IVTokenIsolated"); + comptroller = await smock.fake("ISpokeComptroller"); + seedHealthyMarket(); + + await grant(acm, timelock, yieldGroup.address, "addResource(address,address)", bscmainnet.NORMAL_TIMELOCK); + await yieldGroup.connect(timelock).addResource(market.address, adapter.address); + }); + + /// A market the adapter is happy with, so each test moves exactly one thing away from it: uncapped, + /// unpaused, no supply allowlist, and holding far more cash than anything asked of it. + function seedHealthyMarket() { + market.comptroller.returns(comptroller.address); + market.underlying.returns(asset.address); + market.exchangeRateStored.returns(RATE_SEEDED); + market.totalSupply.returns(parseUnits("1", 18)); + market.balanceOf.returns(parseUnits("1", 18)); + market.getCash.returns(parseUnits("1000000", 18)); + market.totalBorrows.returns(0); + market.totalReserves.returns(0); + market.badDebt.returns(0); + market.mint.returns(0); + market.redeemUnderlying.returns(0); + market.accrueInterest.returns(0); + market.supplyRatePerBlock.returns(0); + market.blocksOrSecondsPerYear.returns(0); + + comptroller.isSupplyAllowlistEnabled.returns(false); + comptroller.isAllowedSupplier.returns(true); + comptroller.actionPaused.returns(false); + comptroller.supplyCaps.returns(constants.MaxUint256); + } + + /// Fund the Hub and route `amount` into the market through the real YieldGroup, the same + /// delegatecall the Hub's own deposit path takes. + async function depositAsHub(amount: BigNumber) { + await asset.connect(hubSigner).faucet(amount); + await asset.connect(hubSigner).approve(yieldGroup.address, amount); + return yieldGroup.connect(hubSigner).depositResource(market.address, amount); + } + + describe("deposit", () => { + it("refuses an amount too small to mint a single vToken", async () => { + // `maxDeposit` already declines to advertise a sub-one-vToken remainder, so a market is only + // ever offered one by a cascade splitting a request across resources. Minting it would hand the + // market the underlying and issue nothing back; reverting hands the leg to the next resource. + const amount = ONE_VTOKEN_UNIT.sub(1); + await expect(depositAsHub(amount)) + .to.be.revertedWithCustomError(adapter, "DepositBelowOneVToken") + .withArgs(market.address, amount, ONE_VTOKEN_UNIT); + expect(market.mint).to.have.callCount(0); + }); + + it("accepts exactly one vToken unit, the smallest amount that mints", async () => { + // The boundary the guard above sits on: one wei less is refused, this is placed. + await depositAsHub(ONE_VTOKEN_UNIT); + expect(market.mint).to.have.been.calledWith(ONE_VTOKEN_UNIT); + }); + + it("surfaces a non-zero mint error code instead of booking the deposit", async () => { + // Isolated pools revert rather than return a code, so this is only reachable if a future market + // reintroduces Compound's convention. Swallowed, it would leave the YieldGroup accounting for + // underlying it holds no vTokens against. + market.mint.returns(7); + await expect(depositAsHub(parseUnits("1000", 18))) + .to.be.revertedWithCustomError(adapter, "VTokenMintFailed") + .withArgs(market.address, 7); + }); + + it("approves the market out of the YieldGroup's balance, never the adapter's", async () => { + // The approval is what proves the delegatecall context: it is granted in the YieldGroup's + // storage, from the YieldGroup's own balance, which is what lets the market pull the + // underlying and credit the vTokens back there. A direct call would have granted it from the + // adapter, whose balance is always zero. + // + // The allowance is still standing afterwards only because this market is a fake and never + // pulls it. Against a real market the mint consumes it, which is why the adapter does not + // reset it - and why a fake is the only place this is observable at all. + const amount = parseUnits("1000", 18); + await depositAsHub(amount); + expect(await asset.allowance(yieldGroup.address, market.address)).to.equal(amount); + expect(await asset.allowance(adapter.address, market.address)).to.equal(0); + expect(market.mint).to.have.been.calledWith(amount); + }); + }); + + describe("withdraw", () => { + const amount = parseUnits("1000", 18); + + it("surfaces a non-zero redeem error code", async () => { + market.redeemUnderlying.returns(9); + await expect(yieldGroup.connect(hubSigner).withdrawResource(market.address, amount, deployer.address)) + .to.be.revertedWithCustomError(adapter, "VTokenRedeemFailed") + .withArgs(market.address, 9); + }); + + it("rejects a redeem that reported success but delivered nothing", async () => { + // The shape a fee-on-transfer underlying takes, and the shape a market bug takes: the call + // returns `NO_ERROR` and the YieldGroup's balance does not move. Forwarding `amount` anyway + // would pay the recipient out of the YieldGroup's idle balance, which belongs to someone else. + await expect(yieldGroup.connect(hubSigner).withdrawResource(market.address, amount, deployer.address)) + .to.be.revertedWithCustomError(adapter, "VTokenUnderfilled") + .withArgs(market.address, amount, 0); + }); + + it("refuses to certify more than the market's payable cash before it ever redeems", async () => { + // The YieldGroup's own liquidity check runs first, against `maxWithdraw`. A request above it is + // rejected there, so the adapter is never handed a redeem the market would fail. + market.getCash.returns(parseUnits("10", 18)); + const liquid = await adapter.maxWithdraw(market.address, yieldGroup.address); + await expect(yieldGroup.connect(hubSigner).withdrawResource(market.address, liquid.add(1), deployer.address)).to + .be.reverted; + expect(market.redeemUnderlying).to.have.callCount(0); + }); + }); + + describe("accrue", () => { + it("surfaces a non-zero accrual error code", async () => { + market.accrueInterest.returns(3); + await expect(adapter.accrue(market.address)) + .to.be.revertedWithCustomError(adapter, "VTokenAccrueFailed") + .withArgs(market.address, 3); + }); + + it("needs no delegatecall context, because it settles the market and not the caller", async () => { + await adapter.accrue(market.address); + expect(market.accrueInterest).to.have.callCount(1); + }); + }); + + describe("totalAssets", () => { + it("reports zero rather than dividing by a zero supply", async () => { + // Unreachable while the holder's balance is non-zero, since those vTokens are part of the + // supply. Kept as a division guard rather than an assumption about a contract this one reads. + market.balanceOf.returns(parseUnits("1", 18)); + market.totalSupply.returns(0); + expect(await adapter.totalAssets(market.address, yieldGroup.address)).to.equal(0); + }); + + it("reports zero when reserves have swallowed the whole backing", async () => { + market.getCash.returns(parseUnits("10", 18)); + market.totalBorrows.returns(0); + market.totalReserves.returns(parseUnits("10", 18)); + expect(await adapter.totalAssets(market.address, yieldGroup.address)).to.equal(0); + }); + + it("values the position off the components, with written-off debt excluded", async () => { + // The market's own rate keeps `badDebt` in its numerator. Dropping it is what marks every Hub + // depositor down at the same instant instead of by exit order. `badDebt` is set absurdly high + // here so that any path still reading `exchangeRateStored` would be obvious. + market.balanceOf.returns(parseUnits("1", 18)); + market.totalSupply.returns(parseUnits("2", 18)); + market.getCash.returns(parseUnits("100", 18)); + market.totalBorrows.returns(parseUnits("60", 18)); + market.totalReserves.returns(parseUnits("10", 18)); + market.badDebt.returns(parseUnits("1000000", 18)); + + // (100 + 60 - 10) / 2 = 75, with `badDebt` playing no part. + expect(await adapter.totalAssets(market.address, yieldGroup.address)).to.equal(parseUnits("75", 18)); + }); + }); + + describe("maxWithdraw", () => { + it("reports zero against a market with a zero exchange rate", async () => { + market.exchangeRateStored.returns(0); + expect(await adapter.maxWithdraw(market.address, yieldGroup.address)).to.equal(0); + }); + + it("reports zero when reserves are at or above the market's cash", async () => { + // `_redeemFresh` gates on `getCash() - totalReserves`, so reserves sitting inside cash are not + // payable liquidity and a market in this state settles nothing. + market.getCash.returns(parseUnits("10", 18)); + market.totalReserves.returns(parseUnits("10", 18)); + expect(await adapter.maxWithdraw(market.address, yieldGroup.address)).to.equal(0); + }); + + it("reports zero while REDEEM is paused, before reading anything else", async () => { + comptroller.actionPaused.whenCalledWith(market.address, ACTION_REDEEM).returns(true); + expect(await adapter.maxWithdraw(market.address, yieldGroup.address)).to.equal(0); + }); + + it("certifies only a bound the market's own redeem arithmetic settles", async () => { + // The invariant the whole-vToken floor and the payout mirror exist to hold, checked across the + // rate regimes a real market spans: at `1e18`, above it, at a seeded 1e28 rate, and below it - + // the last being the case where a redeem burns plenty of tokens and still truncates its payout + // to zero. Whatever this certifies has to survive the market's round-up against payable cash. + const cash = parseUnits("1234567", 18); + const reserves = parseUnits("7", 18); + market.getCash.returns(cash); + market.totalReserves.returns(reserves); + market.totalBorrows.returns(0); + + for (const rate of [EXP, parseUnits("3", 18), RATE_SEEDED, parseUnits("1", 16)]) { + market.exchangeRateStored.returns(rate); + const liquid: BigNumber = await adapter.maxWithdraw(market.address, yieldGroup.address); + if (liquid.isZero()) continue; + const payout = marketPayout(rate, bumpToSettleable(rate, liquid)); + expect(payout, `rate ${rate.toString()}`).to.be.gt(0); + expect(payout, `rate ${rate.toString()}`).to.be.lte(cash.sub(reserves)); + } + }); + }); + + describe("maxDeposit", () => { + it("reports a finite room for an uncapped market, so a YieldGroup can sum it", async () => { + // `YieldGroupBase.maxDeposit()` adds up the room of every queued resource in checked + // arithmetic. Passing `type(uint256).max` through would overflow that sum the moment a second + // uncapped market joined the queue, and the Hub would read the revert as zero capacity against + // a balance it could never move. + comptroller.supplyCaps.returns(constants.MaxUint256); + expect(await adapter.maxDeposit(market.address)).to.equal(MAX_UINT128); + }); + + it("treats a cap of zero as a real cap rather than an unset sentinel", async () => { + // Inverted against the YieldGroup's own resource cap, where zero means unbounded. Reading it + // the YieldGroup's way would advertise unlimited room into a market that rejects every mint. + comptroller.supplyCaps.returns(0); + expect(await adapter.maxDeposit(market.address)).to.equal(0); + }); + + it("withholds a tenth of a percent of the raw headroom", async () => { + // `exchangeRateStored` is pre-accrual while the market re-checks the cap after accruing, so raw + // headroom is optimistic on an interest-accruing market. The margin absorbs a typical + // inter-slot accrual; the routing cascade is the backstop for anything larger. + const cap = parseUnits("1000000", 18); + const supplied = parseUnits("400000", 18); + comptroller.supplyCaps.returns(cap); + market.exchangeRateStored.returns(EXP); + market.totalSupply.returns(supplied); + + const raw = cap.sub(supplied); + expect(await adapter.maxDeposit(market.address)).to.equal(raw.sub(raw.div(1000))); + }); + + it("reports zero once the trimmed headroom is worth less than one vToken", async () => { + // Room that cannot mint a whole vToken is room a deposit would mint nothing for, so advertising + // it only books a leg the cascade has to unwind. + const supply = parseUnits("1", 18); + market.totalSupply.returns(supply); + comptroller.supplyCaps.returns(supply.mul(RATE_SEEDED).div(EXP).add(ONE_VTOKEN_UNIT).sub(1)); + expect(await adapter.maxDeposit(market.address)).to.equal(0); + }); + + it("reports zero at or above the cap instead of underflowing", async () => { + market.exchangeRateStored.returns(EXP); + market.totalSupply.returns(parseUnits("500", 18)); + comptroller.supplyCaps.returns(parseUnits("500", 18)); + expect(await adapter.maxDeposit(market.address)).to.equal(0); + }); + + it("reports zero while MINT is paused", async () => { + comptroller.actionPaused.whenCalledWith(market.address, ACTION_MINT).returns(true); + expect(await adapter.maxDeposit(market.address)).to.equal(0); + }); + }); + + describe("spotAPYBps", () => { + it("saturates instead of wrapping a rate that overflows the return type", async () => { + // A wrapped cast would report a tiny APY for an enormous one, and the Hub ranks resources by + // this number. Saturating is wrong in a direction that cannot be mistaken for a real rate. + market.supplyRatePerBlock.returns(EXP); + market.blocksOrSecondsPerYear.returns(parseUnits("1", 16)); + expect(await adapter.spotAPYBps(market.address, 0)).to.equal(BigNumber.from(2).pow(64).sub(1)); + }); + + it("annualises with the market's own cadence and ignores the argument", async () => { + // One YieldGroup can hold block-based and time-based markets at once, so a group-level constant + // would misprice whichever kind it was not configured for. + market.supplyRatePerBlock.returns(parseUnits("1", 9)); + market.blocksOrSecondsPerYear.returns(10_512_000); + const expected = BigNumber.from(10).pow(9).mul(10_512_000).div(parseUnits("1", 14)); + expect(await adapter.spotAPYBps(market.address, 0)).to.equal(expected); + expect(await adapter.spotAPYBps(market.address, 123_456_789)).to.equal(expected); + }); + }); + + describe("asset and receiptBalance", () => { + it("resolves the market's underlying", async () => { + expect(await adapter.asset(market.address)).to.equal(asset.address); + }); + + it("reports the holder's vToken balance, not its underlying value", async () => { + // The YieldGroup uses this to decide a resource is fully drained and can be deregistered, so it + // has to be the raw receipt count rather than anything the exchange rate has been applied to. + market.balanceOf.whenCalledWith(yieldGroup.address).returns(parseUnits("3", 8)); + expect(await adapter.receiptBalance(market.address, yieldGroup.address)).to.equal(parseUnits("3", 8)); + }); + }); + + describe("validateRegistration", () => { + it("rejects a market whose supply allowlist is armed without the caller on it", async () => { + comptroller.isSupplyAllowlistEnabled.returns(true); + comptroller.isAllowedSupplier.returns(false); + await expect(adapter.connect(deployer).validateRegistration(market.address)) + .to.be.revertedWithCustomError(adapter, "SupplyNotAllowed") + .withArgs(market.address, deployer.address); + }); + + it("accepts a market whose allowlist is armed with the caller on it", async () => { + comptroller.isSupplyAllowlistEnabled.returns(true); + comptroller.isAllowedSupplier.returns(true); + await expect(adapter.validateRegistration(market.address)).to.not.be.reverted; + }); + }); + + describe("delegatecall discipline", () => { + it("refuses a direct call to either mutating entry point", async () => { + // A direct call would mint the vTokens to the adapter itself, where nothing can redeem them. + await expect(adapter.deposit(market.address, 1)).to.be.revertedWithCustomError(adapter, "NotDelegateCall"); + await expect(adapter.withdraw(market.address, 1, deployer.address)).to.be.revertedWithCustomError( + adapter, + "NotDelegateCall", + ); + }); + + it("holds no storage of its own, so a delegatecall cannot collide with the YieldGroup's", async () => { + const before = await Promise.all([yieldGroup.hub(), yieldGroup.asset(), yieldGroup.resources()]); + await depositAsHub(parseUnits("1000", 18)); + expect(await Promise.all([yieldGroup.hub(), yieldGroup.asset(), yieldGroup.resources()])).to.deep.equal(before); + }); + }); + }); +} + +/// Impersonate `who` with enough gas money to send a transaction. +async function impersonate(who: string): Promise { + await ethers.provider.send("hardhat_impersonateAccount", [who]); + await ethers.provider.send("hardhat_setBalance", [who, "0x56bc75e2d63100000"]); + return ethers.getSigner(who); +} + +/// Mirror of `AdapterSpokeV1._redeemPayout`: what the market pays out for `redeemUnderlying(request)`. +function marketPayout(rate: BigNumber, request: BigNumber): BigNumber { + let tokens = request.mul(EXP).div(rate); + const probe = tokens.mul(rate).div(EXP); + if (!probe.isZero() && !probe.eq(request)) tokens = tokens.add(1); + return tokens.mul(rate).div(EXP); +} + +/// Mirror of `AdapterSpokeV1._bumpToSettleable`: the request `withdraw` actually issues for `amount`. +function bumpToSettleable(rate: BigNumber, amount: BigNumber): BigNumber { + if (rate.isZero()) return amount; + if (!marketPayout(rate, amount).isZero()) return amount; + const minTokens = EXP.add(rate).sub(1).div(rate); + return minTokens.mul(rate).add(EXP).sub(1).div(EXP); +} diff --git a/tests/hardhat/Fork/HubSpoke/allowlists.ts b/tests/hardhat/Fork/HubSpoke/allowlists.ts new file mode 100644 index 000000000..9224d3c30 --- /dev/null +++ b/tests/hardhat/Fork/HubSpoke/allowlists.ts @@ -0,0 +1,232 @@ +import { SnapshotRestorer, takeSnapshot } from "@nomicfoundation/hardhat-network-helpers"; +import { expect } from "chai"; +import { BigNumber } from "ethers"; +import { ethers } from "hardhat"; + +import { bscmainnet } from "./constants"; +import { SpokeForkFixture, fundFrom, registerOnHub, registerSpokeResource, spokeForkFixture } from "./fixture"; + +const FORK = process.env.FORK === "true"; +const FORKED_NETWORK = process.env.FORKED_NETWORK || "bscmainnet"; + +const usd = (whole: string) => ethers.utils.parseUnits(whole, 18); +const HUB_CAP = usd("2000000"); + +if (FORK && FORKED_NETWORK === "bscmainnet") { + describe("HubSpoke: allowlists, liquidation and griefing", () => { + let f: SpokeForkFixture; + let snap: SnapshotRestorer; + + before(async () => { + f = await spokeForkFixture(); + await registerSpokeResource(f); + await registerOnHub(f, HUB_CAP); + snap = await takeSnapshot(); + }); + afterEach(async () => snap.restore()); + + describe("supply allowlist", () => { + it("meters the account credited with the vTokens, not the account paying", async () => { + // `mintBehalf` lets a third party fund a mint attributed to someone else. Metering the + // recipient is what bounds the market's supply, so the payer is deliberately unchecked. + const amount = usd("1000"); + await fundFrom(f.usdt, bscmainnet.USDT_HOLDER, f.outsider.address, amount.mul(2)); + await f.usdt.connect(f.outsider).approve(f.vUSDT.address, amount.mul(2)); + + // The outsider cannot supply for itself... + await expect(f.vUSDT.connect(f.outsider).mint(amount)) + .to.be.revertedWithCustomError(f.spoke, "SupplyNotAllowed") + .withArgs(f.vUSDT.address, f.outsider.address); + + // ...but it can pay for a mint credited to the allowlisted YieldGroup, which is a donation + // to the Hub's position rather than a way around the allowlist. + const before = await f.vUSDT.balanceOf(f.spokeSource.address); + await f.vUSDT.connect(f.outsider).mintBehalf(f.spokeSource.address, amount); + expect(await f.vUSDT.balanceOf(f.spokeSource.address)).to.be.gt(before); + }); + + it("lets an account that loses its grant keep and exit its position", async () => { + const amount = usd("10000"); + await fundFrom(f.usdt, bscmainnet.USDT_HOLDER, f.supplier.address, amount); + await f.usdt.connect(f.supplier).approve(f.hub.address, amount); + await f.hub.connect(f.supplier).deposit(amount, f.supplier.address); + + await f.spoke.connect(f.timelock).setAllowedSupplier(f.vUSDT.address, f.spokeSource.address, false); + // Supply is closed... + expect(await f.spokeSource.maxDeposit()).to.equal(0); + // ...but the position is untouched and the exit still works. Redeeming is never gated. + expect(await f.spokeSource.maxWithdraw()).to.be.gt(0); + const shares = await f.hub.balanceOf(f.supplier.address); + await expect(f.hub.connect(f.supplier).redeem(shares, f.supplier.address, f.supplier.address)).to.not.be + .reverted; + }); + + it("does not stop an allowlisted holder transferring vTokens to a non-allowlisted one", async () => { + // Recorded as observed behaviour, not as an endorsement: `preMintHook` is the only + // enforcement point, and `preTransferHook` never checks the destination. Any design that + // needs "the Hub is the sole holder of the liquidity market" as an invariant does not get + // it from this contract. + const amount = usd("10000"); + await fundFrom(f.usdt, bscmainnet.USDT_HOLDER, f.supplier.address, amount); + await f.spoke.connect(f.timelock).setAllowedSupplier(f.vUSDT.address, f.supplier.address, true); + await f.usdt.connect(f.supplier).approve(f.vUSDT.address, amount); + await f.vUSDT.connect(f.supplier).mint(amount); + + const held = await f.vUSDT.balanceOf(f.supplier.address); + expect(await f.spoke.isAllowedSupplier(f.vUSDT.address, f.outsider.address)).to.be.false; + await expect(f.vUSDT.connect(f.supplier).transfer(f.outsider.address, held)).to.not.be.reverted; + expect(await f.vUSDT.balanceOf(f.outsider.address)).to.equal(held); + }); + + it("is per market, so closing the liquidity market leaves the collateral market open", async () => { + expect(await f.spoke.isSupplyAllowlistEnabled(f.vUSDT.address)).to.be.true; + expect(await f.spoke.isSupplyAllowlistEnabled(f.vBTCB.address)).to.be.false; + + const collateral = ethers.utils.parseUnits("0.01", 18); + await fundFrom(f.btcb, bscmainnet.BTCB_HOLDER, f.outsider.address, collateral); + await f.btcb.connect(f.outsider).approve(f.vBTCB.address, collateral); + await expect(f.vBTCB.connect(f.outsider).mint(collateral)).to.not.be.reverted; + }); + }); + + describe("griefing the Hub's capacity", () => { + it("cannot be squatted by a third party while the allowlist is armed", async () => { + // The whole point of the allowlist: with it on, nobody but the source can consume the + // market's supply cap, so the Hub's advertised capacity cannot be taken away from it. + const roomBefore: BigNumber = await f.spokeSource.maxDeposit(); + const amount = usd("100000"); + await fundFrom(f.usdt, bscmainnet.USDT_HOLDER, f.outsider.address, amount); + await f.usdt.connect(f.outsider).approve(f.vUSDT.address, amount); + await expect(f.vUSDT.connect(f.outsider).mint(amount)).to.be.revertedWithCustomError( + f.spoke, + "SupplyNotAllowed", + ); + expect(await f.spokeSource.maxDeposit()).to.equal(roomBefore); + }); + + it("is squattable the moment the allowlist is lifted", async () => { + // Stated explicitly because it is the cost of running a spoke market with the allowlist + // off: anyone can then take the cap the Hub was sized for, and the Hub's own routing has + // to absorb that. + await f.spoke.connect(f.timelock).setSupplyAllowlistEnabled(f.vUSDT.address, false); + const roomBefore: BigNumber = await f.spokeSource.maxDeposit(); + const amount = roomBefore.div(2); + await fundFrom(f.usdt, bscmainnet.USDT_HOLDER, f.outsider.address, amount); + await f.usdt.connect(f.outsider).approve(f.vUSDT.address, amount); + await f.vUSDT.connect(f.outsider).mint(amount); + expect(await f.spokeSource.maxDeposit()).to.be.lt(roomBefore); + }); + + it("cannot be drained by a borrower below the Hub's own exit, only slowed", async () => { + const deposited = usd("100000"); + await fundFrom(f.usdt, bscmainnet.USDT_HOLDER, f.supplier.address, deposited); + await f.usdt.connect(f.supplier).approve(f.hub.address, deposited); + await f.hub.connect(f.supplier).deposit(deposited, f.supplier.address); + await borrowAgainst(f, usd("100000"), ethers.utils.parseUnits("4", 18)); + + // Every borrowable wei is out, so the market's cash is only the seed the pool was listed + // with. The position is still fully valued; it is the liquidity that is gone, and the + // adapter reports exactly that rather than an optimistic number. + expect(await f.adapter.totalAssets(f.vUSDT.address, f.spokeSource.address)).to.be.gte(deposited); + const liquid = await f.adapter.maxWithdraw(f.vUSDT.address, f.spokeSource.address); + expect(liquid).to.be.lt(deposited); + expect(liquid).to.be.lte(await f.vUSDT.getCash()); + + // Repaying restores it, so this is a delay and not a loss. + await f.usdt.connect(f.borrower).approve(f.vUSDT.address, ethers.constants.MaxUint256); + await fundFrom(f.usdt, bscmainnet.USDT_HOLDER, f.borrower.address, usd("1000")); + await f.vUSDT.connect(f.borrower).repayBorrow(ethers.constants.MaxUint256); + expect(await f.adapter.maxWithdraw(f.vUSDT.address, f.spokeSource.address)).to.be.gt(liquid); + }); + }); + + describe("liquidation allowlist", () => { + beforeEach(async () => { + const deposited = usd("100000"); + await fundFrom(f.usdt, bscmainnet.USDT_HOLDER, f.supplier.address, deposited); + await f.usdt.connect(f.supplier).approve(f.hub.address, deposited); + await f.hub.connect(f.supplier).deposit(deposited, f.supplier.address); + }); + + it("is off by default, so any keeper can liquidate", async () => { + expect(await f.spoke.isLiquidationAllowlistEnabled()).to.be.false; + await makeLiquidatable(f); + await expect(liquidate(f, f.liquidator.address)).to.not.be.reverted; + }); + + it("restricts seizing to the accounts on it once enabled", async () => { + await f.spoke.connect(f.timelock).setLiquidationAllowlistEnabled(true); + await makeLiquidatable(f); + + await expect(liquidate(f, f.liquidator.address)) + .to.be.revertedWithCustomError(f.spoke, "LiquidationNotAllowed") + .withArgs(f.liquidator.address); + + await f.spoke.connect(f.timelock).setAllowedLiquidator(f.liquidator.address, true); + await expect(liquidate(f, f.liquidator.address)).to.not.be.reverted; + }); + + it("is pool-wide, so it also gates the keeper that records bad debt", async () => { + // `healAccount` seizes across every market the borrower is in and cannot attribute the + // seizure to one of them, which is why the list is not per market. The operational + // consequence is that any keeper relied on to write off bad debt has to be on it too. + await f.spoke.connect(f.timelock).setLiquidationAllowlistEnabled(true); + await makeLiquidatable(f); + await expect(f.spoke.connect(f.liquidator).healAccount(f.borrower.address)).to.be.revertedWithCustomError( + f.spoke, + "LiquidationNotAllowed", + ); + }); + + it("never puts the Hub's own position at risk of being seized", async () => { + // The YieldGroup supplies and never borrows, so it has no shortfall to liquidate and holds + // no collateral membership to seize from. + expect(await f.spoke.getAssetsIn(f.spokeSource.address)).to.have.lengthOf(0); + const [, liquidity, shortfall] = await f.spoke.getAccountLiquidity(f.spokeSource.address); + expect(liquidity).to.equal(0); + expect(shortfall).to.equal(0); + const vBalBefore = await f.vUSDT.balanceOf(f.spokeSource.address); + await makeLiquidatable(f); + await liquidate(f, f.liquidator.address); + expect(await f.vUSDT.balanceOf(f.spokeSource.address)).to.equal(vBalBefore); + }); + }); + }); +} + +/// Impersonate `who` with enough gas money to send a transaction. +async function impersonate(who: string) { + await ethers.provider.send("hardhat_impersonateAccount", [who]); + await ethers.provider.send("hardhat_setBalance", [who, "0x56bc75e2d63100000"]); + return ethers.getSigner(who); +} + +async function borrowAgainst(f: SpokeForkFixture, borrowAmount: BigNumber, collateral: BigNumber) { + await fundFrom(f.btcb, bscmainnet.BTCB_HOLDER, f.borrower.address, collateral); + await f.btcb.connect(f.borrower).approve(f.vBTCB.address, collateral); + await f.vBTCB.connect(f.borrower).mint(collateral); + await f.spoke.connect(f.borrower).enterMarkets([f.vBTCB.address]); + await f.vUSDT.connect(f.borrower).borrow(borrowAmount); +} + +/** + * Put the borrower under water on real prices. The collateral factor is 0.75 and the liquidation + * threshold 0.8, so raising the threshold above the position's health is the one lever that does not + * require moving a live price feed - and it moves exactly the parameter a risk-parameter VIP would. + */ +async function makeLiquidatable(f: SpokeForkFixture) { + await borrowAgainst(f, ethers.utils.parseUnits("40000", 18), ethers.utils.parseUnits("1", 18)); + await f.spoke + .connect(f.timelock) + .setCollateralFactor(f.vBTCB.address, ethers.utils.parseUnits("0.1", 18), ethers.utils.parseUnits("0.1", 18)); + const [, , shortfall] = await f.spoke.getAccountLiquidity(f.borrower.address); + expect(shortfall).to.be.gt(0); +} + +async function liquidate(f: SpokeForkFixture, who: string) { + const signer = await impersonate(who); + const repay = ethers.utils.parseUnits("1000", 18); + await fundFrom(f.usdt, bscmainnet.USDT_HOLDER, who, repay); + await f.usdt.connect(signer).approve(f.vUSDT.address, repay); + return f.vUSDT.connect(signer).liquidateBorrow(f.borrower.address, repay, f.vBTCB.address); +} diff --git a/tests/hardhat/Fork/HubSpoke/badDebt.ts b/tests/hardhat/Fork/HubSpoke/badDebt.ts new file mode 100644 index 000000000..fb7fadef2 --- /dev/null +++ b/tests/hardhat/Fork/HubSpoke/badDebt.ts @@ -0,0 +1,238 @@ +import { SnapshotRestorer, takeSnapshot } from "@nomicfoundation/hardhat-network-helpers"; +import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; +import { expect } from "chai"; +import { BigNumber } from "ethers"; +import { ethers } from "hardhat"; + +import { bscmainnet } from "./constants"; +import { SpokeForkFixture, fundFrom, grant, registerOnHub, registerSpokeResource, spokeForkFixture } from "./fixture"; + +const FORK = process.env.FORK === "true"; +const FORKED_NETWORK = process.env.FORKED_NETWORK || "bscmainnet"; + +const usd = (whole: string) => ethers.utils.parseUnits(whole, 18); +const HUB_CAP = usd("2000000"); +const EXP_SCALE = ethers.constants.WeiPerEther; + +if (FORK && FORKED_NETWORK === "bscmainnet") { + describe("HubSpoke: bad debt and how the Hub marks it", () => { + let f: SpokeForkFixture; + let snap: SnapshotRestorer; + + before(async () => { + f = await spokeForkFixture(); + await registerSpokeResource(f); + await registerOnHub(f, HUB_CAP); + + // Take the borrow rate to zero for the whole suite. Every assertion here is about a single + // write-off moving a single number; leaving interest running would mean asserting against a + // tolerance instead of against the value, and a tolerance wide enough to absorb accrual is + // wide enough to hide the effect being measured. + await grant( + f.acm, + f.timelock, + f.irm.address, + "updateJumpRateModel(uint256,uint256,uint256,uint256)", + bscmainnet.NORMAL_TIMELOCK, + ); + await f.irm.connect(f.timelock).updateJumpRateModel(0, 0, 0, usd("0.8")); + + snap = await takeSnapshot(); + }); + afterEach(async () => snap.restore()); + + it("writes a loss the market's own exchange rate refuses to show", async () => { + const { hubBefore } = await fundAndDefault(f); + + const rateBefore = await f.vUSDT.exchangeRateStored(); + await healAs(f, f.liquidator); + const badDebt = await f.vUSDT.badDebt(); + + expect(badDebt).to.be.gt(0); + // `_exchangeRateStored` keeps `badDebt` in its numerator, so the write-off moves value out of + // `totalBorrows` and into `badDebt` and the rate does not budge. No supplier is marked down by + // the market, which is exactly why the adapter cannot value the position at this rate. + expect(await f.vUSDT.exchangeRateStored()).to.equal(rateBefore); + expect(hubBefore).to.be.gt(0); + }); + + it("marks the Hub down immediately, by its pro-rata share of the loss", async () => { + const { hubBefore } = await fundAndDefault(f); + const share = (await f.vUSDT.balanceOf(f.spokeSource.address)).mul(EXP_SCALE).div(await f.vUSDT.totalSupply()); + + await healAs(f, f.liquidator); + const badDebt = await f.vUSDT.badDebt(); + const hubAfter: BigNumber = await f.adapter.totalAssets(f.vUSDT.address, f.spokeSource.address); + + // The drop is the position's pro-rata share of the loss, to within the rounding of one + // division - not the whole loss, because the Hub is not the market's only supplier here. + const expectedDrop = badDebt.mul(share).div(EXP_SCALE); + // Two truncating divisions stand between this and the adapter's own single-step rounding, so + // the comparison is exact to the wei only up to that. The tolerance is ~1e-18 of the value. + expect(hubBefore.sub(hubAfter)).to.be.closeTo(expectedDrop, 1_000); + expect(hubAfter).to.be.lt(hubBefore); + }); + + it("lands the loss on every Hub depositor at once, not on whoever exits last", async () => { + await fundAndDefault(f); + + // Two more depositors, both in before the loss and holding equal stakes. + const each = usd("20000"); + for (const who of [f.borrower, f.outsider]) { + await fundFrom(f.usdt, bscmainnet.USDT_HOLDER, who.address, each); + await f.usdt.connect(who).approve(f.hub.address, each); + await f.hub.connect(who).deposit(each, who.address); + } + const ppsBefore: BigNumber = await f.hub.convertToAssets(EXP_SCALE); + + await healAs(f, f.liquidator); + + // One price per share moves for everyone in the same block. Nobody exits at the pre-loss + // price at the expense of whoever is left. + const ppsAfter: BigNumber = await f.hub.convertToAssets(EXP_SCALE); + expect(ppsAfter).to.be.lt(ppsBefore); + + const a: BigNumber = await f.hub.convertToAssets(await f.hub.balanceOf(f.borrower.address)); + const b: BigNumber = await f.hub.convertToAssets(await f.hub.balanceOf(f.outsider.address)); + // Not wei-identical: the two deposits landed in different blocks and the Hub's share price + // moves every block, so they bought marginally different share counts. What matters is that + // the loss did not fall on one of them - a nine-decimal relative agreement is far tighter than + // any exit-order effect would be. + expect(a.sub(b).abs()).to.be.lt(a.div(1_000_000_000)); + expect(a).to.be.lt(each); + }); + + it("never values the position above what the market can actually pay", async () => { + await fundAndDefault(f); + await healAs(f, f.liquidator); + + const value = await f.adapter.totalAssets(f.vUSDT.address, f.spokeSource.address); + const naive = (await f.vUSDT.balanceOf(f.spokeSource.address)) + .mul(await f.vUSDT.exchangeRateStored()) + .div(EXP_SCALE); + // The market's own rate would report `naive`, which includes debt nobody will ever repay. + expect(value).to.be.lt(naive); + + // And a redeem still burns at the market's un-haircut rate, so an exit costs fewer tokens than + // this mark implies. The mark can understate what the position delivers, never overstate it. + const liquid = await f.adapter.maxWithdraw(f.vUSDT.address, f.spokeSource.address); + expect(liquid).to.be.lte(value); + expect(liquid).to.be.lte((await f.vUSDT.getCash()).sub(await f.vUSDT.totalReserves())); + }); + + it("recovers the mark when a Shortfall auction repays the debt", async () => { + const { hubBefore } = await fundAndDefault(f); + await healAs(f, f.liquidator); + const badDebt = await f.vUSDT.badDebt(); + const marked = await f.adapter.totalAssets(f.vUSDT.address, f.spokeSource.address); + + // What an auction settlement does on chain: the Shortfall contract delivers the underlying to + // the market and calls back to lower `badDebt` and raise cash by the same amount. + const shortfall = await impersonate(bscmainnet.SHORTFALL); + await fundFrom(f.usdt, bscmainnet.USDT_HOLDER, bscmainnet.SHORTFALL, badDebt); + await f.usdt.connect(shortfall).transfer(f.vUSDT.address, badDebt); + await f.vUSDT.connect(shortfall).badDebtRecovered(badDebt); + + expect(await f.vUSDT.badDebt()).to.equal(0); + const recovered = await f.adapter.totalAssets(f.vUSDT.address, f.spokeSource.address); + expect(recovered).to.be.gt(marked); + expect(recovered).to.be.closeTo(hubBefore, 2); + }); + + it("routes an under-threshold account to healAccount and refuses the other two paths", async () => { + await fundAndDefault(f); + + // Too small for a regular liquidation... + const repay = usd("10"); + await fundFrom(f.usdt, bscmainnet.USDT_HOLDER, f.liquidator.address, repay); + await f.usdt.connect(f.liquidator).approve(f.vUSDT.address, repay); + await expect( + f.vUSDT.connect(f.liquidator).liquidateBorrow(f.borrower.address, repay, f.vBTCB.address), + ).to.be.revertedWithCustomError(f.spoke, "MinimalCollateralViolated"); + + // ...and the collateral cannot clear the debt, so `liquidateAccount` is the wrong batch path. + await expect( + f.spoke + .connect(f.liquidator) + .liquidateAccount(f.borrower.address, [ + { vTokenCollateral: f.vBTCB.address, vTokenBorrowed: f.vUSDT.address, repayAmount: repay }, + ]), + ).to.be.revertedWithCustomError(f.spoke, "DebtExceedsClearableAmount"); + + await expect(healAs(f, f.liquidator)).to.not.be.reverted; + }); + + it("routes by each collateral market's own liquidation incentive", async () => { + // `maxClearableDebt` sums `collateralValue / liquidationIncentive` at each market's own + // incentive, so this market's override is what moved the account from `liquidateAccount` to + // `healAccount`. Put it back on the pool-wide value and the routing flips. + await fundAndDefault(f); + expect(await f.spoke.effectiveLiquidationIncentive(f.vBTCB.address)).to.equal(usd("2")); + // The pool-wide value is untouched, and the market with no override still reads it. + expect(await f.spoke.effectiveLiquidationIncentive(f.vUSDT.address)).to.equal(usd("1.1")); + + await f.spoke.connect(f.timelock).setMarketLiquidationIncentive(f.vBTCB.address, usd("1.1")); + // At the pool-wide discount the collateral covers the debt, so healing would forgive nothing. + await expect(f.spoke.connect(f.liquidator).healAccount(f.borrower.address)).to.be.revertedWithCustomError( + f.spoke, + "CollateralCoversDebt", + ); + }); + }); +} + +/** + * `healAccount` makes the caller repay its share of the debt, so the healer has to hold and approve + * the underlying of every market the borrower owes in. A keeper that forgets this reverts on the + * transfer, not on any policy check. + */ +async function healAs(f: SpokeForkFixture, who: SignerWithAddress) { + const owed = await f.vUSDT.callStatic.borrowBalanceCurrent(f.borrower.address); + await fundFrom(f.usdt, bscmainnet.USDT_HOLDER, who.address, owed); + await f.usdt.connect(who).approve(f.vUSDT.address, owed); + return f.spoke.connect(who).healAccount(f.borrower.address); +} + +async function impersonate(who: string) { + await ethers.provider.send("hardhat_impersonateAccount", [who]); + await ethers.provider.send("hardhat_setBalance", [who, "0x56bc75e2d63100000"]); + return ethers.getSigner(who); +} + +/** + * Fund the market from the Hub, open a small borrow against BTCB, then move the two risk parameters + * that decide the account's fate: the liquidation threshold, which puts it under water, and the + * collateral market's own liquidation incentive, which shrinks what that collateral can clear below + * what is owed. That is the exact state `healAccount` exists for, and the only one that produces bad + * debt. + * + * Both are governance parameters on this pool, so nothing outside the spoke pool is touched. Moving + * a live price feed would reach the ResilientOracle, which every other pool on this chain shares. + */ +async function fundAndDefault(f: SpokeForkFixture, deposit = usd("100000")) { + await fundFrom(f.usdt, bscmainnet.USDT_HOLDER, f.supplier.address, deposit); + await f.usdt.connect(f.supplier).approve(f.hub.address, deposit); + await f.hub.connect(f.supplier).deposit(deposit, f.supplier.address); + + // Sized against the live BTCB price so the position sits under `minLiquidatableCollateral` + // (100 USD), which is what takes the regular liquidation path off the table. + const btcbPrice: BigNumber = await f.oracle.getPrice(bscmainnet.BTCB); + const collateral = usd("90").mul(EXP_SCALE).div(btcbPrice); + await fundFrom(f.btcb, bscmainnet.BTCB_HOLDER, f.borrower.address, collateral); + await f.btcb.connect(f.borrower).approve(f.vBTCB.address, collateral); + await f.vBTCB.connect(f.borrower).mint(collateral); + await f.spoke.connect(f.borrower).enterMarkets([f.vBTCB.address]); + await f.vUSDT.connect(f.borrower).borrow(usd("65")); // 0.75 x 90 = 67.5 of borrowing power + + // Under water on the liquidation threshold... + await f.spoke.connect(f.timelock).setCollateralFactor(f.vBTCB.address, usd("0.1"), usd("0.1")); + // ...and the collateral can no longer clear the debt at this market's discount: + // maxClearableDebt = 90 / 2 = 45, against 65 owed. + await f.spoke.connect(f.timelock).setMarketLiquidationIncentive(f.vBTCB.address, usd("2")); + + const [, , shortfall] = await f.spoke.getAccountLiquidity(f.borrower.address); + expect(shortfall).to.be.gt(0); + + const hubBefore: BigNumber = await f.adapter.totalAssets(f.vUSDT.address, f.spokeSource.address); + return { hubBefore }; +} diff --git a/tests/hardhat/Fork/HubSpoke/boundedPricing.ts b/tests/hardhat/Fork/HubSpoke/boundedPricing.ts new file mode 100644 index 000000000..5ff442ac0 --- /dev/null +++ b/tests/hardhat/Fork/HubSpoke/boundedPricing.ts @@ -0,0 +1,163 @@ +import { SnapshotRestorer, takeSnapshot } from "@nomicfoundation/hardhat-network-helpers"; +import { expect } from "chai"; +import { BigNumber } from "ethers"; +import { ethers } from "hardhat"; + +import { bscmainnet } from "./constants"; +import { SpokeForkFixture, fundFrom, grant, spokeForkFixture } from "./fixture"; + +const FORK = process.env.FORK === "true"; +const FORKED_NETWORK = process.env.FORKED_NETWORK || "bscmainnet"; + +const EXP_SCALE = ethers.constants.WeiPerEther; +const usd = (whole: string) => ethers.utils.parseUnits(whole, 18); + +if (FORK && FORKED_NETWORK === "bscmainnet") { + describe("HubSpoke: bounded collateral pricing against the live DeviationBoundedOracle", () => { + let f: SpokeForkFixture; + let snap: SnapshotRestorer; + + before(async () => { + f = await spokeForkFixture(); + snap = await takeSnapshot(); + }); + afterEach(async () => snap.restore()); + + it("is wired to the deployed oracle, and that oracle prices this pool's markets", async () => { + expect(await f.spoke.deviationBoundedOracle()).to.equal(bscmainnet.DEVIATION_BOUNDED_ORACLE); + // The oracle resolves a market to its asset through `underlying()`, so a market listed after + // the asset was configured is priced without any oracle-side change. That is what lets a new + // spoke market reuse the chain's existing protection state. + const [collateral, debt] = await f.boundedOracle.getBoundedPricesView(f.vBTCB.address); + expect(collateral).to.be.gt(0); + expect(debt).to.be.gt(0); + }); + + it("has BTCB under a live bound and USDT under none", async () => { + // The asset the borrowers post is protected; the asset the Hub supplies is not initialized on + // this oracle at all. That is a configuration fact a listing VIP has to act on: an + // uninitialized asset does not revert, it silently falls through to spot, so a spoke pool + // that lists one as COLLATERAL has an inert bound and nothing says so. + expect(await f.boundedOracle.isBoundedPricingEnabled(bscmainnet.BTCB)).to.be.true; + expect(await f.boundedOracle.isBoundedPricingEnabled(bscmainnet.USDT)).to.be.false; + + const spot: BigNumber = await f.oracle.getPrice(bscmainnet.USDT); + const [collateral, debt] = await f.boundedOracle.getBoundedPricesView(f.vUSDT.address); + expect(collateral).to.equal(spot); + expect(debt).to.equal(spot); + }); + + it("lets the pool latch protection without any grant of its own", async () => { + // `updateProtectionState` is unpermissioned, which is what allows the comptroller to call it + // from `preBorrowHook` and `preRedeemHook`. If it were ever gated, the listing VIP would have + // to grant the pool a role on the oracle and every borrow would revert until it did. + await expect(f.boundedOracle.connect(f.outsider).updateProtectionState(f.vBTCB.address)).to.not.be.reverted; + }); + + it("prices borrowing capacity at the bound and liquidation at spot", async () => { + await seedAndCollateralise(f); + + const spot: BigNumber = await f.oracle.getPrice(bscmainnet.BTCB); + const collateralBalance = await f.vBTCB.balanceOf(f.borrower.address); + const exchangeRate = await f.vBTCB.exchangeRateStored(); + const posted = collateralBalance.mul(exchangeRate).div(EXP_SCALE); + + // Protection is not active at the pinned block, so the bound equals spot and the two views + // agree. This is the baseline the next test moves away from. + const [, borrowingPower] = await f.spoke.getBorrowingPower(f.borrower.address); + const [, liquidity] = await f.spoke.getAccountLiquidity(f.borrower.address); + expect(borrowingPower).to.be.closeTo(posted.mul(spot).div(EXP_SCALE).mul(75).div(100), usd("1")); + // The liquidation-threshold view weights the same collateral at 0.8 instead of 0.75. + expect(liquidity).to.be.gt(borrowingPower); + }); + + it("cuts borrowing capacity, but not liquidation pricing, once protection latches", async () => { + await seedAndCollateralise(f); + const beforePower = (await f.spoke.getBorrowingPower(f.borrower.address))[1]; + const beforeLiquidity = (await f.spoke.getAccountLiquidity(f.borrower.address))[1]; + + await latchProtection(f); + expect(await f.boundedOracle.currentlyUsingProtectedPrice(bscmainnet.BTCB)).to.be.true; + + // Borrowing is weighted by the collateral factor and so reads the bound, which is now the + // conservative end of a widened window. + const afterPower = (await f.spoke.getBorrowingPower(f.borrower.address))[1]; + expect(afterPower).to.be.lt(beforePower); + + // Liquidation routing stays on spot, so a bounded price cannot make a healthy account + // liquidatable. + expect((await f.spoke.getAccountLiquidity(f.borrower.address))[1]).to.equal(beforeLiquidity); + }); + + it("holds a borrow to the bounded capacity, not the spot capacity", async () => { + await seedAndCollateralise(f); + await latchProtection(f); + + const [, bounded] = await f.spoke.getBorrowingPower(f.borrower.address); + // A borrow inside the bound settles... + await expect(f.vUSDT.connect(f.borrower).borrow(bounded.div(2))).to.not.be.reverted; + // ...and one that only spot would have allowed does not. + await expect(f.vUSDT.connect(f.borrower).borrow(bounded)).to.be.revertedWithCustomError( + f.spoke, + "InsufficientLiquidity", + ); + }); + + it("leaves the Hub's own supply and exit untouched by protection", async () => { + // The YieldGroup never enters a market, so `_checkRedeemAllowed` returns before it reaches + // `_updateProtectionStates`. A latched bound therefore cannot freeze the Hub's redemptions, + // only the borrowers' capacity. + await seedAndCollateralise(f); + await latchProtection(f); + + const src = await impersonate(f.spokeSource.address); + expect(await f.spoke.getAssetsIn(f.spokeSource.address)).to.have.lengthOf(0); + const held = await f.vUSDT.balanceOf(f.spokeSource.address); + expect(held).to.be.gt(0); + await expect(f.vUSDT.connect(src).redeem(held)).to.not.be.reverted; + }); + }); +} + +async function impersonate(who: string) { + await ethers.provider.send("hardhat_impersonateAccount", [who]); + await ethers.provider.send("hardhat_setBalance", [who, "0x56bc75e2d63100000"]); + return ethers.getSigner(who); +} + +/// Fund the liquidity market from the Hub and put real BTCB collateral behind a borrower. +async function seedAndCollateralise(f: SpokeForkFixture) { + const { registerSpokeResource, registerOnHub } = await import("./fixture"); + await registerSpokeResource(f); + await registerOnHub(f, usd("2000000")); + + const deposit = usd("200000"); + await fundFrom(f.usdt, bscmainnet.USDT_HOLDER, f.supplier.address, deposit); + await f.usdt.connect(f.supplier).approve(f.hub.address, deposit); + await f.hub.connect(f.supplier).deposit(deposit, f.supplier.address); + + const collateral = ethers.utils.parseUnits("2", 18); + await fundFrom(f.btcb, bscmainnet.BTCB_HOLDER, f.borrower.address, collateral); + await f.btcb.connect(f.borrower).approve(f.vBTCB.address, collateral); + await f.vBTCB.connect(f.borrower).mint(collateral); + await f.spoke.connect(f.borrower).enterMarkets([f.vBTCB.address]); +} + +/** + * Widen BTCB's price window past its trigger threshold through the oracle's own keeper surface, then + * let the pool observe it. Nothing is faked: `updateMinPrice` is the call the production keeper + * makes, and the trigger, the cooldown and the resulting bound are all the deployed oracle's. + */ +async function latchProtection(f: SpokeForkFixture) { + await grant( + f.acm, + f.timelock, + bscmainnet.DEVIATION_BOUNDED_ORACLE, + "updateMinPrice(address,uint128)", + bscmainnet.NORMAL_TIMELOCK, + ); + const spot: BigNumber = await f.oracle.getPrice(bscmainnet.BTCB); + // Half of spot: far outside the 12.5% trigger this asset is configured with. + await f.boundedOracle.connect(f.timelock).updateMinPrice(bscmainnet.BTCB, spot.div(2)); + await f.boundedOracle.connect(f.outsider).updateProtectionState(f.vBTCB.address); +} diff --git a/tests/hardhat/Fork/HubSpoke/constants.ts b/tests/hardhat/Fork/HubSpoke/constants.ts new file mode 100644 index 000000000..4fff4d280 --- /dev/null +++ b/tests/hardhat/Fork/HubSpoke/constants.ts @@ -0,0 +1,95 @@ +import GovernanceBscMainnet from "@venusprotocol/governance-contracts/deployments/bscmainnet.json"; +import OracleBscMainnet from "@venusprotocol/oracle/deployments/bscmainnet.json"; +import PsrBscMainnet from "@venusprotocol/protocol-reserve/deployments/bscmainnet.json"; +import { getAddress } from "ethers/lib/utils"; + +import { contracts as MainnetContracts } from "../../../../deployments/bscmainnet.json"; + +/** + * Addresses the hub-funded spoke pool suite binds against. Everything here is already live on + * bscmainnet; nothing in this file is deployed by the suite. Addresses come from the package the + * repo compiles against rather than from literals, so a dependency bump moves them here too. + * + * The two exceptions are the hub-side addresses, which live in a repo this one does not depend on. + * They are recorded as literals and re-derived from the chain in `assertions.ts`, so a wrong value + * fails loudly instead of silently testing the wrong contract. + */ +/** + * Checksum every address on the way in. The deployment records these are read from are not + * consistent about case - `@venusprotocol/governance-contracts` stores the bscmainnet + * AccessControlManager lower case and the NormalTimelock checksummed - and an on-chain read always + * returns the checksummed form, so a raw `===` against a recorded value is a coin flip. That is not + * a hypothetical: `deploy/025-deploy-spoke-comptroller.ts` compares them raw and aborts on this + * chain because of it (see `deployment.ts`). + */ +const addr = (a: string) => getAddress(a); + +export const bscmainnet = { + // ── Governance ─────────────────────────────────────────────────────────── + NORMAL_TIMELOCK: addr(GovernanceBscMainnet.contracts.NormalTimelock.address), + ACM: addr(GovernanceBscMainnet.contracts.AccessControlManager.address), + + // ── Isolated pools ─────────────────────────────────────────────────────── + POOL_REGISTRY: addr(MainnetContracts.PoolRegistry.address), + VTOKEN_BEACON: addr(MainnetContracts.VTokenBeacon.address), + COMPTROLLER_BEACON: addr(MainnetContracts.ComptrollerBeacon.address), + SHORTFALL: addr(MainnetContracts.Shortfall.address), + // An existing pool, used only as a control when checking that spoke changes do not leak. + COMPTROLLER_STABLECOINS: addr(MainnetContracts.Comptroller_Stablecoins.address), + + PSR: addr(PsrBscMainnet.contracts.ProtocolShareReserve.address), + + // The chain's shared transparent-proxy admin, owned by the Normal Timelock. The spoke pool + // registry goes behind this one rather than a fresh admin, exactly as the deploy script does. + DEFAULT_PROXY_ADMIN: addr(MainnetContracts.DefaultProxyAdmin.address), + + // ── Oracles ────────────────────────────────────────────────────────────── + RESILIENT_ORACLE: addr(OracleBscMainnet.contracts.ResilientOracle.address), + // Main and pivot behind the ResilientOracle for both of this suite's assets. See `relaxPriceStaleness`. + CHAINLINK_ORACLE: addr(OracleBscMainnet.contracts.ChainlinkOracle.address), + REDSTONE_ORACLE: addr(OracleBscMainnet.contracts.RedStoneOracle.address), + DEVIATION_BOUNDED_ORACLE: addr(OracleBscMainnet.contracts.DeviationBoundedOracle.address), + + // ── Assets ─────────────────────────────────────────────────────────────── + // USDT is the liquidity asset: it matches the live Hub's asset, so the hub-funded leg is real. + USDT: addr("0x55d398326f99059fF775485246999027B3197955"), + // BTCB is the collateral asset. Chosen because the live DeviationBoundedOracle has it initialized + // and bounded-pricing enabled, which is what puts the spoke pool's bounded collateral pricing on a + // real oracle rather than a permissive fallback. USDT is deliberately NOT initialized there; see + // `oracle.ts`. + BTCB: addr("0x7130d2A12B9BCbFAe4f2634d864A1Ee1Ce3Ead9c"), + + // TRX is the one BEP-20 in the live DeviationBoundedOracle's initialized set with fewer than 18 + // decimals (it has 6). A market listed against it carries an initial exchange rate BELOW 1e18, + // which is the regime `AdapterSpokeV1._bumpToSettleable` and `_redeemPayout` exist for and which + // no 18-decimal market can reach. + TRX: addr("0xCE7de646e7208a4Ef112cb6ed5038FA6cC6b12e3"), + + // Whales impersonated to fund actors. Both are contracts holding far more than the suite moves. + USDT_HOLDER: addr("0xa180Fe01B906A1bE37BE6c534a3300785b20d947"), + BTCB_HOLDER: addr("0x882C173bC7Ff3b7786CA16dfeD3DFFfb9Ee7847B"), // core-pool vBTCB + TRX_HOLDER: addr("0xC5D3466aA484B040eE977073fcF337f2c00071c1"), // core-pool vTRX + + // ── Liquidity Hub (venus-liquidity-hub, bscmainnet) ────────────────────── + HUB_USDT: addr("0x18AfDACF30F8671021dec4b78297E39d2FE87226"), + // The generic YieldGroup implementation the Core family runs on. The spoke source is the same + // contract behind its own beacon, so the suite mints its beacon from this deployed implementation + // instead of rebuilding it from source it does not have. + YIELD_GROUP_IMPL: addr("0x3BccED778CAaf97AE8ff8Bedc16d2165aA15771b"), + CORE_SOURCE_USDT: addr("0xC9E6ceD9589363f8dC5695Be2C79AB4dDaECC94B"), + CORE_BEACON: addr("0x195a0F1BCF73C3Beb609a1271E8E08b8E4c098C6"), + HUB_BEACON: addr("0x0f20e1004962e2DF16c16FC15460Dc6480626321"), +}; + +/// Action enum in `ComptrollerInterface`, mirrored for readability in the tests. +export enum Action { + MINT, + REDEEM, + BORROW, + REPAY, + SEIZE, + LIQUIDATE, + TRANSFER, + ENTER_MARKET, + EXIT_MARKET, +} diff --git a/tests/hardhat/Fork/HubSpoke/deployment.ts b/tests/hardhat/Fork/HubSpoke/deployment.ts new file mode 100644 index 000000000..0def2c5b1 --- /dev/null +++ b/tests/hardhat/Fork/HubSpoke/deployment.ts @@ -0,0 +1,151 @@ +import { expect } from "chai"; +import { deployments, ethers, getNamedAccounts } from "hardhat"; + +import { setForkBlock } from "../utils"; +import { bscmainnet } from "./constants"; +import { BLOCK_NUMBER } from "./fixture"; + +const FORK = process.env.FORK === "true"; +const FORKED_NETWORK = process.env.FORKED_NETWORK || "bscmainnet"; + +const EIP_170_LIMIT = 24_576; + +/** + * Runs the real `deploy/025-deploy-spoke-comptroller.ts` and `deploy/027-deploy-spoke-vtoken-beacon.ts` + * against live bscmainnet, which is the only way to find out what they do with the addresses this chain + * actually reports. The behavioural + * suites deploy the same stack directly, because they need the pool in a listed and configured state + * the script deliberately leaves to the listing VIP. + */ +if (FORK && FORKED_NETWORK === "bscmainnet") { + describe("HubSpoke: the deployment script against live bscmainnet", () => { + let deployer: string; + let scriptError: Error | undefined; + + before(async () => { + await setForkBlock(BLOCK_NUMBER); + ({ deployer } = await getNamedAccounts()); + try { + await deployments.fixture(["HubSpokeComptroller", "HubSpokeVTokenBeacon"], { + keepExistingDeployments: true, + }); + } catch (e) { + scriptError = e as Error; + } + }); + + const deployed = async (name: string) => { + const record = await deployments.getOrNull(name); + if (record === null) throw new Error(`${name} was not deployed`); + return record.address; + }; + + it("runs to completion", () => { + // Regression guard for a defect this suite found. The script used to compare addresses as + // strings, and `toAddress` returns whatever `preconfiguredAddresses` holds: + // `@venusprotocol/governance-contracts` records the bscmainnet AccessControlManager in lower + // case while the on-chain read returns it checksummed, so a pre-handover check rejected a + // correct value and the script threw before either ownership transfer. Only a fork run + // catches it, because the casing comes from the network's own deployments package. + expect(scriptError?.message).to.equal(undefined); + }); + + it("deploys a pool registry of its own rather than reusing this chain's", async () => { + // `getAllPools` on the live registry is what the indexer, the frontend pool list and the risk + // tooling iterate. A hub-funded pool whose supply, borrow and liquidation sides are all + // restricted does not belong in that directory, and once it is in there every one of those + // consumers needs a special case keyed on its address. + const registryAddress = await deployed("SpokePoolRegistry"); + expect(registryAddress).to.not.equal(bscmainnet.POOL_REGISTRY); + + const registry = await ethers.getContractAt("PoolRegistry", registryAddress); + expect(await registry.accessControlManager()).to.equal(bscmainnet.ACM); + expect(await registry.getAllPools()).to.have.lengthOf(0); + + // `Ownable2Step`, like the comptroller: the script can only nominate. + expect(await registry.owner()).to.equal(deployer); + expect(await registry.pendingOwner()).to.equal(bscmainnet.NORMAL_TIMELOCK); + }); + + it("constructs the implementation with that registry, not the live one", async () => { + // Immutable, so this is the one wiring decision no setter can walk back. + const spoke = await ethers.getContractAt("SpokeComptroller", await deployed("SpokeComptrollerImpl")); + expect(await spoke.poolRegistry()).to.equal(await deployed("SpokePoolRegistry")); + }); + + it("points its own beacon at that implementation, never the shared ComptrollerBeacon", async () => { + const impl = await deployed("SpokeComptrollerImpl"); + const beacon = await ethers.getContractAt("UpgradeableBeacon", await deployed("SpokeComptrollerBeacon")); + expect(beacon.address).to.not.equal(bscmainnet.COMPTROLLER_BEACON); + expect(await beacon.implementation()).to.equal(impl); + + // Sharing the beacon would move every other isolated pool on this chain onto the spoke + // implementation the moment either side was upgraded. + const shared = await ethers.getContractAt("UpgradeableBeacon", bscmainnet.COMPTROLLER_BEACON); + expect(await shared.implementation()).to.not.equal(impl); + }); + + it("initializes the proxy against the live ACM and leaves the pool unconfigured", async () => { + const spoke = await ethers.getContractAt("SpokeComptroller", await deployed("Comptroller_HubSpoke")); + expect(await spoke.accessControlManager()).to.equal(bscmainnet.ACM); + expect(await spoke.maxLoopsLimit()).to.equal(100); + // Everything the listing VIP owns is still unset, which is what makes the order of that VIP + // load-bearing rather than cosmetic. + expect(await spoke.oracle()).to.equal(ethers.constants.AddressZero); + expect(await spoke.deviationBoundedOracle()).to.equal(ethers.constants.AddressZero); + expect(await spoke.getAllMarkets()).to.have.lengthOf(0); + }); + + it("registers the pool in neither registry, which is the listing VIP's job", async () => { + const comptroller = await deployed("Comptroller_HubSpoke"); + for (const registryAddress of [bscmainnet.POOL_REGISTRY, await deployed("SpokePoolRegistry")]) { + const registry = await ethers.getContractAt("PoolRegistry", registryAddress); + const pool = await registry.getPoolByComptroller(comptroller); + expect(pool.comptroller, `registry ${registryAddress} should not hold the pool`).to.equal( + ethers.constants.AddressZero, + ); + } + }); + + it("points a VToken beacon of its own at a fresh implementation, never the shared VTokenBeacon", async () => { + const impl = await deployed("SpokeVTokenImpl"); + const beacon = await ethers.getContractAt("UpgradeableBeacon", await deployed("SpokeVTokenBeacon")); + expect(beacon.address).to.not.equal(bscmainnet.VTOKEN_BEACON); + expect(await beacon.implementation()).to.equal(impl); + + // `upgradeTo` moves every proxy behind a beacon at once. Sharing this one would tie a VToken change + // for the spoke pool to every isolated market on the chain, in both directions. + const shared = await ethers.getContractAt("UpgradeableBeacon", bscmainnet.VTOKEN_BEACON); + expect(await shared.implementation()).to.not.equal(impl); + }); + + it("hands the VToken beacon to the Normal Timelock inside the deployment transaction", async () => { + const beacon = await ethers.getContractAt("UpgradeableBeacon", await deployed("SpokeVTokenBeacon")); + expect(await beacon.owner()).to.equal(bscmainnet.NORMAL_TIMELOCK); + }); + + it("hands the beacon to the Normal Timelock inside the deployment transaction", async () => { + // `UpgradeableBeacon` is plain `Ownable`, so this handover completes inside the run itself, + // unlike the comptroller's below. + const beacon = await ethers.getContractAt("UpgradeableBeacon", await deployed("SpokeComptrollerBeacon")); + expect(await beacon.owner()).to.equal(bscmainnet.NORMAL_TIMELOCK); + }); + + it("nominates the Normal Timelock on the comptroller and leaves the deployer as owner", async () => { + // `Ownable2Step` means the script can only nominate; the deployer stays the live owner until + // the VIP calls `acceptOwnership`, which is why that call has to be the VIP's first action. + const spoke = await ethers.getContractAt("SpokeComptroller", await deployed("Comptroller_HubSpoke")); + expect(await spoke.owner()).to.equal(deployer); + expect(await spoke.pendingOwner()).to.equal(bscmainnet.NORMAL_TIMELOCK); + }); + + it("fits the deployed implementation under the EIP-170 limit", async () => { + // `allowUnlimitedContractSize` is on for the hardhat network, so an oversized implementation + // deploys here and would only fail on a real chain. Measure it rather than trusting the + // deployment to have caught it. + const code = await ethers.provider.getCode(await deployed("SpokeComptrollerImpl")); + const size = (code.length - 2) / 2; + expect(size, `SpokeComptroller runtime size ${size}`).to.be.lessThanOrEqual(EIP_170_LIMIT); + }); + }); +} diff --git a/tests/hardhat/Fork/HubSpoke/fixture.ts b/tests/hardhat/Fork/HubSpoke/fixture.ts new file mode 100644 index 000000000..0d64dba2a --- /dev/null +++ b/tests/hardhat/Fork/HubSpoke/fixture.ts @@ -0,0 +1,714 @@ +import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; +import { BigNumber, Contract, Signer } from "ethers"; +import { ethers } from "hardhat"; + +import { + AccessControlManager, + AccessControlManager__factory, + AdapterSpokeV1, + IERC20, + IERC20__factory, + JumpRateModelV2, + PoolRegistry, + PoolRegistry__factory, + SpokeComptroller, + SpokeComptroller__factory, + VToken, + VToken__factory, +} from "../../../../typechain"; +import { initMainnetUser, setForkBlock } from "../utils"; +import { bscmainnet } from "./constants"; + +/// Pinned so the live DeviationBoundedOracle windows, the Hub's TVL and every market rate the +/// assertions lean on are the same on every run. +export const BLOCK_NUMBER = 116_847_000; + +const MAX_LOOPS_LIMIT = 100; + +/// Longer than any run. See `relaxPriceStaleness`. +const STALE_PERIOD = 10 * 365 * 24 * 60 * 60; +const EXP_SCALE = ethers.utils.parseUnits("1", 18); + +/// The live VToken implementation this chain's beacon points at is block-based on this cadence. +/// The interest rate model the suite deploys has to be built the same way or the market's rate is +/// off by the ratio between the two. +const BLOCKS_PER_YEAR = 42_048_000; + +/// What `getMaxBorrowRateMantissa` returns for this chain, i.e. the value `009-deploy-vtokens.ts` and +/// `027-deploy-spoke-vtoken-beacon.ts` both construct a VToken implementation with. It is an internal immutable with +/// no getter, so it cannot be read back off the live implementation to compare. +const MAX_BORROW_RATE_MANTISSA = BigNumber.from("5000000000000"); + +/// Role strings the spoke comptroller checks, verbatim. A VIP that grants anything else grants +/// nothing: the ACM hashes the string, so a near-miss is silently a different role. +export const SPOKE_ROLES = { + setCollateralFactor: "setCollateralFactor(address,uint256,uint256)", + setLiquidationIncentive: "setLiquidationIncentive(uint256)", + setCloseFactor: "setCloseFactor(uint256)", + setMinLiquidatableCollateral: "setMinLiquidatableCollateral(uint256)", + setMarketSupplyCaps: "setMarketSupplyCaps(address[],uint256[])", + setMarketBorrowCaps: "setMarketBorrowCaps(address[],uint256[])", + setActionsPaused: "setActionsPaused(address[],uint8[],bool)", + setForcedLiquidation: "setForcedLiquidation(address,bool)", + unlistMarket: "unlistMarket(address)", + // The five the fork of the shared Comptroller adds. None of these role strings exists on any + // other Venus contract, so no pre-existing grant covers them. + setMarketLiquidationIncentive: "setMarketLiquidationIncentive(address,uint256)", + setSupplyAllowlistEnabled: "setSupplyAllowlistEnabled(address,bool)", + setAllowedSupplier: "setAllowedSupplier(address,address,bool)", + setLiquidationAllowlistEnabled: "setLiquidationAllowlistEnabled(bool)", + setAllowedLiquidator: "setAllowedLiquidator(address,bool)", +}; + +/// The calls `PoolRegistry` makes into a comptroller while registering a pool and its markets. The +/// ACM's wildcard grants covering these are keyed on `address(0)` but name the live registry as the +/// account, so a registry deployed for this pool inherits none of them and the VIP grants all six. +export const REGISTRY_DRIVEN_ROLES = [ + SPOKE_ROLES.setCloseFactor, + SPOKE_ROLES.setLiquidationIncentive, + SPOKE_ROLES.setMinLiquidatableCollateral, + SPOKE_ROLES.setCollateralFactor, + SPOKE_ROLES.setMarketSupplyCaps, + SPOKE_ROLES.setMarketBorrowCaps, +]; + +/// Role strings `PoolRegistry` checks, verbatim. `addMarket` passes the struct name, not the expanded +/// tuple, and the ACM hashes whatever string the contract passes. +export const REGISTRY_ROLES = { + addPool: "addPool(string,address,uint256,uint256,uint256)", + addMarket: "addMarket(AddMarketInput)", + setPoolName: "setPoolName(address,string)", + updatePoolMetadata: "updatePoolMetadata(address,VenusPoolMetaData)", +}; + +/// Role strings on the hub side, read from `YieldGroupBase` and `Hub`. +export const HUB_ROLES = { + addYieldGroup: "addYieldGroup(address,uint256,uint16)", + setOuterDepositQueue: "setOuterDepositQueue(address[])", + setOuterWithdrawQueue: "setOuterWithdrawQueue(address[])", + addResource: "addResource(address,address)", + removeResource: "removeResource(address)", + setInnerDepositQueue: "setInnerDepositQueue(address[])", + setInnerWithdrawQueue: "setInnerWithdrawQueue(address[])", + raiseResourceCap: "raiseResourceCap(address,uint256)", + lowerResourceCap: "lowerResourceCap(address,uint256)", + pauseResource: "pauseResource(address)", +}; + +/// Minimal human-readable ABIs for the two hub-side contracts the suite binds by address. Kept +/// here rather than imported, because this repo does not depend on the hub. Every member is +/// exercised by the suite, so a signature that drifts fails a test rather than passing silently. +/// Hub role strings, from `Hub`'s own `_checkAccessAllowed` calls. +export const HUB_ADMIN_ROLES = { + addYieldGroup: "addYieldGroup(address,uint256,uint16)", + setOuterDepositQueue: "setOuterDepositQueue(address[])", + setOuterWithdrawQueue: "setOuterWithdrawQueue(address[])", +}; + +export const HUB_ABI = [ + "function asset() view returns (address)", + "function owner() view returns (address)", + "function accessControlManager() view returns (address)", + "function totalAssets() view returns (uint256)", + "function totalSupply() view returns (uint256)", + "function balanceOf(address) view returns (uint256)", + "function convertToAssets(uint256) view returns (uint256)", + "function maxWithdraw(address) view returns (uint256)", + "function registeredYieldGroups() view returns (address[])", + "function outerDepositQueue() view returns (address[])", + "function outerWithdrawQueue() view returns (address[])", + "function yieldGroupConfig(address) view returns (tuple(uint256 absoluteCap, uint16 percentageCapBps, bool paused, bool registered))", + "function addYieldGroup(address yieldGroup, uint256 absoluteCap, uint16 percentageCapBps)", + "function setOuterDepositQueue(address[] queue)", + "function setOuterWithdrawQueue(address[] queue)", + "function deposit(uint256 assets, address receiver) returns (uint256)", + "function withdraw(uint256 assets, address receiver, address owner) returns (uint256)", + "function redeem(uint256 shares, address receiver, address owner) returns (uint256)", + "function initialize(address asset_, string name_, string symbol_, address acm_, uint8 decimalsOffset_, uint256 initialMaxWithdrawalSize, address feeRecipient_)", +]; + +export const YIELD_GROUP_ABI = [ + "function initialize(address hub_, address asset_, uint256 blocksPerYear_, address acm_)", + "function hub() view returns (address)", + "function asset() view returns (address)", + "function resources() view returns (address[])", + "function totalAssets() view returns (uint256)", + "function maxDeposit() view returns (uint256)", + "function maxWithdraw() view returns (uint256)", + "function deposit(uint256 amount) returns (uint256)", + "function withdraw(uint256 amount, address to)", + "function depositResource(address resource, uint256 amount) returns (uint256)", + "function withdrawResource(address resource, uint256 amount, address to)", + "function addResource(address resource, address adapter)", + "function removeResource(address resource)", + "function setInnerDepositQueue(address[] queue)", + "function setInnerWithdrawQueue(address[] queue)", + "function raiseResourceCap(address resource, uint256 newCap)", + "function lowerResourceCap(address resource, uint256 newCap)", + "function pauseResource(address resource)", + "function resourceCap(address resource) view returns (uint256)", +]; + +/// The slice of `ProtocolShareReserve` this suite touches. It holds one pool registry, which is what +/// makes re-pointing it a protocol-wide decision rather than a spoke-local one. +export const PSR_ABI = [ + "function owner() view returns (address)", + "function poolRegistry() view returns (address)", + "function setPoolRegistry(address newPoolRegistry)", + "function updateAssetsState(address comptroller, address asset, uint8 incomeType)", +]; + +export interface SpokeForkFixture extends SpokeStack, SpokeMarkets, HubSide {} + +const isSame = (a: string, b: string) => a.toLowerCase() === b.toLowerCase(); + +/// Grant `who` permission to call `sig` on `target`, through the live ACM, as the Timelock does in +/// a VIP. `timelock` holds DEFAULT_ADMIN_ROLE on the live ACM, which is what makes this a replay of +/// the real governance action rather than a shortcut around it. +export async function grant(acm: AccessControlManager, timelock: Signer, target: string, sig: string, who: string) { + await acm.connect(timelock).giveCallPermission(target, sig, who); +} + +/** + * Take the pinned block's price rounds out of the picture. + * + * Hardhat advances a forked chain's timestamp with real elapsed time, so every transaction a suite + * runs eats into the staleness budget the pinned block leaves. At `BLOCK_NUMBER` that is about 77 + * seconds for BTCB, past which `ResilientOracle` runs out of oracles that agree and reverts + * "invalid resilient oracle price", failing anything that prices collateral. + * + * Widening `maxStalePeriod` is what the other fork suites here do. Main and pivot are both widened, + * so `ResilientOracle` still validates one against the other. No price changes: the rounds, the + * bound validator and the deviation-bounded oracle are the live ones, and only the clock stops + * being a variable. + */ +async function relaxPriceStaleness(timelock: Signer) { + const acm = AccessControlManager__factory.connect(bscmainnet.ACM, timelock); + const feedAbi = [ + "function tokenConfigs(address) view returns (address asset, address feed, uint256 maxStalePeriod)", + "function setTokenConfig((address asset, address feed, uint256 maxStalePeriod) tokenConfig)", + ]; + + for (const oracleAddress of [bscmainnet.CHAINLINK_ORACLE, bscmainnet.REDSTONE_ORACLE]) { + await acm.giveCallPermission(oracleAddress, "setTokenConfig(TokenConfig)", bscmainnet.NORMAL_TIMELOCK); + const oracle = await ethers.getContractAt(feedAbi, oracleAddress, timelock); + + for (const asset of [bscmainnet.USDT, bscmainnet.BTCB]) { + const { feed } = await oracle.tokenConfigs(asset); + // An oracle that does not price this asset has nothing to widen, and `setTokenConfig` rejects + // the zero feed anyway. + if (feed === ethers.constants.AddressZero) continue; + await oracle.setTokenConfig({ asset, feed, maxStalePeriod: STALE_PERIOD }); + } + } +} + +/// The spoke pool's own `PoolRegistry`, behind the chain's shared proxy admin, standing in for +/// `deploy/024-deploy-spoke-pool-registry.ts`. The live isolated-pools registry is the directory the +/// indexer, the frontend pool list and the risk tooling iterate, and a pool whose supply, borrow and +/// liquidation sides are each restricted does not belong in it. +async function deploySpokePoolRegistry(deployer: SignerWithAddress): Promise { + const implFactory = await ethers.getContractFactory("PoolRegistry", deployer); + const impl = await implFactory.deploy(); + await impl.deployed(); + + const proxyFactory = await ethers.getContractFactory( + "hardhat-deploy/solc_0.8/proxy/OptimizedTransparentUpgradeableProxy.sol:OptimizedTransparentUpgradeableProxy", + deployer, + ); + const proxy = await proxyFactory.deploy( + impl.address, + bscmainnet.DEFAULT_PROXY_ADMIN, + implFactory.interface.encodeFunctionData("initialize", [bscmainnet.ACM]), + ); + await proxy.deployed(); + return PoolRegistry__factory.connect(proxy.address, deployer); +} + +async function deployIrm(acm: string, deployer: SignerWithAddress): Promise { + const factory = await ethers.getContractFactory("JumpRateModelV2", deployer); + return (await factory.deploy( + 0, // baseRatePerYear + ethers.utils.parseUnits("0.1", 18), // multiplierPerYear + ethers.utils.parseUnits("2", 18), // jumpMultiplierPerYear + ethers.utils.parseUnits("0.8", 18), // kink + acm, + false, // timeBased: the live VToken implementation is block-based + BLOCKS_PER_YEAR, + )) as JumpRateModelV2; +} + +async function deployVToken( + deployer: SignerWithAddress, + beacon: string, + underlying: string, + comptroller: string, + irm: string, + name: string, + symbol: string, + decimals: number, + reserveFactorMantissa: BigNumber, + underlyingDecimals: number, + initialExchangeRateMantissa?: BigNumber, +): Promise { + const vTokenFactory = await ethers.getContractFactory("VToken"); + const initData = vTokenFactory.interface.encodeFunctionData("initialize", [ + underlying, + comptroller, + irm, + // 10 ** (18 + underlyingDecimals - vTokenDecimals), the rate every live isolated market is + // listed at. For an 18-decimal underlying that is 1e28; for a 6-decimal one it is 1e16, i.e. + // BELOW `EXP_SCALE`, which is the regime `AdapterSpokeV1._bumpToSettleable` exists for. + initialExchangeRateMantissa ?? ethers.utils.parseUnits("1", 18 + underlyingDecimals - decimals), + name, + symbol, + decimals, + await deployer.getAddress(), + bscmainnet.ACM, + { shortfall: bscmainnet.SHORTFALL, protocolShareReserve: bscmainnet.PSR }, + reserveFactorMantissa, + ]); + // Behind the spoke pool's own beacon, never the chain's shared `VTokenBeacon`. This is where the + // separation is actually decided: the beacon is inert until the markets point at it. + const proxyFactory = await ethers.getContractFactory("BeaconProxy", deployer); + const proxy = await proxyFactory.deploy(beacon, initData); + await proxy.deployed(); + return VToken__factory.connect(proxy.address, deployer); +} + +/// Move `amount` of `token` from a live whale to `to`. +export async function fundFrom(token: IERC20, whale: string, to: string, amount: BigNumber) { + const holder = await initMainnetUser(whale, ethers.utils.parseUnits("2")); + await token.connect(holder).transfer(to, amount); +} + +/** + * Stands the whole hub-funded spoke pool up on a bscmainnet fork, in the order the listing VIP has + * to use. Everything it binds is live; everything it deploys is a contract that genuinely has to be + * deployed to ship this feature. + * + * Deliberately NOT run through `deploy/025-deploy-spoke-comptroller.ts` and + * `deploy/027-deploy-spoke-vtoken-beacon.ts`: `deployment.ts` covers those scripts on their own, and the + * behavioural suites need the pool in a listed, configured state that they explicitly leave to the VIP. + */ +export interface SpokeStack { + timelock: Signer; + deployer: SignerWithAddress; + supplier: SignerWithAddress; + borrower: SignerWithAddress; + liquidator: SignerWithAddress; + outsider: SignerWithAddress; + acm: AccessControlManager; + registry: PoolRegistry; + oracle: Contract; + boundedOracle: Contract; + usdt: IERC20; + btcb: IERC20; + spoke: SpokeComptroller; + spokeBeacon: Contract; + spokeImpl: string; + vTokenBeacon: Contract; +} + +/** + * Deploy the spoke stack exactly as `deploy/025-deploy-spoke-comptroller.ts` and + * `deploy/027-deploy-spoke-vtoken-beacon.ts` do, then hand it to governance. Stops short of registering + * the pool, which is where the listing VIP starts. + * + * `configure: false` leaves the pool in the raw state the deploy script produces - no oracle, no + * ACM grants, deployer still the live owner - so a test can assert what the VIP has to supply. + */ +export async function deploySpokeStack(configure = true): Promise { + await setForkBlock(BLOCK_NUMBER); + + const [deployer, supplier, borrower, liquidator, outsider] = await ethers.getSigners(); + const timelock = await initMainnetUser(bscmainnet.NORMAL_TIMELOCK, ethers.utils.parseUnits("100")); + await relaxPriceStaleness(timelock); + + const acm = AccessControlManager__factory.connect(bscmainnet.ACM, deployer); + const registry = await deploySpokePoolRegistry(deployer); + const oracle = await ethers.getContractAt("ResilientOracleInterface", bscmainnet.RESILIENT_ORACLE); + const boundedOracle = await ethers.getContractAt("IDeviationBoundedOracle", bscmainnet.DEVIATION_BOUNDED_ORACLE); + const usdt = IERC20__factory.connect(bscmainnet.USDT, deployer); + const btcb = IERC20__factory.connect(bscmainnet.BTCB, deployer); + + const implFactory = await ethers.getContractFactory("SpokeComptroller", deployer); + // Immutable, and `supportMarket` only answers this address, so no other registry can list markets + // in this pool. + const impl = await implFactory.deploy(registry.address); + await impl.deployed(); + + const beaconFactory = await ethers.getContractFactory("UpgradeableBeacon", deployer); + const spokeBeacon = await beaconFactory.deploy(impl.address); + await spokeBeacon.deployed(); + + // A VToken beacon of its own, as `027-deploy-spoke-vtoken-beacon.ts` deploys. `upgradeTo` moves every proxy behind a + // beacon in one call, so markets on the chain's shared `VTokenBeacon` could only take a VToken change that every + // isolated market on the chain takes with them. Built from this repo's `VToken` with the same immutables as the live + // implementation, but not the same code: the one the shared beacon points at is older, so these markets run what the + // repo builds today and the isolated ones do not. + const vTokenImplFactory = await ethers.getContractFactory("VToken", deployer); + const vTokenImpl = await vTokenImplFactory.deploy(false, BLOCKS_PER_YEAR, MAX_BORROW_RATE_MANTISSA); + await vTokenImpl.deployed(); + const vTokenBeacon = await beaconFactory.deploy(vTokenImpl.address); + await vTokenBeacon.deployed(); + + const proxyFactory = await ethers.getContractFactory("BeaconProxy", deployer); + const proxy = await proxyFactory.deploy( + spokeBeacon.address, + implFactory.interface.encodeFunctionData("initialize", [MAX_LOOPS_LIMIT, bscmainnet.ACM]), + ); + await proxy.deployed(); + const spoke = SpokeComptroller__factory.connect(proxy.address, deployer); + + // Ownable2Step, so the script can only nominate; the VIP accepts. Two of them, comptroller and + // registry, so the VIP carries two `acceptOwnership` calls. + await spoke.transferOwnership(bscmainnet.NORMAL_TIMELOCK); + await registry.transferOwnership(bscmainnet.NORMAL_TIMELOCK); + await spokeBeacon.transferOwnership(bscmainnet.NORMAL_TIMELOCK); + await vTokenBeacon.transferOwnership(bscmainnet.NORMAL_TIMELOCK); + + const stack: SpokeStack = { + timelock, + deployer, + supplier, + borrower, + liquidator, + outsider, + acm, + registry, + oracle, + boundedOracle, + usdt, + btcb, + spoke, + spokeBeacon, + spokeImpl: impl.address, + vTokenBeacon, + }; + + if (configure) await configureSpokeStack(stack); + return stack; +} + +/// The first three steps of the listing VIP: take ownership, point the pool at both oracles, and +/// grant governance every role the pool's setters check. +export async function configureSpokeStack(s: SpokeStack) { + await s.spoke.connect(s.timelock).acceptOwnership(); + await s.spoke.connect(s.timelock).setPriceOracle(bscmainnet.RESILIENT_ORACLE); + await s.spoke.connect(s.timelock).setDeviationBoundedOracle(bscmainnet.DEVIATION_BOUNDED_ORACLE); + for (const sig of Object.values(SPOKE_ROLES)) { + await grant(s.acm, s.timelock, s.spoke.address, sig, bscmainnet.NORMAL_TIMELOCK); + } + + // A second contract governance has to take over and permission. ACM roles are + // `keccak256(contractAddress, roleString)`, so nothing granted against the isolated-pools registry + // reaches this address. + await s.registry.connect(s.timelock).acceptOwnership(); + for (const sig of Object.values(REGISTRY_ROLES)) { + await grant(s.acm, s.timelock, s.registry.address, sig, bscmainnet.NORMAL_TIMELOCK); + } + + // The other direction: `addPool` and `addMarket` drive six comptroller setters as the registry, and + // the wildcard grants covering those name the live registry's address, not this one. + for (const sig of REGISTRY_DRIVEN_ROLES) { + await grant(s.acm, s.timelock, s.spoke.address, sig, s.registry.address); + } + + await pointProtocolShareReserveAtSpokeRegistry(s); +} + +/** + * `ProtocolShareReserve` rejects income from any non-core pool whose vToken its one configured + * registry does not know, and every `liquidateBorrow` and `reduceReserves` here goes through that + * check, so the pool takes no income until PSR points at its registry. + * + * On a fork that is one owner call. On chain it takes the isolated pools out and their liquidations + * start reverting, which `psrRegistryConflict.ts` pins. PSR support for more than one registry ships + * from its own repo and has to be live before this pool is wired into it. + */ +export async function pointProtocolShareReserveAtSpokeRegistry(s: SpokeStack) { + const psr = await ethers.getContractAt(PSR_ABI, bscmainnet.PSR); + const psrOwner = await initMainnetUser(await psr.owner(), ethers.utils.parseUnits("10")); + await psr.connect(psrOwner).setPoolRegistry(s.registry.address); +} + +/// `PoolRegistry.addPool`, with the pool parameters a hub-funded spoke pool would ship with. +export async function listSpokePool(s: SpokeStack) { + await s.registry.connect(s.timelock).addPool( + "Hub-funded spoke", + s.spoke.address, + ethers.utils.parseUnits("0.5", 18), // close factor + ethers.utils.parseUnits("1.1", 18), // pool-wide liquidation incentive + ethers.utils.parseUnits("100", 18), // minLiquidatableCollateral, in USD + ); +} + +export interface SpokeMarkets { + vUSDT: VToken; + vBTCB: VToken; + irm: JumpRateModelV2; +} + +/** + * List the two markets through the live `PoolRegistry`: the USDT liquidity market the Hub funds, + * and the BTCB collateral market borrowers post against. The liquidity market carries no collateral + * factor, because nobody borrows against it. + */ +export async function addSpokeMarkets(s: SpokeStack): Promise { + const irm = await deployIrm(bscmainnet.ACM, s.deployer); + const vUSDT = await deployVToken( + s.deployer, + s.vTokenBeacon.address, + bscmainnet.USDT, + s.spoke.address, + irm.address, + "Venus USDT (Hub-funded spoke)", + "vUSDT_HubSpoke", + 8, + ethers.utils.parseUnits("0.25", 18), + 18, + ); + const vBTCB = await deployVToken( + s.deployer, + s.vTokenBeacon.address, + bscmainnet.BTCB, + s.spoke.address, + irm.address, + "Venus BTCB (Hub-funded spoke)", + "vBTCB_HubSpoke", + 8, + ethers.utils.parseUnits("0.25", 18), + 18, + ); + + const usdtSeed = ethers.utils.parseUnits("10000", 18); + const btcbSeed = ethers.utils.parseUnits("0.05", 18); + await fundFrom(s.usdt, bscmainnet.USDT_HOLDER, bscmainnet.NORMAL_TIMELOCK, usdtSeed); + await fundFrom(s.btcb, bscmainnet.BTCB_HOLDER, bscmainnet.NORMAL_TIMELOCK, btcbSeed); + await s.usdt.connect(s.timelock).approve(s.registry.address, usdtSeed); + await s.btcb.connect(s.timelock).approve(s.registry.address, btcbSeed); + + await s.registry.connect(s.timelock).addMarket({ + vToken: vUSDT.address, + collateralFactor: 0, + liquidationThreshold: 0, + initialSupply: usdtSeed, + vTokenReceiver: bscmainnet.NORMAL_TIMELOCK, + supplyCap: ethers.utils.parseUnits("5000000", 18), + borrowCap: ethers.utils.parseUnits("4000000", 18), + }); + await s.registry.connect(s.timelock).addMarket({ + vToken: vBTCB.address, + collateralFactor: ethers.utils.parseUnits("0.75", 18), + liquidationThreshold: ethers.utils.parseUnits("0.8", 18), + initialSupply: btcbSeed, + vTokenReceiver: bscmainnet.NORMAL_TIMELOCK, + supplyCap: ethers.utils.parseUnits("100", 18), + borrowCap: 0, + }); + + return { vUSDT, vBTCB, irm }; +} + +export interface HubSide { + hub: Contract; + spokeSource: Contract; + adapter: AdapterSpokeV1; +} + +/** + * Deploy the hub-side pieces this feature needs: the stateless adapter, and one spoke source per + * Hub. The source is the generic `YieldGroup` already deployed on this chain, behind a beacon of + * its own, so the suite drives the exact implementation production would. + */ +export async function deployHubSide(s: SpokeStack): Promise { + const adapterFactory = await ethers.getContractFactory("AdapterSpokeV1", s.deployer); + const adapter = (await adapterFactory.deploy()) as AdapterSpokeV1; + await adapter.deployed(); + + const hub = new ethers.Contract(bscmainnet.HUB_USDT, HUB_ABI, s.deployer); + if (!isSame(await hub.asset(), bscmainnet.USDT)) { + throw new Error(`Hub asset is ${await hub.asset()}, expected USDT ${bscmainnet.USDT}`); + } + + const spokeSource = await deployYieldGroup(s.deployer, bscmainnet.HUB_USDT, bscmainnet.USDT); + for (const sig of [ + HUB_ROLES.addResource, + HUB_ROLES.removeResource, + HUB_ROLES.setInnerDepositQueue, + HUB_ROLES.setInnerWithdrawQueue, + HUB_ROLES.raiseResourceCap, + HUB_ROLES.lowerResourceCap, + HUB_ROLES.pauseResource, + ]) { + await grant(s.acm, s.timelock, spokeSource.address, sig, bscmainnet.NORMAL_TIMELOCK); + } + + return { hub, spokeSource, adapter }; +} + +/// Mint one generic `YieldGroup` proxy from the implementation already deployed on this chain. +export async function deployYieldGroup(deployer: SignerWithAddress, hub: string, asset: string): Promise { + const beaconFactory = await ethers.getContractFactory("UpgradeableBeacon", deployer); + const beacon = await beaconFactory.deploy(bscmainnet.YIELD_GROUP_IMPL); + await beacon.deployed(); + + const proxyFactory = await ethers.getContractFactory("BeaconProxy", deployer); + const ygIface = new ethers.utils.Interface(YIELD_GROUP_ABI); + const proxy = await proxyFactory.deploy( + beacon.address, + // blocksPerYear is 0: each spoke market carries its own annualiser, so the adapter reads that + // rather than a YieldGroup-level constant. + ygIface.encodeFunctionData("initialize", [hub, asset, 0, bscmainnet.ACM]), + ); + await proxy.deployed(); + return new ethers.Contract(proxy.address, YIELD_GROUP_ABI, deployer); +} + +/// The whole thing: a listed, configured, hub-funded spoke pool with both markets and the hub-side +/// pieces deployed but not yet registered on the Hub. +export async function spokeForkFixture(): Promise { + const stack = await deploySpokeStack(); + await listSpokePool(stack); + const markets = await addSpokeMarkets(stack); + const hubSide = await deployHubSide(stack); + return { ...stack, ...markets, ...hubSide }; +} + +/** + * Register the liquidity market as the spoke source's only resource, in the order the listing VIP + * has to use: the supply allowlist grant comes first, because `AdapterSpokeV1.validateRegistration` + * refuses a market whose allowlist is on without the registering YieldGroup on it. + */ +export async function registerSpokeResource(f: SpokeForkFixture, cap?: BigNumber) { + await f.spoke.connect(f.timelock).setSupplyAllowlistEnabled(f.vUSDT.address, true); + await f.spoke.connect(f.timelock).setAllowedSupplier(f.vUSDT.address, f.spokeSource.address, true); + + await f.spokeSource.connect(f.timelock).addResource(f.vUSDT.address, f.adapter.address); + // A fresh resource's cap is already unbounded: on the YieldGroup, `0` means "no cap", which is + // the inverse of the isolated-pools `supplyCaps` sentinel where `0` rejects every mint. Tightening + // it is therefore `lowerResourceCap`, and `raiseResourceCap` on a new resource reverts + // `NotIncreasing`. Only set one when the caller asked for it. + if (cap !== undefined) { + await f.spokeSource.connect(f.timelock).lowerResourceCap(f.vUSDT.address, cap); + } + await f.spokeSource.connect(f.timelock).setInnerDepositQueue([f.vUSDT.address]); + await f.spokeSource.connect(f.timelock).setInnerWithdrawQueue([f.vUSDT.address]); +} + +/** + * Put the spoke source on the live Hub and move it to the head of both outer queues, so a deposit + * routes into the spoke market and a withdrawal comes back out of it. The existing groups are kept, + * in their existing order, behind it: this Hub holds real depositors' money and a VIP that dropped + * a live group from the queues would strand it. + */ +export async function registerOnHub(f: SpokeForkFixture, absoluteCap: BigNumber, percentageCapBps = 10_000) { + await f.hub.connect(f.timelock).addYieldGroup(f.spokeSource.address, absoluteCap, percentageCapBps); + + const depositQueue: string[] = await f.hub.outerDepositQueue(); + const withdrawQueue: string[] = await f.hub.outerWithdrawQueue(); + await f.hub.connect(f.timelock).setOuterDepositQueue([f.spokeSource.address, ...depositQueue]); + await f.hub.connect(f.timelock).setOuterWithdrawQueue([f.spokeSource.address, ...withdrawQueue]); +} + +/** + * List a market whose underlying has FEWER decimals than the vToken, so the market's exchange rate + * starts below `EXP_SCALE`. TRX is the only asset in the live DeviationBoundedOracle's initialized + * set that qualifies on this chain, and the regime matters: below `1e18` a redeem request can burn + * plenty of vTokens and still truncate its payout to zero, which is the case + * `AdapterSpokeV1._bumpToSettleable` exists for and which no 18-decimal market can reach. + * + * Returns the market, a spoke source whose asset is that underlying, and the Hub that owns it. A + * YieldGroup rejects a Hub whose asset differs from its own, and this chain has no TRX Hub, so one + * is minted from the live `HubBeacon` - the same implementation every deployed Hub runs, created the + * same way. Every contract in the path is the real one. + */ +export async function addLowDecimalMarket( + f: SpokeForkFixture, +): Promise<{ vTRX: VToken; trxSource: Contract; trx: IERC20; trxHub: Contract }> { + const trx = IERC20__factory.connect(bscmainnet.TRX, f.deployer); + const vTRX = await deployVToken( + f.deployer, + f.vTokenBeacon.address, + bscmainnet.TRX, + f.spoke.address, + f.irm.address, + "Venus TRX (Hub-funded spoke)", + "vTRX_HubSpoke", + 8, + ethers.utils.parseUnits("0.25", 18), + 6, + // A rate that is deliberately NOT a power of ten. The convention is to list at + // 10 ** (18 + underlyingDecimals - vTokenDecimals), which here is 1e16 - and 1e16 divides 1e18 + // exactly, so every redeem request lands on a whole number of units and the payout never + // truncates. That is true of a market on its first block and of no market after it, because the + // first wei of interest takes the rate off the divisor. Listing at 3.7e16 puts the market where + // it spends its whole life, which is where `_bumpToSettleable` and `_redeemPayout` matter. + BigNumber.from("37000000000000000"), + ); + + const seed = ethers.utils.parseUnits("1000", 6); + await fundFrom(trx, bscmainnet.TRX_HOLDER, bscmainnet.NORMAL_TIMELOCK, seed); + await trx.connect(f.timelock).approve(f.registry.address, seed); + await f.registry.connect(f.timelock).addMarket({ + vToken: vTRX.address, + collateralFactor: 0, + liquidationThreshold: 0, + initialSupply: seed, + vTokenReceiver: bscmainnet.NORMAL_TIMELOCK, + supplyCap: ethers.utils.parseUnits("100000000", 6), + borrowCap: ethers.utils.parseUnits("50000000", 6), + }); + + // A YieldGroup refuses a Hub whose asset is not its own (`HubAssetMismatch`), so the 6-decimal + // asset needs its own Hub. Minted from the chain's live `HubBeacon`, which is exactly how a TRX + // Hub would be created in production; nothing here is a stand-in. + const proxyFactory = await ethers.getContractFactory("BeaconProxy", f.deployer); + const hubIface = new ethers.utils.Interface(HUB_ABI); + const trxHubProxy = await proxyFactory.deploy( + bscmainnet.HUB_BEACON, + hubIface.encodeFunctionData("initialize", [ + bscmainnet.TRX, + "Venus Hub TRX", + "vhTRX", + bscmainnet.ACM, + 6, // decimals offset, the value the production asset-vault script uses + MAX_UINT128, + bscmainnet.NORMAL_TIMELOCK, + ]), + ); + await trxHubProxy.deployed(); + const trxHub = new ethers.Contract(trxHubProxy.address, HUB_ABI, f.deployer); + for (const sig of Object.values(HUB_ADMIN_ROLES)) { + await grant(f.acm, f.timelock, trxHub.address, sig, bscmainnet.NORMAL_TIMELOCK); + } + + const trxSource = await deployYieldGroup(f.deployer, trxHub.address, bscmainnet.TRX); + for (const sig of [ + HUB_ROLES.addResource, + HUB_ROLES.removeResource, + HUB_ROLES.setInnerDepositQueue, + HUB_ROLES.setInnerWithdrawQueue, + HUB_ROLES.raiseResourceCap, + HUB_ROLES.lowerResourceCap, + HUB_ROLES.pauseResource, + ]) { + await grant(f.acm, f.timelock, trxSource.address, sig, bscmainnet.NORMAL_TIMELOCK); + } + + await f.spoke.connect(f.timelock).setSupplyAllowlistEnabled(vTRX.address, true); + await f.spoke.connect(f.timelock).setAllowedSupplier(vTRX.address, trxSource.address, true); + await trxSource.connect(f.timelock).addResource(vTRX.address, f.adapter.address); + await trxSource.connect(f.timelock).setInnerDepositQueue([vTRX.address]); + await trxSource.connect(f.timelock).setInnerWithdrawQueue([vTRX.address]); + + await trxHub.connect(f.timelock).addYieldGroup(trxSource.address, MAX_UINT128, 10_000); + await trxHub.connect(f.timelock).setOuterDepositQueue([trxSource.address]); + await trxHub.connect(f.timelock).setOuterWithdrawQueue([trxSource.address]); + + return { vTRX, trxSource, trx, trxHub }; +} + +/// `type(uint128).max`, the "effectively unbounded" cap the Hub stack uses in place of uint256 max. +export const MAX_UINT128 = BigNumber.from(2).pow(128).sub(1); diff --git a/tests/hardhat/Fork/HubSpoke/hubFlow.ts b/tests/hardhat/Fork/HubSpoke/hubFlow.ts new file mode 100644 index 000000000..6a79e1b6a --- /dev/null +++ b/tests/hardhat/Fork/HubSpoke/hubFlow.ts @@ -0,0 +1,392 @@ +import { SnapshotRestorer, takeSnapshot } from "@nomicfoundation/hardhat-network-helpers"; +import { expect } from "chai"; +import { BigNumber } from "ethers"; +import { ethers } from "hardhat"; + +import { Action, bscmainnet } from "./constants"; +import { SpokeForkFixture, fundFrom, grant, registerOnHub, registerSpokeResource, spokeForkFixture } from "./fixture"; + +const FORK = process.env.FORK === "true"; +const FORKED_NETWORK = process.env.FORKED_NETWORK || "bscmainnet"; + +const usd = (whole: string) => ethers.utils.parseUnits(whole, 18); +const HUB_CAP = usd("2000000"); +const EXP_SCALE = ethers.constants.WeiPerEther; + +if (FORK && FORKED_NETWORK === "bscmainnet") { + describe("HubSpoke: the Hub funding a spoke market", () => { + let f: SpokeForkFixture; + let snap: SnapshotRestorer; + + before(async () => { + f = await spokeForkFixture(); + snap = await takeSnapshot(); + }); + afterEach(async () => snap.restore()); + + // ── Registration ─────────────────────────────────────────────────────── + describe("registering the market as a resource", () => { + it("is refused while the market's allowlist is on without the source on it", async () => { + await f.spoke.connect(f.timelock).setSupplyAllowlistEnabled(f.vUSDT.address, true); + await expect(f.spokeSource.connect(f.timelock).addResource(f.vUSDT.address, f.adapter.address)) + .to.be.revertedWithCustomError(f.adapter, "SupplyNotAllowed") + .withArgs(f.vUSDT.address, f.spokeSource.address); + + await f.spoke.connect(f.timelock).setAllowedSupplier(f.vUSDT.address, f.spokeSource.address, true); + await expect(f.spokeSource.connect(f.timelock).addResource(f.vUSDT.address, f.adapter.address)).to.not.be + .reverted; + }); + + it("gates on the YieldGroup, not the Hub, the adapter or the depositor", async () => { + await f.spoke.connect(f.timelock).setSupplyAllowlistEnabled(f.vUSDT.address, true); + // The account credited with the vTokens under delegatecall is the YieldGroup, so that is + // the only address whose grant matters. Allowlisting anything else changes nothing. + for (const wrong of [bscmainnet.HUB_USDT, f.adapter.address, f.supplier.address]) { + await f.spoke.connect(f.timelock).setAllowedSupplier(f.vUSDT.address, wrong, true); + } + await expect( + f.spokeSource.connect(f.timelock).addResource(f.vUSDT.address, f.adapter.address), + ).to.be.revertedWithCustomError(f.adapter, "SupplyNotAllowed"); + }); + + it("rejects a market from a pool that is not a spoke pool", async () => { + // `validateRegistration` doubles as the type probe: the allowlist accessors exist only on + // the spoke fork, so the call itself reverts against the shared Comptroller. + const stablecoins = await ethers.getContractAt("Comptroller", bscmainnet.COMPTROLLER_STABLECOINS); + const foreign = (await stablecoins.getAllMarkets())[0]; + await expect(f.spokeSource.connect(f.timelock).addResource(foreign, f.adapter.address)).to.be.reverted; + }); + + it("rejects a spoke market whose underlying is not the source's asset", async () => { + await expect(f.spokeSource.connect(f.timelock).addResource(f.vBTCB.address, f.adapter.address)).to.be.reverted; + }); + }); + + // ── Deposit ──────────────────────────────────────────────────────────── + describe("deposit", () => { + beforeEach(async () => { + await registerSpokeResource(f); + await registerOnHub(f, HUB_CAP); + }); + + it("mints vTokens to the YieldGroup and never makes it a market member", async () => { + const amount = usd("50000"); + await fundFrom(f.usdt, bscmainnet.USDT_HOLDER, f.supplier.address, amount); + await f.usdt.connect(f.supplier).approve(f.hub.address, amount); + + const cashBefore = await f.vUSDT.getCash(); + await f.hub.connect(f.supplier).deposit(amount, f.supplier.address); + + expect(await f.vUSDT.getCash()).to.equal(cashBefore.add(amount)); + expect(await f.vUSDT.balanceOf(f.spokeSource.address)).to.be.gt(0); + expect(await f.vUSDT.balanceOf(f.adapter.address)).to.equal(0); + expect(await f.usdt.balanceOf(f.adapter.address)).to.equal(0); + // Supply-only, so it never enters the market. That is what lets `_checkRedeemAllowed` + // return before it touches the deviation-bounded oracle. + expect(await f.spoke.getAssetsIn(f.spokeSource.address)).to.have.lengthOf(0); + expect(await f.spoke.checkMembership(f.spokeSource.address, f.vUSDT.address)).to.be.false; + }); + + it("values the position at exactly what was supplied, to the wei", async () => { + const amount = usd("50000"); + await fundFrom(f.usdt, bscmainnet.USDT_HOLDER, f.supplier.address, amount); + await f.usdt.connect(f.supplier).approve(f.hub.address, amount); + await f.hub.connect(f.supplier).deposit(amount, f.supplier.address); + expect(await f.spokeSource.totalAssets()).to.equal(amount); + expect(await f.adapter.totalAssets(f.vUSDT.address, f.spokeSource.address)).to.equal(amount); + }); + + it("places exactly maxDeposit(), which is the honest headroom of the live market", async () => { + const room: BigNumber = await f.spokeSource.maxDeposit(); + expect(room).to.be.gt(0); + + // Driven from the Hub's own address rather than through `Hub.deposit`, so the assertion is + // about the adapter's headroom alone and not about the Hub's dual cap, which is a separate + // and much smaller bound. + const hubSigner = await impersonate(bscmainnet.HUB_USDT); + await fundFrom(f.usdt, bscmainnet.USDT_HOLDER, bscmainnet.HUB_USDT, room); + await f.usdt.connect(hubSigner).approve(f.spokeSource.address, room); + + const placed = await f.spokeSource.connect(hubSigner).callStatic.deposit(room); + // Explicit gas limit, because `_tryDepositOneResource` routes around a leg that reverts and + // an estimated limit can settle on exactly the gas at which the inner delegatecall runs out + // under the 63/64 rule - which the cascade then reports as a skip rather than a failure. + // See "a gas-estimated deposit can be routed away from this market" below. + await f.spokeSource.connect(hubSigner).deposit(room, { gasLimit: 3_000_000 }); + // Every wei advertised is placed: nothing is refunded to the caller, and the position is + // worth what went in. + expect(placed).to.equal(room); + expect(await f.usdt.balanceOf(bscmainnet.HUB_USDT)).to.equal(0); + expect(await f.spokeSource.totalAssets()).to.equal(room); + }); + + it("reports zero room, and routes around the market, once the supply cap is full", async () => { + // A third party can fill the cap even though it cannot supply to an allowlisted market: + // here the cap is lowered instead, which is the same end state and is reachable by + // governance alone. + const supplied = (await f.vUSDT.totalSupply()) + .mul(await f.vUSDT.exchangeRateStored()) + .div(ethers.constants.WeiPerEther); + await f.spoke.connect(f.timelock).setMarketSupplyCaps([f.vUSDT.address], [supplied]); + expect(await adapterRoomForSource(f)).to.equal(0); + expect(await f.spokeSource.maxDeposit()).to.equal(0); + + // The Hub still takes the deposit: the cascade skips a full leg and places it in the next + // group, rather than reverting the depositor's transaction. + const amount = usd("1000"); + await fundFrom(f.usdt, bscmainnet.USDT_HOLDER, f.supplier.address, amount); + await f.usdt.connect(f.supplier).approve(f.hub.address, amount); + const before = await f.vUSDT.getCash(); + await expect(f.hub.connect(f.supplier).deposit(amount, f.supplier.address)).to.not.be.reverted; + expect(await f.vUSDT.getCash()).to.equal(before); + }); + + it("reads a supply cap of zero as a real cap and uint256 max as uncapped", async () => { + // Inverted relative to the Core Comptroller, where zero is what disables minting. + await f.spoke.connect(f.timelock).setMarketSupplyCaps([f.vUSDT.address], [0]); + expect(await adapterRoomForSource(f)).to.equal(0); + const amount = usd("100"); + await fundFrom(f.usdt, bscmainnet.USDT_HOLDER, f.supplier.address, amount); + await f.usdt.connect(f.supplier).approve(f.vUSDT.address, amount); + await f.spoke.connect(f.timelock).setAllowedSupplier(f.vUSDT.address, f.supplier.address, true); + await expect(f.vUSDT.connect(f.supplier).mint(amount)).to.be.revertedWithCustomError( + f.spoke, + "SupplyCapExceeded", + ); + + await f.spoke.connect(f.timelock).setMarketSupplyCaps([f.vUSDT.address], [ethers.constants.MaxUint256]); + expect(await adapterRoomForSource(f)).to.equal(await f.adapter.UNCAPPED_DEPOSIT_ROOM()); + await expect(f.vUSDT.connect(f.supplier).mint(amount)).to.not.be.reverted; + }); + + it("never loses a deposit when a gas-estimated call routes it away from this market", async () => { + // `YieldGroupBase._tryDepositOneResource` wraps the adapter dispatch in try/catch so a + // resource that reverts is routed around rather than bubbled. Under the 63/64 rule the + // inner call gets only a fraction of the remaining gas, so an `eth_estimateGas` limit can + // land on a value where the inner mint runs out of gas and the leg is reported as skipped. + // The Hub's backstop is what makes that safe: `_routeDeposit` reverts `HubCapacityExceeded` + // if anything is left unplaced, so the depositor is never silently under-served - the + // deposit lands in another group, or the whole transaction reverts. + const amount = usd("25000"); + await fundFrom(f.usdt, bscmainnet.USDT_HOLDER, f.supplier.address, amount); + await f.usdt.connect(f.supplier).approve(f.hub.address, amount); + + const sharesBefore = await f.hub.balanceOf(f.supplier.address); + await f.hub.connect(f.supplier).deposit(amount, f.supplier.address); + expect(await f.hub.balanceOf(f.supplier.address)).to.be.gt(sharesBefore); + // Whatever the routing chose, the depositor's claim is worth what they paid. + expect(await f.hub.maxWithdraw(f.supplier.address)).to.be.gte(amount.mul(9999).div(10000)); + expect(await f.usdt.balanceOf(f.hub.address)).to.equal(0); + }); + + it("reports zero room while MINT is paused, instead of letting the deposit revert", async () => { + await f.spoke.connect(f.timelock).setActionsPaused([f.vUSDT.address], [Action.MINT], true); + expect(await adapterRoomForSource(f)).to.equal(0); + + const amount = usd("1000"); + await fundFrom(f.usdt, bscmainnet.USDT_HOLDER, f.supplier.address, amount); + await f.usdt.connect(f.supplier).approve(f.hub.address, amount); + const before = await f.vUSDT.getCash(); + await expect(f.hub.connect(f.supplier).deposit(amount, f.supplier.address)).to.not.be.reverted; + expect(await f.vUSDT.getCash()).to.equal(before); + }); + }); + + // ── Withdraw ─────────────────────────────────────────────────────────── + describe("withdraw", () => { + const deposited = usd("100000"); + + beforeEach(async () => { + await registerSpokeResource(f); + await registerOnHub(f, HUB_CAP); + await fundFrom(f.usdt, bscmainnet.USDT_HOLDER, f.supplier.address, deposited); + await f.usdt.connect(f.supplier).approve(f.hub.address, deposited); + await f.hub.connect(f.supplier).deposit(deposited, f.supplier.address); + }); + + it("returns the whole position and leaves nothing stranded", async () => { + const shares = await f.hub.balanceOf(f.supplier.address); + const before = await f.usdt.balanceOf(f.supplier.address); + await f.hub.connect(f.supplier).redeem(shares, f.supplier.address, f.supplier.address); + + expect(await f.usdt.balanceOf(f.supplier.address)).to.be.gte(before.add(deposited)); + expect(await f.vUSDT.balanceOf(f.spokeSource.address)).to.equal(0); + expect(await f.usdt.balanceOf(f.spokeSource.address)).to.equal(0); + expect(await f.usdt.balanceOf(f.adapter.address)).to.equal(0); + }); + + it("lets a fully drained market be deregistered", async () => { + const shares = await f.hub.balanceOf(f.supplier.address); + await f.hub.connect(f.supplier).redeem(shares, f.supplier.address, f.supplier.address); + // `removeResource` gates on the raw receipt balance, so the redeem has to have burned every + // last vToken - a value-based check would round a residual to zero and orphan it. + expect(await f.adapter.receiptBalance(f.vUSDT.address, f.spokeSource.address)).to.equal(0); + await expect(f.spokeSource.connect(f.timelock).removeResource(f.vUSDT.address)).to.not.be.reverted; + }); + + it("never certifies more than the market's payable cash", async () => { + await borrowAgainstCollateral(f, usd("70000")); + await f.adapter.accrue(f.vUSDT.address); + + const liquid: BigNumber = await f.adapter.maxWithdraw(f.vUSDT.address, f.spokeSource.address); + const payable = (await f.vUSDT.getCash()).sub(await f.vUSDT.totalReserves()); + const position = await f.adapter.totalAssets(f.vUSDT.address, f.spokeSource.address); + + expect(liquid).to.be.gt(0); + expect(liquid).to.be.lte(payable); + expect(liquid).to.be.lte(position); + // Floored to what a whole number of vTokens is worth, because the redeem rounds its burn + // up: asking for the payable cash exactly would overshoot it by up to one vToken. + const rate = await f.vUSDT.exchangeRateStored(); + expect(payable.mul(EXP_SCALE).div(rate).mul(rate).div(EXP_SCALE)).to.equal(liquid); + }); + + it("settles exactly the certified bound when the market is not accruing under it", async () => { + await borrowAgainstCollateral(f, usd("70000")); + // Freeze the rate. `maxWithdraw` reads stored state, and its own NatSpec says the figure is + // exact only for a caller that settled this market's interest in the same transaction - the + // Hub does, through `accrue`, but a test cannot read and redeem atomically. Taking the rate + // to zero removes the moving part instead of papering over it with a tolerance, so what is + // left is the arithmetic this assertion is actually about. + await grant( + f.acm, + f.timelock, + f.irm.address, + "updateJumpRateModel(uint256,uint256,uint256,uint256)", + bscmainnet.NORMAL_TIMELOCK, + ); + await f.irm.connect(f.timelock).updateJumpRateModel(0, 0, 0, ethers.utils.parseUnits("0.8", 18)); + await f.adapter.accrue(f.vUSDT.address); + + const liquid: BigNumber = await f.adapter.maxWithdraw(f.vUSDT.address, f.spokeSource.address); + const hubSigner = await impersonate(bscmainnet.HUB_USDT); + const before = await f.usdt.balanceOf(f.outsider.address); + await f.spokeSource.connect(hubSigner).withdraw(liquid, f.outsider.address, { gasLimit: 3_000_000 }); + expect(await f.usdt.balanceOf(f.outsider.address)).to.equal(before.add(liquid)); + }); + + it("cannot settle a wei more than the certified bound", async () => { + await borrowAgainstCollateral(f, usd("70000")); + await grant( + f.acm, + f.timelock, + f.irm.address, + "updateJumpRateModel(uint256,uint256,uint256,uint256)", + bscmainnet.NORMAL_TIMELOCK, + ); + await f.irm.connect(f.timelock).updateJumpRateModel(0, 0, 0, ethers.utils.parseUnits("0.8", 18)); + await f.adapter.accrue(f.vUSDT.address); + + const liquid: BigNumber = await f.adapter.maxWithdraw(f.vUSDT.address, f.spokeSource.address); + const hubSigner = await impersonate(bscmainnet.HUB_USDT); + // One wei past the bound is one whole vToken past the market's payable cash, because the + // redeem rounds its burn up. The YieldGroup surfaces that as a liquidity shortfall rather + // than routing around it, which is what stops a partial withdrawal being reported as whole. + await expect( + f.spokeSource.connect(hubSigner).withdraw(liquid.add(1), f.outsider.address, { gasLimit: 3_000_000 }), + ).to.be.reverted; + }); + + it("is optimistic by exactly the reserves the next accrual books on a stale market", async () => { + // The documented staleness: read against unaccrued state, the bound counts cash that the + // next accrual moves into reserves and, past the sweep threshold, out to the + // ProtocolShareReserve entirely. This is why every Hub routing path accrues first. + await borrowAgainstCollateral(f, usd("70000")); + const stale: BigNumber = await f.adapter.maxWithdraw(f.vUSDT.address, f.spokeSource.address); + await f.adapter.accrue(f.vUSDT.address); + const fresh: BigNumber = await f.adapter.maxWithdraw(f.vUSDT.address, f.spokeSource.address); + expect(fresh).to.be.lt(stale); + }); + + it("reports zero withdrawable while REDEEM is paused", async () => { + await f.spoke.connect(f.timelock).setActionsPaused([f.vUSDT.address], [Action.REDEEM], true); + expect(await f.adapter.maxWithdraw(f.vUSDT.address, f.spokeSource.address)).to.equal(0); + }); + }); + + // ── Accrual ──────────────────────────────────────────────────────────── + describe("interest", () => { + beforeEach(async () => { + await registerSpokeResource(f); + await registerOnHub(f, HUB_CAP); + const amount = usd("100000"); + await fundFrom(f.usdt, bscmainnet.USDT_HOLDER, f.supplier.address, amount); + await f.usdt.connect(f.supplier).approve(f.hub.address, amount); + await f.hub.connect(f.supplier).deposit(amount, f.supplier.address); + await borrowAgainstCollateral(f, usd("50000")); + }); + + it("grows the position once the market accrues, and accrue() makes the read fresh", async () => { + const stale = await f.adapter.totalAssets(f.vUSDT.address, f.spokeSource.address); + await ethers.provider.send("hardhat_mine", ["0x100000"]); // ~1M blocks + + // Stored state has not moved yet: `totalAssets` reads `getCash`/`totalBorrows` as stored. + expect(await f.adapter.totalAssets(f.vUSDT.address, f.spokeSource.address)).to.equal(stale); + await f.adapter.accrue(f.vUSDT.address); + const fresh = await f.adapter.totalAssets(f.vUSDT.address, f.spokeSource.address); + expect(fresh).to.be.gt(stale); + }); + + it("annualises from the market's own cadence rather than a YieldGroup constant", async () => { + const apy = await f.adapter.spotAPYBps(f.vUSDT.address, 0); + const expected = (await f.vUSDT.supplyRatePerBlock()) + .mul(await f.vUSDT.blocksOrSecondsPerYear()) + .div(BigNumber.from(10).pow(14)); + expect(apy).to.equal(expected); + // The `blocksPerYear` argument is ignored, which is what lets one source hold block-based + // and time-based markets side by side. + expect(await f.adapter.spotAPYBps(f.vUSDT.address, 12_345_678)).to.equal(apy); + }); + }); + + // ── The adapter itself ───────────────────────────────────────────────── + describe("adapter safety", () => { + it("refuses a direct call to either mutating entry point", async () => { + await expect(f.adapter.deposit(f.vUSDT.address, 1)).to.be.revertedWithCustomError(f.adapter, "NotDelegateCall"); + await expect(f.adapter.withdraw(f.vUSDT.address, 1, f.supplier.address)).to.be.revertedWithCustomError( + f.adapter, + "NotDelegateCall", + ); + }); + + it("holds no storage of its own, so delegatecall cannot collide with the YieldGroup's", async () => { + await registerSpokeResource(f); + await registerOnHub(f, HUB_CAP); + const amount = usd("10000"); + await fundFrom(f.usdt, bscmainnet.USDT_HOLDER, f.supplier.address, amount); + await f.usdt.connect(f.supplier).approve(f.hub.address, amount); + await f.hub.connect(f.supplier).deposit(amount, f.supplier.address); + + for (let slot = 0; slot < 8; slot++) { + expect(await ethers.provider.getStorageAt(f.adapter.address, slot)).to.equal(ethers.constants.HashZero); + } + }); + }); + }); +} + +/** + * `AdapterSpokeV1.maxDeposit` reads the market's supply allowlist against `msg.sender`, because the + * YieldGroup calls it as a plain call and is therefore the prospective supplier. Asking it from any + * other address answers about that address, so every capacity assertion has to ask as the source. + */ +async function adapterRoomForSource(f: SpokeForkFixture): Promise { + return f.adapter.connect(await impersonate(f.spokeSource.address)).maxDeposit(f.vUSDT.address); +} + +/// Impersonate `who` with enough gas money to send a transaction. +async function impersonate(who: string) { + await ethers.provider.send("hardhat_impersonateAccount", [who]); + await ethers.provider.send("hardhat_setBalance", [who, "0x56bc75e2d63100000"]); + return ethers.getSigner(who); +} + +/// Put real collateral behind a real borrow, so the market's cash is genuinely short of its NAV. +async function borrowAgainstCollateral(f: SpokeForkFixture, borrowAmount: BigNumber) { + const collateral = ethers.utils.parseUnits("2", 18); // 2 BTCB, far more than the borrow needs + await fundFrom(f.btcb, bscmainnet.BTCB_HOLDER, f.borrower.address, collateral); + await f.btcb.connect(f.borrower).approve(f.vBTCB.address, collateral); + await f.vBTCB.connect(f.borrower).mint(collateral); + await f.spoke.connect(f.borrower).enterMarkets([f.vBTCB.address]); + await f.vUSDT.connect(f.borrower).borrow(borrowAmount); +} diff --git a/tests/hardhat/Fork/HubSpoke/interfaces.ts b/tests/hardhat/Fork/HubSpoke/interfaces.ts new file mode 100644 index 000000000..a21a6b660 --- /dev/null +++ b/tests/hardhat/Fork/HubSpoke/interfaces.ts @@ -0,0 +1,131 @@ +import { expect } from "chai"; +import { artifacts, ethers } from "hardhat"; + +import { bscmainnet } from "./constants"; +import { SpokeForkFixture, spokeForkFixture } from "./fixture"; + +const FORK = process.env.FORK === "true"; +const FORKED_NETWORK = process.env.FORKED_NETWORK || "bscmainnet"; + +/** + * The hub side compiles against its own minimal views of the two isolated-pools contracts it + * touches. Nothing on that side depends on this repo, so nothing keeps those views in step with the + * contracts they describe - a renamed getter or a widened return type is a silent miscompile there + * and a revert in production here. + * + * These tests are the joint. Two different shapes, because the two views are in different states: + * + * - `ISpokeComptroller` is now bound to this repo's own `SpokeComptrollerViewInterface`, so there is + * no second copy of those declarations here to drift. What still has to hold is that the + * first-party interface matches the implementation, and that it keeps offering the exact members + * the hub's adapter compiles against - listed below as data, because the hub's own copy of them + * lives in a repo this suite cannot import. + * - `IVTokenIsolated` is still a vendored copy, because binding it to `VTokenInterface` would change + * the adapter's call sites (`comptroller()` returns `ComptrollerInterface` here and `address` + * there). It is checked member for member against the real `VToken`. + */ + +/** + * The exact members `AdapterSpokeV1` calls on a spoke pool, with the return types it decodes. This + * list is the contract between the two repos; the hub declares the same four in its own + * `ISpokeComptroller`. Removing one from `SpokeComptrollerViewInterface`, renaming it, or widening + * what it returns breaks the hub's build, and this fails first. + */ +const HUB_REQUIRES = [ + { sig: "supplyCaps(address)", returns: "uint256" }, + { sig: "actionPaused(address,uint8)", returns: "bool" }, + { sig: "isSupplyAllowlistEnabled(address)", returns: "bool" }, + { sig: "isAllowedSupplier(address,address)", returns: "bool" }, +]; +if (FORK && FORKED_NETWORK === "bscmainnet") { + describe("HubSpoke: the hub-side interfaces against the real contracts", () => { + let f: SpokeForkFixture; + + before(async () => { + f = await spokeForkFixture(); + }); + + const conformance = (interfaceName: string, implementationName: string) => { + it(`${interfaceName} matches ${implementationName} selector for selector`, async () => { + const iface = new ethers.utils.Interface((await artifacts.readArtifact(interfaceName)).abi); + const impl = new ethers.utils.Interface((await artifacts.readArtifact(implementationName)).abi); + const implBySelector = new Map( + Object.keys(impl.functions).map(sig => [impl.getSighash(sig), impl.functions[sig]]), + ); + + for (const sig of Object.keys(iface.functions)) { + const selector = iface.getSighash(sig); + const declared = iface.functions[sig]; + const actual = implBySelector.get(selector); + if (actual === undefined) { + throw new Error(`${implementationName} has no function at ${selector} (${sig})`); + } + + const declaredOutputs = (declared.outputs ?? []).map(o => o.type).join(","); + const actualOutputs = (actual.outputs ?? []).map(o => o.type).join(","); + expect(actualOutputs, `${sig} returns`).to.equal(declaredOutputs); + } + }); + }; + + // `actionPaused` is the one worth naming: the view interface declares it as `(address,uint8)` + // while the pool implements it as `(address,Action)`. The enum encodes as `uint8`, so the + // selectors agree - but only as long as the enum stays at or below 256 members and keeps its + // ordering, which is what makes this check worth running rather than assuming. + conformance("SpokeComptrollerViewInterface", "SpokeComptroller"); + conformance("IVTokenIsolated", "VToken"); + + it("keeps offering every member the hub's adapter compiles against", async () => { + const iface = new ethers.utils.Interface((await artifacts.readArtifact("SpokeComptrollerViewInterface")).abi); + const declared = new Set(Object.keys(iface.functions)); + for (const { sig, returns } of HUB_REQUIRES) { + expect(declared.has(sig), `SpokeComptrollerViewInterface no longer declares ${sig}`).to.be.true; + expect((iface.functions[sig].outputs ?? []).map(o => o.type).join(","), `${sig} returns`).to.equal(returns); + } + }); + + it("resolves the adapter's own interface name to those declarations", async () => { + // `ISpokeComptroller` is the name `AdapterSpokeV1` imports. It is an empty extension of the + // first-party interface, so the two ABIs are identical; if that shim ever grows a declaration + // of its own, this catches it. + const shim = new ethers.utils.Interface((await artifacts.readArtifact("ISpokeComptroller")).abi); + const first = new ethers.utils.Interface((await artifacts.readArtifact("SpokeComptrollerViewInterface")).abi); + expect(Object.keys(shim.functions).sort()).to.deep.equal(Object.keys(first.functions).sort()); + }); + + it("answers every one of those members on the live pool", async () => { + const spoke = await ethers.getContractAt("ISpokeComptroller", f.spoke.address); + expect(await spoke.supplyCaps(f.vUSDT.address)).to.be.gt(0); + expect(await spoke.actionPaused(f.vUSDT.address, 0)).to.be.false; // MINT + expect(await spoke.actionPaused(f.vUSDT.address, 1)).to.be.false; // REDEEM + expect(await spoke.isSupplyAllowlistEnabled(f.vUSDT.address)).to.be.false; + expect(await spoke.isAllowedSupplier(f.vUSDT.address, f.spokeSource.address)).to.be.false; + }); + + it("pins the MINT and REDEEM positions the hub hard-codes", async () => { + // `AdapterSpokeV1` passes literal 0 and 1 for MINT and REDEEM. Reordering the enum in + // `ComptrollerInterface` would leave the adapter reading a different action's pause flag. + await f.spoke.connect(f.timelock).setActionsPaused([f.vUSDT.address], [0], true); + const spoke = await ethers.getContractAt("ISpokeComptroller", f.spoke.address); + expect(await spoke.actionPaused(f.vUSDT.address, 0)).to.be.true; + expect(await spoke.actionPaused(f.vUSDT.address, 1)).to.be.false; + + await f.spoke.connect(f.timelock).setActionsPaused([f.vUSDT.address], [1], true); + expect(await spoke.actionPaused(f.vUSDT.address, 1)).to.be.true; + }); + + it("answers every IVTokenIsolated member on a live spoke market", async () => { + const market = await ethers.getContractAt("IVTokenIsolated", f.vUSDT.address); + expect(await market.underlying()).to.equal(bscmainnet.USDT); + expect(await market.comptroller()).to.equal(f.spoke.address); + expect(await market.exchangeRateStored()).to.be.gt(0); + expect(await market.totalSupply()).to.be.gt(0); + expect(await market.getCash()).to.be.gt(0); + expect(await market.totalBorrows()).to.equal(0); + expect(await market.totalReserves()).to.equal(0); + expect(await market.badDebt()).to.equal(0); + expect(await market.blocksOrSecondsPerYear()).to.equal(42_048_000); + expect(await market.balanceOf(f.spokeSource.address)).to.equal(0); + }); + }); +} diff --git a/tests/hardhat/Fork/HubSpoke/listing.ts b/tests/hardhat/Fork/HubSpoke/listing.ts new file mode 100644 index 000000000..bb40f4816 --- /dev/null +++ b/tests/hardhat/Fork/HubSpoke/listing.ts @@ -0,0 +1,295 @@ +import { SnapshotRestorer, takeSnapshot } from "@nomicfoundation/hardhat-network-helpers"; +import { expect } from "chai"; +import { ethers } from "hardhat"; + +import { bscmainnet } from "./constants"; +import { + REGISTRY_DRIVEN_ROLES, + SPOKE_ROLES, + SpokeStack, + addSpokeMarkets, + configureSpokeStack, + deploySpokeStack, + fundFrom, + grant, + listSpokePool, +} from "./fixture"; + +const FORK = process.env.FORK === "true"; +const FORKED_NETWORK = process.env.FORKED_NETWORK || "bscmainnet"; + +/// `isAllowedToCall` keys the role on `msg.sender`, so the only honest way to ask "may PoolRegistry +/// configure THIS comptroller" is to ask it from the comptroller's own address. +const ACM_IFACE = new ethers.utils.Interface(["function isAllowedToCall(address,string) view returns (bool)"]); +async function mayCall(onBehalfOf: string, account: string, sig: string): Promise { + const data = ACM_IFACE.encodeFunctionData("isAllowedToCall", [account, sig]); + const res = await ethers.provider.call({ to: bscmainnet.ACM, data, from: onBehalfOf }); + return ACM_IFACE.decodeFunctionResult("isAllowedToCall", res)[0]; +} + +/// The role strings that exist only on the spoke fork. No pre-existing grant can cover them, +/// because no other Venus contract checks a string with these names. +const SPOKE_ONLY_ROLES = [ + SPOKE_ROLES.setMarketLiquidationIncentive, + SPOKE_ROLES.setSupplyAllowlistEnabled, + SPOKE_ROLES.setAllowedSupplier, + SPOKE_ROLES.setLiquidationAllowlistEnabled, + SPOKE_ROLES.setAllowedLiquidator, +]; + +if (FORK && FORKED_NETWORK === "bscmainnet") { + describe("HubSpoke: listing a spoke pool on the live PoolRegistry", () => { + describe("before the VIP runs", () => { + let s: SpokeStack; + let snap: SnapshotRestorer; + + before(async () => { + s = await deploySpokeStack(false); + snap = await takeSnapshot(); + }); + afterEach(async () => snap.restore()); + + it("cannot be registered while the price oracle is unset", async () => { + await s.spoke.connect(s.timelock).acceptOwnership(); + // `addPool` dereferences `comptroller.oracle()` and rejects the zero address, so the oracle + // has to be set before registration, not after. + await expect(listSpokePool(s)).to.be.revertedWithCustomError(s.spoke, "ZeroAddressNotAllowed"); + }); + + it("keeps the deployer as owner until acceptOwnership, so an owner-gated setter reverts", async () => { + expect(await s.spoke.owner()).to.equal(s.deployer.address); + expect(await s.spoke.pendingOwner()).to.equal(bscmainnet.NORMAL_TIMELOCK); + await expect(s.spoke.connect(s.timelock).setPriceOracle(bscmainnet.RESILIENT_ORACLE)).to.be.revertedWith( + "Ownable: caller is not the owner", + ); + }); + + it("inherits the live PoolRegistry's wildcard grants, which are of no use to this pool", async () => { + // The wildcard roles (keyed on address(0)) the ACM already holds name the live registry as + // the account. A brand-new comptroller inherits them, so the live registry could drive these + // setters here... + for (const sig of REGISTRY_DRIVEN_ROLES) { + expect(await mayCall(s.spoke.address, bscmainnet.POOL_REGISTRY, sig), `PoolRegistry may call ${sig}`).to.be + .true; + } + + // ...and it is not the registry this pool is listed through. The registry that is has no + // grant at all, which is six `giveCallPermission` calls the listing VIP has to carry or + // `addPool` reverts on execution. + for (const sig of REGISTRY_DRIVEN_ROLES) { + expect(await mayCall(s.spoke.address, s.registry.address, sig), `spoke registry may call ${sig}`).to.be.false; + } + }); + + it("has no grant at all for the role strings the spoke fork adds", async () => { + // The VIP has to grant each of these explicitly. A pool listed without them is listed with + // its allowlists and per-market incentives permanently unreachable. + for (const sig of SPOKE_ONLY_ROLES) { + expect(await mayCall(s.spoke.address, bscmainnet.NORMAL_TIMELOCK, sig), `Timelock may call ${sig}`).to.be + .false; + } + }); + + it("rejects each spoke-only setter until its exact role string is granted", async () => { + await s.spoke.connect(s.timelock).acceptOwnership(); + await s.spoke.connect(s.timelock).setPriceOracle(bscmainnet.RESILIENT_ORACLE); + + await expect(s.spoke.connect(s.timelock).setLiquidationAllowlistEnabled(true)).to.be.revertedWithCustomError( + s.spoke, + "Unauthorized", + ); + + // A near-miss role string grants nothing: the ACM hashes the string, so a signature that + // differs by one character is a different role. + await grant( + s.acm, + s.timelock, + s.spoke.address, + "setLiquidationAllowListEnabled(bool)", + bscmainnet.NORMAL_TIMELOCK, + ); + await expect(s.spoke.connect(s.timelock).setLiquidationAllowlistEnabled(true)).to.be.revertedWithCustomError( + s.spoke, + "Unauthorized", + ); + + await grant( + s.acm, + s.timelock, + s.spoke.address, + SPOKE_ROLES.setLiquidationAllowlistEnabled, + bscmainnet.NORMAL_TIMELOCK, + ); + await expect(s.spoke.connect(s.timelock).setLiquidationAllowlistEnabled(true)).to.emit( + s.spoke, + "LiquidationAllowlistEnabledUpdated", + ); + }); + + it("fails borrow and redeem closed while the deviation-bounded oracle is unset", async () => { + await s.spoke.connect(s.timelock).acceptOwnership(); + await s.spoke.connect(s.timelock).setPriceOracle(bscmainnet.RESILIENT_ORACLE); + expect(await s.spoke.deviationBoundedOracle()).to.equal(ethers.constants.AddressZero); + // The pool's own registry has none of the wildcard grants the live one relies on, so the six + // setters `addPool` and `addMarket` drive have to be granted to it first. + for (const sig of REGISTRY_DRIVEN_ROLES) { + await grant(s.acm, s.timelock, s.spoke.address, sig, s.registry.address); + } + await listSpokePool(s); + for (const sig of Object.values(SPOKE_ROLES)) { + await grant(s.acm, s.timelock, s.spoke.address, sig, bscmainnet.NORMAL_TIMELOCK); + } + const { vBTCB } = await addSpokeMarkets(s); + + // Supplying works: `preMintHook` reads no price at all. + const amount = ethers.utils.parseUnits("0.01", 18); + await fundFrom(s.btcb, bscmainnet.BTCB_HOLDER, s.supplier.address, amount); + await s.btcb.connect(s.supplier).approve(vBTCB.address, amount); + await expect(vBTCB.connect(s.supplier).mint(amount)).to.not.be.reverted; + + // Entering the market and then redeeming goes through `_updateProtectionStates`, which + // calls into the zero address. That is the intended fail-closed behaviour, and it is why + // the VIP has to set this oracle before the pool serves anyone who enters a market. + await s.spoke.connect(s.supplier).enterMarkets([vBTCB.address]); + await expect(vBTCB.connect(s.supplier).redeem(1)).to.be.reverted; + }); + }); + + describe("after the VIP runs", () => { + let s: SpokeStack; + let snap: SnapshotRestorer; + let poolCountBefore: number; + + before(async () => { + s = await deploySpokeStack(false); + poolCountBefore = (await s.registry.getAllPools()).length; + await configureSpokeStack(s); + await listSpokePool(s); + await addSpokeMarkets(s); + snap = await takeSnapshot(); + }); + afterEach(async () => snap.restore()); + + it("registers as the only pool in its own registry", async () => { + const pools = await s.registry.getAllPools(); + expect(poolCountBefore).to.equal(0); + expect(pools.length).to.equal(poolCountBefore + 1); + const pool = await s.registry.getPoolByComptroller(s.spoke.address); + expect(pool.comptroller).to.equal(s.spoke.address); + expect(pool.name).to.equal("Hub-funded spoke"); + }); + + it("takes the pool parameters PoolRegistry pushed during addPool", async () => { + expect(await s.spoke.closeFactorMantissa()).to.equal(ethers.utils.parseUnits("0.5", 18)); + expect(await s.spoke.minLiquidatableCollateral()).to.equal(ethers.utils.parseUnits("100", 18)); + // The pool-wide incentive is only readable through the resolver, because the plain getter + // answers for `msg.sender`'s market. + expect(await s.spoke.effectiveLiquidationIncentive(ethers.constants.AddressZero)).to.equal( + ethers.utils.parseUnits("1.1", 18), + ); + }); + + it("leaves every other pool on this chain untouched", async () => { + const shared = await ethers.getContractAt("UpgradeableBeacon", bscmainnet.COMPTROLLER_BEACON); + expect(await shared.implementation()).to.not.equal(s.spokeImpl); + const stablecoins = await ethers.getContractAt("Comptroller", bscmainnet.COMPTROLLER_STABLECOINS); + expect(await stablecoins.poolRegistry()).to.equal(bscmainnet.POOL_REGISTRY); + // The spoke pool is not in any other pool's market list, and vice versa. + const spokeMarkets = (await s.spoke.getAllMarkets()).map((m: string) => m.toLowerCase()); + for (const m of await stablecoins.getAllMarkets()) { + expect(spokeMarkets).to.not.include(m.toLowerCase()); + } + }); + + it("runs its markets on a VToken beacon of its own, not the chain's shared one", async () => { + const shared = await ethers.getContractAt("UpgradeableBeacon", bscmainnet.VTOKEN_BEACON); + const sharedImpl = await ethers.getContractAt("VToken", await shared.implementation()); + const spokeImpl = await s.vTokenBeacon.implementation(); + + // `upgradeTo` moves every proxy behind a beacon in one call. On the shared beacon these + // markets could only take a VToken change that every isolated market on the chain takes + // with them, and the other way round. + expect(s.vTokenBeacon.address).to.not.equal(bscmainnet.VTOKEN_BEACON); + expect(spokeImpl).to.not.equal(sharedImpl.address); + expect(spokeImpl).to.not.equal(ethers.constants.AddressZero); + + const markets = await s.spoke.getAllMarkets(); + for (const market of markets) { + const vToken = await ethers.getContractAt("VToken", market); + expect(await vToken.comptroller()).to.equal(s.spoke.address); + expect(await vToken.protocolShareReserve()).to.equal(bscmainnet.PSR); + expect(await vToken.shortfall()).to.equal(bscmainnet.SHORTFALL); + // A separate implementation, constructed with the same immutables as the live one. The + // code differs from day one, because the shared beacon points at an older `VToken` than + // this repo builds; the immutables are what has to match, and they are checked here. + expect(await vToken.isTimeBased()).to.equal(await sharedImpl.isTimeBased()); + expect(await vToken.blocksOrSecondsPerYear()).to.equal(await sharedImpl.blocksOrSecondsPerYear()); + } + }); + + it("bounds a market's liquidation incentive by that market's own protocol seize share", async () => { + const markets = await s.spoke.getAllMarkets(); + const vToken = await ethers.getContractAt("VToken", markets[0]); + const seizeShare = await vToken.protocolSeizeShareMantissa(); + expect(seizeShare).to.be.gt(0); // the live implementation defaults to 5% + + const floor = ethers.utils.parseUnits("1", 18).add(seizeShare); + await expect( + s.spoke.connect(s.timelock).setMarketLiquidationIncentive(markets[0], floor.sub(1)), + ).to.be.revertedWithCustomError(s.spoke, "InvalidLiquidationIncentive"); + await expect(s.spoke.connect(s.timelock).setMarketLiquidationIncentive(markets[0], floor)) + .to.emit(s.spoke, "NewMarketLiquidationIncentive") + .withArgs(markets[0], 0, floor); + expect(await s.spoke.effectiveLiquidationIncentive(markets[0])).to.equal(floor); + }); + + it("refuses to register a market that belongs to another pool's comptroller", async () => { + // A stray `addMarket` pointed at a live Core-pool market must not be able to pull it in. + const stablecoins = await ethers.getContractAt("Comptroller", bscmainnet.COMPTROLLER_STABLECOINS); + const foreign = (await stablecoins.getAllMarkets())[0]; + await expect( + s.registry.connect(s.timelock).addMarket({ + vToken: foreign, + collateralFactor: 0, + liquidationThreshold: 0, + initialSupply: 1, + vTokenReceiver: bscmainnet.NORMAL_TIMELOCK, + supplyCap: 1, + borrowCap: 0, + }), + ).to.be.reverted; + }); + }); + + describe("supply allowlist versus the listing order", () => { + let s: SpokeStack; + + before(async () => { + s = await deploySpokeStack(); + await listSpokePool(s); + }); + + it("blocks addMarket when the allowlist is armed before the market exists", async () => { + // `PoolRegistry.addMarket` seeds initial supply with `mintBehalf(vTokenReceiver, ...)`, and + // `preMintHook` gates the account CREDITED with the vTokens. The allowlist can therefore + // only be armed after the market is listed, or the receiver has to be allowlisted first - + // and it cannot be allowlisted first, because `setAllowedSupplier` requires the market to + // already be listed. The order in the VIP is forced. + const markets = await addSpokeMarkets(s); + await s.spoke.connect(s.timelock).setSupplyAllowlistEnabled(markets.vUSDT.address, true); + + const extra = ethers.utils.parseUnits("100", 18); + await fundFrom(s.usdt, bscmainnet.USDT_HOLDER, s.supplier.address, extra); + await s.usdt.connect(s.supplier).approve(markets.vUSDT.address, extra); + await expect(markets.vUSDT.connect(s.supplier).mint(extra)) + .to.be.revertedWithCustomError(s.spoke, "SupplyNotAllowed") + .withArgs(markets.vUSDT.address, s.supplier.address); + + await expect( + s.spoke.connect(s.timelock).setAllowedSupplier(markets.vUSDT.address, s.supplier.address, true), + ).to.emit(s.spoke, "AllowedSupplierUpdated"); + await expect(markets.vUSDT.connect(s.supplier).mint(extra)).to.not.be.reverted; + }); + }); + }); +} diff --git a/tests/hardhat/Fork/HubSpoke/lowDecimals.ts b/tests/hardhat/Fork/HubSpoke/lowDecimals.ts new file mode 100644 index 000000000..4619dfba5 --- /dev/null +++ b/tests/hardhat/Fork/HubSpoke/lowDecimals.ts @@ -0,0 +1,163 @@ +import { SnapshotRestorer, takeSnapshot } from "@nomicfoundation/hardhat-network-helpers"; +import { expect } from "chai"; +import { BigNumber, Contract } from "ethers"; +import { ethers } from "hardhat"; + +import { IERC20, VToken } from "../../../../typechain"; +import { bscmainnet } from "./constants"; +import { SpokeForkFixture, addLowDecimalMarket, fundFrom, spokeForkFixture } from "./fixture"; + +const FORK = process.env.FORK === "true"; +const FORKED_NETWORK = process.env.FORKED_NETWORK || "bscmainnet"; + +const EXP_SCALE = ethers.constants.WeiPerEther; +const trxAmt = (whole: string) => ethers.utils.parseUnits(whole, 6); + +if (FORK && FORKED_NETWORK === "bscmainnet") { + describe("HubSpoke: a market whose exchange rate is below 1e18", () => { + let f: SpokeForkFixture; + let vTRX: VToken; + let trxSource: Contract; + let trx: IERC20; + let trxHub: Contract; + let snap: SnapshotRestorer; + + before(async () => { + f = await spokeForkFixture(); + ({ vTRX, trxSource, trx, trxHub } = await addLowDecimalMarket(f)); + snap = await takeSnapshot(); + }); + afterEach(async () => snap.restore()); + + it("really is the sub-EXP_SCALE regime", async () => { + // 10 ** (18 + 6 - 8) = 1e16. Everything below turns on this being under 1e18; an 18-decimal + // market lists at 1e28 and never reaches it. + const rate: BigNumber = await vTRX.exchangeRateStored(); + expect(rate).to.be.lt(EXP_SCALE); + const meta = await ethers.getContractAt("IERC20Metadata", bscmainnet.TRX); + expect(await meta.decimals()).to.equal(6); + }); + + it("round-trips a deposit", async () => { + const amount = trxAmt("100000"); + await deposit(amount); + // Worth what went in, to within the single unit the mint truncates away: the deposit buys + // `floor(amount * 1e18 / rate)` vTokens and the remainder is not enough for one more. + const held: BigNumber = await f.adapter.totalAssets(vTRX.address, trxSource.address); + expect(amount.sub(held)).to.be.lte(1); + + const out = amount.sub(trxAmt("1")); + const before = await trx.balanceOf(f.supplier.address); + await withdrawAsHub(out, f.supplier.address); + expect(await trx.balanceOf(f.supplier.address)).to.equal(before.add(out)); + }); + + it("settles a one-unit withdrawal the market would otherwise pay nothing for", async () => { + // At a rate of 1e16 a 1-unit request burns 100 vTokens and still truncates its payout to + // zero, which the market rejects outright with "redeemAmount is zero". The adapter raises the + // request to the smallest one that settles and still forwards exactly what was asked for. + await deposit(trxAmt("100000")); + + const before = await trx.balanceOf(f.supplier.address); + const idleBefore = await trx.balanceOf(trxSource.address); + await withdrawAsHub(1, f.supplier.address, { gasLimit: 3_000_000 }); + + expect(await trx.balanceOf(f.supplier.address)).to.equal(before.add(1)); + // The surplus the bump redeemed stays idle on the YieldGroup, where `totalAssets` counts it + // and the next withdrawal spends it first. + expect(await trx.balanceOf(trxSource.address)).to.be.gte(idleBefore); + }); + + it("raises a request the market would pay nothing for, instead of reverting the withdrawal", async () => { + await deposit(trxAmt("100000")); + const rate: BigNumber = await vTRX.exchangeRateStored(); + // The regime the bump exists for: one unit buys `floor(1e18 / rate)` vTokens, and those are + // worth less than one unit back, so the payout truncates away. + expect(EXP_SCALE.mod(rate), `rate ${rate} must not divide 1e18 evenly`).to.not.equal(0); + + const src = await impersonate(trxSource.address); + await expect(vTRX.connect(src).redeemUnderlying(1)).to.be.revertedWith("redeemAmount is zero"); + + // Through the adapter the same request settles, and the caller still receives exactly what it + // asked for. A dust remainder handed down a withdraw cascade therefore does not revert an + // otherwise valid withdrawal. + const before = await trx.balanceOf(f.outsider.address); + const idleBefore = await trx.balanceOf(trxSource.address); + await withdrawAsHub(1, f.outsider.address); + expect(await trx.balanceOf(f.outsider.address)).to.equal(before.add(1)); + // The over-redeemed remainder is retained as idle rather than left in the market or lost. + expect(await trx.balanceOf(trxSource.address)).to.be.gt(idleBefore); + }); + + it("certifies a bound the holder's own vTokens can actually settle", async () => { + // KNOWN FAILURE - reports a real defect in `AdapterSpokeV1.maxWithdraw`, not a test artifact. + // + // `maxWithdraw` bounds the answer by the position's PRO-RATA value, + // `vBal * (cash + totalBorrows - totalReserves) / totalSupply`, then certifies it against the + // market's payable cash and against the payout its own redeem arithmetic would produce. It + // never checks the third constraint: that the burn implied by that request fits inside the + // holder's OWN vToken balance. A redeem rounds its burn UP, so the last unit of the pro-rata + // value can need one more vToken than the holder owns, and `redeemUnderlying` then reverts + // with an arithmetic underflow while burning. + // + // Not a loss of funds - `redeem(balanceOf)` still exits the position - but the adapter only + // ever calls `redeemUnderlying`, so the Hub has no path to that last unit: + // `YieldGroupBase.withdraw` surfaces the skipped leg as `ResourceLiquidityInsufficient` and + // the whole withdrawal reverts. It also blocks the drain-to-zero that `removeResource` + // requires, which the adapter's own NatSpec states this flooring is designed to allow. + // + // Reachable when `vBal * backing` does not divide `totalSupply` evenly, which needs a second + // supplier - here the market's own listing seed. It shows up at a low exchange rate because + // one vToken is then worth a fraction of one underlying unit; at the 1e28 rate of an + // 18-decimal market the pro-rata truncation is far larger than one vToken and hides it. + await deposit(trxAmt("100000")); + + const rate: BigNumber = await vTRX.exchangeRateStored(); + const liquid: BigNumber = await f.adapter.maxWithdraw(vTRX.address, trxSource.address); + const held: BigNumber = await vTRX.balanceOf(trxSource.address); + + // The burn `redeemUnderlying(liquid)` performs, computed the way `_redeemFresh` does. + let burn = liquid.mul(EXP_SCALE).div(rate); + const probe = burn.mul(rate).div(EXP_SCALE); + if (!probe.isZero() && !probe.eq(liquid)) burn = burn.add(1); + + expect(burn, `certified ${liquid} needs ${burn} vTokens, holder owns ${held}`).to.be.lte(held); + + // And therefore the position can be emptied through the adapter, which is what + // `removeResource` gates on. + await withdrawAsHub(liquid, f.supplier.address, { gasLimit: 5_000_000 }); + expect(await f.adapter.receiptBalance(vTRX.address, trxSource.address)).to.equal(0); + await expect(trxSource.connect(f.timelock).removeResource(vTRX.address)).to.not.be.reverted; + }); + + it("reports capacity in the underlying's own units, not the vToken's", async () => { + const room: BigNumber = await f.adapter.connect(await impersonate(trxSource.address)).maxDeposit(vTRX.address); + const cap = await f.spoke.supplyCaps(vTRX.address); + const supplied = (await vTRX.totalSupply()).mul(await vTRX.exchangeRateStored()).div(EXP_SCALE); + expect(room).to.be.lte(cap.sub(supplied)); + // Within the 0.1% margin the adapter withholds to absorb an accrual between this read and the + // mint it sizes. + expect(room).to.be.gt(cap.sub(supplied).mul(998).div(1000)); + }); + + /// Deposit through the TRX Hub, which is the only way a YieldGroup is ever funded. + async function deposit(amount: BigNumber) { + await fundFrom(trx, bscmainnet.TRX_HOLDER, f.supplier.address, amount); + await trx.connect(f.supplier).approve(trxHub.address, amount); + await trxHub.connect(f.supplier).deposit(amount, f.supplier.address, { gasLimit: 5_000_000 }); + } + + /// Pull straight from the source as its Hub, so the assertion is about the adapter's own + /// arithmetic rather than about the Hub's share maths on top of it. + async function withdrawAsHub(amount: BigNumber | number, to: string, overrides?: object) { + const hubSigner = await impersonate(trxHub.address); + return trxSource.connect(hubSigner).withdraw(amount, to, overrides ?? { gasLimit: 3_000_000 }); + } + }); +} + +async function impersonate(who: string) { + await ethers.provider.send("hardhat_impersonateAccount", [who]); + await ethers.provider.send("hardhat_setBalance", [who, "0x56bc75e2d63100000"]); + return ethers.getSigner(who); +} diff --git a/tests/hardhat/Fork/HubSpoke/psrRegistryConflict.ts b/tests/hardhat/Fork/HubSpoke/psrRegistryConflict.ts new file mode 100644 index 000000000..f8d97fb3f --- /dev/null +++ b/tests/hardhat/Fork/HubSpoke/psrRegistryConflict.ts @@ -0,0 +1,103 @@ +import { SnapshotRestorer, takeSnapshot } from "@nomicfoundation/hardhat-network-helpers"; +import { expect } from "chai"; +import { Contract } from "ethers"; +import { ethers } from "hardhat"; + +import { initMainnetUser } from "../utils"; +import { bscmainnet } from "./constants"; +import { PSR_ABI, SpokeStack, addSpokeMarkets, configureSpokeStack, deploySpokeStack, listSpokePool } from "./fixture"; + +const FORK = process.env.FORK === "true"; +const FORKED_NETWORK = process.env.FORKED_NETWORK || "bscmainnet"; + +/// `IProtocolShareReserve.IncomeType.SPREAD`, the type `VToken._reduceReservesFresh` reports. +const INCOME_TYPE_SPREAD = 0; + +/** + * Giving the spoke pool a registry of its own is not free: `ProtocolShareReserve` stores exactly one + * registry address and rejects any non-core pool whose vToken that registry does not know. Both + * `VToken` call sites are unconditional, so the check gates `reduceReserves` and, more expensively, + * every liquidation in the pool. + * + * This suite pins both directions of that trade so the constraint is a failing test rather than a + * paragraph someone has to remember: the spoke pool cannot take income while PSR points at the + * isolated-pools registry, and the isolated pools cannot take income once it points at the spoke's. + * Neither is a defect in this repo. Both are the reason PSR has to support more than one registry + * before this pool is wired into it. + */ +if (FORK && FORKED_NETWORK === "bscmainnet") { + describe("HubSpoke: ProtocolShareReserve holds one pool registry", () => { + let s: SpokeStack; + let psr: Contract; + let snapshot: SnapshotRestorer; + + // A pool that is live on this chain today, and the party this trade is made at the expense of. + let livePoolAsset: string; + + before(async () => { + s = await deploySpokeStack(false); + psr = await ethers.getContractAt(PSR_ABI, bscmainnet.PSR); + + const livePool = await ethers.getContractAt("Comptroller", bscmainnet.COMPTROLLER_STABLECOINS); + const [firstMarket] = await livePool.getAllMarkets(); + livePoolAsset = await (await ethers.getContractAt("VToken", firstMarket)).underlying(); + + snapshot = await takeSnapshot(); + }); + + afterEach(async () => { + await snapshot.restore(); + }); + + const reportIncome = (comptroller: string, asset: string) => + psr.updateAssetsState(comptroller, asset, INCOME_TYPE_SPREAD); + + it("starts pointed at the isolated-pools registry", async () => { + expect(await psr.poolRegistry()).to.equal(bscmainnet.POOL_REGISTRY); + expect(await psr.poolRegistry()).to.not.equal(s.registry.address); + }); + + it("rejects the spoke pool while it points at the isolated-pools registry", async () => { + // Listed, funded and working - and still unknown to the registry PSR asks. Every liquidation + // in this pool would revert here, after the collateral has already moved. + await configureSpokeStackWithoutPsr(s); + await listSpokePool(s); + await addSpokeMarkets(s); + await pointPsrAt(bscmainnet.POOL_REGISTRY); + + await expect(reportIncome(s.spoke.address, bscmainnet.USDT)).to.be.reverted; + }); + + it("accepts the spoke pool once it points at the spoke registry", async () => { + await configureSpokeStackWithoutPsr(s); + await listSpokePool(s); + await addSpokeMarkets(s); + await pointPsrAt(s.registry.address); + + await expect(reportIncome(s.spoke.address, bscmainnet.USDT)).to.not.be.reverted; + }); + + it("stops accepting the live isolated pools at the same moment", async () => { + // The whole cost of the switch, in one assertion. This pool takes income today and would stop + // the block this change lands, which is why the PSR upgrade has to come first. + await expect(reportIncome(bscmainnet.COMPTROLLER_STABLECOINS, livePoolAsset)).to.not.be.reverted; + + await pointPsrAt(s.registry.address); + + await expect(reportIncome(bscmainnet.COMPTROLLER_STABLECOINS, livePoolAsset)).to.be.reverted; + }); + + async function pointPsrAt(registry: string) { + const owner = await initMainnetUser(await psr.owner(), ethers.utils.parseUnits("10")); + await psr.connect(owner).setPoolRegistry(registry); + } + + /// `configureSpokeStack` re-points PSR as its last step, which is the one thing these tests set + /// for themselves. + async function configureSpokeStackWithoutPsr(stack: SpokeStack) { + const before = await psr.poolRegistry(); + await configureSpokeStack(stack); + await pointPsrAt(before); + } + }); +} diff --git a/tests/hardhat/Lens/SpokePoolLens.ts b/tests/hardhat/Lens/SpokePoolLens.ts new file mode 100644 index 000000000..19b54df71 --- /dev/null +++ b/tests/hardhat/Lens/SpokePoolLens.ts @@ -0,0 +1,535 @@ +import { smock } from "@defi-wonderland/smock"; +import { impersonateAccount, loadFixture, mine, setBalance } from "@nomicfoundation/hardhat-network-helpers"; +import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; +import { expect } from "chai"; +import { parseUnits } from "ethers/lib/utils"; +import { ethers, upgrades } from "hardhat"; + +import { DEFAULT_BLOCKS_PER_YEAR } from "../../../helpers/deploymentConfig"; +import { + AccessControlManager, + Comptroller, + IDeviationBoundedOracle, + MockPriceOracle, + MockPriceOracle__factory, + MockToken, + MockToken__factory, + PoolLens, + PoolLens__factory, + PoolRegistry, + RewardsDistributor, + SpokeComptroller, + SpokePoolLens, + SpokePoolLens__factory, + VToken, + WhitePaperInterestRateModel__factory, +} from "../../../typechain"; +import { makeVToken } from "../util/TokenTestHelpers"; + +upgrades.silenceWarnings(); + +const MAX_LOOPS_LIMIT = 150; +const CLOSE_FACTOR = parseUnits("0.05", 18); +const MIN_LIQUIDATABLE_COLLATERAL = parseUnits("100", 18); + +/// Both above the 1.05e18 floor a market's default 5% protocol seize share imposes. +const POOL_INCENTIVE = parseUnits("1.1", 18); +const MARKET_INCENTIVE = parseUnits("1.2", 18); + +/// Deliberately unequal, so a test that reads the threshold cannot pass by echoing the collateral factor. +const COLLATERAL_FACTOR = parseUnits("0.7", 18); +const LIQUIDATION_THRESHOLD = parseUnits("0.8", 18); + +/// Every fixture market is priced at $1, so a usd amount the lens reports is the underlying amount. +const PRICE = parseUnits("1", 18); + +/// Enough to leave the borrower solvent at a 0.7 collateral factor after both borrows below. +const BORROWER_SUPPLY = parseUnits("200", 18); +const HEALED_BORROW = parseUnits("40", 18); +const LIVE_BORROW = parseUnits("20", 18); + +const REWARD_SPEED = parseUnits("0.5", 18); + +/** + * `SpokePoolLens` reports the pool and market state a spoke pool has and `PoolLens` has no field for. + * + * A pooled comptroller is deployed alongside as a control, for the one assertion that matters about it: + * this lens answers about spoke pools only, and pointing it at a pool that is not one fails loudly + * rather than reporting a plausible zero. + */ +describe("SpokePoolLens", function () { + // Two registries, four pools and a rewards distributor put the fixture over mocha's default budget. + this.timeout(120000); + interface LensFixture { + spokeLens: SpokePoolLens; + poolLens: PoolLens; + spokeRegistry: PoolRegistry; + pooledRegistry: PoolRegistry; + spoke: SpokeComptroller; + unconfigured: SpokeComptroller; + control: Comptroller; + spokeMarkets: VToken[]; + boundedOracle: IDeviationBoundedOracle; + rewardsDistributor: RewardsDistributor; + rewardToken: MockToken; + borrower: SignerWithAddress; + } + + async function lensFixture(): Promise { + const [owner, , borrower] = await ethers.getSigners(); + + const acm = await smock.fake("AccessControlManager"); + acm.isAllowedToCall.returns(true); + + const PoolRegistryFactory = await ethers.getContractFactory("PoolRegistry"); + const deployRegistry = async () => (await upgrades.deployProxy(PoolRegistryFactory, [acm.address])) as PoolRegistry; + + // Two registries, as a deployment has: a spoke pool is listed in one of its own rather than the + // shared directory, so the spoke lens never meets a pool it has no shape for. + const spokeRegistry = await deployRegistry(); + const pooledRegistry = await deployRegistry(); + + const MockPriceOracleFactory = await ethers.getContractFactory("MockPriceOracle"); + const priceOracle: MockPriceOracle = await MockPriceOracleFactory.deploy(); + + const deployPool = async ( + name: string, + contractName: "SpokeComptroller" | "Comptroller", + registry: PoolRegistry, + ) => { + const factory = await ethers.getContractFactory(contractName); + const beacon = await upgrades.deployBeacon(factory, { constructorArgs: [registry.address] }); + const comptroller = await upgrades.deployBeaconProxy(beacon, factory, [MAX_LOOPS_LIMIT, acm.address]); + await comptroller.setPriceOracle(priceOracle.address); + await registry.addPool(name, comptroller.address, CLOSE_FACTOR, POOL_INCENTIVE, MIN_LIQUIDATABLE_COLLATERAL); + return comptroller; + }; + + const spoke = (await deployPool("Spoke pool", "SpokeComptroller", spokeRegistry)) as SpokeComptroller; + // Registered but otherwise untouched, i.e. the state the deploy script leaves a spoke pool in. + const unconfigured = (await deployPool( + "Unconfigured spoke pool", + "SpokeComptroller", + spokeRegistry, + )) as SpokeComptroller; + const control = (await deployPool("Control pool", "Comptroller", pooledRegistry)) as Comptroller; + + const MockTokenFactory = await ethers.getContractFactory("MockToken"); + const RateModelFactory = await ethers.getContractFactory( + "WhitePaperInterestRateModel", + ); + const rateModel = await RateModelFactory.deploy(0, parseUnits("0.04", 18), false, DEFAULT_BLOCKS_PER_YEAR); + + const initialSupply = parseUnits("1000", 18); + const listMarket = async ( + comptroller: Comptroller | SpokeComptroller, + symbol: string, + registry: PoolRegistry, + ): Promise => { + const underlying: MockToken = await MockTokenFactory.deploy(symbol, symbol, 18); + await priceOracle.setPrice(underlying.address, PRICE); + + const vToken = await makeVToken({ + underlying, + comptroller, + accessControlManager: acm, + decimals: 8, + initialExchangeRateMantissa: parseUnits("1", 18), + admin: owner, + interestRateModel: rateModel, + isTimeBased: false, + blocksPerYear: DEFAULT_BLOCKS_PER_YEAR, + }); + + await underlying.faucet(initialSupply); + await underlying.approve(registry.address, initialSupply); + await registry.addMarket({ + vToken: vToken.address, + collateralFactor: COLLATERAL_FACTOR, + liquidationThreshold: LIQUIDATION_THRESHOLD, + initialSupply, + vTokenReceiver: owner.address, + supplyCap: parseUnits("4000", 18), + borrowCap: parseUnits("2000", 18), + }); + return vToken as VToken; + }; + + // Two markets on the spoke pool: the first carries an incentive and an allowlist of its own, the + // second inherits the pool. + const spokeMarkets = [ + await listMarket(spoke, "SPK1", spokeRegistry), + await listMarket(spoke, "SPK2", spokeRegistry), + ]; + await listMarket(control, "CTL1", pooledRegistry); + + // A borrow reads this, so it has to be a live contract rather than a bare address. Returning spot on + // both legs is what the real one does for an asset it holds no window for. + const boundedOracle = await smock.fake("IDeviationBoundedOracle"); + boundedOracle.getBoundedPricesView.returns([PRICE, PRICE]); + await spoke.setDeviationBoundedOracle(boundedOracle.address); + + await spoke.setMarketLiquidationIncentive(spokeMarkets[0].address, MARKET_INCENTIVE); + await spoke.setSupplyAllowlistEnabled(spokeMarkets[0].address, true); + + // An idle pool carries no bad debt and accrues no rewards, which would leave the parity check below + // comparing zero against zero. Borrow against the second market, which has no supply allowlist, then + // heal the first borrow so one market holds bad debt and the other keeps a live borrow. + const collateral = MockTokenFactory.attach(await spokeMarkets[1].underlying()); + await collateral.connect(borrower).faucet(BORROWER_SUPPLY); + await collateral.connect(borrower).approve(spokeMarkets[1].address, BORROWER_SUPPLY); + await spokeMarkets[1].connect(borrower).mint(BORROWER_SUPPLY); + await spoke.connect(borrower).enterMarkets([spokeMarkets[1].address]); + await spokeMarkets[0].connect(borrower).borrow(HEALED_BORROW); + await spokeMarkets[1].connect(borrower).borrow(LIVE_BORROW); + + // `healBorrow` is the only path that writes `badDebt`, and only the comptroller may call it. + await impersonateAccount(spoke.address); + await setBalance(spoke.address, parseUnits("1", 18)); + const asComptroller = await ethers.getSigner(spoke.address); + await spokeMarkets[0].connect(asComptroller).healBorrow(borrower.address, borrower.address, 0); + + const rewardToken = await MockTokenFactory.deploy("REW", "REW", 18); + const RewardsDistributorFactory = await ethers.getContractFactory("RewardsDistributor"); + const rewardsDistributor = (await upgrades.deployProxy( + RewardsDistributorFactory, + [spoke.address, rewardToken.address, MAX_LOOPS_LIMIT, acm.address], + { constructorArgs: [false, DEFAULT_BLOCKS_PER_YEAR], unsafeAllow: ["internal-function-storage"] }, + )) as RewardsDistributor; + await spoke.addRewardsDistributor(rewardsDistributor.address); + await rewardsDistributor.setRewardTokenSpeeds( + spokeMarkets.map(market => market.address), + [REWARD_SPEED, REWARD_SPEED], + [REWARD_SPEED, REWARD_SPEED], + ); + // The reward indices only move once blocks pass, so every account reads zero until some do. + await mine(1000); + + const SpokePoolLensFactory = await ethers.getContractFactory("SpokePoolLens"); + const spokeLens = await SpokePoolLensFactory.deploy(false, DEFAULT_BLOCKS_PER_YEAR); + + const PoolLensFactory = await ethers.getContractFactory("PoolLens"); + const poolLens = await PoolLensFactory.deploy(false, DEFAULT_BLOCKS_PER_YEAR); + + return { + spokeLens, + poolLens, + spokeRegistry, + pooledRegistry, + spoke, + unconfigured, + control, + spokeMarkets, + boundedOracle, + rewardsDistributor, + rewardToken, + borrower, + }; + } + + let f: LensFixture; + + beforeEach(async () => { + f = await loadFixture(lensFixture); + }); + + const poolData = (comptroller: { address: string }) => + f.spokeLens.getSpokePoolByComptroller(f.spokeRegistry.address, comptroller.address); + + describe("pool state a spoke pool alone has", () => { + it("reports the oracle the pool bounds collateral prices with", async () => { + expect((await poolData(f.spoke)).deviationBoundedOracle).to.equal(f.boundedOracle.address); + }); + + it("reports zero for a pool whose listing VIP has not set one yet", async () => { + const pool = await poolData(f.unconfigured); + expect(pool.deviationBoundedOracle).to.equal(ethers.constants.AddressZero); + expect(pool.vTokens).to.have.lengthOf(0); + }); + + it("follows the pool's liquidation allowlist flag", async () => { + expect((await poolData(f.spoke)).liquidationAllowlistEnabled).to.equal(false); + + await f.spoke.setLiquidationAllowlistEnabled(true); + expect((await poolData(f.spoke)).liquidationAllowlistEnabled).to.equal(true); + }); + + it("reports the pool-wide incentive, not the one a market set for itself", async () => { + // The getter behind this field resolves against its caller, and a lens is not one of the pool's + // markets, so what comes back is the pool-wide fallback. A market carrying its own must not move it. + expect((await poolData(f.spoke)).poolLiquidationIncentiveMantissa).to.equal(POOL_INCENTIVE); + }); + }); + + describe("market state a spoke pool alone has", () => { + it("reports a market's own incentive, and the pool's for a market without one", async () => { + const markets = (await poolData(f.spoke)).vTokens; + + expect(markets[0].effectiveLiquidationIncentiveMantissa).to.equal(MARKET_INCENTIVE); + expect(markets[1].effectiveLiquidationIncentiveMantissa).to.equal(POOL_INCENTIVE); + }); + + it("distinguishes a market that set an incentive from one that inherits", async () => { + // Zero is the "unset" sentinel here, not a discount of zero, which is why both fields are reported. + const markets = (await poolData(f.spoke)).vTokens; + + expect(markets[0].ownLiquidationIncentiveMantissa).to.equal(MARKET_INCENTIVE); + expect(markets[1].ownLiquidationIncentiveMantissa).to.equal(0); + }); + + it("reports the supply allowlist per market, so an armed market does not arm its siblings", async () => { + const markets = (await poolData(f.spoke)).vTokens; + + expect(markets[0].supplyAllowlistEnabled).to.equal(true); + expect(markets[1].supplyAllowlistEnabled).to.equal(false); + }); + + it("follows the forced liquidation flag", async () => { + expect((await f.spokeLens.spokeVTokenMetadata(f.spokeMarkets[0].address)).forcedLiquidationEnabled).to.equal( + false, + ); + + await f.spoke.setForcedLiquidation(f.spokeMarkets[0].address, true); + expect((await f.spokeLens.spokeVTokenMetadata(f.spokeMarkets[0].address)).forcedLiquidationEnabled).to.equal( + true, + ); + }); + + it("reports the liquidation threshold, which no other lens field carries", async () => { + const metadata = await f.spokeLens.spokeVTokenMetadata(f.spokeMarkets[0].address); + + expect(metadata.liquidationThresholdMantissa).to.equal(LIQUIDATION_THRESHOLD); + // Not the collateral factor under another name. + expect(metadata.collateralFactorMantissa).to.equal(COLLATERAL_FACTOR); + }); + }); + + describe("the fields shared with PoolLens", () => { + it("reports the same market metadata PoolLens does", async () => { + const [spokeSide, pooledSide] = await Promise.all([ + f.spokeLens.spokeVTokenMetadata(f.spokeMarkets[0].address), + f.poolLens.vTokenMetadata(f.spokeMarkets[0].address), + ]); + + expect(spokeSide.vToken).to.equal(pooledSide.vToken); + expect(spokeSide.exchangeRateCurrent).to.equal(pooledSide.exchangeRateCurrent); + expect(spokeSide.supplyRatePerBlockOrTimestamp).to.equal(pooledSide.supplyRatePerBlockOrTimestamp); + expect(spokeSide.borrowRatePerBlockOrTimestamp).to.equal(pooledSide.borrowRatePerBlockOrTimestamp); + expect(spokeSide.reserveFactorMantissa).to.equal(pooledSide.reserveFactorMantissa); + expect(spokeSide.supplyCaps).to.equal(pooledSide.supplyCaps); + expect(spokeSide.borrowCaps).to.equal(pooledSide.borrowCaps); + expect(spokeSide.totalBorrows).to.equal(pooledSide.totalBorrows); + expect(spokeSide.totalReserves).to.equal(pooledSide.totalReserves); + expect(spokeSide.totalSupply).to.equal(pooledSide.totalSupply); + expect(spokeSide.totalCash).to.equal(pooledSide.totalCash); + expect(spokeSide.isListed).to.equal(pooledSide.isListed); + expect(spokeSide.collateralFactorMantissa).to.equal(pooledSide.collateralFactorMantissa); + expect(spokeSide.underlyingAssetAddress).to.equal(pooledSide.underlyingAssetAddress); + expect(spokeSide.vTokenDecimals).to.equal(pooledSide.vTokenDecimals); + expect(spokeSide.underlyingDecimals).to.equal(pooledSide.underlyingDecimals); + }); + + it("encodes paused actions the same way, so a consumer decodes both alike", async () => { + const MINT = 0; + await f.spoke.setActionsPaused([f.spokeMarkets[0].address], [MINT], true); + + const [spokeSide, pooledSide] = await Promise.all([ + f.spokeLens.spokeVTokenMetadata(f.spokeMarkets[0].address), + f.poolLens.vTokenMetadata(f.spokeMarkets[0].address), + ]); + + expect(spokeSide.pausedActions).to.equal(1); + expect(spokeSide.pausedActions).to.equal(pooledSide.pausedActions); + }); + + it("reports the same pool-level fields PoolLens does", async () => { + const [spokeSide, pooledSide] = await Promise.all([ + poolData(f.spoke), + f.poolLens.getPoolByComptroller(f.spokeRegistry.address, f.spoke.address), + ]); + + expect(spokeSide.name).to.equal(pooledSide.name); + expect(spokeSide.creator).to.equal(pooledSide.creator); + expect(spokeSide.comptroller).to.equal(pooledSide.comptroller); + expect(spokeSide.blockPosted).to.equal(pooledSide.blockPosted); + expect(spokeSide.timestampPosted).to.equal(pooledSide.timestampPosted); + expect(spokeSide.category).to.equal(pooledSide.category); + expect(spokeSide.logoURL).to.equal(pooledSide.logoURL); + expect(spokeSide.description).to.equal(pooledSide.description); + expect(spokeSide.priceOracle).to.equal(pooledSide.priceOracle); + expect(spokeSide.closeFactor).to.equal(pooledSide.closeFactor); + expect(spokeSide.minLiquidatableCollateral).to.equal(pooledSide.minLiquidatableCollateral); + expect(spokeSide.vTokens).to.have.lengthOf(pooledSide.vTokens.length); + }); + }); + + describe("supply permissions", () => { + it("reports both halves, because either one alone is misleading", async () => { + const [, other] = await ethers.getSigners(); + const markets = f.spokeMarkets.map(market => market.address); + + const before = await f.spokeLens.spokeSupplyPermissions(f.spoke.address, markets, other.address); + // Armed and the account is not on the list: it may not supply. + expect(before[0].allowlistEnabled).to.equal(true); + expect(before[0].accountAllowlisted).to.equal(false); + // Not armed, so the account may supply despite not being on the list. Reading only the second + // field here would report the inverse of the truth. + expect(before[1].allowlistEnabled).to.equal(false); + expect(before[1].accountAllowlisted).to.equal(false); + + await f.spoke.setAllowedSupplier(markets[0], other.address, true); + + const after = await f.spokeLens.spokeSupplyPermissions(f.spoke.address, markets, other.address); + expect(after[0].accountAllowlisted).to.equal(true); + }); + + it("answers per market, in the order asked", async () => { + const [, other] = await ethers.getSigners(); + const reversed = [f.spokeMarkets[1].address, f.spokeMarkets[0].address]; + + const permissions = await f.spokeLens.spokeSupplyPermissions(f.spoke.address, reversed, other.address); + + expect(permissions.map(p => p.vToken)).to.deep.equal(reversed); + expect(permissions[0].allowlistEnabled).to.equal(false); + expect(permissions[1].allowlistEnabled).to.equal(true); + }); + }); + + describe("liquidation permission", () => { + it("reports both halves of the pool-wide allowlist", async () => { + const [, other] = await ethers.getSigners(); + + let permission = await f.spokeLens.spokeLiquidationPermission(f.spoke.address, other.address); + expect(permission.allowlistEnabled).to.equal(false); + expect(permission.accountAllowlisted).to.equal(false); + + await f.spoke.setLiquidationAllowlistEnabled(true); + await f.spoke.setAllowedLiquidator(other.address, true); + + permission = await f.spokeLens.spokeLiquidationPermission(f.spoke.address, other.address); + expect(permission.allowlistEnabled).to.equal(true); + expect(permission.accountAllowlisted).to.equal(true); + }); + }); + + /** + * The half of the surface that mirrors `PoolLens` under the same names and shapes, so that a consumer reads a + * spoke pool from one address. These were ported by copy, so each is also checked against the same call on + * `PoolLens` for the same spoke pool: the two must not drift. + */ + describe("the reads that mirror PoolLens", () => { + it("answers identically to PoolLens for every mirrored read", async () => { + const account = f.borrower.address; + const markets = f.spokeMarkets.map(market => market.address); + const underlying = await f.spokeMarkets[0].underlying(); + const strip = (value: unknown) => JSON.parse(JSON.stringify(value)); + + const badDebt = await f.spokeLens.getPoolBadDebt(f.spoke.address); + const rewards = await f.spokeLens.getPendingRewards(account, f.spoke.address); + + // Without these two the comparisons below read zero on both sides whatever the ported math does, + // and the bad debt and reward reads would be guarded in name only. + expect(badDebt.totalBadDebtUsd).to.be.gt(0); + expect(rewards[0].pendingRewards[1].amount).to.be.gt(0); + + expect(strip(await f.spokeLens.callStatic.vTokenBalancesAll(markets, account))).to.deep.equal( + strip(await f.poolLens.callStatic.vTokenBalancesAll(markets, account)), + ); + expect(strip(await f.spokeLens.vTokenUnderlyingPriceAll(markets))).to.deep.equal( + strip(await f.poolLens.vTokenUnderlyingPriceAll(markets)), + ); + expect(strip(badDebt)).to.deep.equal(strip(await f.poolLens.getPoolBadDebt(f.spoke.address))); + expect(strip(rewards)).to.deep.equal(strip(await f.poolLens.getPendingRewards(account, f.spoke.address))); + expect(await f.spokeLens.getVTokenForAsset(f.spokeRegistry.address, f.spoke.address, underlying)).to.equal( + await f.poolLens.getVTokenForAsset(f.spokeRegistry.address, f.spoke.address, underlying), + ); + expect(strip(await f.spokeLens.getPoolsSupportedByAsset(f.spokeRegistry.address, underlying))).to.deep.equal( + strip(await f.poolLens.getPoolsSupportedByAsset(f.spokeRegistry.address, underlying)), + ); + }); + + it("reports balances", async () => { + const [owner] = await ethers.getSigners(); + const market = f.spokeMarkets[0]; + + const [balances] = await f.spokeLens.callStatic.vTokenBalancesAll([market.address], owner.address); + + expect(balances.vToken).to.equal(market.address); + expect(balances.balanceOf).to.equal(await market.balanceOf(owner.address)); + }); + + it("reports underlying prices", async () => { + const market = f.spokeMarkets[0]; + + const [price] = await f.spokeLens.vTokenUnderlyingPriceAll([market.address]); + + expect(price.vToken).to.equal(market.address); + expect(price.underlyingPrice).to.equal(PRICE); + }); + + it("reports bad debt per market", async () => { + const summary = await f.spokeLens.getPoolBadDebt(f.spoke.address); + const healed = await f.spokeMarkets[0].badDebt(); + + expect(summary.comptroller).to.equal(f.spoke.address); + expect(summary.badDebts.map(entry => entry.vTokenAddress)).to.deep.equal( + f.spokeMarkets.map(market => market.address), + ); + // Priced at $1, so the usd figure is the underlying amount. Only the healed market carries any. + expect(healed).to.equal(HEALED_BORROW); + expect(summary.badDebts[0].badDebtUsd).to.equal(healed); + expect(summary.badDebts[1].badDebtUsd).to.equal(0); + expect(summary.totalBadDebtUsd).to.equal(healed); + }); + + it("reports pending rewards", async () => { + const [summary] = await f.spokeLens.getPendingRewards(f.borrower.address, f.spoke.address); + + expect(summary.distributorAddress).to.equal(f.rewardsDistributor.address); + expect(summary.rewardTokenAddress).to.equal(f.rewardToken.address); + expect(summary.pendingRewards.map(entry => entry.vTokenAddress)).to.deep.equal( + f.spokeMarkets.map(market => market.address), + ); + // The account supplies and borrows in the second market only, so the first accrues it nothing. + expect(summary.pendingRewards[0].amount).to.equal(0); + expect(summary.pendingRewards[1].amount).to.be.gt(0); + }); + + it("resolves markets through the registry", async () => { + const market = f.spokeMarkets[0]; + const underlying = await market.underlying(); + + expect(await f.spokeLens.getVTokenForAsset(f.spokeRegistry.address, f.spoke.address, underlying)).to.equal( + market.address, + ); + expect(await f.spokeLens.getPoolsSupportedByAsset(f.spokeRegistry.address, underlying)).to.deep.equal([ + f.spoke.address, + ]); + }); + }); + + describe("reading a whole registry", () => { + it("describes every pool the spoke registry holds", async () => { + const pools = await f.spokeLens.getAllSpokePools(f.spokeRegistry.address); + + expect(pools.map(pool => pool.comptroller)).to.deep.equal([f.spoke.address, f.unconfigured.address]); + expect(pools[0].deviationBoundedOracle).to.equal(f.boundedOracle.address); + expect(pools[0].vTokens[0].effectiveLiquidationIncentiveMantissa).to.equal(MARKET_INCENTIVE); + }); + }); + + describe("pointed at a pool that is not a spoke pool", () => { + it("reverts rather than reporting a plausible zero", async () => { + // The pooled comptroller has none of the getters this lens reads, so the call finds no function to + // run. Failing here is the point: the alternative is a consumer taking an absent field for a + // configured one, and reading "no bounded oracle" off a pool that simply cannot answer. + await expect(f.spokeLens.getSpokePoolByComptroller(f.pooledRegistry.address, f.control.address)).to.be.reverted; + }); + + it("reverts on a registry holding one, rather than describing the rest", async () => { + // Enumeration is all or nothing. A deployed spoke registry holds spoke pools only, so this is + // unreachable there, but it is the behaviour to rely on if that ever stops being true. + await expect(f.spokeLens.getAllSpokePools(f.pooledRegistry.address)).to.be.reverted; + }); + }); +}); diff --git a/tests/hardhat/Spoke/allowlists.ts b/tests/hardhat/Spoke/allowlists.ts new file mode 100644 index 000000000..a7c2fbc34 --- /dev/null +++ b/tests/hardhat/Spoke/allowlists.ts @@ -0,0 +1,354 @@ +import { MockContract, smock } from "@defi-wonderland/smock"; +import { loadFixture } from "@nomicfoundation/hardhat-network-helpers"; +import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; +import chai from "chai"; +import { constants } from "ethers"; +import { parseUnits } from "ethers/lib/utils"; +import { ethers } from "hardhat"; + +import { SpokeComptroller } from "../../../typechain"; +import { ONE, SpokeFixture, TestMarket, deploySpokeComptroller, givePosition } from "./fixtures"; + +const { expect } = chai; +chai.use(smock.matchers); + +const MINT_AMOUNT = parseUnits("100", 18); + +describe("SpokeComptroller: allowlists", () => { + let fixture: SpokeFixture; + let comptroller: MockContract; + let marketA: TestMarket; + let marketB: TestMarket; + let supplier: SignerWithAddress; + let outsider: SignerWithAddress; + let borrower: SignerWithAddress; + + beforeEach(async () => { + [, supplier, outsider, borrower] = await ethers.getSigners(); + fixture = await loadFixture(deploySpokeComptroller); + fixture.resetPrices(); + ({ comptroller } = fixture); + [marketA, marketB] = fixture.markets; + }); + + describe("supply allowlist", () => { + const mint = (market: TestMarket, minter: SignerWithAddress) => + comptroller.callStatic.preMintHook(market.vToken.address, minter.address, MINT_AMOUNT); + + it("is disabled on a newly listed market, so anyone may be credited with minted vTokens", async () => { + expect(await comptroller.isSupplyAllowlistEnabled(marketA.vToken.address)).to.equal(false); + + await expect(mint(marketA, outsider)).to.not.be.reverted; + }); + + it("rejects an account that is not on the allowlist once enabled", async () => { + await comptroller.setSupplyAllowlistEnabled(marketA.vToken.address, true); + + await expect(mint(marketA, outsider)) + .to.be.revertedWithCustomError(comptroller, "SupplyNotAllowed") + .withArgs(marketA.vToken.address, outsider.address); + }); + + it("accepts an account on the allowlist once enabled", async () => { + await comptroller.setSupplyAllowlistEnabled(marketA.vToken.address, true); + await comptroller.setAllowedSupplier(marketA.vToken.address, supplier.address, true); + + await expect(mint(marketA, supplier)).to.not.be.reverted; + }); + + it("checks the account being credited, not the caller, so a third party may fund the mint", async () => { + await comptroller.setSupplyAllowlistEnabled(marketA.vToken.address, true); + await comptroller.setAllowedSupplier(marketA.vToken.address, supplier.address, true); + + // A vToken reaches this hook with `minter` set to the receiver of the vTokens; the payer never appears in + // the arguments, which is what makes `mintBehalf` by an outsider legitimate. + await expect( + comptroller.connect(outsider).callStatic.preMintHook(marketA.vToken.address, supplier.address, MINT_AMOUNT), + ).to.not.be.reverted; + await expect(mint(marketA, outsider)).to.be.revertedWithCustomError(comptroller, "SupplyNotAllowed"); + }); + + it("rejects again after the account is removed from the allowlist", async () => { + await comptroller.setSupplyAllowlistEnabled(marketA.vToken.address, true); + await comptroller.setAllowedSupplier(marketA.vToken.address, supplier.address, true); + await comptroller.setAllowedSupplier(marketA.vToken.address, supplier.address, false); + + await expect(mint(marketA, supplier)).to.be.revertedWithCustomError(comptroller, "SupplyNotAllowed"); + }); + + it("is scoped per market, both the switch and the entries", async () => { + await comptroller.setSupplyAllowlistEnabled(marketA.vToken.address, true); + await comptroller.setAllowedSupplier(marketA.vToken.address, supplier.address, true); + + // B is untouched, so it still accepts anyone. + await expect(mint(marketB, outsider)).to.not.be.reverted; + + // Once B is gated too, A's entry does not carry over to it. + await comptroller.setSupplyAllowlistEnabled(marketB.vToken.address, true); + await expect(mint(marketA, supplier)).to.not.be.reverted; + await expect(mint(marketB, supplier)).to.be.revertedWithCustomError(comptroller, "SupplyNotAllowed"); + }); + + it("does not gate redeeming, so a holder removed from the allowlist can still exit", async () => { + await comptroller.setSupplyAllowlistEnabled(marketA.vToken.address, true); + + await expect(comptroller.callStatic.preRedeemHook(marketA.vToken.address, outsider.address, ONE)).to.not.be + .reverted; + }); + + // `preMintHook` checks the pause state, then the listing, then the allowlist, then the supply cap. These pin + // that order, so a later change cannot silently make the allowlist the first thing a caller hits. + describe("precedence against the other mint checks", () => { + it("reports an unlisted market before the allowlist", async () => { + await expect(comptroller.callStatic.preMintHook(outsider.address, outsider.address, MINT_AMOUNT)) + .to.be.revertedWithCustomError(comptroller, "MarketNotListed") + .withArgs(outsider.address); + }); + + it("reports the allowlist before the supply cap", async () => { + await comptroller.setMarketSupplyCaps([marketA.vToken.address], [0]); + await comptroller.setSupplyAllowlistEnabled(marketA.vToken.address, true); + + // Both the cap and the allowlist would reject this; the allowlist is the one that reports. + await expect(mint(marketA, outsider)).to.be.revertedWithCustomError(comptroller, "SupplyNotAllowed"); + }); + + it("reports the supply cap once the account is allowed", async () => { + await comptroller.setMarketSupplyCaps([marketA.vToken.address], [0]); + await comptroller.setSupplyAllowlistEnabled(marketA.vToken.address, true); + await comptroller.setAllowedSupplier(marketA.vToken.address, supplier.address, true); + + await expect(mint(marketA, supplier)) + .to.be.revertedWithCustomError(comptroller, "SupplyCapExceeded") + .withArgs(marketA.vToken.address, 0); + }); + + it("reports the pause state before anything else", async () => { + await comptroller.setActionsPaused([marketA.vToken.address], [0], true); + await comptroller.setSupplyAllowlistEnabled(marketA.vToken.address, true); + + await expect(mint(marketA, outsider)) + .to.be.revertedWithCustomError(comptroller, "ActionPaused") + .withArgs(marketA.vToken.address, 0); + }); + }); + }); + + describe("liquidation allowlist", () => { + /// Reaches the allowlist check in `preSeizeHook`: the collateral market is listed, the seizer is a listed + /// market of this pool, and the borrower is a member of the collateral market. + const seize = (liquidator: SignerWithAddress) => + comptroller.callStatic.preSeizeHook( + marketB.vToken.address, + marketA.vToken.address, + liquidator.address, + borrower.address, + ); + + beforeEach(async () => { + await comptroller.connect(borrower).enterMarkets([marketB.vToken.address]); + }); + + it("is disabled by default, so any account may receive seized collateral", async () => { + expect(await comptroller.isLiquidationAllowlistEnabled()).to.equal(false); + + await expect(seize(outsider)).to.not.be.reverted; + }); + + it("rejects a liquidator that is not on the allowlist once enabled", async () => { + await comptroller.setLiquidationAllowlistEnabled(true); + + await expect(seize(outsider)) + .to.be.revertedWithCustomError(comptroller, "LiquidationNotAllowed") + .withArgs(outsider.address); + }); + + it("accepts a liquidator on the allowlist once enabled", async () => { + await comptroller.setLiquidationAllowlistEnabled(true); + await comptroller.setAllowedLiquidator(outsider.address, true); + + await expect(seize(outsider)).to.not.be.reverted; + }); + + it("is pool-wide, so one entry covers every market", async () => { + await comptroller.connect(borrower).enterMarkets([marketA.vToken.address]); + await comptroller.setLiquidationAllowlistEnabled(true); + await comptroller.setAllowedLiquidator(outsider.address, true); + + await expect(seize(outsider)).to.not.be.reverted; + await expect( + comptroller.callStatic.preSeizeHook( + marketA.vToken.address, + marketB.vToken.address, + outsider.address, + borrower.address, + ), + ).to.not.be.reverted; + }); + + it("still gates a forced liquidation, which also ends in a seizure", async () => { + await comptroller.setForcedLiquidation(marketA.vToken.address, true); + await comptroller.setLiquidationAllowlistEnabled(true); + + await expect(seize(outsider)) + .to.be.revertedWithCustomError(comptroller, "LiquidationNotAllowed") + .withArgs(outsider.address); + }); + + // `liquidateAccount` carries no entry check of its own, because every order ends in a seizure. It is covered + // end to end in `liquidationFlows.ts`, against a set of orders that clears the account when the allowlist is off. + it("blocks healAccount for a liquidator that is not on the allowlist", async () => { + await comptroller.setLiquidationAllowlistEnabled(true); + + await expect(comptroller.connect(outsider).healAccount(borrower.address)) + .to.be.revertedWithCustomError(comptroller, "LiquidationNotAllowed") + .withArgs(outsider.address); + }); + + // A borrower holding no vTokens at all can have its whole debt written off through `healBorrow` alone, and + // `healBorrow` reaches no hook carrying the caller. The entry check on `healAccount` is the only thing standing + // between an outsider and free bad debt, so these would fail if it were removed. + describe("a borrower holding no collateral", () => { + beforeEach(async () => { + await givePosition(comptroller, borrower, [{ market: marketB, borrow: ONE }]); + marketB.vToken.seize.reset(); + marketB.vToken.healBorrow.reset(); + }); + + it("lets anyone write the debt off while the allowlist is disabled, at no cost", async () => { + await comptroller.connect(outsider).healAccount(borrower.address); + + expect(marketB.vToken.seize).to.not.have.been.called; + expect(marketB.vToken.healBorrow).to.have.been.calledOnceWith(outsider.address, borrower.address, 0); + }); + + it("blocks the same call once the allowlist is enabled", async () => { + await comptroller.setLiquidationAllowlistEnabled(true); + + await expect(comptroller.connect(outsider).healAccount(borrower.address)) + .to.be.revertedWithCustomError(comptroller, "LiquidationNotAllowed") + .withArgs(outsider.address); + expect(marketB.vToken.healBorrow).to.not.have.been.called; + }); + + it("still allows an allowlisted liquidator through", async () => { + await comptroller.setLiquidationAllowlistEnabled(true); + await comptroller.setAllowedLiquidator(outsider.address, true); + + await comptroller.connect(outsider).healAccount(borrower.address); + + expect(marketB.vToken.healBorrow).to.have.been.calledOnceWith(outsider.address, borrower.address, 0); + }); + }); + }); + + describe("setSupplyAllowlistEnabled", () => { + it("reverts if access control denies the call", async () => { + fixture.acm.isAllowedToCall + .whenCalledWith(supplier.address, "setSupplyAllowlistEnabled(address,bool)") + .returns(false); + + await expect( + comptroller.connect(supplier).setSupplyAllowlistEnabled(marketA.vToken.address, true), + ).to.be.revertedWithCustomError(comptroller, "Unauthorized"); + }); + + it("reverts if the market is not listed", async () => { + await expect(comptroller.setSupplyAllowlistEnabled(outsider.address, true)) + .to.be.revertedWithCustomError(comptroller, "MarketNotListed") + .withArgs(outsider.address); + }); + + it("stores the value and emits an event", async () => { + await expect(comptroller.setSupplyAllowlistEnabled(marketA.vToken.address, true)) + .to.emit(comptroller, "SupplyAllowlistEnabledUpdated") + .withArgs(marketA.vToken.address, true); + expect(await comptroller.isSupplyAllowlistEnabled(marketA.vToken.address)).to.equal(true); + }); + }); + + describe("setAllowedSupplier", () => { + it("reverts if access control denies the call", async () => { + fixture.acm.isAllowedToCall + .whenCalledWith(supplier.address, "setAllowedSupplier(address,address,bool)") + .returns(false); + + await expect( + comptroller.connect(supplier).setAllowedSupplier(marketA.vToken.address, supplier.address, true), + ).to.be.revertedWithCustomError(comptroller, "Unauthorized"); + }); + + it("reverts if the market is not listed", async () => { + await expect(comptroller.setAllowedSupplier(outsider.address, supplier.address, true)) + .to.be.revertedWithCustomError(comptroller, "MarketNotListed") + .withArgs(outsider.address); + }); + + it("reverts if the supplier is the zero address", async () => { + await expect( + comptroller.setAllowedSupplier(marketA.vToken.address, constants.AddressZero, true), + ).to.be.revertedWithCustomError(comptroller, "ZeroAddressNotAllowed"); + }); + + it("stores the value and emits an event", async () => { + await expect(comptroller.setAllowedSupplier(marketA.vToken.address, supplier.address, true)) + .to.emit(comptroller, "AllowedSupplierUpdated") + .withArgs(marketA.vToken.address, supplier.address, true); + expect(await comptroller.isAllowedSupplier(marketA.vToken.address, supplier.address)).to.equal(true); + }); + + it("accepts a write that does not change the value, so overlapping governance actions still execute", async () => { + await comptroller.setAllowedSupplier(marketA.vToken.address, supplier.address, true); + + await expect(comptroller.setAllowedSupplier(marketA.vToken.address, supplier.address, true)) + .to.emit(comptroller, "AllowedSupplierUpdated") + .withArgs(marketA.vToken.address, supplier.address, true); + }); + }); + + describe("setLiquidationAllowlistEnabled", () => { + it("reverts if access control denies the call", async () => { + fixture.acm.isAllowedToCall + .whenCalledWith(supplier.address, "setLiquidationAllowlistEnabled(bool)") + .returns(false); + + await expect(comptroller.connect(supplier).setLiquidationAllowlistEnabled(true)).to.be.revertedWithCustomError( + comptroller, + "Unauthorized", + ); + }); + + it("is disabled by default, and stores the value and emits an event", async () => { + expect(await comptroller.isLiquidationAllowlistEnabled()).to.equal(false); + + await expect(comptroller.setLiquidationAllowlistEnabled(true)) + .to.emit(comptroller, "LiquidationAllowlistEnabledUpdated") + .withArgs(true); + expect(await comptroller.isLiquidationAllowlistEnabled()).to.equal(true); + }); + }); + + describe("setAllowedLiquidator", () => { + it("reverts if access control denies the call", async () => { + fixture.acm.isAllowedToCall.whenCalledWith(supplier.address, "setAllowedLiquidator(address,bool)").returns(false); + + await expect( + comptroller.connect(supplier).setAllowedLiquidator(outsider.address, true), + ).to.be.revertedWithCustomError(comptroller, "Unauthorized"); + }); + + it("reverts if the liquidator is the zero address", async () => { + await expect(comptroller.setAllowedLiquidator(constants.AddressZero, true)).to.be.revertedWithCustomError( + comptroller, + "ZeroAddressNotAllowed", + ); + }); + + it("stores the value and emits an event", async () => { + await expect(comptroller.setAllowedLiquidator(outsider.address, true)) + .to.emit(comptroller, "AllowedLiquidatorUpdated") + .withArgs(outsider.address, true); + expect(await comptroller.isAllowedLiquidator(outsider.address)).to.equal(true); + }); + }); +}); diff --git a/tests/hardhat/Spoke/boundedPricing.ts b/tests/hardhat/Spoke/boundedPricing.ts new file mode 100644 index 000000000..13c6f279f --- /dev/null +++ b/tests/hardhat/Spoke/boundedPricing.ts @@ -0,0 +1,440 @@ +import { FakeContract, MockContract, smock } from "@defi-wonderland/smock"; +import { loadFixture, setBalance } from "@nomicfoundation/hardhat-network-helpers"; +import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; +import chai from "chai"; +import { constants } from "ethers"; +import { parseEther, parseUnits } from "ethers/lib/utils"; +import { ethers } from "hardhat"; + +import { IDeviationBoundedOracle, SpokeComptroller } from "../../../typechain"; +import { + ONE, + SpokeFixture, + TestMarket, + configureBoundedPricing, + deploySpokeComptroller, + givePosition, + resetFakeHistory, + setRiskWeights, +} from "./fixtures"; + +const { expect } = chai; +chai.use(smock.matchers); + +// The collateral market: 10 vTokens at an exchange rate of 1, priced at 100, weighted 0.5 for borrowing and 0.8 +// for liquidation. The debt market carries 400 of borrow at a price of 1. +// +// Every figure below follows from those, through `_accumulateMarketPosition`: +// weightedCollateral = collateralFactor * collateralPrice * balance +// borrows = debtPrice * borrowBalance +const COLLATERAL_BALANCE = parseUnits("10", 18); +const COLLATERAL_SPOT = parseUnits("100", 18); +const COLLATERAL_FACTOR = parseUnits("0.5", 18); +const LIQUIDATION_THRESHOLD = parseUnits("0.8", 18); +const DEBT_SPOT = ONE; +const DEBT_BALANCE = parseUnits("400", 18); + +// 0.5 * 100 * 10 = 500, less 400 of debt. +const CAPACITY_AT_SPOT_100 = parseUnits("100", 18); +// 0.5 * 150 * 10 = 750, less 400. +const CAPACITY_AT_SPOT_150 = parseUnits("350", 18); +// 0.8 * 150 * 10 = 1200, less 400. +const LIQUIDATION_VIEW_AT_SPOT_150 = parseUnits("800", 18); + +const PUMPED_SPOT = parseUnits("150", 18); +const TRIGGER_THRESHOLD = parseUnits("0.2", 18); + +describe("SpokeComptroller: deviation-bounded pricing", () => { + let fixture: SpokeFixture; + let comptroller: MockContract; + let collateral: TestMarket; + let debt: TestMarket; + let borrower: SignerWithAddress; + + describe("against the real DeviationBoundedOracle", () => { + let boundedOracle: IDeviationBoundedOracle; + + async function realOracleFixture(): Promise { + const f = await deploySpokeComptroller({ realBoundedOracle: true, spotPrice: COLLATERAL_SPOT }); + // The debt market prices at 1 while the collateral market prices at 100. + f.setBaselinePrice(f.markets[1], DEBT_SPOT); + await setRiskWeights(f.comptroller, f.markets[0], COLLATERAL_FACTOR, LIQUIDATION_THRESHOLD); + return f; + } + + beforeEach(async () => { + [, , borrower] = await ethers.getSigners(); + fixture = await loadFixture(realOracleFixture); + fixture.resetPrices(); + ({ comptroller } = fixture); + [collateral, debt] = fixture.markets; + boundedOracle = fixture.boundedOracle as IDeviationBoundedOracle; + await givePosition(comptroller, borrower, [ + { market: collateral, collateral: COLLATERAL_BALANCE }, + { market: debt, borrow: DEBT_BALANCE }, + ]); + }); + + describe("collateral leg", () => { + it("caps borrowing capacity at the window low when the collateral price deviates upward", async () => { + await configureBoundedPricing(boundedOracle, collateral.underlying, { + triggerThreshold: TRIGGER_THRESHOLD, + }); + // A 50% print against a window seeded at 100 exceeds the 20% trigger, so the collateral leg resolves to + // min(spot, windowMin) = 100 rather than the 150 the pump asked for. + fixture.setSpotPrice(collateral, PUMPED_SPOT); + + const [collateralPrice, debtPrice] = await boundedOracle.getBoundedPricesView(collateral.vToken.address); + expect(collateralPrice).to.equal(COLLATERAL_SPOT); + expect(debtPrice).to.equal(PUMPED_SPOT); + + const { liquidity, shortfall } = await comptroller.getBorrowingPower(borrower.address); + expect(liquidity).to.equal(CAPACITY_AT_SPOT_100); + expect(shortfall).to.equal(0); + }); + + it("hands the pumped price straight through when bounded pricing is not enabled for the asset", async () => { + // Same pump, no configuration: the oracle returns spot on both legs and the capacity inflates by 250. + fixture.setSpotPrice(collateral, PUMPED_SPOT); + + const { liquidity } = await comptroller.getBorrowingPower(borrower.address); + expect(liquidity).to.equal(CAPACITY_AT_SPOT_150); + }); + }); + + describe("debt leg", () => { + it("keeps measuring debt at the window high when the borrowed asset's price is dumped", async () => { + await configureBoundedPricing(boundedOracle, debt.underlying, { triggerThreshold: TRIGGER_THRESHOLD }); + // Halving the debt price would halve measured debt and free 200 of capacity. The max leg refuses: the + // window high stays at the pre-dump 1, so the debt is still valued at 400. + fixture.setSpotPrice(debt, parseUnits("0.5", 18)); + + const [, debtPrice] = await boundedOracle.getBoundedPricesView(debt.vToken.address); + expect(debtPrice).to.equal(DEBT_SPOT); + + const { liquidity } = await comptroller.getBorrowingPower(borrower.address); + expect(liquidity).to.equal(CAPACITY_AT_SPOT_100); + }); + + it("lets the dump through when bounded pricing is not enabled for the asset", async () => { + fixture.setSpotPrice(debt, parseUnits("0.5", 18)); + + // 500 of weighted collateral less 200 of debt. + const { liquidity } = await comptroller.getBorrowingPower(borrower.address); + expect(liquidity).to.equal(parseUnits("300", 18)); + }); + }); + + // The property the whole design rests on: bounding must never reach the numbers that route a liquidation. + describe("liquidation-threshold path", () => { + it("prices collateral and debt at spot while the collateral price is protected", async () => { + await configureBoundedPricing(boundedOracle, collateral.underlying, { + triggerThreshold: TRIGGER_THRESHOLD, + }); + fixture.setSpotPrice(collateral, PUMPED_SPOT); + await boundedOracle.updateProtectionState(collateral.vToken.address); + expect(await boundedOracle.currentlyUsingProtectedPrice(collateral.underlying)).to.equal(true); + + const { liquidity, shortfall } = await comptroller.getAccountLiquidity(borrower.address); + expect(liquidity).to.equal(LIQUIDATION_VIEW_AT_SPOT_150); + expect(shortfall).to.equal(0); + }); + + it("reports the same numbers with the asset unprotected, so protection cannot move a liquidation", async () => { + fixture.setSpotPrice(collateral, PUMPED_SPOT); + + const { liquidity, shortfall } = await comptroller.getAccountLiquidity(borrower.address); + expect(liquidity).to.equal(LIQUIDATION_VIEW_AT_SPOT_150); + expect(shortfall).to.equal(0); + }); + }); + + describe("hypothetical redeem", () => { + const redeemTokens = parseUnits("4", 18); + + it("restricts a redeem while the collateral price is bounded", async () => { + await configureBoundedPricing(boundedOracle, collateral.underlying, { + triggerThreshold: TRIGGER_THRESHOLD, + }); + fixture.setSpotPrice(collateral, PUMPED_SPOT); + + // The redeem effect uses the same bounded collateral price as the collateral itself: 0.5 * 100 * 4 = 200 + // against 500 of weighted collateral and 400 of debt, so the account is 100 short. + const { liquidity, shortfall } = await comptroller.getHypotheticalAccountLiquidity( + borrower.address, + collateral.vToken.address, + redeemTokens, + 0, + ); + expect(shortfall).to.equal(parseUnits("100", 18)); + expect(liquidity).to.equal(0); + }); + + it("allows the same redeem when the price is not bounded, proving bounding only ever restricts", async () => { + fixture.setSpotPrice(collateral, PUMPED_SPOT); + + // 0.5 * 150 * 4 = 300 of effect against 750 of weighted collateral and 400 of debt. + const { liquidity, shortfall } = await comptroller.getHypotheticalAccountLiquidity( + borrower.address, + collateral.vToken.address, + redeemTokens, + 0, + ); + expect(liquidity).to.equal(parseUnits("50", 18)); + expect(shortfall).to.equal(0); + }); + }); + + describe("latching", () => { + // Once the window spans 100 to 150, the trigger comparison is + // spot > windowMin * 1.2 (= 120) || spot < windowMax * 0.8 (= 120) + // so every price except exactly 120 re-triggers on its own. 120 is therefore the single spot at which the + // stored flag is the only thing that can still bound the price, which is what isolates the latch. + const NEUTRAL_SPOT = parseUnits("120", 18); + + beforeEach(async () => { + await configureBoundedPricing(boundedOracle, collateral.underlying, { + triggerThreshold: TRIGGER_THRESHOLD, + }); + }); + + it("stays protected after the price returns to a level that would not trigger on its own", async () => { + fixture.setSpotPrice(collateral, PUMPED_SPOT); + await boundedOracle.updateProtectionState(collateral.vToken.address); + fixture.setSpotPrice(collateral, NEUTRAL_SPOT); + + expect(await boundedOracle.currentlyUsingProtectedPrice(collateral.underlying)).to.equal(true); + const [collateralPrice, debtPrice] = await boundedOracle.getBoundedPricesView(collateral.vToken.address); + expect(collateralPrice).to.equal(COLLATERAL_SPOT); + expect(debtPrice).to.equal(PUMPED_SPOT); + + // Capacity stays at the protected figure rather than the 200 that 120 of unbounded spot would give. + const { liquidity } = await comptroller.getBorrowingPower(borrower.address); + expect(liquidity).to.equal(CAPACITY_AT_SPOT_100); + }); + + it("prices at spot at the same level when the deviation was never recorded", async () => { + fixture.setSpotPrice(collateral, NEUTRAL_SPOT); + + expect(await boundedOracle.currentlyUsingProtectedPrice(collateral.underlying)).to.equal(false); + // 0.5 * 120 * 10 = 600, less 400. + const { liquidity } = await comptroller.getBorrowingPower(borrower.address); + expect(liquidity).to.equal(parseUnits("200", 18)); + }); + }); + + describe("transient cache", () => { + // `updateProtectionState` caches the resolved pair for the rest of the transaction, and the view falls back + // to recomputing on a miss. Both routes have to agree, or a borrow would be priced differently from the + // view that quoted it. + it("resolves to the same capacity whether or not the pair was cached first", async () => { + await configureBoundedPricing(boundedOracle, collateral.underlying, { + triggerThreshold: TRIGGER_THRESHOLD, + caching: true, + }); + fixture.setSpotPrice(collateral, PUMPED_SPOT); + + const beforeCaching = await comptroller.getBorrowingPower(borrower.address); + await boundedOracle.updateProtectionState(collateral.vToken.address); + const afterCaching = await comptroller.getBorrowingPower(borrower.address); + + expect(beforeCaching.liquidity).to.equal(CAPACITY_AT_SPOT_100); + expect(afterCaching.liquidity).to.equal(CAPACITY_AT_SPOT_100); + }); + + it("resolves identically with caching disabled for the asset", async () => { + await configureBoundedPricing(boundedOracle, collateral.underlying, { + triggerThreshold: TRIGGER_THRESHOLD, + caching: false, + }); + fixture.setSpotPrice(collateral, PUMPED_SPOT); + await boundedOracle.updateProtectionState(collateral.vToken.address); + + const { liquidity } = await comptroller.getBorrowingPower(borrower.address); + expect(liquidity).to.equal(CAPACITY_AT_SPOT_100); + }); + }); + + it("prices an asset the oracle holds no configuration for at spot on both legs", async () => { + const [collateralPrice, debtPrice] = await boundedOracle.getBoundedPricesView(collateral.vToken.address); + + expect(collateralPrice).to.equal(COLLATERAL_SPOT); + expect(debtPrice).to.equal(COLLATERAL_SPOT); + }); + }); + + // Call counts need a fake: the point is which hooks reach the oracle, not what it answers. + describe("protection state pre-pass", () => { + let boundedOracle: FakeContract; + + // Weights on both markets and a heavily over-collateralised position, so the liquidity check always passes + // and the only thing these tests observe is which hooks reach the oracle. + async function prePassFixture(): Promise { + const f = await deploySpokeComptroller(); + for (const market of f.markets) { + await setRiskWeights(f.comptroller, market, COLLATERAL_FACTOR, LIQUIDATION_THRESHOLD); + } + return f; + } + + beforeEach(async () => { + [, , borrower] = await ethers.getSigners(); + fixture = await loadFixture(prePassFixture); + fixture.resetPrices(); + ({ comptroller } = fixture); + [collateral, debt] = fixture.markets; + boundedOracle = fixture.boundedOracle as FakeContract; + resetFakeHistory(fixture); + // Collateral in both markets so that exiting either one still leaves the small debt covered; otherwise + // `exitMarket` reverts on the liquidity check before it can be observed reaching the oracle. + await givePosition(comptroller, borrower, [ + { market: collateral, collateral: parseUnits("1000", 18) }, + { market: debt, collateral: parseUnits("1000", 18), borrow: parseUnits("1", 18) }, + ]); + await setBalance(debt.vToken.address, parseEther("1")); + boundedOracle.updateProtectionState.reset(); + }); + + it("runs once per entered market on borrow", async () => { + await comptroller + .connect(debt.vToken.wallet) + .preBorrowHook(debt.vToken.address, borrower.address, parseUnits("1", 18)); + + expect(boundedOracle.updateProtectionState).to.have.callCount(2); + expect(boundedOracle.updateProtectionState).to.have.been.calledWith(collateral.vToken.address); + expect(boundedOracle.updateProtectionState).to.have.been.calledWith(debt.vToken.address); + }); + + it("runs on redeem", async () => { + await comptroller.preRedeemHook(collateral.vToken.address, borrower.address, parseUnits("1", 18)); + + expect(boundedOracle.updateProtectionState).to.have.callCount(2); + }); + + it("runs on transfer, which reaches the same redeem check", async () => { + const [, recipient] = await ethers.getSigners(); + await comptroller.preTransferHook( + collateral.vToken.address, + borrower.address, + recipient.address, + parseUnits("1", 18), + ); + + expect(boundedOracle.updateProtectionState).to.have.callCount(2); + }); + + it("runs on exitMarket, which also reaches the redeem check", async () => { + await comptroller.connect(borrower).exitMarket(collateral.vToken.address); + + expect(boundedOracle.updateProtectionState).to.have.callCount(2); + }); + + it("never runs on any path that routes a liquidation", async () => { + const [, liquidator] = await ethers.getSigners(); + // 1000 of collateral in each market at a price of 1, against a minimum of 100. + const totalCollateral = parseUnits("2000", 18); + const minLiquidatableCollateral = parseUnits("100", 18); + + // Each of these reverts only *after* computing a liquidity snapshot, so the snapshot loop really did run on + // every one of them. Asserting the specific error is what proves that, rather than swallowing the revert and + // learning nothing about how far the call got. + await expect( + comptroller.preLiquidateHook( + debt.vToken.address, + collateral.vToken.address, + borrower.address, + parseUnits("1", 18), + false, + ), + ).to.be.revertedWithCustomError(comptroller, "InsufficientShortfall"); + await expect(comptroller.connect(liquidator).healAccount(borrower.address)) + .to.be.revertedWithCustomError(comptroller, "CollateralExceedsThreshold") + .withArgs(minLiquidatableCollateral, totalCollateral); + await expect(comptroller.connect(liquidator).liquidateAccount(borrower.address, [])) + .to.be.revertedWithCustomError(comptroller, "CollateralExceedsThreshold") + .withArgs(minLiquidatableCollateral, totalCollateral); + await comptroller.preSeizeHook( + collateral.vToken.address, + debt.vToken.address, + liquidator.address, + borrower.address, + ); + + expect(boundedOracle.updateProtectionState).to.have.callCount(0); + }); + + it("never runs on mint or repay", async () => { + await comptroller.preMintHook(collateral.vToken.address, borrower.address, parseUnits("1", 18)); + await comptroller.preRepayHook(debt.vToken.address, borrower.address); + + expect(boundedOracle.updateProtectionState).to.have.callCount(0); + }); + }); + + describe("with no bounded oracle set", () => { + // `_safeGetUnderlyingPrices` calls the zero address, which returns no data, and decoding the missing return values + // reverts. Borrowing capacity therefore fails closed rather than falling back to unbounded spot. + it("reverts the borrowing-power path once an account holds a position", async () => { + const f = await deploySpokeComptroller({ setBoundedOracle: false }); + const [, , account] = await ethers.getSigners(); + await setRiskWeights(f.comptroller, f.markets[0], COLLATERAL_FACTOR, LIQUIDATION_THRESHOLD); + await givePosition(f.comptroller, account, [{ market: f.markets[0], collateral: COLLATERAL_BALANCE }]); + + expect(await f.comptroller.deviationBoundedOracle()).to.equal(constants.AddressZero); + await expect(f.comptroller.getBorrowingPower(account.address)).to.be.reverted; + }); + + it("still serves the liquidation-threshold path, which never reads the bounded oracle", async () => { + const f = await deploySpokeComptroller({ setBoundedOracle: false, spotPrice: COLLATERAL_SPOT }); + const [, , account] = await ethers.getSigners(); + await setRiskWeights(f.comptroller, f.markets[0], COLLATERAL_FACTOR, LIQUIDATION_THRESHOLD); + await givePosition(f.comptroller, account, [{ market: f.markets[0], collateral: COLLATERAL_BALANCE }]); + + // 0.8 * 100 * 10, with no debt. + const { liquidity, shortfall } = await f.comptroller.getAccountLiquidity(account.address); + expect(liquidity).to.equal(parseUnits("800", 18)); + expect(shortfall).to.equal(0); + }); + + it("answers the borrowing-power path for an account in no markets, so listing is not blocked", async () => { + const f = await deploySpokeComptroller({ setBoundedOracle: false }); + const [, , account] = await ethers.getSigners(); + + const { liquidity, shortfall } = await f.comptroller.getBorrowingPower(account.address); + expect(liquidity).to.equal(0); + expect(shortfall).to.equal(0); + }); + }); + + describe("setDeviationBoundedOracle", () => { + beforeEach(async () => { + fixture = await loadFixture(deploySpokeComptroller); + ({ comptroller } = fixture); + }); + + it("rejects a caller that is not the owner", async () => { + const [, stranger] = await ethers.getSigners(); + + await expect(comptroller.connect(stranger).setDeviationBoundedOracle(stranger.address)).to.be.revertedWith( + "Ownable: caller is not the owner", + ); + }); + + it("rejects the zero address", async () => { + await expect(comptroller.setDeviationBoundedOracle(constants.AddressZero)).to.be.revertedWithCustomError( + comptroller, + "ZeroAddressNotAllowed", + ); + }); + + it("stores the new oracle and reports the one it replaced", async () => { + const previous = await comptroller.deviationBoundedOracle(); + const [, , , replacement] = await ethers.getSigners(); + + await expect(comptroller.setDeviationBoundedOracle(replacement.address)) + .to.emit(comptroller, "NewDeviationBoundedOracle") + .withArgs(previous, replacement.address); + expect(await comptroller.deviationBoundedOracle()).to.equal(replacement.address); + }); + }); +}); diff --git a/tests/hardhat/Spoke/deployment.ts b/tests/hardhat/Spoke/deployment.ts new file mode 100644 index 000000000..74f45e53e --- /dev/null +++ b/tests/hardhat/Spoke/deployment.ts @@ -0,0 +1,202 @@ +import { expect } from "chai"; +import { artifacts, deployments, ethers, getNamedAccounts } from "hardhat"; + +import { getBlockOrTimestampBasedDeploymentInfo } from "../../../helpers/deploymentUtils"; +import { getRateModelName, getRateModelParams } from "../../../helpers/rateModelHelpers"; +import { getSpokePoolConfig } from "../../../helpers/spokeDeploymentConfig"; + +const EIP_170_LIMIT = 24576; +const BEACON_ABI = ["function implementation() view returns (address)", "function owner() view returns (address)"]; + +// `bytes32(uint256(keccak256("eip1967.proxy.beacon")) - 1)`, where a `BeaconProxy` keeps the beacon it delegates to. +// There is no getter for it, and it is the only place the market records which beacon it will follow through upgrades. +const EIP_1967_BEACON_SLOT = "0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50"; + +// The markets the deploy scripts build on this network. `009-deploy-vtokens.ts` never sees these, which is why they +// live outside `deploymentConfig.ts`. +const spokeMarkets = getSpokePoolConfig("hardhat")?.vtokens ?? []; +const timeManagerParams = getBlockOrTimestampBasedDeploymentInfo("hardhat"); + +async function spokeComptroller() { + return ethers.getContractAt("SpokeComptroller", (await deployments.get("Comptroller_HubSpoke")).address); +} + +describe("SpokeComptroller: deployment", function () { + // The hardhat-deploy fixture stands up the whole isolated-pools deployment, which runs well past mocha's 40s + // default when the full suite shares the process. Same allowance the integration suite uses. + this.timeout(500000); + + before(async () => { + await deployments.fixture(["MockTokens", "OracleDeploy", "Oracle", "il", "HubSpoke"]); + }); + + it("wires the spoke to its own beacon and leaves the shared one alone", async () => { + const spokeBeacon = await ethers.getContractAt( + BEACON_ABI, + ( + await deployments.get("SpokeComptrollerBeacon") + ).address, + ); + const sharedBeacon = await ethers.getContractAt(BEACON_ABI, (await deployments.get("ComptrollerBeacon")).address); + + const spokeImpl = (await deployments.get("SpokeComptrollerImpl")).address; + const sharedImpl = (await deployments.get("ComptrollerImpl")).address; + + expect(await spokeBeacon.implementation()).to.equal(spokeImpl); + // The shared beacon every other pool in this repo upgrades through must be untouched. + expect(await sharedBeacon.implementation()).to.equal(sharedImpl); + expect(spokeImpl).to.not.equal(sharedImpl); + expect(spokeBeacon.address).to.not.equal(sharedBeacon.address); + }); + + it("gives the spoke markets a VToken beacon of their own", async () => { + const spokeBeacon = await ethers.getContractAt(BEACON_ABI, (await deployments.get("SpokeVTokenBeacon")).address); + const sharedBeacon = await ethers.getContractAt(BEACON_ABI, (await deployments.get("VTokenBeacon")).address); + + const spokeImpl = (await deployments.get("SpokeVTokenImpl")).address; + const sharedImpl = (await deployments.get("VTokenImpl")).address; + + expect(await spokeBeacon.implementation()).to.equal(spokeImpl); + // `upgradeTo` moves every proxy behind a beacon at once, so sharing this one would tie a VToken change for the + // spoke pool to every isolated market on the chain, in both directions. + expect(await sharedBeacon.implementation()).to.equal(sharedImpl); + expect(spokeImpl).to.not.equal(sharedImpl); + expect(spokeBeacon.address).to.not.equal(sharedBeacon.address); + }); + + it("hands the VToken beacon to the configured owner inside the deployment run", async () => { + const { deployer } = await getNamedAccounts(); + const beacon = await ethers.getContractAt(BEACON_ABI, (await deployments.get("SpokeVTokenBeacon")).address); + + // `UpgradeableBeacon` is plain `Ownable`, so unlike the comptroller there is nothing left for the VIP to accept. + // No timelock is configured on the hardhat network, so the deployer is the expected owner here. + expect(await beacon.owner()).to.equal(deployer); + }); + + it("builds every configured market behind the spoke VToken beacon", async () => { + const spokeBeacon = (await deployments.get("SpokeVTokenBeacon")).address; + const sharedBeacon = (await deployments.get("VTokenBeacon")).address; + const comptroller = await spokeComptroller(); + + expect(spokeMarkets, "no spoke markets configured for this network").to.not.have.lengthOf(0); + for (const { symbol } of spokeMarkets) { + const market = (await deployments.get(`VToken_${symbol}`)).address; + const slot = await ethers.provider.getStorageAt(market, EIP_1967_BEACON_SLOT); + const beacon = ethers.utils.getAddress(ethers.utils.hexDataSlice(slot, 12)); + + // The whole point of 027 is decided here: a market on the shared beacon could only take a VToken change that + // every isolated market on the chain takes with it, and the other way round. + expect(beacon, `${symbol} beacon`).to.equal(spokeBeacon); + expect(beacon, `${symbol} beacon`).to.not.equal(sharedBeacon); + expect(await (await ethers.getContractAt("VToken", market)).comptroller()).to.equal(comptroller.address); + } + }); + + it("initializes each market with its configured token metadata and rate model", async () => { + for (const config of spokeMarkets) { + const market = await ethers.getContractAt("VToken", (await deployments.get(`VToken_${config.symbol}`)).address); + + expect(await market.name()).to.equal(config.name); + expect(await market.symbol()).to.equal(config.symbol); + expect(await market.decimals()).to.equal(8); + expect(await market.reserveFactorMantissa()).to.equal(config.reserveFactor); + + // The rate model name is a pure function of the curve, so resolving it from the same config the script read is + // what proves the market took the configured curve rather than whichever model happened to be deployed first. + const rateModelName = getRateModelName(getRateModelParams(config), timeManagerParams); + expect(await market.interestRateModel()).to.equal((await deployments.get(rateModelName)).address); + } + }); + + it("binds the spoke to its own pool registry, not the isolated-pools one", async () => { + // The registry is the directory every consumer iterates to answer "which pools exist". Sharing it would hand the + // indexer, the frontend and the risk tooling a pool whose supply, borrow and liquidation sides are all restricted. + const spokeRegistry = (await deployments.get("SpokePoolRegistry")).address; + const isolatedRegistry = (await deployments.get("PoolRegistry")).address; + + expect(spokeRegistry).to.not.equal(isolatedRegistry); + expect(await (await spokeComptroller()).poolRegistry()).to.equal(spokeRegistry); + + // Same implementation, so the only thing keeping the two directories apart is that they are separate instances. + const registry = await ethers.getContract("SpokePoolRegistry"); + expect(await registry.accessControlManager()).to.equal((await deployments.get("AccessControlManager")).address); + expect(await registry.getAllPools()).to.have.lengthOf(0); + }); + + it("deploys the proxy as a SpokeComptroller, initialized, owned by the deployer", async () => { + const { deployer } = await getNamedAccounts(); + const comptroller = await spokeComptroller(); + + expect(await comptroller.poolRegistry()).to.equal((await deployments.get("SpokePoolRegistry")).address); + expect(await comptroller.accessControlManager()).to.equal((await deployments.get("AccessControlManager")).address); + expect(await comptroller.maxLoopsLimit()).to.equal(100); + expect(await comptroller.owner()).to.equal(deployer); + + // Spoke-only surface, which proves the proxy runs the fork rather than the shared implementation. + expect(await comptroller.isLiquidationAllowlistEnabled()).to.equal(false); + expect(await comptroller.deviationBoundedOracle()).to.equal(ethers.constants.AddressZero); + }); + + it("leaves the pool unregistered and unconfigured, which is the listing VIP's job", async () => { + const comptroller = await spokeComptroller(); + + for (const name of ["SpokePoolRegistry", "PoolRegistry"]) { + const registry = await ethers.getContract(name); + const registered = (await registry.getAllPools()).map((p: { comptroller: string }) => p.comptroller); + expect(registered, `${name} should not hold the spoke pool`).to.not.include(comptroller.address); + } + + expect(await comptroller.oracle()).to.equal(ethers.constants.AddressZero); + expect(await comptroller.closeFactorMantissa()).to.equal(0); + expect(await comptroller.liquidationIncentiveMantissa()).to.equal(0); + expect(await comptroller.minLiquidatableCollateral()).to.equal(0); + expect((await comptroller.getAllMarkets()).length).to.equal(0); + }); + + it("answers borrowing-power reads for an account in no markets", async () => { + const comptroller = await spokeComptroller(); + const { deployer } = await getNamedAccounts(); + + // Nothing is entered, so the snapshot loop never runs and never reaches the unset bounded oracle. The + // fail-closed guarantee comes from the per-market price read, not from a check on the oracle address, and this + // documents that listing a market is what makes the oracle mandatory. + const [, liquidity, shortfall] = await comptroller.getBorrowingPower(deployer); + expect(liquidity).to.equal(0); + expect(shortfall).to.equal(0); + }); + + describe("contract size", () => { + // Two traps make this awkward to assert. The hardhat network sets `allowUnlimitedContractSize`, so an oversized + // contract deploys without complaint and the check has to read the artifact instead. And hardhat.config.ts sets + // the optimizer's `details.yul` to `!process.env.CI`, so a CI build produces roughly 1.1 KB more bytecode than + // a production build and would fail this on perfectly good code. The assertion therefore only runs when the Yul + // optimizer is on, which is the setting production deploys use. + // + // This is a backstop, not the real gate. A size check that runs under production settings belongs in CI. + const yulEnabled = !process.env.CI; + + it("keeps SpokeComptroller under the EIP-170 limit under production optimizer settings", async function () { + if (!yulEnabled) { + this.skip(); + } + const { deployedBytecode } = await artifacts.readArtifact("SpokeComptroller"); + const size = (deployedBytecode.length - 2) / 2; + + expect(size, `SpokeComptroller is ${size} bytes, ${size - EIP_170_LIMIT} over the limit`).to.be.at.most( + EIP_170_LIMIT, + ); + }); + + it("does not push the shared Comptroller over the limit either", async function () { + if (!yulEnabled) { + this.skip(); + } + // The per-contract optimizer override in hardhat.config.ts applies to SpokeComptroller only. If it ever + // leaked to the shared implementation this would catch the regression. + const { deployedBytecode } = await artifacts.readArtifact("Comptroller"); + const size = (deployedBytecode.length - 2) / 2; + + expect(size).to.be.at.most(EIP_170_LIMIT); + }); + }); +}); diff --git a/tests/hardhat/Spoke/errorsAndEvents.ts b/tests/hardhat/Spoke/errorsAndEvents.ts new file mode 100644 index 000000000..abef0275e --- /dev/null +++ b/tests/hardhat/Spoke/errorsAndEvents.ts @@ -0,0 +1,351 @@ +import { MockContract, smock } from "@defi-wonderland/smock"; +import { loadFixture } from "@nomicfoundation/hardhat-network-helpers"; +import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; +import chai from "chai"; +import { constants } from "ethers"; +import { parseUnits } from "ethers/lib/utils"; +import { readFileSync } from "fs"; +import { ethers } from "hardhat"; +import { resolve } from "path"; + +import { SpokeComptroller } from "../../../typechain"; +import { Action, ONE, SpokeFixture, TestMarket, deploySpokeComptroller } from "./fixtures"; + +const { expect } = chai; +chai.use(smock.matchers); + +const MIN_CLOSE_FACTOR = parseUnits("0.05", 18); +const MAX_CLOSE_FACTOR = parseUnits("0.9", 18); +const MAX_COLLATERAL_FACTOR = parseUnits("0.95", 18); + +describe("SpokeComptroller: errors, events and access control", () => { + let fixture: SpokeFixture; + let comptroller: MockContract; + let marketA: TestMarket; + let marketB: TestMarket; + let stranger: SignerWithAddress; + + beforeEach(async () => { + [, stranger] = await ethers.getSigners(); + fixture = await loadFixture(deploySpokeComptroller); + fixture.resetPrices(); + ({ comptroller } = fixture); + [marketA, marketB] = fixture.markets; + }); + + // Upstream `Comptroller` rejects each of these with a revert string. The fork replaced every one with a custom + // error, which is both cheaper and smaller. These pin the replacement, so a re-sync against upstream cannot + // quietly reintroduce a string or change which error a condition reports. + describe("conditions upstream reported with revert strings", () => { + it("rejects a close factor above the maximum", async () => { + await expect(comptroller.setCloseFactor(MAX_CLOSE_FACTOR.add(1))).to.be.revertedWithCustomError( + comptroller, + "InvalidCloseFactor", + ); + }); + + it("rejects a close factor below the minimum with the same error", async () => { + // Upstream distinguished the two bounds by message. The fork reports one error for both, so a caller learns + // that the value is out of range but not which end it violated. + await expect(comptroller.setCloseFactor(MIN_CLOSE_FACTOR.sub(1))).to.be.revertedWithCustomError( + comptroller, + "InvalidCloseFactor", + ); + }); + + it("accepts both bounds exactly", async () => { + await comptroller.setCloseFactor(MIN_CLOSE_FACTOR); + expect(await comptroller.closeFactorMantissa()).to.equal(MIN_CLOSE_FACTOR); + + await comptroller.setCloseFactor(MAX_CLOSE_FACTOR); + expect(await comptroller.closeFactorMantissa()).to.equal(MAX_CLOSE_FACTOR); + }); + + it("rejects a market that does not identify itself as a vToken", async () => { + marketB.vToken.isVToken.returns(false); + const unlisted = await smock.fake("VToken"); + unlisted.isVToken.returns(false); + + await expect( + comptroller.connect(fixture.poolRegistry.wallet).supportMarket(unlisted.address), + ).to.be.revertedWithCustomError(comptroller, "InvalidVToken"); + }); + + it("rejects a market that is already listed", async () => { + await expect(comptroller.connect(fixture.poolRegistry.wallet).supportMarket(marketA.vToken.address)) + .to.be.revertedWithCustomError(comptroller, "MarketAlreadyListed") + .withArgs(marketA.vToken.address); + }); + + it("rejects empty and mismatched arrays on both cap setters", async () => { + for (const call of [ + comptroller.setMarketBorrowCaps([], []), + comptroller.setMarketBorrowCaps([marketA.vToken.address], []), + comptroller.setMarketSupplyCaps([], []), + comptroller.setMarketSupplyCaps([marketA.vToken.address], []), + ]) { + await expect(call).to.be.revertedWithCustomError(comptroller, "InvalidArrayLength"); + } + }); + + it("rejects a rewards distributor the pool already has", async () => { + const distributor = await smock.fake("RewardsDistributor"); + distributor.rewardToken.returns(marketA.underlying); + await comptroller.addRewardsDistributor(distributor.address); + + await expect(comptroller.addRewardsDistributor(distributor.address)).to.be.revertedWithCustomError( + comptroller, + "RewardsDistributorAlreadyExists", + ); + }); + + it("refuses to pause an action on a market that is not listed", async () => { + await expect(comptroller.setActionsPaused([stranger.address], [Action.MINT], true)) + .to.be.revertedWithCustomError(comptroller, "MarketNotListed") + .withArgs(stranger.address); + }); + }); + + describe("collateral factor and liquidation threshold bounds", () => { + it("rejects a collateral factor above 0.95", async () => { + await expect( + comptroller.setCollateralFactor(marketA.vToken.address, MAX_COLLATERAL_FACTOR.add(1), ONE), + ).to.be.revertedWithCustomError(comptroller, "InvalidCollateralFactor"); + }); + + it("rejects a liquidation threshold above 1", async () => { + await expect( + comptroller.setCollateralFactor(marketA.vToken.address, parseUnits("0.5", 18), ONE.add(1)), + ).to.be.revertedWithCustomError(comptroller, "InvalidLiquidationThreshold"); + }); + + it("rejects a liquidation threshold below the collateral factor", async () => { + await expect( + comptroller.setCollateralFactor(marketA.vToken.address, parseUnits("0.5", 18), parseUnits("0.4", 18)), + ).to.be.revertedWithCustomError(comptroller, "InvalidLiquidationThreshold"); + }); + + it("rejects a nonzero collateral factor while the price is zero", async () => { + fixture.setSpotPrice(marketA, 0); + + await expect(comptroller.setCollateralFactor(marketA.vToken.address, parseUnits("0.5", 18), ONE)) + .to.be.revertedWithCustomError(comptroller, "PriceError") + .withArgs(marketA.vToken.address); + }); + + it("emits both events and stores both values", async () => { + const collateralFactor = parseUnits("0.5", 18); + const threshold = parseUnits("0.8", 18); + + const tx = comptroller.setCollateralFactor(marketA.vToken.address, collateralFactor, threshold); + await expect(tx) + .to.emit(comptroller, "NewCollateralFactor") + .withArgs(marketA.vToken.address, 0, collateralFactor); + await expect(tx).to.emit(comptroller, "NewLiquidationThreshold").withArgs(marketA.vToken.address, 0, threshold); + + const market = await comptroller.markets(marketA.vToken.address); + expect(market.collateralFactorMantissa).to.equal(collateralFactor); + expect(market.liquidationThresholdMantissa).to.equal(threshold); + }); + }); + + // A role string is hashed with the contract address into an ACM role, so it has to be exactly the string the + // contract passes. A mismatch means the VIP that grants the permission never matches the call, and the only way + // to catch that here is to deny the precise string and confirm the call is refused. + describe("access control role strings", () => { + const roles: { role: string; call: (c: MockContract, m: TestMarket) => Promise }[] = [ + { role: "setCloseFactor(uint256)", call: c => c.setCloseFactor(parseUnits("0.5", 18)) }, + { + role: "setCollateralFactor(address,uint256,uint256)", + call: (c, m) => c.setCollateralFactor(m.vToken.address, parseUnits("0.5", 18), ONE), + }, + { role: "setLiquidationIncentive(uint256)", call: c => c.setLiquidationIncentive(parseUnits("1.2", 18)) }, + { + role: "setMarketBorrowCaps(address[],uint256[])", + call: (c, m) => c.setMarketBorrowCaps([m.vToken.address], [ONE]), + }, + { + role: "setMarketSupplyCaps(address[],uint256[])", + call: (c, m) => c.setMarketSupplyCaps([m.vToken.address], [ONE]), + }, + { + role: "setActionsPaused(address[],uint256[],bool)", + call: (c, m) => c.setActionsPaused([m.vToken.address], [Action.MINT], true), + }, + { role: "setMinLiquidatableCollateral(uint256)", call: c => c.setMinLiquidatableCollateral(ONE) }, + { role: "unlistMarket(address)", call: (c, m) => c.unlistMarket(m.vToken.address) }, + { role: "setForcedLiquidation(address,bool)", call: (c, m) => c.setForcedLiquidation(m.vToken.address, true) }, + { + role: "setMarketLiquidationIncentive(address,uint256)", + call: (c, m) => c.setMarketLiquidationIncentive(m.vToken.address, parseUnits("1.2", 18)), + }, + { + role: "setSupplyAllowlistEnabled(address,bool)", + call: (c, m) => c.setSupplyAllowlistEnabled(m.vToken.address, true), + }, + { + role: "setAllowedSupplier(address,address,bool)", + call: (c, m) => c.setAllowedSupplier(m.vToken.address, m.vToken.address, true), + }, + { role: "setLiquidationAllowlistEnabled(bool)", call: c => c.setLiquidationAllowlistEnabled(true) }, + { + role: "setAllowedLiquidator(address,bool)", + call: (c, m) => c.setAllowedLiquidator(m.vToken.address, true), + }, + { + role: "enterMarketBehalf(address,address)", + call: (c, m) => c.enterMarketBehalf(m.vToken.address, m.vToken.address), + }, + ]; + + for (const { role, call } of roles) { + it(`gates ${role}`, async () => { + fixture.acm.isAllowedToCall.whenCalledWith(stranger.address, role).returns(false); + + await expect( + call(comptroller.connect(stranger) as MockContract, marketA), + ).to.be.revertedWithCustomError(comptroller, "Unauthorized"); + }); + } + + it("covers every role string the contract checks", () => { + // Read the strings straight out of the source rather than restating them, so adding an access-controlled + // setter without a case above fails here instead of going untested. + const source = readFileSync(resolve(__dirname, "../../../contracts/Spoke/SpokeComptroller.sol"), "utf8"); + const declared = new Set([...source.matchAll(/_checkAccessAllowed\("([^"]+)"\)/g)].map(m => m[1])); + const covered = new Set(roles.map(r => r.role)); + + expect([...declared].sort()).to.deep.equal([...covered].sort()); + }); + }); + + // The vToken calls one of these as the last step of every successful operation. All seven are declared in + // `ComptrollerInterface`, so none can be dropped, and all seven are no-ops here now that Prime is gone. + describe("post-action verify hooks", () => { + it("are all callable by anyone and change nothing", async () => { + const calls = [ + comptroller.connect(stranger).mintVerify(marketA.vToken.address, stranger.address, ONE, ONE), + comptroller.connect(stranger).redeemVerify(marketA.vToken.address, stranger.address, ONE, ONE), + comptroller.connect(stranger).borrowVerify(marketA.vToken.address, stranger.address, ONE), + comptroller + .connect(stranger) + .repayBorrowVerify(marketA.vToken.address, stranger.address, stranger.address, ONE, ONE), + comptroller + .connect(stranger) + .liquidateBorrowVerify( + marketA.vToken.address, + marketB.vToken.address, + stranger.address, + stranger.address, + ONE, + ONE, + ), + comptroller + .connect(stranger) + .seizeVerify(marketA.vToken.address, marketB.vToken.address, stranger.address, stranger.address, ONE), + comptroller.connect(stranger).transferVerify(marketA.vToken.address, stranger.address, stranger.address, ONE), + ]; + + for (const call of calls) { + const receipt = await (await call).wait(); + // A no-op must not emit anything; an event here would mean a hook grew a side effect. + expect(receipt.logs.length).to.equal(0); + } + }); + + it("leaves the market state untouched", async () => { + const before = await comptroller.markets(marketA.vToken.address); + + await comptroller.connect(stranger).mintVerify(marketA.vToken.address, stranger.address, ONE, ONE); + await comptroller + .connect(stranger) + .seizeVerify(marketA.vToken.address, marketB.vToken.address, stranger.address, stranger.address, ONE); + + const after = await comptroller.markets(marketA.vToken.address); + expect(after.isListed).to.equal(before.isListed); + expect(after.collateralFactorMantissa).to.equal(before.collateralFactorMantissa); + expect(after.liquidationThresholdMantissa).to.equal(before.liquidationThresholdMantissa); + }); + }); + + describe("Prime removal", () => { + it("exposes no prime token surface", () => { + // `setPrimeToken` and the `prime` getter existed upstream. Their absence from the ABI is what frees the + // storage slot the gap reclaims, so a re-sync that reintroduced them would break the layout note. + const names = Object.keys(comptroller.interface.functions).map(f => f.split("(")[0]); + expect(names).to.not.include("setPrimeToken"); + expect(names).to.not.include("prime"); + }); + }); + + describe("unlistMarket", () => { + it("requires every action to be paused first", async () => { + await expect(comptroller.unlistMarket(marketA.vToken.address)).to.be.revertedWithCustomError( + comptroller, + "BorrowActionNotPaused", + ); + }); + + it("requires the caps and collateral factor to be cleared", async () => { + const actions = Object.values(Action); + await comptroller.setActionsPaused([marketA.vToken.address], actions, true); + + // Supply and borrow caps are at max from the fixture. + await expect(comptroller.unlistMarket(marketA.vToken.address)).to.be.revertedWithCustomError( + comptroller, + "BorrowCapIsNotZero", + ); + + await comptroller.setMarketBorrowCaps([marketA.vToken.address], [0]); + await expect(comptroller.unlistMarket(marketA.vToken.address)).to.be.revertedWithCustomError( + comptroller, + "SupplyCapIsNotZero", + ); + + await comptroller.setMarketSupplyCaps([marketA.vToken.address], [0]); + await comptroller.setCollateralFactor(marketA.vToken.address, parseUnits("0.5", 18), ONE); + await expect(comptroller.unlistMarket(marketA.vToken.address)).to.be.revertedWithCustomError( + comptroller, + "CollateralFactorIsNotZero", + ); + }); + + it("unlists the market and emits the event once every precondition holds", async () => { + await comptroller.setActionsPaused([marketA.vToken.address], Object.values(Action), true); + await comptroller.setMarketBorrowCaps([marketA.vToken.address], [0]); + await comptroller.setMarketSupplyCaps([marketA.vToken.address], [0]); + + await expect(comptroller.unlistMarket(marketA.vToken.address)) + .to.emit(comptroller, "MarketUnlisted") + .withArgs(marketA.vToken.address); + expect((await comptroller.markets(marketA.vToken.address)).isListed).to.equal(false); + }); + }); + + describe("updateDelegate", () => { + it("rejects the zero address", async () => { + await expect(comptroller.updateDelegate(constants.AddressZero, true)).to.be.revertedWithCustomError( + comptroller, + "ZeroAddressNotAllowed", + ); + }); + + it("rejects a write that does not change the value", async () => { + await comptroller.updateDelegate(stranger.address, true); + + await expect(comptroller.updateDelegate(stranger.address, true)).to.be.revertedWithCustomError( + comptroller, + "DelegationStatusUnchanged", + ); + }); + + it("stores the value and emits an event", async () => { + const [owner] = await ethers.getSigners(); + + await expect(comptroller.updateDelegate(stranger.address, true)) + .to.emit(comptroller, "DelegateUpdated") + .withArgs(owner.address, stranger.address, true); + expect(await comptroller.approvedDelegates(owner.address, stranger.address)).to.equal(true); + }); + }); +}); diff --git a/tests/hardhat/Spoke/fixtures.ts b/tests/hardhat/Spoke/fixtures.ts new file mode 100644 index 000000000..f45e0a0cc --- /dev/null +++ b/tests/hardhat/Spoke/fixtures.ts @@ -0,0 +1,319 @@ +import { FakeContract, MockContract, smock } from "@defi-wonderland/smock"; +import { setBalance } from "@nomicfoundation/hardhat-network-helpers"; +import { BigNumber, BigNumberish, constants } from "ethers"; +import { parseEther, parseUnits } from "ethers/lib/utils"; +import { ethers, upgrades } from "hardhat"; + +import { + AccessControlManager, + IDeviationBoundedOracle, + IDeviationBoundedOracle__factory, + OptimizedTransparentUpgradeableProxy__factory, + PoolRegistry, + ResilientOracleInterface, + SpokeComptroller, + SpokeComptroller__factory, + VToken, +} from "../../../typechain"; + +/* eslint-disable @typescript-eslint/no-var-requires */ +// The published DeviationBoundedOracle is compiled for the Cancun EVM and uses TSTORE/TLOAD through its +// Transient library. This repo compiles at `paris`, so adding the source to `dependencyCompiler` would fail to +// compile it. The hardhat network does run Cancun, so the shipped bytecode is deployed directly instead. That +// also means these tests exercise exactly the bytecode that is deployed on chain. +const deviationBoundedOracleArtifact = require("@venusprotocol/oracle/artifacts/contracts/DeviationBoundedOracle.sol/DeviationBoundedOracle.json"); +/* eslint-enable @typescript-eslint/no-var-requires */ + +export const ONE = parseUnits("1", 18); +export const MAX_LOOPS_LIMIT = 150; + +/// Matches `enum Action` in contracts/ComptrollerInterface.sol, in declaration order. +export const Action = { + MINT: 0, + REDEEM: 1, + BORROW: 2, + REPAY: 3, + SEIZE: 4, + LIQUIDATE: 5, + TRANSFER: 6, + ENTER_MARKET: 7, + EXIT_MARKET: 8, +}; + +/// A vToken this pool lists, paired with the underlying the oracles key prices by. +export interface TestMarket { + vToken: FakeContract; + underlying: string; + /// Price `resetPrices` restores. See the note there for why a baseline is needed at all. + baselinePrice: BigNumber; +} + +export interface SpokeFixture { + acm: FakeContract; + poolRegistry: FakeContract; + oracle: FakeContract; + comptroller: MockContract; + markets: TestMarket[]; + /// The bounded oracle wired into the comptroller, or undefined when the fixture left it unset. Real when + /// `realBoundedOracle` was requested, otherwise a fake whose calls can be counted. + boundedOracle?: IDeviationBoundedOracle | FakeContract; + /// Sets a market's spot price on both oracle entry points at once. `SpokeComptroller` asks for + /// `getUnderlyingPrice(vToken)` while the deviation-bounded oracle asks for `getPrice(underlying)`; letting the + /// two drift would silently invalidate every price assertion, so nothing sets them separately. + setSpotPrice: (market: TestMarket, price: BigNumberish) => void; + /// Same, but also records the price as the market's baseline, so `resetPrices` restores it. + setBaselinePrice: (market: TestMarket, price: BigNumberish) => void; + /// Restores every market to its baseline price. + /// + /// `loadFixture` rewinds chain state but returns the same smock fakes, and a fake's configured behaviour lives + /// in JavaScript rather than in the EVM, so a price set by one test is still in place for the next one. Any + /// test that moves a price has to be followed by this, or later assertions silently measure the wrong prices. + resetPrices: () => void; +} + +export interface SpokeFixtureOptions { + /// Number of markets to list. Each gets a distinct underlying address. + marketCount?: number; + /// Pool-wide liquidation incentive. Pass null to leave it unset, which is the state of a pool that + /// `PoolRegistry.addPool` has not registered yet. + liquidationIncentive?: BigNumber | null; + minLiquidatableCollateral?: BigNumberish; + /// Leave false to test the fail-closed behaviour of an unset deviation-bounded oracle. + setBoundedOracle?: boolean; + /// When true the bounded oracle is the real contract; otherwise a fake, which is what call-count assertions need. + realBoundedOracle?: boolean; + /// Initial spot price applied to every market. + spotPrice?: BigNumberish; + /// Loop limit the pool is initialized with. It can only ever be raised, so a test that needs the guard to bite + /// has to start low. Must leave room for `marketCount`, which is bounded by the same limit. + maxLoopsLimit?: number; +} + +/// `_getUnderlyingAsset` short-circuits the configured native market to a synthetic native address, so it must +/// never collide with a vToken under test or that market's prices resolve to the wrong asset. +const UNRELATED_NATIVE_MARKET = "0x0000000000000000000000000000000000001111"; + +function underlyingAddressFor(index: number): string { + return ethers.utils.getAddress(`0x${(index + 1).toString(16).padStart(40, "0")}`); +} + +/** + * Deploys the real DeviationBoundedOracle behind a transparent proxy. + * + * The proxy admin must not be the account that later calls through the proxy: OpenZeppelin's transparent proxy + * refuses to forward calls made by its own admin. + */ +export async function deployRealBoundedOracle( + oracle: FakeContract, + acm: FakeContract, +): Promise { + const [deployer, , , proxyAdmin] = await ethers.getSigners(); + + const implementation = await new ethers.ContractFactory( + deviationBoundedOracleArtifact.abi, + deviationBoundedOracleArtifact.bytecode, + deployer, + ).deploy(oracle.address, UNRELATED_NATIVE_MARKET, constants.AddressZero); + await implementation.deployed(); + + const proxy = await new OptimizedTransparentUpgradeableProxy__factory(deployer).deploy( + implementation.address, + proxyAdmin.address, + implementation.interface.encodeFunctionData("initialize", [acm.address]), + ); + await proxy.deployed(); + + return IDeviationBoundedOracle__factory.connect(proxy.address, deployer); +} + +/** + * Registers an asset with the real bounded oracle. Seeds the price window from the current spot, so the spot price + * has to be set before this runs. + * + * @param triggerThreshold Deviation that activates protection. The oracle only accepts 5% to 50%. + * @param caching Whether the resolved pair is cached in transient storage for the rest of the transaction. + */ +export async function configureBoundedPricing( + boundedOracle: IDeviationBoundedOracle, + underlying: string, + { + triggerThreshold = parseUnits("0.2", 18), + resetThreshold = parseUnits("0.05", 18), + cooldownPeriod = 3600, + caching = true, + } = {}, +): Promise { + await boundedOracle.setTokenConfig({ + asset: underlying, + cooldownPeriod, + triggerThreshold, + resetThreshold, + enableBoundedPricing: true, + enableCaching: caching, + }); +} + +/** + * Deploys a SpokeComptroller with fake markets. Access control allows everything, so tests that care about it + * deny a specific role explicitly. + */ +export async function deploySpokeComptroller(options: SpokeFixtureOptions = {}): Promise { + const { + marketCount = 2, + liquidationIncentive = parseUnits("1.1", 18), + minLiquidatableCollateral = parseUnits("100", 18), + setBoundedOracle = true, + realBoundedOracle = false, + spotPrice = ONE, + maxLoopsLimit = MAX_LOOPS_LIMIT, + } = options; + + const poolRegistry = await smock.fake("PoolRegistry"); + const oracle = await smock.fake("ResilientOracleInterface"); + const acm = await smock.fake("AccessControlManager"); + acm.isAllowedToCall.returns(true); + + const comptrollerFactory = await smock.mock("SpokeComptroller"); + const comptroller = (await upgrades.deployProxy(comptrollerFactory, [maxLoopsLimit, acm.address], { + constructorArgs: [poolRegistry.address], + initializer: "initialize(uint256,address)", + })) as MockContract; + + await comptroller.setPriceOracle(oracle.address); + + const markets: TestMarket[] = []; + const setSpotPrice = (market: TestMarket, price: BigNumberish) => { + oracle.getUnderlyingPrice.whenCalledWith(market.vToken.address).returns(price); + oracle.getPrice.whenCalledWith(market.underlying).returns(price); + }; + const setBaselinePrice = (market: TestMarket, price: BigNumberish) => { + market.baselinePrice = BigNumber.from(price); + setSpotPrice(market, price); + }; + const resetPrices = () => { + oracle.getUnderlyingPrice.reset(); + oracle.getPrice.reset(); + for (const market of markets) { + setSpotPrice(market, market.baselinePrice); + } + }; + + await setBalance(poolRegistry.address, parseEther("1")); + for (let i = 0; i < marketCount; ++i) { + const vToken = await smock.fake("VToken"); + const market: TestMarket = { + vToken, + underlying: underlyingAddressFor(i), + baselinePrice: BigNumber.from(spotPrice), + }; + + vToken.isVToken.returns(true); + vToken.comptroller.returns(comptroller.address); + vToken.underlying.returns(market.underlying); + vToken.exchangeRateStored.returns(ONE); + // [error, vTokenBalance, borrowBalance, exchangeRate]: no position until a test gives one. + vToken.getAccountSnapshot.returns([0, 0, 0, ONE]); + vToken.borrowBalanceStored.returns(0); + vToken.totalSupply.returns(0); + vToken.totalBorrows.returns(0); + + setSpotPrice(market, spotPrice); + await comptroller.connect(poolRegistry.wallet).supportMarket(vToken.address); + // Uncapped on both sides. Caps default to zero, which blocks every mint and borrow, so leaving them would make + // the cap branch decide outcomes that tests are attributing to something else. + await comptroller.setMarketSupplyCaps([vToken.address], [constants.MaxUint256]); + await comptroller.setMarketBorrowCaps([vToken.address], [constants.MaxUint256]); + markets.push(market); + } + + let boundedOracle: IDeviationBoundedOracle | FakeContract | undefined; + if (setBoundedOracle) { + if (realBoundedOracle) { + boundedOracle = await deployRealBoundedOracle(oracle, acm); + } else { + const fake = await smock.fake("IDeviationBoundedOracle"); + // Outside protection the real oracle returns spot on both legs; the fake has to match that. + fake.getBoundedPricesView.returns([spotPrice, spotPrice]); + boundedOracle = fake; + } + await comptroller.setDeviationBoundedOracle(boundedOracle.address); + } + + if (liquidationIncentive !== null) { + await comptroller.setLiquidationIncentive(liquidationIncentive); + } + await comptroller.setMinLiquidatableCollateral(minLiquidatableCollateral); + + return { + acm, + poolRegistry, + oracle, + comptroller, + markets, + boundedOracle, + setSpotPrice, + setBaselinePrice, + resetPrices, + }; +} + +/** + * Sets a market's risk weights. `setCollateralFactor` rejects a liquidation threshold below the collateral factor + * and rejects a nonzero collateral factor while the price is zero, so the price must already be set. + */ +export async function setRiskWeights( + comptroller: MockContract, + market: TestMarket, + collateralFactor: BigNumberish, + liquidationThreshold: BigNumberish, +): Promise { + await comptroller.setCollateralFactor(market.vToken.address, collateralFactor, liquidationThreshold); +} + +/** + * Gives an account a position in a market and enters it. A borrower is a member of every market it borrows from, + * including ones it holds no collateral in, which is what `preBorrowHook` does. + */ +export async function givePosition( + comptroller: MockContract, + account: { address: string }, + positions: { market: TestMarket; collateral?: BigNumberish; borrow?: BigNumberish }[], +): Promise { + const signer = await ethers.getSigner(account.address); + for (const { market, collateral = 0, borrow = 0 } of positions) { + market.vToken.getAccountSnapshot.whenCalledWith(account.address).returns([0, collateral, borrow, ONE]); + market.vToken.borrowBalanceStored.whenCalledWith(account.address).returns(borrow); + market.vToken.balanceOf.whenCalledWith(account.address).returns(collateral); + } + await comptroller.connect(signer).enterMarkets(positions.map(p => p.market.vToken.address)); +} + +/** + * Takes a market out of the pool. `unlistMarket` refuses while any action is still live or any risk parameter is + * still set, so the caller would otherwise have to reproduce all three preconditions. + */ +export async function unlistMarket(comptroller: MockContract, market: TestMarket): Promise { + await comptroller.setActionsPaused([market.vToken.address], Object.values(Action), true); + await comptroller.setMarketBorrowCaps([market.vToken.address], [0]); + await comptroller.setMarketSupplyCaps([market.vToken.address], [0]); + await comptroller.setCollateralFactor(market.vToken.address, 0, 0); + await comptroller.unlistMarket(market.vToken.address); +} + +/** + * Clears the recorded call history of every fake in the fixture. + * + * `loadFixture` rewinds chain state but returns the same fake objects, so call history survives into the next + * test. Any assertion on a call count is wrong without this. + */ +export function resetFakeHistory(fixture: SpokeFixture): void { + fixture.oracle.updatePrice.reset(); + fixture.acm.isAllowedToCall.reset(); + fixture.acm.isAllowedToCall.returns(true); + for (const { vToken } of fixture.markets) { + vToken.seize.reset(); + vToken.healBorrow.reset(); + vToken.accrueInterest.reset(); + vToken.forceLiquidateBorrow.reset(); + } +} diff --git a/tests/hardhat/Spoke/hooks.ts b/tests/hardhat/Spoke/hooks.ts new file mode 100644 index 000000000..71c625a2a --- /dev/null +++ b/tests/hardhat/Spoke/hooks.ts @@ -0,0 +1,350 @@ +import { MockContract, smock } from "@defi-wonderland/smock"; +import { loadFixture, setBalance } from "@nomicfoundation/hardhat-network-helpers"; +import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; +import chai from "chai"; +import { constants } from "ethers"; +import { parseEther, parseUnits } from "ethers/lib/utils"; +import { ethers } from "hardhat"; + +import { SpokeComptroller } from "../../../typechain"; +import { + SpokeFixture, + TestMarket, + deploySpokeComptroller, + givePosition, + resetFakeHistory, + setRiskWeights, +} from "./fixtures"; + +const { expect } = chai; +chai.use(smock.matchers); + +const COLLATERAL = parseUnits("1000", 18); +const COLLATERAL_FACTOR = parseUnits("0.5", 18); +const LIQUIDATION_THRESHOLD = parseUnits("0.8", 18); +const BORROW_AMOUNT = parseUnits("100", 18); +const CLOSE_FACTOR = parseUnits("0.5", 18); + +describe("SpokeComptroller: policy hooks", () => { + let fixture: SpokeFixture; + let comptroller: MockContract; + let collateral: TestMarket; + let debt: TestMarket; + let borrower: SignerWithAddress; + let stranger: SignerWithAddress; + + /// A borrower with collateral worth 1000 in one market and nothing borrowed yet. The collateral factor leaves + /// 500 of borrowing power, so the amounts below stay clear of the liquidity check unless a test aims at it. + async function collateralizedBorrower(): Promise { + await setRiskWeights(comptroller, collateral, COLLATERAL_FACTOR, LIQUIDATION_THRESHOLD); + await setRiskWeights(comptroller, debt, COLLATERAL_FACTOR, LIQUIDATION_THRESHOLD); + await givePosition(comptroller, borrower, [{ market: collateral, collateral: COLLATERAL }]); + } + + beforeEach(async () => { + [, borrower, stranger] = await ethers.getSigners(); + fixture = await loadFixture(deploySpokeComptroller); + fixture.resetPrices(); + resetFakeHistory(fixture); + ({ comptroller } = fixture); + [collateral, debt] = fixture.markets; + // The fakes stand in for markets that call the hooks, and a fake needs gas to send a transaction. + for (const market of fixture.markets) { + await setBalance(market.vToken.address, parseEther("1")); + } + }); + + // Two hooks refuse to serve an arbitrary caller, and in both cases the check is what keeps an outsider from + // writing to storage on someone else's behalf. Nothing else in the suite calls them with the wrong sender. + describe("caller checks", () => { + it("lets a market enter a borrower that is not yet in it", async () => { + await collateralizedBorrower(); + + await expect( + comptroller.connect(debt.vToken.wallet).preBorrowHook(debt.vToken.address, borrower.address, BORROW_AMOUNT), + ) + .to.emit(comptroller, "MarketEntered") + .withArgs(debt.vToken.address, borrower.address); + + expect(await comptroller.checkMembership(borrower.address, debt.vToken.address)).to.equal(true); + }); + + it("rejects any other caller borrowing for an account that is not in the market", async () => { + await collateralizedBorrower(); + + await expect(comptroller.connect(stranger).preBorrowHook(debt.vToken.address, borrower.address, BORROW_AMOUNT)) + .to.be.revertedWithCustomError(comptroller, "UnexpectedSender") + .withArgs(debt.vToken.address, stranger.address); + }); + + it("accepts any caller once the account is already in the market", async () => { + // The membership branch is the only thing the sender check guards, so an entered account makes the hook + // callable by anyone. Harmless, because the hook writes nothing else. + await collateralizedBorrower(); + await comptroller.connect(borrower).enterMarkets([debt.vToken.address]); + + await expect( + comptroller.connect(stranger).callStatic.preBorrowHook(debt.vToken.address, borrower.address, BORROW_AMOUNT), + ).to.not.be.reverted; + }); + + it("restricts listing a market to the pool registry", async () => { + const unlisted = await smock.fake("VToken"); + unlisted.isVToken.returns(true); + + await expect(comptroller.connect(stranger).supportMarket(unlisted.address)) + .to.be.revertedWithCustomError(comptroller, "UnexpectedSender") + .withArgs(fixture.poolRegistry.address, stranger.address); + }); + }); + + describe("borrow caps", () => { + const borrow = (amount = BORROW_AMOUNT) => + comptroller.connect(borrower).callStatic.preBorrowHook(debt.vToken.address, borrower.address, amount); + + beforeEach(async () => { + await collateralizedBorrower(); + await comptroller.connect(borrower).enterMarkets([debt.vToken.address]); + }); + + it("blocks a borrow that would take total borrows past the cap", async () => { + debt.vToken.totalBorrows.returns(parseUnits("900", 18)); + await comptroller.setMarketBorrowCaps([debt.vToken.address], [parseUnits("1000", 18)]); + + await expect(borrow(parseUnits("100", 18).add(1))) + .to.be.revertedWithCustomError(comptroller, "BorrowCapExceeded") + .withArgs(debt.vToken.address, parseUnits("1000", 18)); + }); + + it("allows a borrow that lands exactly on the cap", async () => { + debt.vToken.totalBorrows.returns(parseUnits("900", 18)); + await comptroller.setMarketBorrowCaps([debt.vToken.address], [parseUnits("1000", 18)]); + + await expect(borrow(parseUnits("100", 18))).to.not.be.reverted; + }); + + it("counts the market's bad debt against the cap", async () => { + // Bad debt is principal the pool has already written off. Leaving it out of the sum would let a market + // re-lend the room its own losses occupy. + debt.vToken.totalBorrows.returns(parseUnits("900", 18)); + debt.vToken.badDebt.returns(parseUnits("50", 18)); + await comptroller.setMarketBorrowCaps([debt.vToken.address], [parseUnits("1000", 18)]); + + await expect(borrow(parseUnits("50", 18))).to.not.be.reverted; + await expect(borrow(parseUnits("50", 18).add(1))).to.be.revertedWithCustomError(comptroller, "BorrowCapExceeded"); + }); + + it("blocks every borrow while the cap is zero, which is what a newly listed market defaults to", async () => { + await comptroller.setMarketBorrowCaps([debt.vToken.address], [0]); + + await expect(borrow(1)) + .to.be.revertedWithCustomError(comptroller, "BorrowCapExceeded") + .withArgs(debt.vToken.address, 0); + }); + + it("skips the cap arithmetic entirely for an uncapped market", async () => { + // `type(uint256).max` is the documented uncapped value, and skipping the reads is the reason it is special + // cased rather than just being a very large cap. + await comptroller.setMarketBorrowCaps([debt.vToken.address], [constants.MaxUint256]); + debt.vToken.totalBorrows.reset(); + debt.vToken.badDebt.reset(); + + await borrow(); + + expect(debt.vToken.totalBorrows).to.have.callCount(0); + expect(debt.vToken.badDebt).to.have.callCount(0); + }); + + it("reports the cap before the liquidity check", async () => { + // A borrow can fail both checks at once. The cap is a market-wide limit and cheaper to reason about, so it + // is the one the borrower should be told about. + await comptroller.setMarketBorrowCaps([debt.vToken.address], [parseUnits("1", 18)]); + + await expect(borrow(parseUnits("600", 18))).to.be.revertedWithCustomError(comptroller, "BorrowCapExceeded"); + }); + }); + + describe("close factor", () => { + /// Collateral 200 at a 0.8 threshold against a borrow of 300: under water, and above the 100 collateral + /// threshold that would route the account to the batch operations instead. + const BORROW_BALANCE = parseUnits("300", 18); + + const liquidate = (repayAmount: string) => + comptroller.callStatic.preLiquidateHook( + debt.vToken.address, + collateral.vToken.address, + borrower.address, + parseUnits(repayAmount, 18), + false, + ); + + beforeEach(async () => { + await comptroller.setCloseFactor(CLOSE_FACTOR); + await setRiskWeights(comptroller, collateral, COLLATERAL_FACTOR, LIQUIDATION_THRESHOLD); + await setRiskWeights(comptroller, debt, COLLATERAL_FACTOR, LIQUIDATION_THRESHOLD); + await givePosition(comptroller, borrower, [ + { market: collateral, collateral: parseUnits("200", 18) }, + { market: debt, borrow: BORROW_BALANCE }, + ]); + }); + + it("caps a single liquidation at the close factor share of the borrow", async () => { + // 0.5 of 300. + await expect(liquidate("150")).to.not.be.reverted; + await expect(liquidate("150.000000000000000001")).to.be.revertedWithCustomError(comptroller, "TooMuchRepay"); + }); + + it("moves with the close factor", async () => { + await comptroller.setCloseFactor(parseUnits("0.9", 18)); + + await expect(liquidate("270")).to.not.be.reverted; + await expect(liquidate("270.000000000000000001")).to.be.revertedWithCustomError(comptroller, "TooMuchRepay"); + }); + + it("is the last check, so a healthy account is reported as healthy", async () => { + collateral.vToken.getAccountSnapshot + .whenCalledWith(borrower.address) + .returns([0, parseUnits("1000", 18), 0, parseUnits("1", 18)]); + + await expect(liquidate("1000")).to.be.revertedWithCustomError(comptroller, "InsufficientShortfall"); + }); + }); + + describe("forced liquidation", () => { + const liquidate = (repayAmount: string, skipLiquidityCheck = false) => + comptroller.callStatic.preLiquidateHook( + debt.vToken.address, + collateral.vToken.address, + borrower.address, + parseUnits(repayAmount, 18), + skipLiquidityCheck, + ); + + beforeEach(async () => { + await comptroller.setCloseFactor(CLOSE_FACTOR); + await setRiskWeights(comptroller, collateral, COLLATERAL_FACTOR, LIQUIDATION_THRESHOLD); + await setRiskWeights(comptroller, debt, COLLATERAL_FACTOR, LIQUIDATION_THRESHOLD); + // Fully collateralized, and holding far less collateral than `minLiquidatableCollateral`. Every check the + // flag is supposed to skip would reject this account. + await givePosition(comptroller, borrower, [ + { market: collateral, collateral: parseUnits("10", 18) }, + { market: debt, borrow: parseUnits("1", 18) }, + ]); + }); + + it("rejects a healthy account while the flag is off", async () => { + await expect(liquidate("1")).to.be.revertedWithCustomError(comptroller, "MinimalCollateralViolated"); + }); + + it("liquidates a healthy account once the borrowed market is flagged", async () => { + await comptroller.setForcedLiquidation(debt.vToken.address, true); + + // The whole borrow at once, which both the close factor and the collateral threshold would otherwise block. + await expect(liquidate("1")).to.not.be.reverted; + }); + + it("still refuses to repay more than the account owes", async () => { + await comptroller.setForcedLiquidation(debt.vToken.address, true); + + await expect(liquidate("1.000000000000000001")).to.be.revertedWithCustomError(comptroller, "TooMuchRepay"); + }); + + it("is keyed by the borrowed market, not the pool", async () => { + // Flagging the collateral market must not make debt in another market forcibly liquidatable. + await comptroller.setForcedLiquidation(collateral.vToken.address, true); + + await expect(liquidate("1")).to.be.revertedWithCustomError(comptroller, "MinimalCollateralViolated"); + }); + + it("takes the same branch when the caller passes skipLiquidityCheck", async () => { + // The batch operations pass this flag on every order they place, which is how a batch liquidation clears + // debt the regular path would refuse to touch. + await expect(liquidate("1", true)).to.not.be.reverted; + await expect(liquidate("1.000000000000000001", true)).to.be.revertedWithCustomError(comptroller, "TooMuchRepay"); + }); + + it("stores the flag per market and emits the change", async () => { + await expect(comptroller.setForcedLiquidation(debt.vToken.address, true)) + .to.emit(comptroller, "IsForcedLiquidationEnabledUpdated") + .withArgs(debt.vToken.address, true); + + expect(await comptroller.isForcedLiquidationEnabled(debt.vToken.address)).to.equal(true); + expect(await comptroller.isForcedLiquidationEnabled(collateral.vToken.address)).to.equal(false); + }); + + it("refuses to flag a market that is not listed", async () => { + await expect(comptroller.setForcedLiquidation(stranger.address, true)) + .to.be.revertedWithCustomError(comptroller, "MarketNotListed") + .withArgs(stranger.address); + }); + }); + + describe("updatePrices", () => { + it("refreshes every market the account has entered", async () => { + await givePosition(comptroller, borrower, [{ market: collateral }, { market: debt }]); + fixture.oracle.updatePrice.reset(); + + await comptroller.updatePrices(borrower.address); + + expect(fixture.oracle.updatePrice).to.have.callCount(2); + expect(fixture.oracle.updatePrice).to.have.been.calledWith(collateral.vToken.address); + expect(fixture.oracle.updatePrice).to.have.been.calledWith(debt.vToken.address); + }); + + it("does nothing for an account in no markets", async () => { + fixture.oracle.updatePrice.reset(); + + await comptroller.updatePrices(stranger.address); + + expect(fixture.oracle.updatePrice).to.have.callCount(0); + }); + + it("is callable by anyone, since it only pushes prices the oracle already publishes", async () => { + await givePosition(comptroller, borrower, [{ market: collateral }]); + + await expect(comptroller.connect(stranger).updatePrices(borrower.address)).to.not.be.reverted; + }); + + it("runs on the borrow path", async () => { + await collateralizedBorrower(); + await comptroller.connect(borrower).enterMarkets([debt.vToken.address]); + fixture.oracle.updatePrice.reset(); + + await comptroller.connect(borrower).preBorrowHook(debt.vToken.address, borrower.address, BORROW_AMOUNT); + + expect(fixture.oracle.updatePrice).to.have.callCount(2); + }); + + it("runs on the liquidation path", async () => { + await comptroller.setCloseFactor(CLOSE_FACTOR); + await setRiskWeights(comptroller, collateral, COLLATERAL_FACTOR, LIQUIDATION_THRESHOLD); + await givePosition(comptroller, borrower, [ + { market: collateral, collateral: parseUnits("200", 18) }, + { market: debt, borrow: parseUnits("300", 18) }, + ]); + fixture.oracle.updatePrice.reset(); + + await comptroller.preLiquidateHook( + debt.vToken.address, + collateral.vToken.address, + borrower.address, + parseUnits("150", 18), + false, + ); + + expect(fixture.oracle.updatePrice).to.have.callCount(2); + }); + + it("runs on the repay path, for the repaid market alone", async () => { + // A repayment can only improve the account, so it is the market being repaid that has to be priced fresh, + // not the whole position. + await givePosition(comptroller, borrower, [{ market: collateral }, { market: debt }]); + fixture.oracle.updatePrice.reset(); + + await comptroller.preRepayHook(debt.vToken.address, borrower.address); + + expect(fixture.oracle.updatePrice).to.have.callCount(1); + expect(fixture.oracle.updatePrice).to.have.been.calledWith(debt.vToken.address); + }); + }); +}); diff --git a/tests/hardhat/Spoke/liquidationFlows.ts b/tests/hardhat/Spoke/liquidationFlows.ts new file mode 100644 index 000000000..6d9ebf414 --- /dev/null +++ b/tests/hardhat/Spoke/liquidationFlows.ts @@ -0,0 +1,439 @@ +import { FakeContract, MockContract, smock } from "@defi-wonderland/smock"; +import { loadFixture, setBalance } from "@nomicfoundation/hardhat-network-helpers"; +import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; +import chai from "chai"; +import { BigNumber, constants } from "ethers"; +import { parseEther, parseUnits } from "ethers/lib/utils"; +import { ethers, upgrades } from "hardhat"; + +import { + AccessControlManager, + ERC20Harness, + IDeviationBoundedOracle, + InterestRateModel, + PoolRegistry, + ResilientOracleInterface, + SpokeComptroller, + SpokeComptroller__factory, + VTokenHarness, + VTokenHarness__factory, +} from "../../../typechain"; +import { fakeInterestRateModel, makeVToken, mockUnderlying } from "../util/TokenTestHelpers"; + +const { expect } = chai; +chai.use(smock.matchers); + +const ONE = parseUnits("1", 18); + +// Both markets price at 1 and the collateral market's exchange rate is pinned at 1, so a token count is also a +// value. Then seizeTokens = repayAmount * collateralMarketIncentive. +const REPAY_AMOUNT = parseUnits("100", 18); +const BORROW_BALANCE = parseUnits("1000", 18); +const COLLATERAL_BALANCE = parseUnits("1000", 18); +const CLOSE_FACTOR = parseUnits("0.5", 18); // allows repaying up to 500 of the 1000 borrowed +const COLLATERAL_FACTOR = parseUnits("0.4", 18); +const LIQUIDATION_THRESHOLD = parseUnits("0.5", 18); // 0.5 * 1000 = 500 against 1000 of debt, so a shortfall +const MIN_LIQUIDATABLE_COLLATERAL = parseUnits("100", 18); // below the 1000 held, so single liquidation applies + +interface FlowFixture { + comptroller: MockContract; + oracle: FakeContract; + borrowed: VTokenHarness; + collateral: VTokenHarness; + borrowedUnderlying: MockContract; + collateralUnderlying: MockContract; + protocolShareReserve: FakeContract; +} + +/** + * A spoke pool with two real vTokens, so that `VToken._seize` runs for real against the comptroller's per-market + * incentive. A fake comptroller would let the seize arithmetic be stated rather than derived, which is exactly the + * interaction under test here. + */ +async function deployFlowFixture(): Promise { + const [, , , proxyAdminSigner] = await ethers.getSigners(); + const poolRegistry = await smock.fake("PoolRegistry"); + const oracle = await smock.fake("ResilientOracleInterface"); + const acm = await smock.fake("AccessControlManager"); + const boundedOracle = await smock.fake("IDeviationBoundedOracle"); + acm.isAllowedToCall.returns(true); + boundedOracle.getBoundedPricesView.returns([ONE, ONE]); + + const comptrollerFactory = await smock.mock("SpokeComptroller"); + const comptroller = (await upgrades.deployProxy(comptrollerFactory, [150, acm.address], { + constructorArgs: [poolRegistry.address], + initializer: "initialize(uint256,address)", + })) as MockContract; + await comptroller.setPriceOracle(oracle.address); + await comptroller.setDeviationBoundedOracle(boundedOracle.address); + await comptroller.setCloseFactor(CLOSE_FACTOR); + await comptroller.setMinLiquidatableCollateral(MIN_LIQUIDATABLE_COLLATERAL); + // `PoolRegistry.addPool` always sets this while registering a pool, and the snapshot divides by it, so a fixture + // that skips the registry has to supply it. Individual tests override it where the value is what is under test. + await comptroller.setLiquidationIncentive(parseUnits("1.1", 18)); + + const protocolShareReserve = await smock.fake("ProtocolShareReserve"); + // Zero rates, so no interest accrues and every figure below stays exact. + const interestRateModel: FakeContract = await fakeInterestRateModel(); + + const borrowedUnderlying = await mockUnderlying("Borrowed", "BRW"); + const collateralUnderlying = await mockUnderlying("Collateral", "COL"); + + const borrowed = await makeVToken( + { + underlying: borrowedUnderlying, + comptroller, + accessControlManager: acm, + admin: proxyAdminSigner, + interestRateModel, + protocolShareReserve, + }, + { kind: "VTokenHarness" }, + ); + const collateral = await makeVToken( + { + underlying: collateralUnderlying, + comptroller, + accessControlManager: acm, + admin: proxyAdminSigner, + interestRateModel, + protocolShareReserve, + }, + { kind: "VTokenHarness" }, + ); + + oracle.getUnderlyingPrice.whenCalledWith(borrowed.address).returns(ONE); + oracle.getUnderlyingPrice.whenCalledWith(collateral.address).returns(ONE); + + await setBalance(poolRegistry.address, parseEther("1")); + for (const vToken of [borrowed, collateral]) { + await comptroller.connect(poolRegistry.wallet).supportMarket(vToken.address); + await comptroller.setMarketSupplyCaps([vToken.address], [constants.MaxUint256]); + await comptroller.setMarketBorrowCaps([vToken.address], [constants.MaxUint256]); + await comptroller.setCollateralFactor(vToken.address, COLLATERAL_FACTOR, LIQUIDATION_THRESHOLD); + } + + return { + comptroller, + oracle, + borrowed, + collateral, + borrowedUnderlying, + collateralUnderlying, + protocolShareReserve, + }; +} + +describe("SpokeComptroller: liquidation flows against real vTokens", () => { + let fixture: FlowFixture; + let comptroller: MockContract; + let borrowed: VTokenHarness; + let collateral: VTokenHarness; + let liquidator: SignerWithAddress; + let borrower: SignerWithAddress; + + beforeEach(async () => { + [, liquidator, borrower] = await ethers.getSigners(); + fixture = await loadFixture(deployFlowFixture); + ({ comptroller, borrowed, collateral } = fixture); + + // The borrower owes 1000 in the borrowed market and holds 1000 of collateral, priced 1:1. + await borrowed.harnessSetAccountBorrows(borrower.address, BORROW_BALANCE, ONE); + await borrowed.harnessSetTotalBorrows(BORROW_BALANCE); + await collateral.harnessSetBalance(borrower.address, COLLATERAL_BALANCE); + await collateral.harnessSetTotalSupply(COLLATERAL_BALANCE); + await collateral.harnessSetExchangeRate(ONE); + await comptroller.connect(borrower).enterMarkets([collateral.address, borrowed.address]); + + // The liquidator funds the repayment, and the collateral market holds enough underlying to pay the protocol + // its share out. + await fixture.borrowedUnderlying.harnessSetBalance(liquidator.address, REPAY_AMOUNT); + await fixture.borrowedUnderlying.connect(liquidator).approve(borrowed.address, REPAY_AMOUNT); + // `internalCash` is tracked separately from the ERC20 balance, and `_doTransferOut` decrements it, so seeding + // the balance alone would underflow. Both have to be set for the market to be able to pay anything out. + const collateralCash = parseUnits("100", 18); + await fixture.collateralUnderlying.harnessSetBalance(collateral.address, collateralCash); + await collateral.harnessSetInternalCash(collateralCash); + }); + + // `liquidateCalculateSeizeTokens` prices the seizure at the collateral market's incentive, and `VToken._seize` + // divides the protocol's share back out by that same incentive, because `liquidationIncentiveMantissa()` answers + // for the calling market. So the protocol takes exactly `protocolSeizeShareMantissa` of the value repaid and the + // liquidator keeps the whole discount, whichever side of the pool-wide value the market's incentive falls on. Each + // case pins the token counts, and every case also checks the borrower loses precisely `seizeTokens`. + const PROTOCOL_SEIZE_TOKENS = parseUnits("5", 18); // 5% of the 100 repaid, at the market's default seize share + + const cases = [ + { + name: "takes the protocol's share of the repayment when the market and pool incentives agree", + marketIncentive: parseUnits("1.1", 18), + poolIncentive: parseUnits("1.1", 18), + seizeTokens: parseUnits("110", 18), + liquidatorSeizeTokens: parseUnits("105", 18), + }, + { + name: "takes the same share when the market incentive is the higher one", + marketIncentive: parseUnits("1.15", 18), + poolIncentive: parseUnits("1.1", 18), + seizeTokens: parseUnits("115", 18), + liquidatorSeizeTokens: parseUnits("110", 18), + }, + { + name: "takes the same share when the market incentive is the lower one", + marketIncentive: parseUnits("1.05", 18), + poolIncentive: parseUnits("1.1", 18), + seizeTokens: parseUnits("105", 18), + liquidatorSeizeTokens: parseUnits("100", 18), + }, + ]; + + describe("protocol seize split", () => { + for (const testCase of cases) { + it(testCase.name, async () => { + await comptroller.setLiquidationIncentive(testCase.poolIncentive); + await comptroller.setMarketLiquidationIncentive(collateral.address, testCase.marketIncentive); + + const [, seizeTokens] = await comptroller.liquidateCalculateSeizeTokens( + borrowed.address, + collateral.address, + REPAY_AMOUNT, + ); + expect(seizeTokens).to.equal(testCase.seizeTokens); + const { liquidatorSeizeTokens } = testCase; + + const supplyBefore = await collateral.totalSupply(); + await borrowed.connect(liquidator).liquidateBorrow(borrower.address, REPAY_AMOUNT, collateral.address); + + // The borrower's loss is set entirely by the collateral market's own incentive. + expect(await collateral.balanceOf(borrower.address)).to.equal(COLLATERAL_BALANCE.sub(seizeTokens)); + expect(await collateral.balanceOf(liquidator.address)).to.equal(liquidatorSeizeTokens); + expect(await collateral.totalSupply()).to.equal(supplyBefore.sub(PROTOCOL_SEIZE_TOKENS)); + }); + } + + it("still repays the liquidator in full at the lowest incentive the setter allows", async () => { + // `1e18 + protocolSeizeShareMantissa` is the floor `setMarketLiquidationIncentive` enforces, and it is the + // break-even point: the liquidator recovers exactly what it repaid and the protocol takes the whole discount. + // The pool-wide value is parked well above it to show it no longer feeds this split - were it leaking in, the + // seizure below would price at 150 rather than 105. + const floor = ONE.add(await collateral.protocolSeizeShareMantissa()); + await comptroller.setLiquidationIncentive(parseUnits("1.5", 18)); + await comptroller.setMarketLiquidationIncentive(collateral.address, floor); + + const [, seizeTokens] = await comptroller.liquidateCalculateSeizeTokens( + borrowed.address, + collateral.address, + REPAY_AMOUNT, + ); + expect(seizeTokens).to.equal(parseUnits("105", 18)); + + await borrowed.connect(liquidator).liquidateBorrow(borrower.address, REPAY_AMOUNT, collateral.address); + + expect(await collateral.balanceOf(liquidator.address)).to.equal(REPAY_AMOUNT); + expect(await collateral.balanceOf(borrower.address)).to.equal(COLLATERAL_BALANCE.sub(seizeTokens)); + }); + + it("rejects a market incentive one wei below that floor", async () => { + const floor = ONE.add(await collateral.protocolSeizeShareMantissa()); + + await expect( + comptroller.setMarketLiquidationIncentive(collateral.address, floor.sub(1)), + ).to.be.revertedWithCustomError(comptroller, "InvalidLiquidationIncentive"); + }); + + it("bounds a market's protocol seize share against that market's own incentive", async () => { + await comptroller.setLiquidationIncentive(parseUnits("1.1", 18)); + await comptroller.setMarketLiquidationIncentive(collateral.address, parseUnits("1.15", 18)); + + // A 12% share clears the market's own 1.15 but not the pool-wide 1.1, so it is only accepted because + // `liquidationIncentiveMantissa()` answers for the calling market. + await collateral.setProtocolSeizeShare(parseUnits("0.12", 18)); + expect(await collateral.protocolSeizeShareMantissa()).to.equal(parseUnits("0.12", 18)); + + await expect(collateral.setProtocolSeizeShare(parseUnits("0.16", 18))).to.be.revertedWithCustomError( + collateral, + "ProtocolSeizeShareTooBig", + ); + }); + + it("sends the protocol's share of the underlying to the protocol share reserve", async () => { + await comptroller.setLiquidationIncentive(parseUnits("1.1", 18)); + await comptroller.setMarketLiquidationIncentive(collateral.address, parseUnits("1.1", 18)); + // 110 seized at a 5% protocol share over a pool-wide incentive of 1.1, and an exchange rate of 1, so the + // underlying amount equals the token count. + const protocolSeizeAmount = parseUnits("5", 18); + + await expect(borrowed.connect(liquidator).liquidateBorrow(borrower.address, REPAY_AMOUNT, collateral.address)) + .to.emit(collateral, "ProtocolSeize") + .withArgs(borrower.address, fixture.protocolShareReserve.address, protocolSeizeAmount); + + expect(await fixture.collateralUnderlying.balanceOf(fixture.protocolShareReserve.address)).to.equal( + protocolSeizeAmount, + ); + }); + }); + // Both batch operations require the position to sit below `minLiquidatableCollateral`, so these raise it above + // the 1000 of collateral the borrower holds. + describe("batch operations", () => { + const BATCH_MIN_COLLATERAL = parseUnits("10000", 18); + const POOL_INCENTIVE = parseUnits("1.1", 18); + + beforeEach(async () => { + await comptroller.setMinLiquidatableCollateral(BATCH_MIN_COLLATERAL); + await comptroller.setLiquidationIncentive(POOL_INCENTIVE); + }); + + it("heals an underwater account, repaying maxClearableDebt and recording the rest as bad debt", async () => { + // 1000 of collateral at an incentive of 1.1 clears 909.090909090909090909 of debt, against 1000 owed, so + // the percentage is that ratio and the shortfall between it and the debt becomes bad debt. + const maxClearableDebt = BigNumber.from("909090909090909090909"); + const repayment = BigNumber.from("909090909090909090000"); + const expectedBadDebt = BORROW_BALANCE.sub(repayment); + + await fixture.borrowedUnderlying.harnessSetBalance(liquidator.address, repayment); + await fixture.borrowedUnderlying.connect(liquidator).approve(borrowed.address, repayment); + + await expect(comptroller.connect(liquidator).healAccount(borrower.address)) + .to.emit(borrowed, "BadDebtIncreased") + .withArgs(borrower.address, expectedBadDebt, 0, expectedBadDebt); + + expect(await borrowed.badDebt()).to.equal(expectedBadDebt); + expect(await borrowed.borrowBalanceStored(borrower.address)).to.equal(0); + // The whole collateral position is seized; the protocol takes 5% of it over the pool-wide incentive. + expect(await collateral.balanceOf(borrower.address)).to.equal(0); + expect(await collateral.balanceOf(liquidator.address)).to.equal(COLLATERAL_BALANCE.sub("45454545454545454545")); + expect(maxClearableDebt).to.be.lt(BORROW_BALANCE); + }); + + it("clears every borrow through liquidateAccount and leaves no bad debt", async () => { + // 600 of debt against 909.09 of clearable collateral, so liquidateAccount is the correct path. + const debt = parseUnits("600", 18); + await borrowed.harnessSetAccountBorrows(borrower.address, debt, ONE); + await borrowed.harnessSetTotalBorrows(debt); + await fixture.borrowedUnderlying.harnessSetBalance(liquidator.address, debt); + await fixture.borrowedUnderlying.connect(liquidator).approve(borrowed.address, debt); + + await comptroller + .connect(liquidator) + .liquidateAccount(borrower.address, [ + { vTokenCollateral: collateral.address, vTokenBorrowed: borrowed.address, repayAmount: debt }, + ]); + + expect(await borrowed.borrowBalanceStored(borrower.address)).to.equal(0); + expect(await borrowed.badDebt()).to.equal(0); + // 600 repaid at an incentive of 1.1 seizes 660, of which the protocol takes 30. + expect(await collateral.balanceOf(borrower.address)).to.equal(COLLATERAL_BALANCE.sub(parseUnits("660", 18))); + expect(await collateral.balanceOf(liquidator.address)).to.equal(parseUnits("630", 18)); + }); + + // `liquidateAccount` runs its orders with the liquidity check skipped, so the entry snapshot is the only thing + // that decides whether the account belongs here at all. Taken on stored balances it would route on a debt the + // market has already outgrown: 900 owed sits under the 909.09 the collateral can clear, while the interest + // pending since the last accrual puts the real debt above it, which is `healAccount`'s case. + it("routes liquidateAccount on accrued debt rather than the stored balance", async () => { + const debt = parseUnits("900", 18); + await borrowed.harnessSetAccountBorrows(borrower.address, debt, ONE); + await borrowed.harnessSetTotalBorrows(debt); + + // 5e12 per block over 5000 blocks compounds the debt by 2.5%, to 922.5. + const interestRateModel = await fakeInterestRateModel(); + interestRateModel.getBorrowRate.returns(parseUnits("5", 12)); + await borrowed.harnessSetInterestRateModel(interestRateModel.address); + await borrowed.harnessFastForward(5000); + + const accruedDebt = parseUnits("922.5", 18); + const maxClearableDebt = BigNumber.from("909090909090909090909"); + + await expect( + comptroller + .connect(liquidator) + .liquidateAccount(borrower.address, [ + { vTokenCollateral: collateral.address, vTokenBorrowed: borrowed.address, repayAmount: debt }, + ]), + ) + .to.be.revertedWithCustomError(comptroller, "DebtExceedsClearableAmount") + .withArgs(accruedDebt, maxClearableDebt); + }); + + // The entry point holds no allowlist check of its own, so this proves the seizure on the first order is what + // stops a liquidator that is not on the list. Same scenario as the clearing test above, which passes with the + // allowlist disabled. + it("gates liquidateAccount through the seizure on the first order", async () => { + const debt = parseUnits("600", 18); + await borrowed.harnessSetAccountBorrows(borrower.address, debt, ONE); + await borrowed.harnessSetTotalBorrows(debt); + await fixture.borrowedUnderlying.harnessSetBalance(liquidator.address, debt); + await fixture.borrowedUnderlying.connect(liquidator).approve(borrowed.address, debt); + await comptroller.setLiquidationAllowlistEnabled(true); + + await expect( + comptroller + .connect(liquidator) + .liquidateAccount(borrower.address, [ + { vTokenCollateral: collateral.address, vTokenBorrowed: borrowed.address, repayAmount: debt }, + ]), + ) + .to.be.revertedWithCustomError(comptroller, "LiquidationNotAllowed") + .withArgs(liquidator.address); + }); + + it("rejects orders that leave a borrow outstanding", async () => { + const debt = parseUnits("600", 18); + await borrowed.harnessSetAccountBorrows(borrower.address, debt, ONE); + await borrowed.harnessSetTotalBorrows(debt); + const partial = parseUnits("300", 18); + await fixture.borrowedUnderlying.harnessSetBalance(liquidator.address, partial); + await fixture.borrowedUnderlying.connect(liquidator).approve(borrowed.address, partial); + + await expect( + comptroller + .connect(liquidator) + .liquidateAccount(borrower.address, [ + { vTokenCollateral: collateral.address, vTokenBorrowed: borrowed.address, repayAmount: partial }, + ]), + ).to.be.revertedWithCustomError(comptroller, "NonzeroBorrowBalanceAfterLiquidation"); + }); + }); + + describe("liquidation allowlist against real seizures", () => { + it("gates a cross-market seizure", async () => { + await comptroller.setLiquidationAllowlistEnabled(true); + + await expect(borrowed.connect(liquidator).liquidateBorrow(borrower.address, REPAY_AMOUNT, collateral.address)) + .to.be.revertedWithCustomError(comptroller, "LiquidationNotAllowed") + .withArgs(liquidator.address); + }); + + it("gates an in-kind seizure, which takes the internal _seize branch", async () => { + // Collateral and borrowed are the same market, so `_liquidateBorrowFresh` calls `_seize` directly instead of + // going through the external `seize`. That branch still has to reach `preSeizeHook`. + await collateral.harnessSetBalance(borrower.address, 0); + await borrowed.harnessSetBalance(borrower.address, COLLATERAL_BALANCE); + await borrowed.harnessSetTotalSupply(COLLATERAL_BALANCE); + await borrowed.harnessSetExchangeRate(ONE); + await comptroller.setLiquidationAllowlistEnabled(true); + + await expect(borrowed.connect(liquidator).liquidateBorrow(borrower.address, REPAY_AMOUNT, borrowed.address)) + .to.be.revertedWithCustomError(comptroller, "LiquidationNotAllowed") + .withArgs(liquidator.address); + }); + + it("lets an allowlisted liquidator through the same in-kind path", async () => { + await collateral.harnessSetBalance(borrower.address, 0); + await borrowed.harnessSetBalance(borrower.address, COLLATERAL_BALANCE); + await borrowed.harnessSetTotalSupply(COLLATERAL_BALANCE); + await borrowed.harnessSetExchangeRate(ONE); + await fixture.borrowedUnderlying.harnessSetBalance(borrowed.address, parseUnits("100", 18)); + await borrowed.harnessSetInternalCash(parseUnits("100", 18)); + await comptroller.setLiquidationAllowlistEnabled(true); + await comptroller.setAllowedLiquidator(liquidator.address, true); + await comptroller.setLiquidationIncentive(parseUnits("1.1", 18)); + + await borrowed.connect(liquidator).liquidateBorrow(borrower.address, REPAY_AMOUNT, borrowed.address); + + // 100 repaid at 1.1 seizes 110, of which the protocol takes 5. + expect(await borrowed.balanceOf(borrower.address)).to.equal(COLLATERAL_BALANCE.sub(parseUnits("110", 18))); + expect(await borrowed.balanceOf(liquidator.address)).to.equal(parseUnits("105", 18)); + }); + }); +}); diff --git a/tests/hardhat/Spoke/liquidationIncentive.ts b/tests/hardhat/Spoke/liquidationIncentive.ts new file mode 100644 index 000000000..4d4308b40 --- /dev/null +++ b/tests/hardhat/Spoke/liquidationIncentive.ts @@ -0,0 +1,454 @@ +import { MockContract, smock } from "@defi-wonderland/smock"; +import { loadFixture } from "@nomicfoundation/hardhat-network-helpers"; +import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; +import chai from "chai"; +import { BigNumber } from "ethers"; +import { parseUnits } from "ethers/lib/utils"; +import { ethers } from "hardhat"; + +import { SpokeComptroller } from "../../../typechain"; +import { ONE, SpokeFixture, TestMarket, deploySpokeComptroller, givePosition, setRiskWeights } from "./fixtures"; + +const { expect } = chai; +chai.use(smock.matchers); + +// Every market prices at 1 with an exchange rate of 1, so a vToken balance is also its collateral value and the +// arithmetic under test stays visible: +// maxClearableDebt = sum over the account's collateral markets of balance / thatMarketsIncentive +const POOL_WIDE_INCENTIVE = parseUnits("1.2", 18); +const MARKET_A_INCENTIVE = parseUnits("1.1", 18); +const COLLATERAL_A = parseUnits("110", 18); // 110 / 1.1 = 100 +const COLLATERAL_B = parseUnits("120", 18); // 120 / 1.2 = 100, through the pool-wide fallback +const MAX_CLEARABLE_DEBT = parseUnits("200", 18); +const TOTAL_COLLATERAL = COLLATERAL_A.add(COLLATERAL_B); + +const LIQUIDATION_THRESHOLD = parseUnits("0.8", 18); +const COLLATERAL_FACTOR = parseUnits("0.5", 18); + +// Above the whole position, so the batch operations are the correct path throughout. +const HIGH_MIN_COLLATERAL = parseUnits("1000", 18); +// Below it, so a single liquidation is the correct path. +const LOW_MIN_COLLATERAL = parseUnits("100", 18); + +describe("SpokeComptroller: per-market liquidation incentive", () => { + let fixture: SpokeFixture; + let comptroller: MockContract; + let collateralA: TestMarket; + let collateralB: TestMarket; + let debtMarket: TestMarket; + let liquidator: SignerWithAddress; + let borrower: SignerWithAddress; + + /// Only market A carries an incentive of its own. B falls back to the pool-wide value, and the debt market never + /// has one set, which is the state every borrow-only market is in. + async function incentiveFixture(minLiquidatableCollateral = HIGH_MIN_COLLATERAL): Promise { + const f = await deploySpokeComptroller({ + marketCount: 3, + liquidationIncentive: POOL_WIDE_INCENTIVE, + minLiquidatableCollateral, + }); + await f.comptroller.setMarketLiquidationIncentive(f.markets[0].vToken.address, MARKET_A_INCENTIVE); + return f; + } + + const lowMinFixture = () => incentiveFixture(LOW_MIN_COLLATERAL); + + /// Collateral in A and B, debt in the third market. The debt market is entered while holding no collateral in + /// it, which is what `preBorrowHook` does to every borrower. + async function givePositionWithDebt(debt: BigNumber): Promise { + await givePosition(comptroller, borrower, [ + { market: collateralA, collateral: COLLATERAL_A }, + { market: collateralB, collateral: COLLATERAL_B }, + { market: debtMarket, borrow: debt }, + ]); + } + + function bind(f: SpokeFixture): void { + fixture = f; + f.resetPrices(); + ({ comptroller } = f); + [collateralA, collateralB, debtMarket] = f.markets; + for (const { vToken } of f.markets) { + vToken.seize.reset(); + vToken.healBorrow.reset(); + } + } + + beforeEach(async () => { + [, liquidator, borrower] = await ethers.getSigners(); + bind(await loadFixture(incentiveFixture)); + }); + + describe("maxClearableDebt", () => { + it("sums each market's collateral at that market's own incentive", async () => { + await givePositionWithDebt(parseUnits("250", 18)); + + // 110 / 1.1 + 120 / 1.2 = 200, surfaced through the revert rather than read directly. + await expect(comptroller.connect(liquidator).liquidateAccount(borrower.address, [])) + .to.be.revertedWithCustomError(comptroller, "DebtExceedsClearableAmount") + .withArgs(parseUnits("250", 18), MAX_CLEARABLE_DEBT); + }); + + it("falls back to the pool-wide incentive for a market with no value of its own", async () => { + await comptroller.setMarketLiquidationIncentive(collateralA.vToken.address, POOL_WIDE_INCENTIVE); + await givePositionWithDebt(parseUnits("250", 18)); + + // Both markets now divide by 1.2: (110 + 120) / 1.2, truncated once per market. + const expected = COLLATERAL_A.mul(ONE) + .div(POOL_WIDE_INCENTIVE) + .add(COLLATERAL_B.mul(ONE).div(POOL_WIDE_INCENTIVE)); + await expect(comptroller.connect(liquidator).liquidateAccount(borrower.address, [])) + .to.be.revertedWithCustomError(comptroller, "DebtExceedsClearableAmount") + .withArgs(parseUnits("250", 18), expected); + }); + + it("truncates once per market rather than once on the total", async () => { + // 1 / 1.1 does not divide evenly. Dividing each market separately and then adding loses a wei that + // adding first and dividing once would keep: 1818181818181818180 against 1818181818181818181. Rounding + // down per market is the conservative direction, since it can only route an account to healAccount. + const incentive = parseUnits("1.1", 18); + await comptroller.setMarketLiquidationIncentive(collateralA.vToken.address, incentive); + await comptroller.setMarketLiquidationIncentive(collateralB.vToken.address, incentive); + await givePosition(comptroller, borrower, [ + { market: collateralA, collateral: ONE }, + { market: collateralB, collateral: ONE }, + { market: debtMarket, borrow: parseUnits("2", 18) }, + ]); + + const perMarketTruncation = BigNumber.from("1818181818181818180"); + expect(ONE.add(ONE).mul(ONE).div(incentive)).to.equal(perMarketTruncation.add(1)); + + await expect(comptroller.connect(liquidator).liquidateAccount(borrower.address, [])) + .to.be.revertedWithCustomError(comptroller, "DebtExceedsClearableAmount") + .withArgs(parseUnits("2", 18), perMarketTruncation); + }); + }); + + describe("routing between liquidateAccount and healAccount", () => { + it("sends an account whose collateral covers the debt to liquidateAccount", async () => { + await givePositionWithDebt(parseUnits("150", 18)); + + await expect(comptroller.connect(liquidator).healAccount(borrower.address)) + .to.be.revertedWithCustomError(comptroller, "CollateralCoversDebt") + .withArgs(parseUnits("150", 18), MAX_CLEARABLE_DEBT); + // Past the collateral gate; it now fails on the closing check, which fake markets cannot satisfy. + await expect( + comptroller.connect(liquidator).liquidateAccount(borrower.address, []), + ).to.be.revertedWithCustomError(comptroller, "NonzeroBorrowBalanceAfterLiquidation"); + }); + + it("sends an account whose collateral falls short to healAccount", async () => { + await givePositionWithDebt(parseUnits("250", 18)); + + await expect( + comptroller.connect(liquidator).liquidateAccount(borrower.address, []), + ).to.be.revertedWithCustomError(comptroller, "DebtExceedsClearableAmount"); + await comptroller.connect(liquidator).healAccount(borrower.address); + }); + + it("sends an account whose debt exactly equals maxClearableDebt to healAccount", async () => { + await givePositionWithDebt(MAX_CLEARABLE_DEBT); + + await expect(comptroller.connect(liquidator).liquidateAccount(borrower.address, [])) + .to.be.revertedWithCustomError(comptroller, "DebtExceedsClearableAmount") + .withArgs(MAX_CLEARABLE_DEBT, MAX_CLEARABLE_DEBT); + + // percentage is exactly 1, so healing repays the whole debt and forgives nothing. + await comptroller.connect(liquidator).healAccount(borrower.address); + expect(debtMarket.vToken.healBorrow).to.have.been.calledOnceWith( + liquidator.address, + borrower.address, + MAX_CLEARABLE_DEBT, + ); + }); + + it("rejects a batch liquidation while the collateral is above the threshold", async () => { + bind(await loadFixture(lowMinFixture)); + await givePositionWithDebt(parseUnits("250", 18)); + + for (const call of [ + comptroller.connect(liquidator).healAccount(borrower.address), + comptroller.connect(liquidator).liquidateAccount(borrower.address, []), + ]) { + await expect(call) + .to.be.revertedWithCustomError(comptroller, "CollateralExceedsThreshold") + .withArgs(LOW_MIN_COLLATERAL, TOTAL_COLLATERAL); + } + }); + + it("rejects a single liquidation while the collateral is below the threshold", async () => { + await givePositionWithDebt(parseUnits("250", 18)); + + await expect( + comptroller.preLiquidateHook( + debtMarket.vToken.address, + collateralA.vToken.address, + borrower.address, + parseUnits("1", 18), + false, + ), + ) + .to.be.revertedWithCustomError(comptroller, "MinimalCollateralViolated") + .withArgs(HIGH_MIN_COLLATERAL, TOTAL_COLLATERAL); + }); + }); + + // Without risk weights every account is in shortfall, which leaves the shortfall guards untested. These set the + // liquidation threshold so that a healthy account can exist. + describe("a healthy account", () => { + const HEALTHY_DEBT = parseUnits("150", 18); + + beforeEach(async () => { + for (const market of [collateralA, collateralB]) { + await setRiskWeights(comptroller, market, COLLATERAL_FACTOR, LIQUIDATION_THRESHOLD); + } + // 0.8 * 230 = 184 of weighted collateral against 150 of debt, so there is no shortfall. The debt is also + // below maxClearableDebt, so neither batch path can exit on its collateral check first. + await givePositionWithDebt(HEALTHY_DEBT); + const { shortfall } = await comptroller.getAccountLiquidity(borrower.address); + expect(shortfall).to.equal(0); + }); + + it("cannot be healed", async () => { + await expect(comptroller.connect(liquidator).healAccount(borrower.address)).to.be.revertedWithCustomError( + comptroller, + "InsufficientShortfall", + ); + }); + + it("cannot be batch liquidated", async () => { + await expect( + comptroller.connect(liquidator).liquidateAccount(borrower.address, []), + ).to.be.revertedWithCustomError(comptroller, "InsufficientShortfall"); + }); + + it("cannot be liquidated one market at a time", async () => { + bind(await loadFixture(lowMinFixture)); + for (const market of [collateralA, collateralB]) { + await setRiskWeights(comptroller, market, COLLATERAL_FACTOR, LIQUIDATION_THRESHOLD); + } + await givePositionWithDebt(HEALTHY_DEBT); + + await expect( + comptroller.preLiquidateHook( + debtMarket.vToken.address, + collateralA.vToken.address, + borrower.address, + parseUnits("1", 18), + false, + ), + ).to.be.revertedWithCustomError(comptroller, "InsufficientShortfall"); + }); + }); + + describe("healAccount", () => { + it("repays maxClearableDebt/borrows of the debt and seizes every collateral market in full", async () => { + await givePositionWithDebt(parseUnits("250", 18)); + + await comptroller.connect(liquidator).healAccount(borrower.address); + + // percentage = 200/250 = 0.8, so 0.8 * 250 = 200 is repaid and the remaining 50 becomes bad debt. + expect(debtMarket.vToken.healBorrow).to.have.been.calledOnceWith( + liquidator.address, + borrower.address, + MAX_CLEARABLE_DEBT, + ); + expect(collateralA.vToken.seize).to.have.been.calledOnceWith(liquidator.address, borrower.address, COLLATERAL_A); + expect(collateralB.vToken.seize).to.have.been.calledOnceWith(liquidator.address, borrower.address, COLLATERAL_B); + expect(debtMarket.vToken.seize).to.not.have.been.called; + }); + + it("repays less when a collateral market's incentive is raised, since the same collateral clears less debt", async () => { + await comptroller.setMarketLiquidationIncentive(collateralA.vToken.address, parseUnits("2", 18)); + await givePositionWithDebt(parseUnits("250", 18)); + + // 110 / 2 + 120 / 1.2 = 155, so percentage = 155/250 = 0.62 and 155 is repaid. + await comptroller.connect(liquidator).healAccount(borrower.address); + + expect(debtMarket.vToken.healBorrow).to.have.been.calledOnceWith( + liquidator.address, + borrower.address, + parseUnits("155", 18), + ); + }); + }); + + describe("liquidateCalculateSeizeTokens", () => { + it("prices the seizure at the collateral market's incentive, not the pool-wide one", async () => { + const repayAmount = parseUnits("100", 18); + + const [, seizeFromA] = await comptroller.liquidateCalculateSeizeTokens( + debtMarket.vToken.address, + collateralA.vToken.address, + repayAmount, + ); + const [, seizeFromB] = await comptroller.liquidateCalculateSeizeTokens( + debtMarket.vToken.address, + collateralB.vToken.address, + repayAmount, + ); + + expect(seizeFromA).to.equal(parseUnits("110", 18)); // market A's own 1.1 + expect(seizeFromB).to.equal(parseUnits("120", 18)); // pool-wide 1.2, market B has no value of its own + }); + }); + + // A pool that `PoolRegistry.addPool` has not registered has no pool-wide incentive, so a market with no value of + // its own divides by zero. Nothing can reach that state through the registry, and these pin why. + describe("with no incentive set anywhere", () => { + let unregistered: SpokeFixture; + + beforeEach(async () => { + unregistered = await deploySpokeComptroller({ marketCount: 2, liquidationIncentive: null }); + expect(await unregistered.comptroller.liquidationIncentiveMantissa()).to.equal(0); + expect(await unregistered.comptroller.liquidationIncentives(unregistered.markets[0].vToken.address)).to.equal(0); + }); + + it("reverts the snapshot for an account that actually holds collateral", async () => { + await givePosition(unregistered.comptroller, borrower, [ + { market: unregistered.markets[0], collateral: ONE }, + { market: unregistered.markets[1], borrow: ONE }, + ]); + + await expect(unregistered.comptroller.connect(liquidator).healAccount(borrower.address)).to.be.revertedWithPanic( + 0x12, + ); + }); + + it("is unreachable for a market the account holds no collateral in, which the balance guard skips", async () => { + await givePosition(unregistered.comptroller, borrower, [ + { market: unregistered.markets[0], collateral: 0 }, + { market: unregistered.markets[1], borrow: ONE }, + ]); + + // No collateral anywhere, so the accumulator never divides and healing writes the whole debt off. + await unregistered.comptroller.connect(liquidator).healAccount(borrower.address); + expect(unregistered.markets[1].vToken.healBorrow).to.have.been.calledOnceWith( + liquidator.address, + borrower.address, + 0, + ); + }); + }); + + describe("setMarketLiquidationIncentive", () => { + it("reverts if access control denies the call", async () => { + fixture.acm.isAllowedToCall + .whenCalledWith(borrower.address, "setMarketLiquidationIncentive(address,uint256)") + .returns(false); + + await expect( + comptroller.connect(borrower).setMarketLiquidationIncentive(collateralA.vToken.address, parseUnits("1.3", 18)), + ).to.be.revertedWithCustomError(comptroller, "Unauthorized"); + }); + + it("reverts if the market is not listed", async () => { + await expect(comptroller.setMarketLiquidationIncentive(liquidator.address, parseUnits("1.3", 18))) + .to.be.revertedWithCustomError(comptroller, "MarketNotListed") + .withArgs(liquidator.address); + }); + + it("reverts below 1e18, which would seize less value than was repaid", async () => { + collateralA.vToken.protocolSeizeShareMantissa.returns(0); + + await expect( + comptroller.setMarketLiquidationIncentive(collateralA.vToken.address, ONE.sub(1)), + ).to.be.revertedWithCustomError(comptroller, "InvalidLiquidationIncentive"); + }); + + it("accepts exactly 1e18 when the market takes no protocol seize share", async () => { + collateralA.vToken.protocolSeizeShareMantissa.returns(0); + await comptroller.setMarketLiquidationIncentive(collateralA.vToken.address, ONE); + + expect(await comptroller.liquidationIncentives(collateralA.vToken.address)).to.equal(ONE); + }); + + it("reverts below 1e18 plus the market's protocol seize share, which the liquidator does not get", async () => { + // `VToken._seize` hands the protocol its share out of the collateral seized, so the liquidator only keeps + // `incentive - protocolSeizeShareMantissa` of the value it repaid. Anything under 1.05 here is a loss. + const seizeShare = parseUnits("0.05", 18); + collateralA.vToken.protocolSeizeShareMantissa.returns(seizeShare); + const floor = ONE.add(seizeShare); + + await expect( + comptroller.setMarketLiquidationIncentive(collateralA.vToken.address, floor.sub(1)), + ).to.be.revertedWithCustomError(comptroller, "InvalidLiquidationIncentive"); + + await comptroller.setMarketLiquidationIncentive(collateralA.vToken.address, floor); + expect(await comptroller.liquidationIncentives(collateralA.vToken.address)).to.equal(floor); + }); + + it("rejects zero, so a mistaken value cannot silently fall back to the pool-wide incentive", async () => { + await expect( + comptroller.setMarketLiquidationIncentive(collateralA.vToken.address, 0), + ).to.be.revertedWithCustomError(comptroller, "InvalidLiquidationIncentive"); + expect(await comptroller.liquidationIncentives(collateralA.vToken.address)).to.equal(MARKET_A_INCENTIVE); + }); + + it("stores the value and reports the one it replaced", async () => { + const updated = parseUnits("1.35", 18); + + await expect(comptroller.setMarketLiquidationIncentive(collateralA.vToken.address, updated)) + .to.emit(comptroller, "NewMarketLiquidationIncentive") + .withArgs(collateralA.vToken.address, MARKET_A_INCENTIVE, updated); + expect(await comptroller.liquidationIncentives(collateralA.vToken.address)).to.equal(updated); + }); + }); + + describe("liquidationIncentiveMantissa", () => { + /// The getter answers for `msg.sender`, and a signer-bound contract cannot override `from`, so the call goes out + /// through the provider instead. + const asCaller = (from: string) => comptroller.connect(ethers.provider).liquidationIncentiveMantissa({ from }); + + it("answers the pool-wide value to a caller that is not a market of this pool", async () => { + expect(await comptroller.liquidationIncentiveMantissa()).to.equal(POOL_WIDE_INCENTIVE); + expect(await asCaller(liquidator.address)).to.equal(POOL_WIDE_INCENTIVE); + }); + + it("answers a calling market with the incentive that prices its own collateral", async () => { + // `VToken._seize` and `VToken.setProtocolSeizeShare` both read this getter on themselves, so the answer has to + // depend on which market is asking. + expect(await asCaller(collateralA.vToken.address)).to.equal(MARKET_A_INCENTIVE); + expect(await asCaller(collateralB.vToken.address)).to.equal(POOL_WIDE_INCENTIVE); + }); + }); + + describe("effectiveLiquidationIncentive", () => { + it("reports a market's incentive without depending on who is asking", async () => { + expect(await comptroller.effectiveLiquidationIncentive(collateralA.vToken.address)).to.equal(MARKET_A_INCENTIVE); + expect(await comptroller.effectiveLiquidationIncentive(collateralB.vToken.address)).to.equal(POOL_WIDE_INCENTIVE); + expect(await comptroller.effectiveLiquidationIncentive(liquidator.address)).to.equal(POOL_WIDE_INCENTIVE); + }); + }); + + describe("setLiquidationIncentive", () => { + it("guards the floor a default-share market needs, with a custom error", async () => { + // Upstream stops at 1e18. This fork refuses anything below `1e18 + the default protocol seize share`, because a + // market listed with no incentive of its own falls back to this value and carries that share. + const floor = parseUnits("1.05", 18); + + await expect(comptroller.setLiquidationIncentive(floor.sub(1))).to.be.revertedWithCustomError( + comptroller, + "InvalidLiquidationIncentive", + ); + await expect(comptroller.setLiquidationIncentive(ONE)).to.be.revertedWithCustomError( + comptroller, + "InvalidLiquidationIncentive", + ); + + await expect(comptroller.setLiquidationIncentive(floor)).to.emit(comptroller, "NewLiquidationIncentive"); + }); + + it("keeps serving as the fallback for markets without a value of their own", async () => { + const updated = parseUnits("1.5", 18); + await comptroller.setLiquidationIncentive(updated); + await givePositionWithDebt(parseUnits("250", 18)); + + // A keeps its own 1.1 while B now divides by the new pool-wide 1.5. + const expected = COLLATERAL_A.mul(ONE).div(MARKET_A_INCENTIVE).add(COLLATERAL_B.mul(ONE).div(updated)); + await expect(comptroller.connect(liquidator).liquidateAccount(borrower.address, [])) + .to.be.revertedWithCustomError(comptroller, "DebtExceedsClearableAmount") + .withArgs(parseUnits("250", 18), expected); + }); + }); +}); diff --git a/tests/hardhat/Spoke/markets.ts b/tests/hardhat/Spoke/markets.ts new file mode 100644 index 000000000..a936a478f --- /dev/null +++ b/tests/hardhat/Spoke/markets.ts @@ -0,0 +1,280 @@ +import { MockContract, smock } from "@defi-wonderland/smock"; +import { loadFixture } from "@nomicfoundation/hardhat-network-helpers"; +import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; +import chai from "chai"; +import { parseUnits } from "ethers/lib/utils"; +import { ethers } from "hardhat"; + +import { SpokeComptroller, VToken } from "../../../typechain"; +import { + Action, + ONE, + SpokeFixture, + TestMarket, + deploySpokeComptroller, + givePosition, + setRiskWeights, + unlistMarket, +} from "./fixtures"; + +const { expect } = chai; +chai.use(smock.matchers); + +const COLLATERAL = parseUnits("1000", 18); +const COLLATERAL_FACTOR = parseUnits("0.5", 18); +const LIQUIDATION_THRESHOLD = parseUnits("0.8", 18); + +/// Three markets, so the swap-and-pop removal below has a middle entry to remove. +const threeMarkets = () => deploySpokeComptroller({ marketCount: 3 }); + +describe("SpokeComptroller: market membership", () => { + let fixture: SpokeFixture; + let comptroller: MockContract; + let marketA: TestMarket; + let marketB: TestMarket; + let marketC: TestMarket; + let account: SignerWithAddress; + let router: SignerWithAddress; + + const assetsIn = () => comptroller.getAssetsIn(account.address); + const addresses = async () => (await assetsIn()).map(asset => asset.toString()); + + beforeEach(async () => { + [, account, router] = await ethers.getSigners(); + fixture = await loadFixture(threeMarkets); + fixture.resetPrices(); + ({ comptroller } = fixture); + [marketA, marketB, marketC] = fixture.markets; + }); + + describe("enterMarkets", () => { + it("adds the caller to every market in the list", async () => { + const tx = comptroller.connect(account).enterMarkets([marketA.vToken.address, marketB.vToken.address]); + + await expect(tx).to.emit(comptroller, "MarketEntered").withArgs(marketA.vToken.address, account.address); + await expect(tx).to.emit(comptroller, "MarketEntered").withArgs(marketB.vToken.address, account.address); + expect(await addresses()).to.deep.equal([marketA.vToken.address, marketB.vToken.address]); + }); + + it("returns NO_ERROR per market, which is what core tooling expects", async () => { + const results = await comptroller + .connect(account) + .callStatic.enterMarkets([marketA.vToken.address, marketB.vToken.address]); + + expect(results.map(result => result.toNumber())).to.deep.equal([0, 0]); + }); + + it("accepts an empty list", async () => { + await expect(comptroller.connect(account).enterMarkets([])).to.not.be.reverted; + expect(await addresses()).to.deep.equal([]); + }); + + it("does not enter the same market twice", async () => { + await comptroller.connect(account).enterMarkets([marketA.vToken.address]); + + // The second call is a no-op rather than an error, because the vToken hooks enter markets on the account's + // behalf and cannot know whether it is already in. + await expect( + comptroller.connect(account).enterMarkets([marketA.vToken.address, marketA.vToken.address]), + ).to.not.emit(comptroller, "MarketEntered"); + expect(await addresses()).to.deep.equal([marketA.vToken.address]); + }); + + it("rejects the whole list if any market is not listed, entering none of them", async () => { + const unlisted = await smock.fake("VToken"); + + await expect(comptroller.connect(account).enterMarkets([marketA.vToken.address, unlisted.address])) + .to.be.revertedWithCustomError(comptroller, "MarketNotListed") + .withArgs(unlisted.address); + expect(await addresses()).to.deep.equal([]); + }); + }); + + describe("enterMarketBehalf", () => { + it("enters the account rather than the caller", async () => { + // The whole point of the function: a supply router mints with `mintBehalf` and collateralises in the same + // transaction, so membership has to land on the supplier and not on the router that called it. + await expect(comptroller.connect(router).enterMarketBehalf(marketA.vToken.address, account.address)) + .to.emit(comptroller, "MarketEntered") + .withArgs(marketA.vToken.address, account.address); + + expect(await addresses()).to.deep.equal([marketA.vToken.address]); + expect(await comptroller.getAssetsIn(router.address)).to.deep.equal([]); + }); + + it("is a no-op when the account is already in the market", async () => { + await comptroller.connect(account).enterMarkets([marketA.vToken.address]); + + await expect(comptroller.connect(router).enterMarketBehalf(marketA.vToken.address, account.address)).to.not.emit( + comptroller, + "MarketEntered", + ); + expect(await addresses()).to.deep.equal([marketA.vToken.address]); + }); + + it("rejects a market that is not listed", async () => { + const unlisted = await smock.fake("VToken"); + + await expect(comptroller.connect(router).enterMarketBehalf(unlisted.address, account.address)) + .to.be.revertedWithCustomError(comptroller, "MarketNotListed") + .withArgs(unlisted.address); + }); + + it("rejects the zero account", async () => { + await expect( + comptroller.connect(router).enterMarketBehalf(marketA.vToken.address, ethers.constants.AddressZero), + ).to.be.revertedWithCustomError(comptroller, "ZeroAddressNotAllowed"); + }); + + it("respects the market's enter-market pause", async () => { + await comptroller.setActionsPaused([marketA.vToken.address], [Action.ENTER_MARKET], true); + + await expect(comptroller.connect(router).enterMarketBehalf(marketA.vToken.address, account.address)) + .to.be.revertedWithCustomError(comptroller, "ActionPaused") + .withArgs(marketA.vToken.address, Action.ENTER_MARKET); + }); + }); + + describe("exitMarket", () => { + beforeEach(async () => { + await setRiskWeights(comptroller, marketA, COLLATERAL_FACTOR, LIQUIDATION_THRESHOLD); + await setRiskWeights(comptroller, marketB, COLLATERAL_FACTOR, LIQUIDATION_THRESHOLD); + }); + + it("drops membership and the asset from the list", async () => { + await givePosition(comptroller, account, [{ market: marketA, collateral: COLLATERAL }]); + + await expect(comptroller.connect(account).exitMarket(marketA.vToken.address)) + .to.emit(comptroller, "MarketExited") + .withArgs(marketA.vToken.address, account.address); + + expect(await comptroller.checkMembership(account.address, marketA.vToken.address)).to.equal(false); + expect(await addresses()).to.deep.equal([]); + }); + + it("does nothing for an account that was never in the market", async () => { + await expect(comptroller.connect(account).exitMarket(marketA.vToken.address)).to.not.emit( + comptroller, + "MarketExited", + ); + }); + + it("refuses while the account still owes in that market", async () => { + await givePosition(comptroller, account, [{ market: marketA, collateral: COLLATERAL, borrow: ONE }]); + + await expect(comptroller.connect(account).exitMarket(marketA.vToken.address)).to.be.revertedWithCustomError( + comptroller, + "NonzeroBorrowBalance", + ); + }); + + it("refuses to withdraw collateral another market's borrow depends on", async () => { + // Exiting is a redeem of the whole balance as far as the liquidity check is concerned, so collateral backing + // a live borrow cannot leave even though the borrow is in a different market. + await givePosition(comptroller, account, [ + { market: marketA, collateral: COLLATERAL }, + { market: marketB, borrow: parseUnits("400", 18) }, + ]); + + await expect(comptroller.connect(account).exitMarket(marketA.vToken.address)).to.be.revertedWithCustomError( + comptroller, + "InsufficientLiquidity", + ); + }); + + it("allows the exit once the borrow is gone", async () => { + await givePosition(comptroller, account, [ + { market: marketA, collateral: COLLATERAL }, + { market: marketB, borrow: parseUnits("400", 18) }, + ]); + marketB.vToken.getAccountSnapshot.whenCalledWith(account.address).returns([0, 0, 0, ONE]); + + await expect(comptroller.connect(account).exitMarket(marketA.vToken.address)).to.emit( + comptroller, + "MarketExited", + ); + }); + + it("rejects a market this pool does not list", async () => { + const unlisted = await smock.fake("VToken"); + + await expect(comptroller.connect(account).exitMarket(unlisted.address)) + .to.be.revertedWithCustomError(comptroller, "MarketNotListed") + .withArgs(unlisted.address); + }); + + it("surfaces a market that fails to report the account's position", async () => { + // A nonzero error code from the vToken means the numbers behind it are meaningless, so nothing downstream is + // allowed to use them. + await givePosition(comptroller, account, [{ market: marketA, collateral: COLLATERAL }]); + marketA.vToken.getAccountSnapshot.whenCalledWith(account.address).returns([1, 0, 0, ONE]); + + await expect(comptroller.connect(account).exitMarket(marketA.vToken.address)) + .to.be.revertedWithCustomError(comptroller, "SnapshotError") + .withArgs(marketA.vToken.address, account.address); + }); + }); + + describe("the account's asset list", () => { + beforeEach(async () => { + await givePosition(comptroller, account, [{ market: marketA }, { market: marketB }, { market: marketC }]); + }); + + it("fills the hole left by an exit with the last entry", async () => { + // Removal is a swap and pop, so the order after an exit is not the order the markets were entered in. + // Nothing depends on that order, and this records it so a change to the removal is a deliberate one. + await comptroller.connect(account).exitMarket(marketA.vToken.address); + + expect(await addresses()).to.deep.equal([marketC.vToken.address, marketB.vToken.address]); + }); + + it("leaves the remaining memberships intact", async () => { + await comptroller.connect(account).exitMarket(marketB.vToken.address); + + expect(await comptroller.checkMembership(account.address, marketA.vToken.address)).to.equal(true); + expect(await comptroller.checkMembership(account.address, marketB.vToken.address)).to.equal(false); + expect(await comptroller.checkMembership(account.address, marketC.vToken.address)).to.equal(true); + }); + + it("appends a re-entered market at the end", async () => { + await comptroller.connect(account).exitMarket(marketA.vToken.address); + await comptroller.connect(account).enterMarkets([marketA.vToken.address]); + + expect(await addresses()).to.deep.equal([marketC.vToken.address, marketB.vToken.address, marketA.vToken.address]); + }); + + it("is empty for an account in no markets", async () => { + const [, , stranger] = await ethers.getSigners(); + + expect((await comptroller.getAssetsIn(stranger.address)).length).to.equal(0); + }); + }); + + describe("an unlisted market", () => { + beforeEach(async () => { + await givePosition(comptroller, account, [{ market: marketA }, { market: marketB }]); + await unlistMarket(comptroller, marketA); + }); + + it("drops out of the account's assets, so no liquidity calculation reaches it", async () => { + expect(await addresses()).to.deep.equal([marketB.vToken.address]); + }); + + it("stays in the stored list and keeps its membership flag", async () => { + // Unlisting filters at the read rather than clearing state, so the account is still recorded as a member of a + // market that no longer exists. Harmless while every path that matters reads through `getAssetsIn`, and the + // reason a re-listed market does not need its holders to enter again. + expect(await comptroller.accountAssets(account.address, 0)).to.equal(marketA.vToken.address); + expect(await comptroller.checkMembership(account.address, marketA.vToken.address)).to.equal(true); + }); + + it("is still reported by getAllMarkets", async () => { + // `allMarkets` is append-only: unlisting does not remove the entry, so consumers have to check `isListed` + // themselves. + const all = (await comptroller.getAllMarkets()).map(market => market.toString()); + + expect(all).to.include(marketA.vToken.address); + expect(await comptroller.isMarketListed(marketA.vToken.address)).to.equal(false); + }); + }); +}); diff --git a/tests/hardhat/Spoke/parity.ts b/tests/hardhat/Spoke/parity.ts new file mode 100644 index 000000000..99d3d4a52 --- /dev/null +++ b/tests/hardhat/Spoke/parity.ts @@ -0,0 +1,314 @@ +import { FakeContract, smock } from "@defi-wonderland/smock"; +import { loadFixture, setBalance } from "@nomicfoundation/hardhat-network-helpers"; +import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; +import chai from "chai"; +import { BigNumber, Contract, constants } from "ethers"; +import { Interface, parseEther, parseUnits } from "ethers/lib/utils"; +import { readFileSync } from "fs"; +import { artifacts, ethers, upgrades } from "hardhat"; +import { resolve } from "path"; + +import { AccessControlManager, PoolRegistry, ResilientOracleInterface, VToken } from "../../../typechain"; +import { MAX_LOOPS_LIMIT, ONE } from "./fixtures"; + +const { expect } = chai; +chai.use(smock.matchers); + +const SHARED = "Comptroller"; +const SPOKE = "SpokeComptroller"; + +const POOL_INCENTIVE = parseUnits("1.1", 18); +const MARKET_INCENTIVE = parseUnits("1.25", 18); +const CLOSE_FACTOR = parseUnits("0.5", 18); +const SUPPLY_CAP = parseUnits("1000", 18); + +/// Functions and events the spoke drops, because Prime does not exist on a spoke pool. +const REMOVED_FUNCTIONS = ["prime()", "setPrimeToken(address)"]; +const REMOVED_EVENTS = ["NewPrimeToken(address,address)"]; + +/// The spoke's own surface: per-market liquidation incentives, the two allowlists, and the bounded oracle. +const ADDED_FUNCTIONS = [ + "deviationBoundedOracle()", + "effectiveLiquidationIncentive(address)", + "enterMarketBehalf(address,address)", + "isAllowedLiquidator(address)", + "isAllowedSupplier(address,address)", + "isLiquidationAllowlistEnabled()", + "isSupplyAllowlistEnabled(address)", + "liquidationIncentives(address)", + "setAllowedLiquidator(address,bool)", + "setAllowedSupplier(address,address,bool)", + "setDeviationBoundedOracle(address)", + "setLiquidationAllowlistEnabled(bool)", + "setMarketLiquidationIncentive(address,uint256)", + "setSupplyAllowlistEnabled(address,bool)", +]; +const ADDED_EVENTS = [ + "AllowedLiquidatorUpdated(address,bool)", + "AllowedSupplierUpdated(address,address,bool)", + "LiquidationAllowlistEnabledUpdated(bool)", + "NewDeviationBoundedOracle(address,address)", + "NewMarketLiquidationIncentive(address,uint256,uint256)", + "SupplyAllowlistEnabledUpdated(address,bool)", +]; + +/// Roles the spoke adds. Everything upstream checks has to still be checked under the same string. +const ADDED_ROLES = [ + "enterMarketBehalf(address,address)", + "setAllowedLiquidator(address,bool)", + "setAllowedSupplier(address,address,bool)", + "setLiquidationAllowlistEnabled(bool)", + "setMarketLiquidationIncentive(address,uint256)", + "setSupplyAllowlistEnabled(address,bool)", +]; + +async function signatures(contractName: string, kind: "function" | "event"): Promise { + const { abi } = await artifacts.readArtifact(contractName); + return new Interface(abi).fragments + .filter(fragment => fragment.type === kind) + .map(fragment => fragment.format("sighash")) + .sort(); +} + +function roleStrings(sourcePath: string): string[] { + const source = readFileSync(resolve(__dirname, "../../..", sourcePath), "utf8"); + return [...source.matchAll(/_checkAccessAllowed\("([^"]+)"\)/g)].map(match => match[1]).sort(); +} + +interface Pool { + comptroller: Contract; + markets: FakeContract[]; + oracle: FakeContract; +} + +/// A pool of either implementation, configured the same way. Both take the pool registry as a constructor argument +/// and share the same initializer, which is what makes one setup function enough. +async function deployPool(contractName: string): Promise { + const poolRegistry = await smock.fake("PoolRegistry"); + const acm = await smock.fake("AccessControlManager"); + acm.isAllowedToCall.returns(true); + const oracle = await smock.fake("ResilientOracleInterface"); + await setBalance(poolRegistry.address, parseEther("1")); + + const factory = await ethers.getContractFactory(contractName); + const comptroller = await upgrades.deployProxy(factory, [MAX_LOOPS_LIMIT, acm.address], { + constructorArgs: [poolRegistry.address], + initializer: "initialize(uint256,address)", + }); + + await comptroller.setPriceOracle(oracle.address); + await comptroller.setCloseFactor(CLOSE_FACTOR); + await comptroller.setLiquidationIncentive(POOL_INCENTIVE); + + const markets: FakeContract[] = []; + for (let i = 0; i < 2; ++i) { + const vToken = await smock.fake("VToken"); + vToken.isVToken.returns(true); + vToken.exchangeRateStored.returns(ONE); + vToken.getAccountSnapshot.returns([0, 0, 0, ONE]); + vToken.totalSupply.returns(0); + oracle.getUnderlyingPrice.whenCalledWith(vToken.address).returns(ONE); + + await comptroller.connect(poolRegistry.wallet).supportMarket(vToken.address); + await comptroller.setMarketSupplyCaps([vToken.address], [SUPPLY_CAP]); + await comptroller.setMarketBorrowCaps([vToken.address], [constants.MaxUint256]); + markets.push(vToken); + } + + // The spoke's deviation-bounded oracle is deliberately left unset: none of the paths below read it, and leaving it + // out keeps the two pools configured identically. + return { comptroller, markets, oracle }; +} + +async function deployBothPools(): Promise> { + return { [SHARED]: await deployPool(SHARED), [SPOKE]: await deployPool(SPOKE) }; +} + +// The spoke is a hand-maintained fork of `Comptroller`, and everything built against the shared implementation +// (the lens, the periphery contracts, VIP calldata, subgraphs) assumes the two behave the same wherever the fork +// was not meant to change anything. These tests state that assumption, so a re-sync that quietly drops a function, +// renames a role or changes a number has to fail somewhere other than on a live pool. +describe("SpokeComptroller: parity with the shared Comptroller", () => { + let pools: Record; + let account: SignerWithAddress; + + const both = () => Object.entries(pools); + + /// Runs the same call against both implementations. The fork replaced upstream's revert strings with custom + /// errors, so the two agree on the outcome rather than on the error, and the assertions only ask for that. + async function bothReject(call: (pool: Pool) => Promise): Promise { + for (const [name, pool] of both()) { + await expect(call(pool), `${name} should reject`).to.be.reverted; + } + } + + async function bothAccept(call: (pool: Pool) => Promise): Promise { + for (const [name, pool] of both()) { + await expect(call(pool), `${name} should accept`).to.not.be.reverted; + } + } + + describe("surface", () => { + it("keeps every function upstream exposes, apart from the Prime pair", async () => { + const shared = await signatures(SHARED, "function"); + const spoke = await signatures(SPOKE, "function"); + + expect(shared.filter(signature => !spoke.includes(signature))).to.deep.equal(REMOVED_FUNCTIONS); + expect(spoke.filter(signature => !shared.includes(signature))).to.deep.equal(ADDED_FUNCTIONS); + }); + + it("keeps every event upstream emits, apart from the Prime one", async () => { + // Indexers key off event signatures, so a renamed or reshaped event breaks them silently. + const shared = await signatures(SHARED, "event"); + const spoke = await signatures(SPOKE, "event"); + + expect(shared.filter(signature => !spoke.includes(signature))).to.deep.equal(REMOVED_EVENTS); + expect(spoke.filter(signature => !shared.includes(signature))).to.deep.equal(ADDED_EVENTS); + }); + + it("keeps liquidationIncentiveMantissa() in the ABI even though it answers per caller now", async () => { + // Same selector, and for anything that is not a market of the pool the same answer. That is what keeps the + // lens and every off-chain reader working. + expect(await signatures(SPOKE, "function")).to.include("liquidationIncentiveMantissa()"); + expect(await signatures(SHARED, "function")).to.include("liquidationIncentiveMantissa()"); + }); + + it("checks every access-controlled role under the same string", async () => { + // The ACM hashes the string, so a role that upstream spells one way and the spoke another needs a different + // grant in the listing VIP. Copying upstream's grants would then leave the setter permanently unauthorized. + const shared = roleStrings("contracts/Comptroller.sol"); + const spoke = roleStrings("contracts/Spoke/SpokeComptroller.sol"); + + expect(shared.filter(role => !spoke.includes(role))).to.deep.equal([]); + expect(spoke.filter(role => !shared.includes(role))).to.deep.equal(ADDED_ROLES); + }); + }); + + describe("shared behaviour", () => { + beforeEach(async () => { + [, account] = await ethers.getSigners(); + pools = await loadFixture(deployBothPools); + + // `loadFixture` rewinds the chain but hands back the same fakes, and a fake's behaviour lives in JavaScript + // rather than in the EVM. A price one test moves is still in place for the next one without this. + for (const [, pool] of both()) { + pool.oracle.getUnderlyingPrice.reset(); + for (const market of pool.markets) { + pool.oracle.getUnderlyingPrice.whenCalledWith(market.address).returns(ONE); + } + } + }); + + it("prices a seizure identically while no market carries its own incentive", async () => { + const cases = [ + { repay: parseUnits("100", 18), borrowedPrice: ONE, collateralPrice: ONE }, + { repay: parseUnits("37.5", 18), borrowedPrice: parseUnits("2", 18), collateralPrice: ONE }, + { repay: parseUnits("1", 18), borrowedPrice: ONE, collateralPrice: parseUnits("3", 18) }, + ]; + + for (const { repay, borrowedPrice, collateralPrice } of cases) { + const results: BigNumber[] = []; + for (const [, pool] of both()) { + const [borrowed, collateral] = pool.markets; + pool.oracle.getUnderlyingPrice.whenCalledWith(borrowed.address).returns(borrowedPrice); + pool.oracle.getUnderlyingPrice.whenCalledWith(collateral.address).returns(collateralPrice); + + const [error, seizeTokens] = await pool.comptroller.liquidateCalculateSeizeTokens( + borrowed.address, + collateral.address, + repay, + ); + expect(error).to.equal(0); + results.push(seizeTokens); + } + + // repay * incentive * borrowedPrice / (collateralPrice * exchangeRate) + const expected = repay.mul(POOL_INCENTIVE).div(ONE).mul(borrowedPrice).div(collateralPrice); + expect(results[0]).to.equal(expected); + expect(results[1]).to.equal(results[0]); + } + }); + + it("diverges only once the collateral market carries its own incentive", async () => { + const [borrowed, collateral] = pools[SPOKE].markets; + await pools[SPOKE].comptroller.setMarketLiquidationIncentive(collateral.address, MARKET_INCENTIVE); + const repay = parseUnits("100", 18); + + // Only one of the two ABIs names the return values, so both are read positionally. + const seizeTokens = async (pool: Pool) => + ( + await pool.comptroller.liquidateCalculateSeizeTokens(pool.markets[0].address, pool.markets[1].address, repay) + )[1]; + + expect(await seizeTokens(pools[SHARED])).to.equal(repay.mul(POOL_INCENTIVE).div(ONE)); + expect(await seizeTokens(pools[SPOKE])).to.equal(repay.mul(MARKET_INCENTIVE).div(ONE)); + expect(borrowed.address).to.not.equal(collateral.address); + }); + + it("reports the pool-wide incentive to an ordinary caller", async () => { + for (const [name, pool] of both()) { + expect(await pool.comptroller.liquidationIncentiveMantissa(), name).to.equal(POOL_INCENTIVE); + } + }); + + it("draws the close factor bounds in the same place", async () => { + await bothAccept(pool => pool.comptroller.setCloseFactor(parseUnits("0.05", 18))); + await bothAccept(pool => pool.comptroller.setCloseFactor(parseUnits("0.9", 18))); + await bothReject(pool => pool.comptroller.setCloseFactor(parseUnits("0.05", 18).sub(1))); + await bothReject(pool => pool.comptroller.setCloseFactor(parseUnits("0.9", 18).add(1))); + }); + + it("draws the collateral factor and threshold bounds in the same place", async () => { + const weights = (pool: Pool, factor: BigNumber, threshold: BigNumber) => + pool.comptroller.setCollateralFactor(pool.markets[0].address, factor, threshold); + + await bothAccept(pool => weights(pool, parseUnits("0.95", 18), ONE)); + await bothReject(pool => weights(pool, parseUnits("0.95", 18).add(1), ONE)); + await bothReject(pool => weights(pool, parseUnits("0.5", 18), ONE.add(1))); + // A threshold below the collateral factor would let an account borrow itself straight into liquidation. + await bothReject(pool => weights(pool, parseUnits("0.5", 18), parseUnits("0.4", 18))); + }); + + it("draws a higher floor under the pool-wide liquidation incentive than the shared pool", async () => { + // Deliberate divergence. The shared pool stops at 1e18, which pays a default-share market's liquidator 5% less + // than it repaid. The spoke refuses anything below `1e18 + the default seize share`. + const spokeFloor = parseUnits("1.05", 18); + + await bothAccept(pool => pool.comptroller.setLiquidationIncentive(spokeFloor)); + await bothReject(pool => pool.comptroller.setLiquidationIncentive(ONE.sub(1))); + + // The gap between the two floors: accepted by the shared pool, refused by the spoke. + await pools[SHARED].comptroller.setLiquidationIncentive(ONE); + await expect(pools[SPOKE].comptroller.setLiquidationIncentive(spokeFloor.sub(1))).to.be.revertedWithCustomError( + pools[SPOKE].comptroller, + "InvalidLiquidationIncentive", + ); + }); + + it("meters supply against the cap the same way", async () => { + const mint = (pool: Pool, amount: BigNumber) => + pool.comptroller.callStatic.preMintHook(pool.markets[0].address, account.address, amount); + + await bothAccept(pool => mint(pool, SUPPLY_CAP)); + await bothReject(pool => mint(pool, SUPPLY_CAP.add(1))); + }); + + it("records the same asset list for an account", async () => { + for (const [name, pool] of both()) { + const addresses = pool.markets.map(market => market.address); + await pool.comptroller.connect(account).enterMarkets(addresses); + + expect((await pool.comptroller.getAssetsIn(account.address)).map(String), name).to.deep.equal(addresses); + expect(await pool.comptroller.checkMembership(account.address, addresses[0]), name).to.equal(true); + } + }); + + it("rejects an unlisted market from the same entry points", async () => { + const unlisted = await smock.fake("VToken"); + + await bothReject(pool => pool.comptroller.connect(account).enterMarkets([unlisted.address])); + await bothReject(pool => pool.comptroller.callStatic.preMintHook(unlisted.address, account.address, ONE)); + await bothReject(pool => pool.comptroller.setCollateralFactor(unlisted.address, parseUnits("0.5", 18), ONE)); + }); + }); +}); diff --git a/tests/hardhat/Spoke/pauses.ts b/tests/hardhat/Spoke/pauses.ts new file mode 100644 index 000000000..db2420de6 --- /dev/null +++ b/tests/hardhat/Spoke/pauses.ts @@ -0,0 +1,236 @@ +import { MockContract, smock } from "@defi-wonderland/smock"; +import { loadFixture, setBalance } from "@nomicfoundation/hardhat-network-helpers"; +import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; +import chai from "chai"; +import { parseEther, parseUnits } from "ethers/lib/utils"; +import { ethers } from "hardhat"; + +import { SpokeComptroller } from "../../../typechain"; +import { Action, SpokeFixture, TestMarket, deploySpokeComptroller, givePosition, setRiskWeights } from "./fixtures"; + +const { expect } = chai; +chai.use(smock.matchers); + +const COLLATERAL = parseUnits("1000", 18); +const BORROW = parseUnits("100", 18); +const COLLATERAL_FACTOR = parseUnits("0.5", 18); +const LIQUIDATION_THRESHOLD = parseUnits("0.8", 18); + +/// One path per action, each reaching the pause check of exactly one `(market, action)` pair. `run` has to succeed +/// while nothing is paused, which is what makes the matrix below meaningful in both directions. +interface PausePath { + action: number; + name: string; + market: () => TestMarket; + run: () => Promise; +} + +describe("SpokeComptroller: action pauses", () => { + let fixture: SpokeFixture; + let comptroller: MockContract; + let collateral: TestMarket; + let debt: TestMarket; + let spare: TestMarket; + let borrower: SignerWithAddress; + let liquidator: SignerWithAddress; + + /// Collateral in one market, debt in another, and membership without a balance in a third. The third market is + /// what makes an exit succeed: leaving either of the other two would fail the liquidity check instead. + async function positioned(): Promise { + const f = await deploySpokeComptroller({ marketCount: 3 }); + const [collateralMarket, debtMarket] = f.markets; + await setRiskWeights(f.comptroller, collateralMarket, COLLATERAL_FACTOR, LIQUIDATION_THRESHOLD); + await setRiskWeights(f.comptroller, debtMarket, COLLATERAL_FACTOR, LIQUIDATION_THRESHOLD); + await f.comptroller.setCloseFactor(parseUnits("0.5", 18)); + return f; + } + + const paths: PausePath[] = [ + { + action: Action.MINT, + name: "minting", + market: () => collateral, + run: () => comptroller.callStatic.preMintHook(collateral.vToken.address, borrower.address, 1), + }, + { + action: Action.REDEEM, + name: "redeeming", + market: () => collateral, + run: () => comptroller.callStatic.preRedeemHook(collateral.vToken.address, borrower.address, 1), + }, + { + action: Action.BORROW, + name: "borrowing", + market: () => debt, + run: () => comptroller.connect(borrower).callStatic.preBorrowHook(debt.vToken.address, borrower.address, 1), + }, + { + action: Action.REPAY, + name: "repaying", + market: () => debt, + run: () => comptroller.callStatic.preRepayHook(debt.vToken.address, borrower.address), + }, + { + action: Action.SEIZE, + name: "seizing", + market: () => collateral, + run: () => + comptroller.callStatic.preSeizeHook( + collateral.vToken.address, + debt.vToken.address, + liquidator.address, + borrower.address, + ), + }, + { + action: Action.LIQUIDATE, + name: "liquidating", + market: () => debt, + run: () => + comptroller.callStatic.preLiquidateHook( + debt.vToken.address, + collateral.vToken.address, + borrower.address, + 1, + true, + ), + }, + { + action: Action.TRANSFER, + name: "transferring", + market: () => collateral, + run: () => + comptroller.callStatic.preTransferHook(collateral.vToken.address, borrower.address, liquidator.address, 1), + }, + { + action: Action.ENTER_MARKET, + name: "entering", + market: () => spare, + run: () => comptroller.connect(liquidator).callStatic.enterMarkets([spare.vToken.address]), + }, + { + action: Action.EXIT_MARKET, + name: "exiting", + market: () => spare, + run: () => comptroller.connect(borrower).callStatic.exitMarket(spare.vToken.address), + }, + ]; + + beforeEach(async () => { + [, borrower, liquidator] = await ethers.getSigners(); + fixture = await loadFixture(positioned); + fixture.resetPrices(); + ({ comptroller } = fixture); + [collateral, debt, spare] = fixture.markets; + for (const market of fixture.markets) { + await setBalance(market.vToken.address, parseEther("1")); + } + await givePosition(comptroller, borrower, [ + { market: collateral, collateral: COLLATERAL }, + { market: debt, borrow: BORROW }, + { market: spare }, + ]); + }); + + it("leaves every action unpaused on a newly listed market", async () => { + for (const path of paths) { + expect(await comptroller.actionPaused(path.market().vToken.address, path.action)).to.equal(false); + await expect(path.run(), `${path.name} should be allowed`).to.not.be.reverted; + } + }); + + // The pause state is keyed by `(market, action)`, so each pause has to stop exactly one path and leave the other + // eight alone. Anything that reads the wrong action, or checks the wrong market, shows up here as a path that + // either survives its own pause or dies under someone else's. + for (const path of paths) { + describe(`pausing ${path.name}`, () => { + beforeEach(async () => { + await comptroller.setActionsPaused([path.market().vToken.address], [path.action], true); + }); + + it("blocks its own path", async () => { + await expect(path.run()) + .to.be.revertedWithCustomError(comptroller, "ActionPaused") + .withArgs(path.market().vToken.address, path.action); + }); + + it("blocks nothing else", async () => { + for (const other of paths.filter(candidate => candidate !== path)) { + await expect(other.run(), `${other.name} should be unaffected`).to.not.be.reverted; + } + }); + + it("is lifted by the same call", async () => { + await comptroller.setActionsPaused([path.market().vToken.address], [path.action], false); + + await expect(path.run()).to.not.be.reverted; + }); + }); + } + + describe("scope", () => { + it("is per market, so pausing one market leaves the same action open on another", async () => { + await comptroller.setActionsPaused([collateral.vToken.address], [Action.MINT], true); + + await expect(comptroller.callStatic.preMintHook(debt.vToken.address, borrower.address, 1)).to.not.be.reverted; + }); + + it("applies every market against every action in one call", async () => { + const both = [collateral.vToken.address, debt.vToken.address]; + + await expect(comptroller.setActionsPaused(both, [Action.MINT, Action.BORROW], true)) + .to.emit(comptroller, "ActionPausedMarket") + .withArgs(collateral.vToken.address, Action.MINT, true); + + for (const market of both) { + for (const action of [Action.MINT, Action.BORROW]) { + expect(await comptroller.actionPaused(market, action)).to.equal(true); + } + } + expect(await comptroller.actionPaused(collateral.vToken.address, Action.REDEEM)).to.equal(false); + }); + + // A liquidation touches two markets, and the two halves are paused from opposite ends: LIQUIDATE on the market + // whose debt is being repaid, SEIZE on the market whose collateral is being taken. Getting this backwards would + // leave a market that looks paused still liquidatable. + it("takes liquidating from the borrowed market and seizing from the collateral", async () => { + await comptroller.setActionsPaused([collateral.vToken.address], [Action.LIQUIDATE], true); + await comptroller.setActionsPaused([debt.vToken.address], [Action.SEIZE], true); + + await expect( + comptroller.callStatic.preLiquidateHook( + debt.vToken.address, + collateral.vToken.address, + borrower.address, + 1, + true, + ), + ).to.not.be.reverted; + await expect( + comptroller.callStatic.preSeizeHook( + collateral.vToken.address, + debt.vToken.address, + liquidator.address, + borrower.address, + ), + ).to.not.be.reverted; + }); + + it("stops a market from entering a new borrower through the borrow hook", async () => { + // The borrow hook enters the borrower itself when it is not already a member, and that write goes through + // the same entry check as `enterMarkets`. + await comptroller.setActionsPaused([spare.vToken.address], [Action.ENTER_MARKET], true); + + await expect(comptroller.connect(spare.vToken.wallet).preBorrowHook(spare.vToken.address, liquidator.address, 1)) + .to.be.revertedWithCustomError(comptroller, "ActionPaused") + .withArgs(spare.vToken.address, Action.ENTER_MARKET); + }); + + it("does not stop a borrow by an account already in the market", async () => { + await comptroller.setActionsPaused([debt.vToken.address], [Action.ENTER_MARKET], true); + + await expect(comptroller.connect(borrower).callStatic.preBorrowHook(debt.vToken.address, borrower.address, 1)).to + .not.be.reverted; + }); + }); +}); diff --git a/tests/hardhat/Spoke/rewards.ts b/tests/hardhat/Spoke/rewards.ts new file mode 100644 index 000000000..d5aa2a427 --- /dev/null +++ b/tests/hardhat/Spoke/rewards.ts @@ -0,0 +1,329 @@ +import { FakeContract, MockContract, smock } from "@defi-wonderland/smock"; +import { loadFixture, setBalance } from "@nomicfoundation/hardhat-network-helpers"; +import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; +import chai from "chai"; +import { parseEther, parseUnits } from "ethers/lib/utils"; +import { ethers } from "hardhat"; + +import { RewardsDistributor, SpokeComptroller, VToken } from "../../../typechain"; +import { + Action, + ONE, + SpokeFixture, + TestMarket, + deploySpokeComptroller, + givePosition, + setRiskWeights, +} from "./fixtures"; + +const { expect } = chai; +chai.use(smock.matchers); + +const COLLATERAL = parseUnits("1000", 18); +const COLLATERAL_FACTOR = parseUnits("0.5", 18); +const LIQUIDATION_THRESHOLD = parseUnits("0.8", 18); +const BORROW_INDEX = parseUnits("1.05", 18); +const SUPPLY_SPEED = parseUnits("3", 18); +const BORROW_SPEED = parseUnits("7", 18); + +/// A distributor whose reward token address is distinguishable in assertions. +async function fakeDistributor(rewardToken: string): Promise> { + const distributor = await smock.fake("RewardsDistributor"); + distributor.rewardToken.returns(rewardToken); + return distributor; +} + +describe("SpokeComptroller: rewards distributors", () => { + let fixture: SpokeFixture; + let comptroller: MockContract; + let collateral: TestMarket; + let debt: TestMarket; + let borrower: SignerWithAddress; + let liquidator: SignerWithAddress; + let distributor: FakeContract; + + beforeEach(async () => { + [, borrower, liquidator] = await ethers.getSigners(); + fixture = await loadFixture(deploySpokeComptroller); + fixture.resetPrices(); + ({ comptroller } = fixture); + [collateral, debt] = fixture.markets; + distributor = await fakeDistributor("0x000000000000000000000000000000000000dEaD"); + }); + + describe("addRewardsDistributor", () => { + it("initializes every listed market and reports the reward token", async () => { + await expect(comptroller.addRewardsDistributor(distributor.address)) + .to.emit(comptroller, "NewRewardsDistributor") + .withArgs(distributor.address, "0x000000000000000000000000000000000000dEaD"); + + expect(distributor.initializeMarket).to.have.callCount(2); + expect(distributor.initializeMarket).to.have.been.calledWith(collateral.vToken.address); + expect(distributor.initializeMarket).to.have.been.calledWith(debt.vToken.address); + }); + + it("is restricted to the owner rather than to a role", async () => { + // Every other setter on this contract goes through the AccessControlManager. This one is `onlyOwner`, which + // on a live pool is the timelock rather than a role holder. + await expect(comptroller.connect(borrower).addRewardsDistributor(distributor.address)).to.be.revertedWith( + "Ownable: caller is not the owner", + ); + }); + + it("records it in the pool's distributor list", async () => { + await comptroller.addRewardsDistributor(distributor.address); + const second = await fakeDistributor("0x000000000000000000000000000000000000bEEF"); + await comptroller.addRewardsDistributor(second.address); + + const listed = (await comptroller.getRewardDistributors()).map(address => address.toString()); + expect(listed).to.deep.equal([distributor.address, second.address]); + }); + + it("initializes a market listed after it was added", async () => { + await comptroller.addRewardsDistributor(distributor.address); + distributor.initializeMarket.reset(); + + const late = await smock.fake("VToken"); + late.isVToken.returns(true); + await setBalance(fixture.poolRegistry.address, parseEther("1")); + await comptroller.connect(fixture.poolRegistry.wallet).supportMarket(late.address); + + expect(distributor.initializeMarket).to.have.been.calledOnceWith(late.address); + }); + }); + + // Every hook that changes a balance has to move the flywheel for the accounts involved before the balance + // changes, or the account accrues rewards at the wrong index. These pin which accounts each hook covers: the + // hooks that move collateral cover both sides of the transfer, the borrow-side hooks cover the borrower only. + describe("flywheel updates", () => { + beforeEach(async () => { + await comptroller.addRewardsDistributor(distributor.address); + await setRiskWeights(comptroller, collateral, COLLATERAL_FACTOR, LIQUIDATION_THRESHOLD); + await setRiskWeights(comptroller, debt, COLLATERAL_FACTOR, LIQUIDATION_THRESHOLD); + debt.vToken.borrowIndex.returns(BORROW_INDEX); + await givePosition(comptroller, borrower, [ + { market: collateral, collateral: COLLATERAL }, + { market: debt, borrow: parseUnits("100", 18) }, + ]); + distributor.updateRewardTokenSupplyIndex.reset(); + distributor.distributeSupplierRewardToken.reset(); + distributor.updateRewardTokenBorrowIndex.reset(); + distributor.distributeBorrowerRewardToken.reset(); + }); + + it("credits the minter on the supply side", async () => { + await comptroller.preMintHook(collateral.vToken.address, borrower.address, ONE); + + expect(distributor.updateRewardTokenSupplyIndex).to.have.been.calledOnceWith(collateral.vToken.address); + expect(distributor.distributeSupplierRewardToken).to.have.been.calledOnceWith( + collateral.vToken.address, + borrower.address, + ); + }); + + it("credits the redeemer on the supply side", async () => { + await comptroller.preRedeemHook(collateral.vToken.address, borrower.address, ONE); + + expect(distributor.updateRewardTokenSupplyIndex).to.have.been.calledOnceWith(collateral.vToken.address); + expect(distributor.distributeSupplierRewardToken).to.have.been.calledOnceWith( + collateral.vToken.address, + borrower.address, + ); + }); + + it("credits the borrower at the market's current borrow index", async () => { + await comptroller.connect(borrower).preBorrowHook(debt.vToken.address, borrower.address, ONE); + + expect(distributor.updateRewardTokenBorrowIndex).to.have.been.calledOnceWith(debt.vToken.address, [BORROW_INDEX]); + expect(distributor.distributeBorrowerRewardToken).to.have.been.calledOnceWith( + debt.vToken.address, + borrower.address, + [BORROW_INDEX], + ); + }); + + it("credits the borrower on a repayment too", async () => { + await comptroller.preRepayHook(debt.vToken.address, borrower.address); + + expect(distributor.updateRewardTokenBorrowIndex).to.have.been.calledOnceWith(debt.vToken.address, [BORROW_INDEX]); + expect(distributor.distributeBorrowerRewardToken).to.have.been.calledOnceWith( + debt.vToken.address, + borrower.address, + [BORROW_INDEX], + ); + }); + + it("credits both sides of a seizure, since the collateral changes hands", async () => { + await comptroller.preSeizeHook( + collateral.vToken.address, + debt.vToken.address, + liquidator.address, + borrower.address, + ); + + expect(distributor.updateRewardTokenSupplyIndex).to.have.been.calledOnceWith(collateral.vToken.address); + expect(distributor.distributeSupplierRewardToken).to.have.callCount(2); + expect(distributor.distributeSupplierRewardToken).to.have.been.calledWith( + collateral.vToken.address, + borrower.address, + ); + expect(distributor.distributeSupplierRewardToken).to.have.been.calledWith( + collateral.vToken.address, + liquidator.address, + ); + }); + + it("credits both sides of a transfer", async () => { + await comptroller.preTransferHook(collateral.vToken.address, borrower.address, liquidator.address, ONE); + + expect(distributor.updateRewardTokenSupplyIndex).to.have.been.calledOnceWith(collateral.vToken.address); + expect(distributor.distributeSupplierRewardToken).to.have.callCount(2); + }); + + it("runs every distributor the pool has, not just the first", async () => { + const second = await fakeDistributor("0x000000000000000000000000000000000000bEEF"); + await comptroller.addRewardsDistributor(second.address); + + await comptroller.preMintHook(collateral.vToken.address, borrower.address, ONE); + + expect(distributor.distributeSupplierRewardToken).to.have.callCount(1); + expect(second.distributeSupplierRewardToken).to.have.callCount(1); + }); + }); + + describe("getRewardsByMarket", () => { + it("is empty for a pool with no distributor", async () => { + expect(await comptroller.getRewardsByMarket(collateral.vToken.address)).to.deep.equal([]); + expect(await comptroller.getRewardDistributors()).to.deep.equal([]); + }); + + it("reports each distributor's token and both speeds for the market", async () => { + distributor.rewardTokenSupplySpeeds.whenCalledWith(collateral.vToken.address).returns(SUPPLY_SPEED); + distributor.rewardTokenBorrowSpeeds.whenCalledWith(collateral.vToken.address).returns(BORROW_SPEED); + await comptroller.addRewardsDistributor(distributor.address); + + const [speeds] = await comptroller.getRewardsByMarket(collateral.vToken.address); + + expect(speeds.rewardToken).to.equal("0x000000000000000000000000000000000000dEaD"); + expect(speeds.supplySpeed).to.equal(SUPPLY_SPEED); + expect(speeds.borrowSpeed).to.equal(BORROW_SPEED); + }); + + it("reports zero speeds for a market the distributor pays nothing on", async () => { + await comptroller.addRewardsDistributor(distributor.address); + + const [speeds] = await comptroller.getRewardsByMarket(debt.vToken.address); + + expect(speeds.supplySpeed).to.equal(0); + expect(speeds.borrowSpeed).to.equal(0); + }); + }); +}); + +/// Every unbounded loop in the contract is guarded by this limit. The pool it is set on has to start low for the +/// guard to be reachable at all, which is why these run against their own fixture. +describe("SpokeComptroller: max loops limit", () => { + const LIMIT = 2; + const tightLoops = () => deploySpokeComptroller({ maxLoopsLimit: LIMIT }); + + let fixture: SpokeFixture; + let comptroller: MockContract; + let marketA: TestMarket; + let marketB: TestMarket; + let stranger: SignerWithAddress; + + beforeEach(async () => { + [, stranger] = await ethers.getSigners(); + fixture = await loadFixture(tightLoops); + fixture.resetPrices(); + ({ comptroller } = fixture); + [marketA, marketB] = fixture.markets; + }); + + it("can only be raised", async () => { + // The setter refuses to lower it, so a pool that is given too high a limit cannot be walked back. Worth + // knowing before a listing VIP picks the number. + expect(await comptroller.maxLoopsLimit()).to.equal(LIMIT); + + await expect(comptroller.setMaxLoopsLimit(LIMIT)).to.be.revertedWith("Comptroller: Invalid maxLoopsLimit"); + await expect(comptroller.setMaxLoopsLimit(LIMIT - 1)).to.be.revertedWith("Comptroller: Invalid maxLoopsLimit"); + + await expect(comptroller.setMaxLoopsLimit(LIMIT + 1)) + .to.emit(comptroller, "MaxLoopsLimitUpdated") + .withArgs(LIMIT, LIMIT + 1); + }); + + it("is restricted to the owner", async () => { + await expect(comptroller.connect(stranger).setMaxLoopsLimit(LIMIT + 1)).to.be.revertedWith( + "Ownable: caller is not the owner", + ); + }); + + it("bounds the number of markets the pool can list", async () => { + const third = await smock.fake("VToken"); + third.isVToken.returns(true); + await setBalance(fixture.poolRegistry.address, parseEther("1")); + + await expect(comptroller.connect(fixture.poolRegistry.wallet).supportMarket(third.address)) + .to.be.revertedWithCustomError(comptroller, "MaxLoopsLimitExceeded") + .withArgs(LIMIT, LIMIT + 1); + }); + + it("bounds both cap setters by the number of markets in the call", async () => { + const markets = [marketA.vToken.address, marketB.vToken.address, marketA.vToken.address]; + const values = [0, 0, 0]; + + await expect(comptroller.setMarketBorrowCaps(markets, values)) + .to.be.revertedWithCustomError(comptroller, "MaxLoopsLimitExceeded") + .withArgs(LIMIT, 3); + await expect(comptroller.setMarketSupplyCaps(markets, values)).to.be.revertedWithCustomError( + comptroller, + "MaxLoopsLimitExceeded", + ); + }); + + it("bounds setActionsPaused by markets multiplied by actions", async () => { + // Two markets against two actions is four iterations, so a limit of two rejects a call that looks small. + await expect( + comptroller.setActionsPaused( + [marketA.vToken.address, marketB.vToken.address], + [Action.MINT, Action.BORROW], + true, + ), + ) + .to.be.revertedWithCustomError(comptroller, "MaxLoopsLimitExceeded") + .withArgs(LIMIT, 4); + }); + + it("bounds the number of distributors the pool can hold", async () => { + for (const token of ["0x000000000000000000000000000000000000dEaD", "0x000000000000000000000000000000000000bEEF"]) { + await comptroller.addRewardsDistributor((await fakeDistributor(token)).address); + } + const third = await fakeDistributor("0x000000000000000000000000000000000000cAFE"); + + await expect(comptroller.addRewardsDistributor(third.address)) + .to.be.revertedWithCustomError(comptroller, "MaxLoopsLimitExceeded") + .withArgs(LIMIT, LIMIT + 1); + }); + + it("bounds a batch liquidation at half the number of orders", async () => { + // The guard halves the count because a batch normally pairs each borrow with a collateral market, so the + // effective limit is on markets touched rather than on orders placed. + const order = { + vTokenCollateral: marketA.vToken.address, + vTokenBorrowed: marketB.vToken.address, + repayAmount: ONE, + }; + // Under water, below the collateral threshold, and still holding enough collateral to clear the debt at the + // pool's incentive: the three checks the batch path runs before it reaches the loop guard. + await setRiskWeights(comptroller, marketA, COLLATERAL_FACTOR, LIQUIDATION_THRESHOLD); + await givePosition(comptroller, stranger, [ + { market: marketA, collateral: parseUnits("10", 18) }, + { market: marketB, borrow: parseUnits("9", 18) }, + ]); + + await expect(comptroller.liquidateAccount(stranger.address, Array(6).fill(order))) + .to.be.revertedWithCustomError(comptroller, "MaxLoopsLimitExceeded") + .withArgs(LIMIT, 3); + }); +}); diff --git a/tests/hardhat/Spoke/storageLayout.ts b/tests/hardhat/Spoke/storageLayout.ts new file mode 100644 index 000000000..2a5edbea7 --- /dev/null +++ b/tests/hardhat/Spoke/storageLayout.ts @@ -0,0 +1,194 @@ +import { expect } from "chai"; +import { artifacts } from "hardhat"; + +// `SpokeComptrollerStorage` is a hand-maintained fork of `ComptrollerStorage`, and its trailing gap size was +// worked out by hand rather than derived by the compiler. Nothing in the build fails if a re-sync inserts a +// variable in the middle of the layout, drops one, or gets the gap arithmetic wrong; the first symptom would be a +// live pool reading someone else's slot after an implementation upgrade. These tests read the layout solc emits +// (`outputSelection` in hardhat.config.ts requests `storageLayout`) and pin it. + +const SPOKE = "contracts/Spoke/SpokeComptroller.sol:SpokeComptroller"; +const SHARED = "contracts/Comptroller.sol:Comptroller"; + +/// The slot upstream gives to the Prime token, which the spoke does not use. +const PRIME_SLOT = 214; + +interface Entry { + label: string; + slot: number; + offset: number; + /// Human-readable type, with the declaring contract stripped off struct and enum names so the two forks compare. + type: string; +} + +interface Layout { + entries: Entry[]; + byLabel: Map; + types: Record; +} + +/// Both forks name their structs after the contract that declares them, so `ComptrollerStorage.Market` and +/// `SpokeComptrollerStorage.Market` are the same layout under different labels. Only the qualifier differs, and +/// comparing it would report a difference on every struct forever. +function normalizeType(label: string): string { + return label.replace(/\b(Spoke)?ComptrollerStorage\./g, ""); +} + +async function readLayout(fullyQualifiedName: string): Promise { + const [sourceName, contractName] = fullyQualifiedName.split(":"); + const buildInfo = await artifacts.getBuildInfo(fullyQualifiedName); + if (!buildInfo) { + throw new Error(`no build info for ${fullyQualifiedName}; compile first`); + } + + /* eslint-disable @typescript-eslint/no-explicit-any */ + const output = (buildInfo.output.contracts as any)[sourceName][contractName]; + const layout = output.storageLayout; + if (!layout) { + throw new Error(`no storageLayout for ${fullyQualifiedName}; check outputSelection in hardhat.config.ts`); + } + + const toEntry = (item: any): Entry => ({ + label: item.label, + slot: Number(item.slot), + offset: item.offset, + type: normalizeType(layout.types[item.type].label), + }); + const entries: Entry[] = layout.storage.map(toEntry); + /* eslint-enable @typescript-eslint/no-explicit-any */ + + // `__gap` appears once per inherited base, so it is deliberately left out of the lookup: everything that reads + // by label is asking about a named variable. + const byLabel = new Map(entries.filter(e => e.label !== "__gap").map(e => [e.label, e])); + + return { entries, byLabel, types: layout.types }; +} + +/// The named variable, or a failure naming what is missing. A layout that no longer declares it is the thing these +/// tests are looking for, so it has to fail loudly rather than compare against undefined. +function variable(layout: Layout, label: string): Entry { + const found = layout.byLabel.get(label); + if (!found) { + throw new Error(`no storage variable named ${label}`); + } + return found; +} + +function gapAt(layout: Layout, slot: number): Entry { + const found = layout.entries.find(entry => entry.label === "__gap" && entry.slot === slot); + if (!found) { + throw new Error(`no reserved gap at slot ${slot}`); + } + return found; +} + +/// Members of the `Market` struct each fork declares, keyed off the mapping `markets` resolves to. +function marketStructMembers(layout: Layout): Entry[] { + /* eslint-disable @typescript-eslint/no-explicit-any */ + const marketsTypeKey = Object.keys(layout.types).find(key => layout.types[key].label.endsWith("Market)")); + if (!marketsTypeKey) { + throw new Error("no mapping to a Market struct in the layout"); + } + const structType = layout.types[(layout.types[marketsTypeKey] as any).value]; + return (structType.members as Entry[]).map(member => ({ + label: member.label, + slot: Number(member.slot), + offset: member.offset, + type: normalizeType(layout.types[(member as any).type].label), + })); + /* eslint-enable @typescript-eslint/no-explicit-any */ +} + +describe("SpokeComptroller: storage layout", () => { + let spoke: Layout; + let shared: Layout; + + before(async () => { + spoke = await readLayout(SPOKE); + shared = await readLayout(SHARED); + }); + + it("matches the recorded layout, slot for slot", () => { + // Every slot the implementation occupies, including the gaps the OpenZeppelin bases reserve. Anything that + // moves a variable, changes its type, or pulls in a base with a different footprint fails here, and the + // failure is the review: either the change is safe for deployed pools or the layout has to be restored. + const expected = [ + { label: "_initialized", slot: 0, offset: 0, type: "uint8" }, + { label: "_initializing", slot: 0, offset: 1, type: "bool" }, + { label: "__gap", slot: 1, offset: 0, type: "uint256[50]" }, + { label: "_owner", slot: 51, offset: 0, type: "address" }, + { label: "__gap", slot: 52, offset: 0, type: "uint256[49]" }, + { label: "_pendingOwner", slot: 101, offset: 0, type: "address" }, + { label: "__gap", slot: 102, offset: 0, type: "uint256[49]" }, + { label: "_accessControlManager", slot: 151, offset: 0, type: "contract IAccessControlManagerV8" }, + { label: "__gap", slot: 152, offset: 0, type: "uint256[49]" }, + { label: "oracle", slot: 201, offset: 0, type: "contract ResilientOracleInterface" }, + { label: "closeFactorMantissa", slot: 202, offset: 0, type: "uint256" }, + { label: "_poolLiquidationIncentiveMantissa", slot: 203, offset: 0, type: "uint256" }, + { label: "accountAssets", slot: 204, offset: 0, type: "mapping(address => contract VToken[])" }, + { label: "markets", slot: 205, offset: 0, type: "mapping(address => struct Market)" }, + { label: "allMarkets", slot: 206, offset: 0, type: "contract VToken[]" }, + { label: "borrowCaps", slot: 207, offset: 0, type: "mapping(address => uint256)" }, + { label: "minLiquidatableCollateral", slot: 208, offset: 0, type: "uint256" }, + { label: "supplyCaps", slot: 209, offset: 0, type: "mapping(address => uint256)" }, + { label: "_actionPaused", slot: 210, offset: 0, type: "mapping(address => mapping(enum Action => bool))" }, + { label: "rewardsDistributors", slot: 211, offset: 0, type: "contract RewardsDistributor[]" }, + { label: "rewardsDistributorExists", slot: 212, offset: 0, type: "mapping(address => bool)" }, + { label: "isForcedLiquidationEnabled", slot: 213, offset: 0, type: "mapping(address => bool)" }, + { label: "approvedDelegates", slot: 214, offset: 0, type: "mapping(address => mapping(address => bool))" }, + { label: "isSupplyAllowlistEnabled", slot: 215, offset: 0, type: "mapping(address => bool)" }, + { label: "isAllowedSupplier", slot: 216, offset: 0, type: "mapping(address => mapping(address => bool))" }, + { label: "isLiquidationAllowlistEnabled", slot: 217, offset: 0, type: "bool" }, + { label: "isAllowedLiquidator", slot: 218, offset: 0, type: "mapping(address => bool)" }, + { label: "liquidationIncentives", slot: 219, offset: 0, type: "mapping(address => uint256)" }, + { label: "deviationBoundedOracle", slot: 220, offset: 0, type: "contract IDeviationBoundedOracle" }, + { label: "__gap", slot: 221, offset: 0, type: "uint256[42]" }, + { label: "maxLoopsLimit", slot: 263, offset: 0, type: "uint256" }, + { label: "__gap", slot: 264, offset: 0, type: "uint256[49]" }, + ]; + + expect(spoke.entries).to.deep.equal(expected); + }); + + it("shares every slot with the shared Comptroller up to the Prime slot", () => { + // Below the Prime slot the two forks are the same contract, so a spoke variable that drifts here is a re-sync + // mistake rather than a design decision. + const upTo = (layout: Layout) => layout.entries.filter(entry => entry.slot < PRIME_SLOT); + + expect(upTo(spoke)).to.deep.equal( + upTo(shared).map(entry => + // The pool-wide incentive is the one rename, forced by the getter of the same name the spoke adds. + entry.label === "liquidationIncentiveMantissa" + ? { ...entry, label: "_poolLiquidationIncentiveMantissa" } + : entry, + ), + ); + }); + + it("returns the unused Prime slot to the gap rather than leaving a hole", () => { + expect(spoke.byLabel.has("prime")).to.equal(false); + expect(variable(shared, "prime").slot).to.equal(PRIME_SLOT); + + // Dropping the variable shifts everything below it up by one, so a spoke pool's storage is not + // interchangeable with the shared implementation's. That is safe only because spoke pools upgrade through + // their own beacon: see the deployment tests. + expect(variable(spoke, "approvedDelegates").slot).to.equal(variable(shared, "approvedDelegates").slot - 1); + }); + + it("sizes the trailing gap so the fork occupies the same slots as the contract it came from", () => { + // Six variables were added and one removed, so the gap absorbs a net five slots: 47 upstream, 42 here. + expect(gapAt(shared, 216).type).to.equal("uint256[47]"); + expect(gapAt(spoke, 221).type).to.equal("uint256[42]"); + + // What the gap arithmetic is actually protecting: the variable that follows it, contributed by the + // `MaxLoopsLimitHelper` base both forks inherit, has to land on the same slot in both. + expect(variable(spoke, "maxLoopsLimit").slot).to.equal(variable(shared, "maxLoopsLimit").slot); + expect(spoke.entries[spoke.entries.length - 1]).to.deep.equal(shared.entries[shared.entries.length - 1]); + }); + + it("keeps the Market struct identical to upstream", () => { + // `markets` is a mapping, so its value struct is not part of the flat layout above and a member added or + // reordered inside it would go unnoticed there. + expect(marketStructMembers(spoke)).to.deep.equal(marketStructMembers(shared)); + }); +}); diff --git a/tests/hardhat/Spoke/upgrade.ts b/tests/hardhat/Spoke/upgrade.ts new file mode 100644 index 000000000..dd5eb9f08 --- /dev/null +++ b/tests/hardhat/Spoke/upgrade.ts @@ -0,0 +1,205 @@ +import { FakeContract, smock } from "@defi-wonderland/smock"; +import { setBalance } from "@nomicfoundation/hardhat-network-helpers"; +import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; +import chai from "chai"; +import { Contract } from "ethers"; +import { parseEther, parseUnits } from "ethers/lib/utils"; +import { ethers, upgrades } from "hardhat"; + +import { + AccessControlManager, + IDeviationBoundedOracle, + PoolRegistry, + ResilientOracleInterface, + SpokeComptroller, + SpokeComptroller__factory, + UpgradedSpokeComptroller__factory, + VToken, +} from "../../../typechain"; +import { Action, MAX_LOOPS_LIMIT, ONE } from "./fixtures"; + +const { expect } = chai; +chai.use(smock.matchers); + +const CLOSE_FACTOR = parseUnits("0.5", 18); +const COLLATERAL_FACTOR = parseUnits("0.6", 18); +const LIQUIDATION_THRESHOLD = parseUnits("0.8", 18); +const POOL_INCENTIVE = parseUnits("1.1", 18); +const MARKET_INCENTIVE = parseUnits("1.15", 18); +const MIN_LIQUIDATABLE_COLLATERAL = parseUnits("100", 18); +const SUPPLY_CAP = parseUnits("5000", 18); +const BORROW_CAP = parseUnits("4000", 18); + +// Spoke pools sit behind a beacon of their own, so one upgrade moves every pool at once and there is no per-pool +// chance to catch a mistake. These tests cover what that costs: storage has to survive the swap, and the values the +// implementation carries in bytecode rather than in storage have to be re-supplied correctly on every upgrade. +describe("SpokeComptroller: beacon upgrade", () => { + let account: SignerWithAddress; + let delegate: SignerWithAddress; + let poolRegistry: FakeContract; + let acm: FakeContract; + let oracle: FakeContract; + let boundedOracle: FakeContract; + let market: FakeContract; + let beacon: Contract; + let comptroller: SpokeComptroller; + + /// Everything a listing VIP configures, read back through the public surface. Compared as a whole after an + /// upgrade, so a value that silently moves does not need its own test to be noticed. + async function configuration() { + const marketData = await comptroller.markets(market.address); + return { + owner: await comptroller.owner(), + accessControlManager: await comptroller.accessControlManager(), + maxLoopsLimit: await comptroller.maxLoopsLimit(), + oracle: await comptroller.oracle(), + deviationBoundedOracle: await comptroller.deviationBoundedOracle(), + closeFactor: await comptroller.closeFactorMantissa(), + minLiquidatableCollateral: await comptroller.minLiquidatableCollateral(), + marketIncentive: await comptroller.effectiveLiquidationIncentive(market.address), + isListed: marketData.isListed, + collateralFactor: marketData.collateralFactorMantissa, + liquidationThreshold: marketData.liquidationThresholdMantissa, + supplyCap: await comptroller.supplyCaps(market.address), + borrowCap: await comptroller.borrowCaps(market.address), + allMarkets: (await comptroller.getAllMarkets()).map(address => address.toString()), + assetsIn: (await comptroller.getAssetsIn(account.address)).map(address => address.toString()), + isMember: await comptroller.checkMembership(account.address, market.address), + supplyAllowlistEnabled: await comptroller.isSupplyAllowlistEnabled(market.address), + allowedSupplier: await comptroller.isAllowedSupplier(market.address, account.address), + liquidationAllowlistEnabled: await comptroller.isLiquidationAllowlistEnabled(), + allowedLiquidator: await comptroller.isAllowedLiquidator(account.address), + forcedLiquidation: await comptroller.isForcedLiquidationEnabled(market.address), + transferPaused: await comptroller.actionPaused(market.address, Action.TRANSFER), + delegate: await comptroller.approvedDelegates(account.address, delegate.address), + }; + } + + beforeEach(async () => { + [, account, delegate] = await ethers.getSigners(); + + poolRegistry = await smock.fake("PoolRegistry"); + acm = await smock.fake("AccessControlManager"); + acm.isAllowedToCall.returns(true); + oracle = await smock.fake("ResilientOracleInterface"); + boundedOracle = await smock.fake("IDeviationBoundedOracle"); + market = await smock.fake("VToken"); + market.isVToken.returns(true); + oracle.getUnderlyingPrice.whenCalledWith(market.address).returns(ONE); + await setBalance(poolRegistry.address, parseEther("1")); + + const factory = (await ethers.getContractFactory("SpokeComptroller")) as SpokeComptroller__factory; + beacon = await upgrades.deployBeacon(factory, { constructorArgs: [poolRegistry.address] }); + comptroller = (await upgrades.deployBeaconProxy(beacon, factory, [MAX_LOOPS_LIMIT, acm.address], { + initializer: "initialize(uint256,address)", + constructorArgs: [poolRegistry.address], + })) as SpokeComptroller; + + await comptroller.setPriceOracle(oracle.address); + await comptroller.setDeviationBoundedOracle(boundedOracle.address); + await comptroller.setCloseFactor(CLOSE_FACTOR); + await comptroller.setLiquidationIncentive(POOL_INCENTIVE); + await comptroller.setMinLiquidatableCollateral(MIN_LIQUIDATABLE_COLLATERAL); + await comptroller.connect(poolRegistry.wallet).supportMarket(market.address); + await comptroller.setCollateralFactor(market.address, COLLATERAL_FACTOR, LIQUIDATION_THRESHOLD); + await comptroller.setMarketSupplyCaps([market.address], [SUPPLY_CAP]); + await comptroller.setMarketBorrowCaps([market.address], [BORROW_CAP]); + await comptroller.setMarketLiquidationIncentive(market.address, MARKET_INCENTIVE); + await comptroller.setSupplyAllowlistEnabled(market.address, true); + await comptroller.setAllowedSupplier(market.address, account.address, true); + await comptroller.setLiquidationAllowlistEnabled(true); + await comptroller.setAllowedLiquidator(account.address, true); + await comptroller.setForcedLiquidation(market.address, true); + await comptroller.setActionsPaused([market.address], [Action.TRANSFER], true); + await comptroller.connect(account).updateDelegate(delegate.address, true); + await comptroller.connect(account).enterMarkets([market.address]); + }); + + async function upgradeTo(registry: string = poolRegistry.address): Promise { + const upgraded = (await ethers.getContractFactory("UpgradedSpokeComptroller")) as UpgradedSpokeComptroller__factory; + await upgrades.upgradeBeacon(beacon, upgraded, { constructorArgs: [registry] }); + } + + it("carries every configured value through the upgrade", async () => { + const before = await configuration(); + + await upgradeTo(); + + expect(await configuration()).to.deep.equal(before); + }); + + it("runs the new implementation, whose appended slot starts empty", async () => { + await upgradeTo(); + + const upgraded = await ethers.getContractAt("UpgradedSpokeComptroller", comptroller.address); + expect(await upgraded.addedAfterUpgrade()).to.equal(0); + + // Proves the proxy really is running the new code rather than the old, which is what makes the assertion above + // about storage meaningful. + await upgraded.setAddedAfterUpgrade(42); + expect(await upgraded.addedAfterUpgrade()).to.equal(42); + }); + + it("keeps the pool usable, with the risk parameters it had before", async () => { + await upgradeTo(); + + // The allowlist is on and this account is on it, and the market is capped well above the amount. + market.totalSupply.returns(0); + market.exchangeRateStored.returns(ONE); + await expect(comptroller.callStatic.preMintHook(market.address, account.address, ONE)).to.not.be.reverted; + await expect(comptroller.callStatic.preMintHook(market.address, delegate.address, ONE)) + .to.be.revertedWithCustomError(comptroller, "SupplyNotAllowed") + .withArgs(market.address, delegate.address); + }); + + it("cannot be initialized a second time", async () => { + await upgradeTo(); + + await expect(comptroller.initialize(MAX_LOOPS_LIMIT + 1, acm.address)).to.be.revertedWith( + "Initializable: contract is already initialized", + ); + }); + + it("moves every pool on the beacon at once", async () => { + const factory = (await ethers.getContractFactory("SpokeComptroller")) as SpokeComptroller__factory; + const second = (await upgrades.deployBeaconProxy(beacon, factory, [MAX_LOOPS_LIMIT, acm.address], { + initializer: "initialize(uint256,address)", + constructorArgs: [poolRegistry.address], + })) as SpokeComptroller; + + await upgradeTo(); + + for (const pool of [comptroller.address, second.address]) { + const upgraded = await ethers.getContractAt("UpgradedSpokeComptroller", pool); + await expect(upgraded.setAddedAfterUpgrade(1)).to.not.be.reverted; + } + }); + + it("takes the pool registry from the new implementation, not from storage", async () => { + // `poolRegistry` is immutable, so it lives in the implementation's bytecode. An upgrade that passes a different + // constructor argument re-points every pool on the beacon at another registry, and nothing in storage records + // that it changed. The value has to be re-supplied correctly on each upgrade, and this is what would catch it. + const wrongRegistry = await smock.fake("PoolRegistry"); + expect(await comptroller.poolRegistry()).to.equal(poolRegistry.address); + + await upgradeTo(wrongRegistry.address); + + expect(await comptroller.poolRegistry()).to.equal(wrongRegistry.address); + }); + + it("is not interchangeable with the shared Comptroller implementation", async () => { + // The spoke returns upstream's Prime slot to the gap, so the two layouts diverge from that slot on: see the + // storage layout tests. Pointing a spoke pool at the shared implementation would read `approvedDelegates` as + // `prime`. OpenZeppelin's own validator is what a deploy script would run, so this is the check that matters. + const shared = await ethers.getContractFactory("Comptroller"); + const spoke = await ethers.getContractFactory("SpokeComptroller"); + + await expect( + upgrades.validateUpgrade(spoke, shared, { + kind: "beacon", + constructorArgs: [poolRegistry.address], + unsafeAllowRenames: true, + }), + ).to.be.rejectedWith("New storage layout is incompatible"); + }); +}); diff --git a/yarn.lock b/yarn.lock index ed5c46143..5b13e77a7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3349,7 +3349,7 @@ __metadata: "@typescript-eslint/eslint-plugin": ^5.27.1 "@typescript-eslint/parser": ^5.27.1 "@venusprotocol/governance-contracts": 2.13.0 - "@venusprotocol/oracle": 2.14.0 + "@venusprotocol/oracle": 2.15.0 "@venusprotocol/protocol-reserve": 3.4.0 "@venusprotocol/solidity-utilities": 2.1.0 "@venusprotocol/venus-protocol": 10.0.0 @@ -3385,7 +3385,29 @@ __metadata: languageName: unknown linkType: soft -"@venusprotocol/oracle@npm:2.14.0, @venusprotocol/oracle@npm:^2.12.0": +"@venusprotocol/oracle@npm:2.15.0": + version: 2.15.0 + resolution: "@venusprotocol/oracle@npm:2.15.0" + dependencies: + "@chainlink/contracts": ^0.5.1 + "@defi-wonderland/smock": 2.4.0 + "@nomicfoundation/hardhat-network-helpers": ^1.0.8 + "@openzeppelin/contracts": ^4.6.0 + "@openzeppelin/contracts-upgradeable": ^4.7.3 + "@venusprotocol/governance-contracts": ^2.13.0 + "@venusprotocol/solidity-utilities": ^2.0.0 + "@venusprotocol/venus-protocol": ^9.7.0 + ethers: ^5.6.8 + hardhat: 2.22.18 + hardhat-deploy: ^0.12.4 + module-alias: ^2.2.2 + patch-package: ^8.0.0 + solidity-docgen: ^0.6.0-beta.29 + checksum: c1d6a6eacedc9f6883a5a2cc85530fe5e9390ecb9cfcc0f526f09cf059ea25098c1c9e208767e74f73cd8e13ae04bb4acd6f6a43431db8686a0e75bd72794af9 + languageName: node + linkType: hard + +"@venusprotocol/oracle@npm:^2.12.0": version: 2.14.0 resolution: "@venusprotocol/oracle@npm:2.14.0" dependencies: