Contract Information

This is the implementation of the market logic contract, as deployed by the Comet Factory via the Configurator. Do not interact with this contract directly; instead use the cUSDCv3 proxy address with the Comet Interface ABI.
More Info

Comet Source Code

// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.15; import "./CometMainInterface.sol"; import "./ERC20.sol"; import "./vendor/@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol"; /** * @title Compound's Comet Contract * @notice An efficient monolithic money market protocol * @author Compound */ contract Comet is CometMainInterface { /** General configuration constants **/ /// @notice The admin of the protocol address public override immutable governor; /// @notice The account which may trigger pauses address public override immutable pauseGuardian; /// @notice The address of the base token contract address public override immutable baseToken; /// @notice The address of the price feed for the base token address public override immutable baseTokenPriceFeed; /// @notice The address of the extension contract delegate address public override immutable extensionDelegate; /// @notice The point in the supply rates separating the low interest rate slope and the high interest rate slope (factor) /// @dev uint64 uint public override immutable supplyKink; /// @notice Per second supply interest rate slope applied when utilization is below kink (factor) /// @dev uint64 uint public override immutable supplyPerSecondInterestRateSlopeLow; /// @notice Per second supply interest rate slope applied when utilization is above kink (factor) /// @dev uint64 uint public override immutable supplyPerSecondInterestRateSlopeHigh; /// @notice Per second supply base interest rate (factor) /// @dev uint64 uint public override immutable supplyPerSecondInterestRateBase; /// @notice The point in the borrow rate separating the low interest rate slope and the high interest rate slope (factor) /// @dev uint64 uint public override immutable borrowKink; /// @notice Per second borrow interest rate slope applied when utilization is below kink (factor) /// @dev uint64 uint public override immutable borrowPerSecondInterestRateSlopeLow; /// @notice Per second borrow interest rate slope applied when utilization is above kink (factor) /// @dev uint64 uint public override immutable borrowPerSecondInterestRateSlopeHigh; /// @notice Per second borrow base interest rate (factor) /// @dev uint64 uint public override immutable borrowPerSecondInterestRateBase; /// @notice The fraction of the liquidation penalty that goes to buyers of collateral instead of the protocol /// @dev uint64 uint public override immutable storeFrontPriceFactor; /// @notice The scale for base token (must be less than 18 decimals) /// @dev uint64 uint public override immutable baseScale; /// @notice The scale for reward tracking /// @dev uint64 uint public override immutable trackingIndexScale; /// @notice The speed at which supply rewards are tracked (in trackingIndexScale) /// @dev uint64 uint public override immutable baseTrackingSupplySpeed; /// @notice The speed at which borrow rewards are tracked (in trackingIndexScale) /// @dev uint64 uint public override immutable baseTrackingBorrowSpeed; /// @notice The minimum amount of base principal wei for rewards to accrue /// @dev This must be large enough so as to prevent division by base wei from overflowing the 64 bit indices /// @dev uint104 uint public override immutable baseMinForRewards; /// @notice The minimum base amount required to initiate a borrow uint public override immutable baseBorrowMin; /// @notice The minimum base token reserves which must be held before collateral is hodled uint public override immutable targetReserves; /// @notice The number of decimals for wrapped base token uint8 public override immutable decimals; /// @notice The number of assets this contract actually supports uint8 public override immutable numAssets; /// @notice Factor to divide by when accruing rewards in order to preserve 6 decimals (i.e. baseScale / 1e6) uint internal immutable accrualDescaleFactor; /** Collateral asset configuration (packed) **/ uint256 internal immutable asset00_a; uint256 internal immutable asset00_b; uint256 internal immutable asset01_a; uint256 internal immutable asset01_b; uint256 internal immutable asset02_a; uint256 internal immutable asset02_b; uint256 internal immutable asset03_a; uint256 internal immutable asset03_b; uint256 internal immutable asset04_a; uint256 internal immutable asset04_b; uint256 internal immutable asset05_a; uint256 internal immutable asset05_b; uint256 internal immutable asset06_a; uint256 internal immutable asset06_b; uint256 internal immutable asset07_a; uint256 internal immutable asset07_b; uint256 internal immutable asset08_a; uint256 internal immutable asset08_b; uint256 internal immutable asset09_a; uint256 internal immutable asset09_b; uint256 internal immutable asset10_a; uint256 internal immutable asset10_b; uint256 internal immutable asset11_a; uint256 internal immutable asset11_b; uint256 internal immutable asset12_a; uint256 internal immutable asset12_b; uint256 internal immutable asset13_a; uint256 internal immutable asset13_b; uint256 internal immutable asset14_a; uint256 internal immutable asset14_b; /** * @notice Construct a new protocol instance * @param config The mapping of initial/constant parameters **/ constructor(Configuration memory config) { // Sanity checks uint8 decimals_ = ERC20(config.baseToken).decimals(); if (decimals_ > MAX_BASE_DECIMALS) revert BadDecimals(); if (config.storeFrontPriceFactor > FACTOR_SCALE) revert BadDiscount(); if (config.assetConfigs.length > MAX_ASSETS) revert TooManyAssets(); if (config.baseMinForRewards == 0) revert BadMinimum(); if (AggregatorV3Interface(config.baseTokenPriceFeed).decimals() != PRICE_FEED_DECIMALS) revert BadDecimals(); // Copy configuration unchecked { governor = config.governor; pauseGuardian = config.pauseGuardian; baseToken = config.baseToken; baseTokenPriceFeed = config.baseTokenPriceFeed; extensionDelegate = config.extensionDelegate; storeFrontPriceFactor = config.storeFrontPriceFactor; decimals = decimals_; baseScale = uint64(10 ** decimals_); trackingIndexScale = config.trackingIndexScale; if (baseScale < BASE_ACCRUAL_SCALE) revert BadDecimals(); accrualDescaleFactor = baseScale / BASE_ACCRUAL_SCALE; baseMinForRewards = config.baseMinForRewards; baseTrackingSupplySpeed = config.baseTrackingSupplySpeed; baseTrackingBorrowSpeed = config.baseTrackingBorrowSpeed; baseBorrowMin = config.baseBorrowMin; targetReserves = config.targetReserves; } // Set interest rate model configs unchecked { supplyKink = config.supplyKink; supplyPerSecondInterestRateSlopeLow = config.supplyPerYearInterestRateSlopeLow / SECONDS_PER_YEAR; supplyPerSecondInterestRateSlopeHigh = config.supplyPerYearInterestRateSlopeHigh / SECONDS_PER_YEAR; supplyPerSecondInterestRateBase = config.supplyPerYearInterestRateBase / SECONDS_PER_YEAR; borrowKink = config.borrowKink; borrowPerSecondInterestRateSlopeLow = config.borrowPerYearInterestRateSlopeLow / SECONDS_PER_YEAR; borrowPerSecondInterestRateSlopeHigh = config.borrowPerYearInterestRateSlopeHigh / SECONDS_PER_YEAR; borrowPerSecondInterestRateBase = config.borrowPerYearInterestRateBase / SECONDS_PER_YEAR; } // Set asset info numAssets = uint8(config.assetConfigs.length); (asset00_a, asset00_b) = getPackedAssetInternal(config.assetConfigs, 0); (asset01_a, asset01_b) = getPackedAssetInternal(config.assetConfigs, 1); (asset02_a, asset02_b) = getPackedAssetInternal(config.assetConfigs, 2); (asset03_a, asset03_b) = getPackedAssetInternal(config.assetConfigs, 3); (asset04_a, asset04_b) = getPackedAssetInternal(config.assetConfigs, 4); (asset05_a, asset05_b) = getPackedAssetInternal(config.assetConfigs, 5); (asset06_a, asset06_b) = getPackedAssetInternal(config.assetConfigs, 6); (asset07_a, asset07_b) = getPackedAssetInternal(config.assetConfigs, 7); (asset08_a, asset08_b) = getPackedAssetInternal(config.assetConfigs, 8); (asset09_a, asset09_b) = getPackedAssetInternal(config.assetConfigs, 9); (asset10_a, asset10_b) = getPackedAssetInternal(config.assetConfigs, 10); (asset11_a, asset11_b) = getPackedAssetInternal(config.assetConfigs, 11); (asset12_a, asset12_b) = getPackedAssetInternal(config.assetConfigs, 12); (asset13_a, asset13_b) = getPackedAssetInternal(config.assetConfigs, 13); (asset14_a, asset14_b) = getPackedAssetInternal(config.assetConfigs, 14); } /** * @notice Initialize storage for the contract * @dev Can be used from constructor or proxy */ function initializeStorage() override external { if (lastAccrualTime != 0) revert AlreadyInitialized(); // Initialize aggregates lastAccrualTime = getNowInternal(); baseSupplyIndex = BASE_INDEX_SCALE; baseBorrowIndex = BASE_INDEX_SCALE; // Implicit initialization (not worth increasing contract size) // trackingSupplyIndex = 0; // trackingBorrowIndex = 0; } /** * @dev Checks and gets the packed asset info for storage */ function getPackedAssetInternal(AssetConfig[] memory assetConfigs, uint i) internal view returns (uint256, uint256) { AssetConfig memory assetConfig; if (i < assetConfigs.length) { assembly { assetConfig := mload(add(add(assetConfigs, 0x20), mul(i, 0x20))) } } else { return (0, 0); } address asset = assetConfig.asset; address priceFeed = assetConfig.priceFeed; uint8 decimals_ = assetConfig.decimals; // Short-circuit if asset is nil if (asset == address(0)) { return (0, 0); } // Sanity check price feed and asset decimals if (AggregatorV3Interface(priceFeed).decimals() != PRICE_FEED_DECIMALS) revert BadDecimals(); if (ERC20(asset).decimals() != decimals_) revert BadDecimals(); // Ensure collateral factors are within range if (assetConfig.borrowCollateralFactor >= assetConfig.liquidateCollateralFactor) revert BorrowCFTooLarge(); if (assetConfig.liquidateCollateralFactor > MAX_COLLATERAL_FACTOR) revert LiquidateCFTooLarge(); unchecked { // Keep 4 decimals for each factor uint64 descale = FACTOR_SCALE / 1e4; uint16 borrowCollateralFactor = uint16(assetConfig.borrowCollateralFactor / descale); uint16 liquidateCollateralFactor = uint16(assetConfig.liquidateCollateralFactor / descale); uint16 liquidationFactor = uint16(assetConfig.liquidationFactor / descale); // Be nice and check descaled values are still within range if (borrowCollateralFactor >= liquidateCollateralFactor) revert BorrowCFTooLarge(); // Keep whole units of asset for supply cap uint64 supplyCap = uint64(assetConfig.supplyCap / (10 ** decimals_)); uint256 word_a = (uint160(asset) << 0 | uint256(borrowCollateralFactor) << 160 | uint256(liquidateCollateralFactor) << 176 | uint256(liquidationFactor) << 192); uint256 word_b = (uint160(priceFeed) << 0 | uint256(decimals_) << 160 | uint256(supplyCap) << 168); return (word_a, word_b); } } /** * @notice Get the i-th asset info, according to the order they were passed in originally * @param i The index of the asset info to get * @return The asset info object */ function getAssetInfo(uint8 i) override public view returns (AssetInfo memory) { if (i >= numAssets) revert BadAsset(); uint256 word_a; uint256 word_b; if (i == 0) { word_a = asset00_a; word_b = asset00_b; } else if (i == 1) { word_a = asset01_a; word_b = asset01_b; } else if (i == 2) { word_a = asset02_a; word_b = asset02_b; } else if (i == 3) { word_a = asset03_a; word_b = asset03_b; } else if (i == 4) { word_a = asset04_a; word_b = asset04_b; } else if (i == 5) { word_a = asset05_a; word_b = asset05_b; } else if (i == 6) { word_a = asset06_a; word_b = asset06_b; } else if (i == 7) { word_a = asset07_a; word_b = asset07_b; } else if (i == 8) { word_a = asset08_a; word_b = asset08_b; } else if (i == 9) { word_a = asset09_a; word_b = asset09_b; } else if (i == 10) { word_a = asset10_a; word_b = asset10_b; } else if (i == 11) { word_a = asset11_a; word_b = asset11_b; } else if (i == 12) { word_a = asset12_a; word_b = asset12_b; } else if (i == 13) { word_a = asset13_a; word_b = asset13_b; } else if (i == 14) { word_a = asset14_a; word_b = asset14_b; } else { revert Absurd(); } address asset = address(uint160(word_a & type(uint160).max)); uint64 rescale = FACTOR_SCALE / 1e4; uint64 borrowCollateralFactor = uint64(((word_a >> 160) & type(uint16).max) * rescale); uint64 liquidateCollateralFactor = uint64(((word_a >> 176) & type(uint16).max) * rescale); uint64 liquidationFactor = uint64(((word_a >> 192) & type(uint16).max) * rescale); address priceFeed = address(uint160(word_b & type(uint160).max)); uint8 decimals_ = uint8(((word_b >> 160) & type(uint8).max)); uint64 scale = uint64(10 ** decimals_); uint128 supplyCap = uint128(((word_b >> 168) & type(uint64).max) * scale); return AssetInfo({ offset: i, asset: asset, priceFeed: priceFeed, scale: scale, borrowCollateralFactor: borrowCollateralFactor, liquidateCollateralFactor: liquidateCollateralFactor, liquidationFactor: liquidationFactor, supplyCap: supplyCap }); } /** * @dev Determine index of asset that matches given address */ function getAssetInfoByAddress(address asset) override public view returns (AssetInfo memory) { for (uint8 i = 0; i < numAssets; ) { AssetInfo memory assetInfo = getAssetInfo(i); if (assetInfo.asset == asset) { return assetInfo; } unchecked { i++; } } revert BadAsset(); } /** * @return The current timestamp **/ function getNowInternal() virtual internal view returns (uint40) { if (block.timestamp >= 2**40) revert TimestampTooLarge(); return uint40(block.timestamp); } /** * @dev Calculate accrued interest indices for base token supply and borrows **/ function accruedInterestIndices(uint timeElapsed) internal view returns (uint64, uint64) { uint64 baseSupplyIndex_ = baseSupplyIndex; uint64 baseBorrowIndex_ = baseBorrowIndex; if (timeElapsed > 0) { uint utilization = getUtilization(); uint supplyRate = getSupplyRate(utilization); uint borrowRate = getBorrowRate(utilization); baseSupplyIndex_ += safe64(mulFactor(baseSupplyIndex_, supplyRate * timeElapsed)); baseBorrowIndex_ += safe64(mulFactor(baseBorrowIndex_, borrowRate * timeElapsed)); } return (baseSupplyIndex_, baseBorrowIndex_); } /** * @dev Accrue interest (and rewards) in base token supply and borrows **/ function accrueInternal() internal { uint40 now_ = getNowInternal(); uint timeElapsed = uint256(now_ - lastAccrualTime); if (timeElapsed > 0) { (baseSupplyIndex, baseBorrowIndex) = accruedInterestIndices(timeElapsed); if (totalSupplyBase >= baseMinForRewards) { trackingSupplyIndex += safe64(divBaseWei(baseTrackingSupplySpeed * timeElapsed, totalSupplyBase)); } if (totalBorrowBase >= baseMinForRewards) { trackingBorrowIndex += safe64(divBaseWei(baseTrackingBorrowSpeed * timeElapsed, totalBorrowBase)); } lastAccrualTime = now_; } } /** * @notice Accrue interest and rewards for an account **/ function accrueAccount(address account) override external { accrueInternal(); UserBasic memory basic = userBasic[account]; updateBasePrincipal(account, basic, basic.principal); } /** * @dev Note: Does not accrue interest first * @param utilization The utilization to check the supply rate for * @return The per second supply rate at `utilization` */ function getSupplyRate(uint utilization) override public view returns (uint64) { if (utilization <= supplyKink) { // interestRateBase + interestRateSlopeLow * utilization return safe64(supplyPerSecondInterestRateBase + mulFactor(supplyPerSecondInterestRateSlopeLow, utilization)); } else { // interestRateBase + interestRateSlopeLow * kink + interestRateSlopeHigh * (utilization - kink) return safe64(supplyPerSecondInterestRateBase + mulFactor(supplyPerSecondInterestRateSlopeLow, supplyKink) + mulFactor(supplyPerSecondInterestRateSlopeHigh, (utilization - supplyKink))); } } /** * @dev Note: Does not accrue interest first * @param utilization The utilization to check the borrow rate for * @return The per second borrow rate at `utilization` */ function getBorrowRate(uint utilization) override public view returns (uint64) { if (utilization <= borrowKink) { // interestRateBase + interestRateSlopeLow * utilization return safe64(borrowPerSecondInterestRateBase + mulFactor(borrowPerSecondInterestRateSlopeLow, utilization)); } else { // interestRateBase + interestRateSlopeLow * kink + interestRateSlopeHigh * (utilization - kink) return safe64(borrowPerSecondInterestRateBase + mulFactor(borrowPerSecondInterestRateSlopeLow, borrowKink) + mulFactor(borrowPerSecondInterestRateSlopeHigh, (utilization - borrowKink))); } } /** * @dev Note: Does not accrue interest first * @return The utilization rate of the base asset */ function getUtilization() override public view returns (uint) { uint totalSupply_ = presentValueSupply(baseSupplyIndex, totalSupplyBase); uint totalBorrow_ = presentValueBorrow(baseBorrowIndex, totalBorrowBase); if (totalSupply_ == 0) { return 0; } else { return totalBorrow_ * FACTOR_SCALE / totalSupply_; } } /** * @notice Get the current price from a feed * @param priceFeed The address of a price feed * @return The price, scaled by `PRICE_SCALE` */ function getPrice(address priceFeed) override public view returns (uint256) { (, int price, , , ) = AggregatorV3Interface(priceFeed).latestRoundData(); if (price <= 0) revert BadPrice(); return uint256(price); } /** * @notice Gets the total balance of protocol collateral reserves for an asset * @dev Note: Reverts if collateral reserves are somehow negative, which should not be possible * @param asset The collateral asset */ function getCollateralReserves(address asset) override public view returns (uint) { return ERC20(asset).balanceOf(address(this)) - totalsCollateral[asset].totalSupplyAsset; } /** * @notice Gets the total amount of protocol reserves of the base asset */ function getReserves() override public view returns (int) { (uint64 baseSupplyIndex_, uint64 baseBorrowIndex_) = accruedInterestIndices(getNowInternal() - lastAccrualTime); uint balance = ERC20(baseToken).balanceOf(address(this)); uint totalSupply_ = presentValueSupply(baseSupplyIndex_, totalSupplyBase); uint totalBorrow_ = presentValueBorrow(baseBorrowIndex_, totalBorrowBase); return signed256(balance) - signed256(totalSupply_) + signed256(totalBorrow_); } /** * @notice Check whether an account has enough collateral to borrow * @param account The address to check * @return Whether the account is minimally collateralized enough to borrow */ function isBorrowCollateralized(address account) override public view returns (bool) { int104 principal = userBasic[account].principal; if (principal >= 0) { return true; } uint16 assetsIn = userBasic[account].assetsIn; int liquidity = signedMulPrice( presentValue(principal), getPrice(baseTokenPriceFeed), uint64(baseScale) ); for (uint8 i = 0; i < numAssets; ) { if (isInAsset(assetsIn, i)) { if (liquidity >= 0) { return true; } AssetInfo memory asset = getAssetInfo(i); uint newAmount = mulPrice( userCollateral[account][asset.asset].balance, getPrice(asset.priceFeed), asset.scale ); liquidity += signed256(mulFactor( newAmount, asset.borrowCollateralFactor )); } unchecked { i++; } } return liquidity >= 0; } /** * @notice Check whether an account has enough collateral to not be liquidated * @param account The address to check * @return Whether the account is minimally collateralized enough to not be liquidated */ function isLiquidatable(address account) override public view returns (bool) { int104 principal = userBasic[account].principal; if (principal >= 0) { return false; } uint16 assetsIn = userBasic[account].assetsIn; int liquidity = signedMulPrice( presentValue(principal), getPrice(baseTokenPriceFeed), uint64(baseScale) ); for (uint8 i = 0; i < numAssets; ) { if (isInAsset(assetsIn, i)) { if (liquidity >= 0) { return false; } AssetInfo memory asset = getAssetInfo(i); uint newAmount = mulPrice( userCollateral[account][asset.asset].balance, getPrice(asset.priceFeed), asset.scale ); liquidity += signed256(mulFactor( newAmount, asset.liquidateCollateralFactor )); } unchecked { i++; } } return liquidity < 0; } /** * @dev The change in principal broken into repay and supply amounts */ function repayAndSupplyAmount(int104 oldPrincipal, int104 newPrincipal) internal pure returns (uint104, uint104) { // If the new principal is less than the old principal, then no amount has been repaid or supplied if (newPrincipal < oldPrincipal) return (0, 0); if (newPrincipal <= 0) { return (uint104(newPrincipal - oldPrincipal), 0); } else if (oldPrincipal >= 0) { return (0, uint104(newPrincipal - oldPrincipal)); } else { return (uint104(-oldPrincipal), uint104(newPrincipal)); } } /** * @dev The change in principal broken into withdraw and borrow amounts */ function withdrawAndBorrowAmount(int104 oldPrincipal, int104 newPrincipal) internal pure returns (uint104, uint104) { // If the new principal is greater than the old principal, then no amount has been withdrawn or borrowed if (newPrincipal > oldPrincipal) return (0, 0); if (newPrincipal >= 0) { return (uint104(oldPrincipal - newPrincipal), 0); } else if (oldPrincipal <= 0) { return (0, uint104(oldPrincipal - newPrincipal)); } else { return (uint104(oldPrincipal), uint104(-newPrincipal)); } } /** * @notice Pauses different actions within Comet * @param supplyPaused Boolean for pausing supply actions * @param transferPaused Boolean for pausing transfer actions * @param withdrawPaused Boolean for pausing withdraw actions * @param absorbPaused Boolean for pausing absorb actions * @param buyPaused Boolean for pausing buy actions */ function pause( bool supplyPaused, bool transferPaused, bool withdrawPaused, bool absorbPaused, bool buyPaused ) override external { if (msg.sender != governor && msg.sender != pauseGuardian) revert Unauthorized(); pauseFlags = uint8(0) | (toUInt8(supplyPaused) << PAUSE_SUPPLY_OFFSET) | (toUInt8(transferPaused) << PAUSE_TRANSFER_OFFSET) | (toUInt8(withdrawPaused) << PAUSE_WITHDRAW_OFFSET) | (toUInt8(absorbPaused) << PAUSE_ABSORB_OFFSET) | (toUInt8(buyPaused) << PAUSE_BUY_OFFSET); emit PauseAction(supplyPaused, transferPaused, withdrawPaused, absorbPaused, buyPaused); } /** * @return Whether or not supply actions are paused */ function isSupplyPaused() override public view returns (bool) { return toBool(pauseFlags & (uint8(1) << PAUSE_SUPPLY_OFFSET)); } /** * @return Whether or not transfer actions are paused */ function isTransferPaused() override public view returns (bool) { return toBool(pauseFlags & (uint8(1) << PAUSE_TRANSFER_OFFSET)); } /** * @return Whether or not withdraw actions are paused */ function isWithdrawPaused() override public view returns (bool) { return toBool(pauseFlags & (uint8(1) << PAUSE_WITHDRAW_OFFSET)); } /** * @return Whether or not absorb actions are paused */ function isAbsorbPaused() override public view returns (bool) { return toBool(pauseFlags & (uint8(1) << PAUSE_ABSORB_OFFSET)); } /** * @return Whether or not buy actions are paused */ function isBuyPaused() override public view returns (bool) { return toBool(pauseFlags & (uint8(1) << PAUSE_BUY_OFFSET)); } /** * @dev Multiply a number by a factor */ function mulFactor(uint n, uint factor) internal pure returns (uint) { return n * factor / FACTOR_SCALE; } /** * @dev Divide a number by an amount of base */ function divBaseWei(uint n, uint baseWei) internal view returns (uint) { return n * baseScale / baseWei; } /** * @dev Multiply a `fromScale` quantity by a price, returning a common price quantity */ function mulPrice(uint n, uint price, uint64 fromScale) internal pure returns (uint) { return n * price / fromScale; } /** * @dev Multiply a signed `fromScale` quantity by a price, returning a common price quantity */ function signedMulPrice(int n, uint price, uint64 fromScale) internal pure returns (int) { return n * signed256(price) / int256(uint256(fromScale)); } /** * @dev Divide a common price quantity by a price, returning a `toScale` quantity */ function divPrice(uint n, uint price, uint64 toScale) internal pure returns (uint) { return n * toScale / price; } /** * @dev Whether user has a non-zero balance of an asset, given assetsIn flags */ function isInAsset(uint16 assetsIn, uint8 assetOffset) internal pure returns (bool) { return (assetsIn & (uint16(1) << assetOffset) != 0); } /** * @dev Update assetsIn bit vector if user has entered or exited an asset */ function updateAssetsIn( address account, AssetInfo memory assetInfo, uint128 initialUserBalance, uint128 finalUserBalance ) internal { if (initialUserBalance == 0 && finalUserBalance != 0) { // set bit for asset userBasic[account].assetsIn |= (uint16(1) << assetInfo.offset); } else if (initialUserBalance != 0 && finalUserBalance == 0) { // clear bit for asset userBasic[account].assetsIn &= ~(uint16(1) << assetInfo.offset); } } /** * @dev Write updated principal to store and tracking participation */ function updateBasePrincipal(address account, UserBasic memory basic, int104 principalNew) internal { int104 principal = basic.principal; basic.principal = principalNew; if (principal >= 0) { uint indexDelta = uint256(trackingSupplyIndex - basic.baseTrackingIndex); basic.baseTrackingAccrued += safe64(uint104(principal) * indexDelta / trackingIndexScale / accrualDescaleFactor); } else { uint indexDelta = uint256(trackingBorrowIndex - basic.baseTrackingIndex); basic.baseTrackingAccrued += safe64(uint104(-principal) * indexDelta / trackingIndexScale / accrualDescaleFactor); } if (principalNew >= 0) { basic.baseTrackingIndex = trackingSupplyIndex; } else { basic.baseTrackingIndex = trackingBorrowIndex; } userBasic[account] = basic; } /** * @dev Safe ERC20 transfer in, assumes no fee is charged and amount is transferred */ function doTransferIn(address asset, address from, uint amount) internal { bool success = ERC20(asset).transferFrom(from, address(this), amount); if (!success) revert TransferInFailed(); } /** * @dev Safe ERC20 transfer out */ function doTransferOut(address asset, address to, uint amount) internal { bool success = ERC20(asset).transfer(to, amount); if (!success) revert TransferOutFailed(); } /** * @notice Supply an amount of asset to the protocol * @param asset The asset to supply * @param amount The quantity to supply */ function supply(address asset, uint amount) override external { return supplyInternal(msg.sender, msg.sender, msg.sender, asset, amount); } /** * @notice Supply an amount of asset to dst * @param dst The address which will hold the balance * @param asset The asset to supply * @param amount The quantity to supply */ function supplyTo(address dst, address asset, uint amount) override external { return supplyInternal(msg.sender, msg.sender, dst, asset, amount); } /** * @notice Supply an amount of asset from `from` to dst, if allowed * @param from The supplier address * @param dst The address which will hold the balance * @param asset The asset to supply * @param amount The quantity to supply */ function supplyFrom(address from, address dst, address asset, uint amount) override external { return supplyInternal(msg.sender, from, dst, asset, amount); } /** * @dev Supply either collateral or base asset, depending on the asset, if operator is allowed * @dev Note: Specifying an `amount` of uint256.max will repay all of `dst`'s accrued base borrow balance */ function supplyInternal(address operator, address from, address dst, address asset, uint amount) internal { if (isSupplyPaused()) revert Paused(); if (!hasPermission(from, operator)) revert Unauthorized(); if (asset == baseToken) { if (amount == type(uint256).max) { amount = borrowBalanceOf(dst); } return supplyBase(from, dst, amount); } else { return supplyCollateral(from, dst, asset, safe128(amount)); } } /** * @dev Supply an amount of base asset from `from` to dst */ function supplyBase(address from, address dst, uint256 amount) internal { doTransferIn(baseToken, from, amount); accrueInternal(); UserBasic memory dstUser = userBasic[dst]; int104 dstPrincipal = dstUser.principal; int256 dstBalance = presentValue(dstPrincipal) + signed256(amount); int104 dstPrincipalNew = principalValue(dstBalance); (uint104 repayAmount, uint104 supplyAmount) = repayAndSupplyAmount(dstPrincipal, dstPrincipalNew); totalSupplyBase += supplyAmount; totalBorrowBase -= repayAmount; updateBasePrincipal(dst, dstUser, dstPrincipalNew); emit Supply(from, dst, amount); if (supplyAmount > 0) { emit Transfer(address(0), dst, presentValueSupply(baseSupplyIndex, supplyAmount)); } } /** * @dev Supply an amount of collateral asset from `from` to dst */ function supplyCollateral(address from, address dst, address asset, uint128 amount) internal { doTransferIn(asset, from, amount); AssetInfo memory assetInfo = getAssetInfoByAddress(asset); TotalsCollateral memory totals = totalsCollateral[asset]; totals.totalSupplyAsset += amount; if (totals.totalSupplyAsset > assetInfo.supplyCap) revert SupplyCapExceeded(); uint128 dstCollateral = userCollateral[dst][asset].balance; uint128 dstCollateralNew = dstCollateral + amount; totalsCollateral[asset] = totals; userCollateral[dst][asset].balance = dstCollateralNew; updateAssetsIn(dst, assetInfo, dstCollateral, dstCollateralNew); emit SupplyCollateral(from, dst, asset, amount); } /** * @notice ERC20 transfer an amount of base token to dst * @param dst The recipient address * @param amount The quantity to transfer * @return true */ function transfer(address dst, uint amount) override external returns (bool) { transferInternal(msg.sender, msg.sender, dst, baseToken, amount); return true; } /** * @notice ERC20 transfer an amount of base token from src to dst, if allowed * @param src The sender address * @param dst The recipient address * @param amount The quantity to transfer * @return true */ function transferFrom(address src, address dst, uint amount) override external returns (bool) { transferInternal(msg.sender, src, dst, baseToken, amount); return true; } /** * @notice Transfer an amount of asset to dst * @param dst The recipient address * @param asset The asset to transfer * @param amount The quantity to transfer */ function transferAsset(address dst, address asset, uint amount) override external { return transferInternal(msg.sender, msg.sender, dst, asset, amount); } /** * @notice Transfer an amount of asset from src to dst, if allowed * @param src The sender address * @param dst The recipient address * @param asset The asset to transfer * @param amount The quantity to transfer */ function transferAssetFrom(address src, address dst, address asset, uint amount) override external { return transferInternal(msg.sender, src, dst, asset, amount); } /** * @dev Transfer either collateral or base asset, depending on the asset, if operator is allowed * @dev Note: Specifying an `amount` of uint256.max will transfer all of `src`'s accrued base balance */ function transferInternal(address operator, address src, address dst, address asset, uint amount) internal { if (isTransferPaused()) revert Paused(); if (!hasPermission(src, operator)) revert Unauthorized(); if (src == dst) revert NoSelfTransfer(); if (asset == baseToken) { if (amount == type(uint256).max) { amount = balanceOf(src); } return transferBase(src, dst, amount); } else { return transferCollateral(src, dst, asset, safe128(amount)); } } /** * @dev Transfer an amount of base asset from src to dst, borrowing if possible/necessary */ function transferBase(address src, address dst, uint256 amount) internal { accrueInternal(); UserBasic memory srcUser = userBasic[src]; UserBasic memory dstUser = userBasic[dst]; int104 srcPrincipal = srcUser.principal; int104 dstPrincipal = dstUser.principal; int256 srcBalance = presentValue(srcPrincipal) - signed256(amount); int256 dstBalance = presentValue(dstPrincipal) + signed256(amount); int104 srcPrincipalNew = principalValue(srcBalance); int104 dstPrincipalNew = principalValue(dstBalance); (uint104 withdrawAmount, uint104 borrowAmount) = withdrawAndBorrowAmount(srcPrincipal, srcPrincipalNew); (uint104 repayAmount, uint104 supplyAmount) = repayAndSupplyAmount(dstPrincipal, dstPrincipalNew); // Note: Instead of `total += addAmount - subAmount` to avoid underflow errors. totalSupplyBase = totalSupplyBase + supplyAmount - withdrawAmount; totalBorrowBase = totalBorrowBase + borrowAmount - repayAmount; updateBasePrincipal(src, srcUser, srcPrincipalNew); updateBasePrincipal(dst, dstUser, dstPrincipalNew); if (srcBalance < 0) { if (uint256(-srcBalance) < baseBorrowMin) revert BorrowTooSmall(); if (!isBorrowCollateralized(src)) revert NotCollateralized(); } if (withdrawAmount > 0) { emit Transfer(src, address(0), presentValueSupply(baseSupplyIndex, withdrawAmount)); } if (supplyAmount > 0) { emit Transfer(address(0), dst, presentValueSupply(baseSupplyIndex, supplyAmount)); } } /** * @dev Transfer an amount of collateral asset from src to dst */ function transferCollateral(address src, address dst, address asset, uint128 amount) internal { uint128 srcCollateral = userCollateral[src][asset].balance; uint128 dstCollateral = userCollateral[dst][asset].balance; uint128 srcCollateralNew = srcCollateral - amount; uint128 dstCollateralNew = dstCollateral + amount; userCollateral[src][asset].balance = srcCollateralNew; userCollateral[dst][asset].balance = dstCollateralNew; AssetInfo memory assetInfo = getAssetInfoByAddress(asset); updateAssetsIn(src, assetInfo, srcCollateral, srcCollateralNew); updateAssetsIn(dst, assetInfo, dstCollateral, dstCollateralNew); // Note: no accrue interest, BorrowCF < LiquidationCF covers small changes if (!isBorrowCollateralized(src)) revert NotCollateralized(); emit TransferCollateral(src, dst, asset, amount); } /** * @notice Withdraw an amount of asset from the protocol * @param asset The asset to withdraw * @param amount The quantity to withdraw */ function withdraw(address asset, uint amount) override external { return withdrawInternal(msg.sender, msg.sender, msg.sender, asset, amount); } /** * @notice Withdraw an amount of asset to `to` * @param to The recipient address * @param asset The asset to withdraw * @param amount The quantity to withdraw */ function withdrawTo(address to, address asset, uint amount) override external { return withdrawInternal(msg.sender, msg.sender, to, asset, amount); } /** * @notice Withdraw an amount of asset from src to `to`, if allowed * @param src The sender address * @param to The recipient address * @param asset The asset to withdraw * @param amount The quantity to withdraw */ function withdrawFrom(address src, address to, address asset, uint amount) override external { return withdrawInternal(msg.sender, src, to, asset, amount); } /** * @dev Withdraw either collateral or base asset, depending on the asset, if operator is allowed * @dev Note: Specifying an `amount` of uint256.max will withdraw all of `src`'s accrued base balance */ function withdrawInternal(address operator, address src, address to, address asset, uint amount) internal { if (isWithdrawPaused()) revert Paused(); if (!hasPermission(src, operator)) revert Unauthorized(); if (asset == baseToken) { if (amount == type(uint256).max) { amount = balanceOf(src); } return withdrawBase(src, to, amount); } else { return withdrawCollateral(src, to, asset, safe128(amount)); } } /** * @dev Withdraw an amount of base asset from src to `to`, borrowing if possible/necessary */ function withdrawBase(address src, address to, uint256 amount) internal { accrueInternal(); UserBasic memory srcUser = userBasic[src]; int104 srcPrincipal = srcUser.principal; int256 srcBalance = presentValue(srcPrincipal) - signed256(amount); int104 srcPrincipalNew = principalValue(srcBalance); (uint104 withdrawAmount, uint104 borrowAmount) = withdrawAndBorrowAmount(srcPrincipal, srcPrincipalNew); totalSupplyBase -= withdrawAmount; totalBorrowBase += borrowAmount; updateBasePrincipal(src, srcUser, srcPrincipalNew); if (srcBalance < 0) { if (uint256(-srcBalance) < baseBorrowMin) revert BorrowTooSmall(); if (!isBorrowCollateralized(src)) revert NotCollateralized(); } doTransferOut(baseToken, to, amount); emit Withdraw(src, to, amount); if (withdrawAmount > 0) { emit Transfer(src, address(0), presentValueSupply(baseSupplyIndex, withdrawAmount)); } } /** * @dev Withdraw an amount of collateral asset from src to `to` */ function withdrawCollateral(address src, address to, address asset, uint128 amount) internal { uint128 srcCollateral = userCollateral[src][asset].balance; uint128 srcCollateralNew = srcCollateral - amount; totalsCollateral[asset].totalSupplyAsset -= amount; userCollateral[src][asset].balance = srcCollateralNew; AssetInfo memory assetInfo = getAssetInfoByAddress(asset); updateAssetsIn(src, assetInfo, srcCollateral, srcCollateralNew); // Note: no accrue interest, BorrowCF < LiquidationCF covers small changes if (!isBorrowCollateralized(src)) revert NotCollateralized(); doTransferOut(asset, to, amount); emit WithdrawCollateral(src, to, asset, amount); } /** * @notice Absorb a list of underwater accounts onto the protocol balance sheet * @param absorber The recipient of the incentive paid to the caller of absorb * @param accounts The list of underwater accounts to absorb */ function absorb(address absorber, address[] calldata accounts) override external { if (isAbsorbPaused()) revert Paused(); uint startGas = gasleft(); accrueInternal(); for (uint i = 0; i < accounts.length; ) { absorbInternal(absorber, accounts[i]); unchecked { i++; } } uint gasUsed = startGas - gasleft(); // Note: liquidator points are an imperfect tool for governance, // to be used while evaluating strategies for incentivizing absorption. // Using gas price instead of base fee would more accurately reflect spend, // but is also subject to abuse if refunds were to be given automatically. LiquidatorPoints memory points = liquidatorPoints[absorber]; points.numAbsorbs++; points.numAbsorbed += safe64(accounts.length); points.approxSpend += safe128(gasUsed * block.basefee); liquidatorPoints[absorber] = points; } /** * @dev Transfer user's collateral and debt to the protocol itself. */ function absorbInternal(address absorber, address account) internal { if (!isLiquidatable(account)) revert NotLiquidatable(); UserBasic memory accountUser = userBasic[account]; int104 oldPrincipal = accountUser.principal; int256 oldBalance = presentValue(oldPrincipal); uint16 assetsIn = accountUser.assetsIn; uint256 basePrice = getPrice(baseTokenPriceFeed); uint256 deltaValue = 0; for (uint8 i = 0; i < numAssets; ) { if (isInAsset(assetsIn, i)) { AssetInfo memory assetInfo = getAssetInfo(i); address asset = assetInfo.asset; uint128 seizeAmount = userCollateral[account][asset].balance; userCollateral[account][asset].balance = 0; totalsCollateral[asset].totalSupplyAsset -= seizeAmount; uint256 value = mulPrice(seizeAmount, getPrice(assetInfo.priceFeed), assetInfo.scale); deltaValue += mulFactor(value, assetInfo.liquidationFactor); emit AbsorbCollateral(absorber, account, asset, seizeAmount, value); } unchecked { i++; } } uint256 deltaBalance = divPrice(deltaValue, basePrice, uint64(baseScale)); int256 newBalance = oldBalance + signed256(deltaBalance); // New balance will not be negative, all excess debt absorbed by reserves if (newBalance < 0) { newBalance = 0; } int104 newPrincipal = principalValue(newBalance); updateBasePrincipal(account, accountUser, newPrincipal); // reset assetsIn userBasic[account].assetsIn = 0; (uint104 repayAmount, uint104 supplyAmount) = repayAndSupplyAmount(oldPrincipal, newPrincipal); // Reserves are decreased by increasing total supply and decreasing borrows // the amount of debt repaid by reserves is `newBalance - oldBalance` totalSupplyBase += supplyAmount; totalBorrowBase -= repayAmount; uint256 basePaidOut = unsigned256(newBalance - oldBalance); uint256 valueOfBasePaidOut = mulPrice(basePaidOut, basePrice, uint64(baseScale)); emit AbsorbDebt(absorber, account, basePaidOut, valueOfBasePaidOut); if (newPrincipal > 0) { emit Transfer(address(0), account, presentValueSupply(baseSupplyIndex, unsigned104(newPrincipal))); } } /** * @notice Buy collateral from the protocol using base tokens, increasing protocol reserves A minimum collateral amount should be specified to indicate the maximum slippage acceptable for the buyer. * @param asset The asset to buy * @param minAmount The minimum amount of collateral tokens that should be received by the buyer * @param baseAmount The amount of base tokens used to buy the collateral * @param recipient The recipient address */ function buyCollateral(address asset, uint minAmount, uint baseAmount, address recipient) override external { if (isBuyPaused()) revert Paused(); int reserves = getReserves(); if (reserves >= 0 && uint(reserves) >= targetReserves) revert NotForSale(); // Note: Re-entrancy can skip the reserves check above on a second buyCollateral call. doTransferIn(baseToken, msg.sender, baseAmount); uint collateralAmount = quoteCollateral(asset, baseAmount); if (collateralAmount < minAmount) revert TooMuchSlippage(); if (collateralAmount > getCollateralReserves(asset)) revert InsufficientReserves(); // Note: Pre-transfer hook can re-enter buyCollateral with a stale collateral ERC20 balance. // Assets should not be listed which allow re-entry from pre-transfer now, as too much collateral could be bought. // This is also a problem if quoteCollateral derives its discount from the collateral ERC20 balance. doTransferOut(asset, recipient, safe128(collateralAmount)); emit BuyCollateral(msg.sender, asset, baseAmount, collateralAmount); } /** * @notice Gets the quote for a collateral asset in exchange for an amount of base asset * @param asset The collateral asset to get the quote for * @param baseAmount The amount of the base asset to get the quote for * @return The quote in terms of the collateral asset */ function quoteCollateral(address asset, uint baseAmount) override public view returns (uint) { AssetInfo memory assetInfo = getAssetInfoByAddress(asset); uint256 assetPrice = getPrice(assetInfo.priceFeed); // Store front discount is derived from the collateral asset's liquidationFactor and storeFrontPriceFactor // discount = storeFrontPriceFactor * (1e18 - liquidationFactor) uint256 discountFactor = mulFactor(storeFrontPriceFactor, FACTOR_SCALE - assetInfo.liquidationFactor); uint256 assetPriceDiscounted = mulFactor(assetPrice, FACTOR_SCALE - discountFactor); uint256 basePrice = getPrice(baseTokenPriceFeed); // # of collateral assets // = (TotalValueOfBaseAmount / DiscountedPriceOfCollateralAsset) * assetScale // = ((basePrice * baseAmount / baseScale) / assetPriceDiscounted) * assetScale return basePrice * baseAmount * assetInfo.scale / assetPriceDiscounted / baseScale; } /** * @notice Withdraws base token reserves if called by the governor * @param to An address of the receiver of withdrawn reserves * @param amount The amount of reserves to be withdrawn from the protocol */ function withdrawReserves(address to, uint amount) override external { if (msg.sender != governor) revert Unauthorized(); int reserves = getReserves(); if (reserves < 0 || amount > unsigned256(reserves)) revert InsufficientReserves(); doTransferOut(baseToken, to, amount); emit WithdrawReserves(to, amount); } /** * @notice Sets Comet's ERC20 allowance of an asset for a manager * @dev Only callable by governor * @dev Note: Setting the `asset` as Comet's address will allow the manager * to withdraw from Comet's Comet balance * @param asset The asset that the manager will gain approval of * @param manager The account which will be allowed or disallowed * @param amount The amount of an asset to approve */ function approveThis(address manager, address asset, uint amount) override external { if (msg.sender != governor) revert Unauthorized(); ERC20(asset).approve(manager, amount); } /** * @notice Get the total number of tokens in circulation * @dev Note: uses updated interest indices to calculate * @return The supply of tokens **/ function totalSupply() override external view returns (uint256) { (uint64 baseSupplyIndex_, ) = accruedInterestIndices(getNowInternal() - lastAccrualTime); return presentValueSupply(baseSupplyIndex_, totalSupplyBase); } /** * @notice Get the total amount of debt * @dev Note: uses updated interest indices to calculate * @return The amount of debt **/ function totalBorrow() override external view returns (uint256) { (, uint64 baseBorrowIndex_) = accruedInterestIndices(getNowInternal() - lastAccrualTime); return presentValueBorrow(baseBorrowIndex_, totalBorrowBase); } /** * @notice Query the current positive base balance of an account or zero * @dev Note: uses updated interest indices to calculate * @param account The account whose balance to query * @return The present day base balance magnitude of the account, if positive */ function balanceOf(address account) override public view returns (uint256) { (uint64 baseSupplyIndex_, ) = accruedInterestIndices(getNowInternal() - lastAccrualTime); int104 principal = userBasic[account].principal; return principal > 0 ? presentValueSupply(baseSupplyIndex_, unsigned104(principal)) : 0; } /** * @notice Query the current negative base balance of an account or zero * @dev Note: uses updated interest indices to calculate * @param account The account whose balance to query * @return The present day base balance magnitude of the account, if negative */ function borrowBalanceOf(address account) override public view returns (uint256) { (, uint64 baseBorrowIndex_) = accruedInterestIndices(getNowInternal() - lastAccrualTime); int104 principal = userBasic[account].principal; return principal < 0 ? presentValueBorrow(baseBorrowIndex_, unsigned104(-principal)) : 0; } /** * @notice Fallback to calling the extension delegate for everything else */ fallback() external payable { address delegate = extensionDelegate; assembly { calldatacopy(0, 0, calldatasize()) let result := delegatecall(gas(), delegate, 0, calldatasize(), 0, 0) returndatacopy(0, 0, returndatasize()) switch result case 0 { revert(0, returndatasize()) } default { return(0, returndatasize()) } } } }
< // SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.15;

import "./CometMainInterface.sol";
import "./ERC20.sol";
import "./vendor/@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";

/**
 * @title Compound's Comet Contract
 * @notice An efficient monolithic money market protocol
 * @author Compound
 */
contract Comet is CometMainInterface {
    /** General configuration constants **/

    /// @notice The admin of the protocol
    address public override immutable governor;

    /// @notice The account which may trigger pauses
    address public override immutable pauseGuardian;

    /// @notice The address of the base token contract
    address public override immutable baseToken;

    /// @notice The address of the price feed for the base token
    address public override immutable baseTokenPriceFeed;

    /// @notice The address of the extension contract delegate
    address public override immutable extensionDelegate;

    /// @notice The point in the supply rates separating the low interest rate slope and the high interest rate slope (factor)
    /// @dev uint64
    uint public override immutable supplyKink;

    /// @notice Per second supply interest rate slope applied when utilization is below kink (factor)
    /// @dev uint64
    uint public override immutable supplyPerSecondInterestRateSlopeLow;

    /// @notice Per second supply interest rate slope applied when utilization is above kink (factor)
    /// @dev uint64
    uint public override immutable supplyPerSecondInterestRateSlopeHigh;

    /// @notice Per second supply base interest rate (factor)
    /// @dev uint64
    uint public override immutable supplyPerSecondInterestRateBase;

    /// @notice The point in the borrow rate separating the low interest rate slope and the high interest rate slope (factor)
    /// @dev uint64
    uint public override immutable borrowKink;

    /// @notice Per second borrow interest rate slope applied when utilization is below kink (factor)
    /// @dev uint64
    uint public override immutable borrowPerSecondInterestRateSlopeLow;

    /// @notice Per second borrow interest rate slope applied when utilization is above kink (factor)
    /// @dev uint64
    uint public override immutable borrowPerSecondInterestRateSlopeHigh;

    /// @notice Per second borrow base interest rate (factor)
    /// @dev uint64
    uint public override immutable borrowPerSecondInterestRateBase;

    /// @notice The fraction of the liquidation penalty that goes to buyers of collateral instead of the protocol
    /// @dev uint64
    uint public override immutable storeFrontPriceFactor;

    /// @notice The scale for base token (must be less than 18 decimals)
    /// @dev uint64
    uint public override immutable baseScale;

    /// @notice The scale for reward tracking
    /// @dev uint64
    uint public override immutable trackingIndexScale;

    /// @notice The speed at which supply rewards are tracked (in trackingIndexScale)
    /// @dev uint64
    uint public override immutable baseTrackingSupplySpeed;

    /// @notice The speed at which borrow rewards are tracked (in trackingIndexScale)
    /// @dev uint64
    uint public override immutable baseTrackingBorrowSpeed;

    /// @notice The minimum amount of base principal wei for rewards to accrue
    /// @dev This must be large enough so as to prevent division by base wei from overflowing the 64 bit indices
    /// @dev uint104
    uint public override immutable baseMinForRewards;

    /// @notice The minimum base amount required to initiate a borrow
    uint public override immutable baseBorrowMin;

    /// @notice The minimum base token reserves which must be held before collateral is hodled
    uint public override immutable targetReserves;

    /// @notice The number of decimals for wrapped base token
    uint8 public override immutable decimals;

    /// @notice The number of assets this contract actually supports
    uint8 public override immutable numAssets;

    /// @notice Factor to divide by when accruing rewards in order to preserve 6 decimals (i.e. baseScale / 1e6)
    uint internal immutable accrualDescaleFactor;

    /** Collateral asset configuration (packed) **/

    uint256 internal immutable asset00_a;
    uint256 internal immutable asset00_b;
    uint256 internal immutable asset01_a;
    uint256 internal immutable asset01_b;
    uint256 internal immutable asset02_a;
    uint256 internal immutable asset02_b;
    uint256 internal immutable asset03_a;
    uint256 internal immutable asset03_b;
    uint256 internal immutable asset04_a;
    uint256 internal immutable asset04_b;
    uint256 internal immutable asset05_a;
    uint256 internal immutable asset05_b;
    uint256 internal immutable asset06_a;
    uint256 internal immutable asset06_b;
    uint256 internal immutable asset07_a;
    uint256 internal immutable asset07_b;
    uint256 internal immutable asset08_a;
    uint256 internal immutable asset08_b;
    uint256 internal immutable asset09_a;
    uint256 internal immutable asset09_b;
    uint256 internal immutable asset10_a;
    uint256 internal immutable asset10_b;
    uint256 internal immutable asset11_a;
    uint256 internal immutable asset11_b;
    uint256 internal immutable asset12_a;
    uint256 internal immutable asset12_b;
    uint256 internal immutable asset13_a;
    uint256 internal immutable asset13_b;
    uint256 internal immutable asset14_a;
    uint256 internal immutable asset14_b;

    /**
     * @notice Construct a new protocol instance
     * @param config The mapping of initial/constant parameters
     **/
    constructor(Configuration memory config) {
        // Sanity checks
        uint8 decimals_ = ERC20(config.baseToken).decimals();
        if (decimals_ > MAX_BASE_DECIMALS) revert BadDecimals();
        if (config.storeFrontPriceFactor > FACTOR_SCALE) revert BadDiscount();
        if (config.assetConfigs.length > MAX_ASSETS) revert TooManyAssets();
        if (config.baseMinForRewards == 0) revert BadMinimum();
        if (AggregatorV3Interface(config.baseTokenPriceFeed).decimals() != PRICE_FEED_DECIMALS) revert BadDecimals();

        // Copy configuration
        unchecked {
            governor = config.governor;
            pauseGuardian = config.pauseGuardian;
            baseToken = config.baseToken;
            baseTokenPriceFeed = config.baseTokenPriceFeed;
            extensionDelegate = config.extensionDelegate;
            storeFrontPriceFactor = config.storeFrontPriceFactor;

            decimals = decimals_;
            baseScale = uint64(10 ** decimals_);
            trackingIndexScale = config.trackingIndexScale;
            if (baseScale < BASE_ACCRUAL_SCALE) revert BadDecimals();
            accrualDescaleFactor = baseScale / BASE_ACCRUAL_SCALE;

            baseMinForRewards = config.baseMinForRewards;
            baseTrackingSupplySpeed = config.baseTrackingSupplySpeed;
            baseTrackingBorrowSpeed = config.baseTrackingBorrowSpeed;

            baseBorrowMin = config.baseBorrowMin;
            targetReserves = config.targetReserves;
        }

        // Set interest rate model configs
        unchecked {
            supplyKink = config.supplyKink;
            supplyPerSecondInterestRateSlopeLow = config.supplyPerYearInterestRateSlopeLow / SECONDS_PER_YEAR;
            supplyPerSecondInterestRateSlopeHigh = config.supplyPerYearInterestRateSlopeHigh / SECONDS_PER_YEAR;
            supplyPerSecondInterestRateBase = config.supplyPerYearInterestRateBase / SECONDS_PER_YEAR;
            borrowKink = config.borrowKink;
            borrowPerSecondInterestRateSlopeLow = config.borrowPerYearInterestRateSlopeLow / SECONDS_PER_YEAR;
            borrowPerSecondInterestRateSlopeHigh = config.borrowPerYearInterestRateSlopeHigh / SECONDS_PER_YEAR;
            borrowPerSecondInterestRateBase = config.borrowPerYearInterestRateBase / SECONDS_PER_YEAR;
        }

        // Set asset info
        numAssets = uint8(config.assetConfigs.length);

        (asset00_a, asset00_b) = getPackedAssetInternal(config.assetConfigs, 0);
        (asset01_a, asset01_b) = getPackedAssetInternal(config.assetConfigs, 1);
        (asset02_a, asset02_b) = getPackedAssetInternal(config.assetConfigs, 2);
        (asset03_a, asset03_b) = getPackedAssetInternal(config.assetConfigs, 3);
        (asset04_a, asset04_b) = getPackedAssetInternal(config.assetConfigs, 4);
        (asset05_a, asset05_b) = getPackedAssetInternal(config.assetConfigs, 5);
        (asset06_a, asset06_b) = getPackedAssetInternal(config.assetConfigs, 6);
        (asset07_a, asset07_b) = getPackedAssetInternal(config.assetConfigs, 7);
        (asset08_a, asset08_b) = getPackedAssetInternal(config.assetConfigs, 8);
        (asset09_a, asset09_b) = getPackedAssetInternal(config.assetConfigs, 9);
        (asset10_a, asset10_b) = getPackedAssetInternal(config.assetConfigs, 10);
        (asset11_a, asset11_b) = getPackedAssetInternal(config.assetConfigs, 11);
        (asset12_a, asset12_b) = getPackedAssetInternal(config.assetConfigs, 12);
        (asset13_a, asset13_b) = getPackedAssetInternal(config.assetConfigs, 13);
        (asset14_a, asset14_b) = getPackedAssetInternal(config.assetConfigs, 14);
    }

    /**
     * @notice Initialize storage for the contract
     * @dev Can be used from constructor or proxy
     */
    function initializeStorage() override external {
        if (lastAccrualTime != 0) revert AlreadyInitialized();

        // Initialize aggregates
        lastAccrualTime = getNowInternal();
        baseSupplyIndex = BASE_INDEX_SCALE;
        baseBorrowIndex = BASE_INDEX_SCALE;

        // Implicit initialization (not worth increasing contract size)
        // trackingSupplyIndex = 0;
        // trackingBorrowIndex = 0;
    }

    /**
     * @dev Checks and gets the packed asset info for storage
     */
    function getPackedAssetInternal(AssetConfig[] memory assetConfigs, uint i) internal view returns (uint256, uint256) {
        AssetConfig memory assetConfig;
        if (i < assetConfigs.length) {
            assembly {
                assetConfig := mload(add(add(assetConfigs, 0x20), mul(i, 0x20)))
            }
        } else {
            return (0, 0);
        }
        address asset = assetConfig.asset;
        address priceFeed = assetConfig.priceFeed;
        uint8 decimals_ = assetConfig.decimals;

        // Short-circuit if asset is nil
        if (asset == address(0)) {
            return (0, 0);
        }

        // Sanity check price feed and asset decimals
        if (AggregatorV3Interface(priceFeed).decimals() != PRICE_FEED_DECIMALS) revert BadDecimals();
        if (ERC20(asset).decimals() != decimals_) revert BadDecimals();

        // Ensure collateral factors are within range
        if (assetConfig.borrowCollateralFactor >= assetConfig.liquidateCollateralFactor) revert BorrowCFTooLarge();
        if (assetConfig.liquidateCollateralFactor > MAX_COLLATERAL_FACTOR) revert LiquidateCFTooLarge();

        unchecked {
            // Keep 4 decimals for each factor
            uint64 descale = FACTOR_SCALE / 1e4;
            uint16 borrowCollateralFactor = uint16(assetConfig.borrowCollateralFactor / descale);
            uint16 liquidateCollateralFactor = uint16(assetConfig.liquidateCollateralFactor / descale);
            uint16 liquidationFactor = uint16(assetConfig.liquidationFactor / descale);

            // Be nice and check descaled values are still within range
            if (borrowCollateralFactor >= liquidateCollateralFactor) revert BorrowCFTooLarge();

            // Keep whole units of asset for supply cap
            uint64 supplyCap = uint64(assetConfig.supplyCap / (10 ** decimals_));

            uint256 word_a = (uint160(asset) << 0 |
                              uint256(borrowCollateralFactor) << 160 |
                              uint256(liquidateCollateralFactor) << 176 |
                              uint256(liquidationFactor) << 192);
            uint256 word_b = (uint160(priceFeed) << 0 |
                              uint256(decimals_) << 160 |
                              uint256(supplyCap) << 168);

            return (word_a, word_b);
        }
    }

    /**
     * @notice Get the i-th asset info, according to the order they were passed in originally
     * @param i The index of the asset info to get
     * @return The asset info object
     */
    function getAssetInfo(uint8 i) override public view returns (AssetInfo memory) {
        if (i >= numAssets) revert BadAsset();

        uint256 word_a;
        uint256 word_b;

        if (i == 0) {
            word_a = asset00_a;
            word_b = asset00_b;
        } else if (i == 1) {
            word_a = asset01_a;
            word_b = asset01_b;
        } else if (i == 2) {
            word_a = asset02_a;
            word_b = asset02_b;
        } else if (i == 3) {
            word_a = asset03_a;
            word_b = asset03_b;
        } else if (i == 4) {
            word_a = asset04_a;
            word_b = asset04_b;
        } else if (i == 5) {
            word_a = asset05_a;
            word_b = asset05_b;
        } else if (i == 6) {
            word_a = asset06_a;
            word_b = asset06_b;
        } else if (i == 7) {
            word_a = asset07_a;
            word_b = asset07_b;
        } else if (i == 8) {
            word_a = asset08_a;
            word_b = asset08_b;
        } else if (i == 9) {
            word_a = asset09_a;
            word_b = asset09_b;
        } else if (i == 10) {
            word_a = asset10_a;
            word_b = asset10_b;
        } else if (i == 11) {
            word_a = asset11_a;
            word_b = asset11_b;
        } else if (i == 12) {
            word_a = asset12_a;
            word_b = asset12_b;
        } else if (i == 13) {
            word_a = asset13_a;
            word_b = asset13_b;
        } else if (i == 14) {
            word_a = asset14_a;
            word_b = asset14_b;
        } else {
            revert Absurd();
        }

        address asset = address(uint160(word_a & type(uint160).max));
        uint64 rescale = FACTOR_SCALE / 1e4;
        uint64 borrowCollateralFactor = uint64(((word_a >> 160) & type(uint16).max) * rescale);
        uint64 liquidateCollateralFactor = uint64(((word_a >> 176) & type(uint16).max) * rescale);
        uint64 liquidationFactor = uint64(((word_a >> 192) & type(uint16).max) * rescale);

        address priceFeed = address(uint160(word_b & type(uint160).max));
        uint8 decimals_ = uint8(((word_b >> 160) & type(uint8).max));
        uint64 scale = uint64(10 ** decimals_);
        uint128 supplyCap = uint128(((word_b >> 168) & type(uint64).max) * scale);

        return AssetInfo({
            offset: i,
            asset: asset,
            priceFeed: priceFeed,
            scale: scale,
            borrowCollateralFactor: borrowCollateralFactor,
            liquidateCollateralFactor: liquidateCollateralFactor,
            liquidationFactor: liquidationFactor,
            supplyCap: supplyCap
         });
    }

    /**
     * @dev Determine index of asset that matches given address
     */
    function getAssetInfoByAddress(address asset) override public view returns (AssetInfo memory) {
        for (uint8 i = 0; i < numAssets; ) {
            AssetInfo memory assetInfo = getAssetInfo(i);
            if (assetInfo.asset == asset) {
                return assetInfo;
            }
            unchecked { i++; }
        }
        revert BadAsset();
    }

    /**
     * @return The current timestamp
     **/
    function getNowInternal() virtual internal view returns (uint40) {
        if (block.timestamp >= 2**40) revert TimestampTooLarge();
        return uint40(block.timestamp);
    }

    /**
     * @dev Calculate accrued interest indices for base token supply and borrows
     **/
    function accruedInterestIndices(uint timeElapsed) internal view returns (uint64, uint64) {
        uint64 baseSupplyIndex_ = baseSupplyIndex;
        uint64 baseBorrowIndex_ = baseBorrowIndex;
        if (timeElapsed > 0) {
            uint utilization = getUtilization();
            uint supplyRate = getSupplyRate(utilization);
            uint borrowRate = getBorrowRate(utilization);
            baseSupplyIndex_ += safe64(mulFactor(baseSupplyIndex_, supplyRate * timeElapsed));
            baseBorrowIndex_ += safe64(mulFactor(baseBorrowIndex_, borrowRate * timeElapsed));
        }
        return (baseSupplyIndex_, baseBorrowIndex_);
    }

    /**
     * @dev Accrue interest (and rewards) in base token supply and borrows
     **/
    function accrueInternal() internal {
        uint40 now_ = getNowInternal();
        uint timeElapsed = uint256(now_ - lastAccrualTime);
        if (timeElapsed > 0) {
            (baseSupplyIndex, baseBorrowIndex) = accruedInterestIndices(timeElapsed);
            if (totalSupplyBase >= baseMinForRewards) {
                trackingSupplyIndex += safe64(divBaseWei(baseTrackingSupplySpeed * timeElapsed, totalSupplyBase));
            }
            if (totalBorrowBase >= baseMinForRewards) {
                trackingBorrowIndex += safe64(divBaseWei(baseTrackingBorrowSpeed * timeElapsed, totalBorrowBase));
            }
            lastAccrualTime = now_;
        }
    }

    /**
     * @notice Accrue interest and rewards for an account
     **/
    function accrueAccount(address account) override external {
        accrueInternal();

        UserBasic memory basic = userBasic[account];
        updateBasePrincipal(account, basic, basic.principal);
    }

    /**
     * @dev Note: Does not accrue interest first
     * @param utilization The utilization to check the supply rate for
     * @return The per second supply rate at `utilization`
     */
    function getSupplyRate(uint utilization) override public view returns (uint64) {
        if (utilization <= supplyKink) {
            // interestRateBase + interestRateSlopeLow * utilization
            return safe64(supplyPerSecondInterestRateBase + mulFactor(supplyPerSecondInterestRateSlopeLow, utilization));
        } else {
            // interestRateBase + interestRateSlopeLow * kink + interestRateSlopeHigh * (utilization - kink)
            return safe64(supplyPerSecondInterestRateBase + mulFactor(supplyPerSecondInterestRateSlopeLow, supplyKink) + mulFactor(supplyPerSecondInterestRateSlopeHigh, (utilization - supplyKink)));
        }
    }

    /**
     * @dev Note: Does not accrue interest first
     * @param utilization The utilization to check the borrow rate for
     * @return The per second borrow rate at `utilization`
     */
    function getBorrowRate(uint utilization) override public view returns (uint64) {
        if (utilization <= borrowKink) {
            // interestRateBase + interestRateSlopeLow * utilization
            return safe64(borrowPerSecondInterestRateBase + mulFactor(borrowPerSecondInterestRateSlopeLow, utilization));
        } else {
            // interestRateBase + interestRateSlopeLow * kink + interestRateSlopeHigh * (utilization - kink)
            return safe64(borrowPerSecondInterestRateBase + mulFactor(borrowPerSecondInterestRateSlopeLow, borrowKink) + mulFactor(borrowPerSecondInterestRateSlopeHigh, (utilization - borrowKink)));
        }
    }

    /**
     * @dev Note: Does not accrue interest first
     * @return The utilization rate of the base asset
     */
    function getUtilization() override public view returns (uint) {
        uint totalSupply_ = presentValueSupply(baseSupplyIndex, totalSupplyBase);
        uint totalBorrow_ = presentValueBorrow(baseBorrowIndex, totalBorrowBase);
        if (totalSupply_ == 0) {
            return 0;
        } else {
            return totalBorrow_ * FACTOR_SCALE / totalSupply_;
        }
    }

    /**
     * @notice Get the current price from a feed
     * @param priceFeed The address of a price feed
     * @return The price, scaled by `PRICE_SCALE`
     */
    function getPrice(address priceFeed) override public view returns (uint256) {
        (, int price, , , ) = AggregatorV3Interface(priceFeed).latestRoundData();
        if (price <= 0) revert BadPrice();
        return uint256(price);
    }

    /**
     * @notice Gets the total balance of protocol collateral reserves for an asset
     * @dev Note: Reverts if collateral reserves are somehow negative, which should not be possible
     * @param asset The collateral asset
     */
    function getCollateralReserves(address asset) override public view returns (uint) {
        return ERC20(asset).balanceOf(address(this)) - totalsCollateral[asset].totalSupplyAsset;
    }

    /**
     * @notice Gets the total amount of protocol reserves of the base asset
     */
    function getReserves() override public view returns (int) {
        (uint64 baseSupplyIndex_, uint64 baseBorrowIndex_) = accruedInterestIndices(getNowInternal() - lastAccrualTime);
        uint balance = ERC20(baseToken).balanceOf(address(this));
        uint totalSupply_ = presentValueSupply(baseSupplyIndex_, totalSupplyBase);
        uint totalBorrow_ = presentValueBorrow(baseBorrowIndex_, totalBorrowBase);
        return signed256(balance) - signed256(totalSupply_) + signed256(totalBorrow_);
    }

    /**
     * @notice Check whether an account has enough collateral to borrow
     * @param account The address to check
     * @return Whether the account is minimally collateralized enough to borrow
     */
    function isBorrowCollateralized(address account) override public view returns (bool) {
        int104 principal = userBasic[account].principal;

        if (principal >= 0) {
            return true;
        }

        uint16 assetsIn = userBasic[account].assetsIn;
        int liquidity = signedMulPrice(
            presentValue(principal),
            getPrice(baseTokenPriceFeed),
            uint64(baseScale)
        );

        for (uint8 i = 0; i < numAssets; ) {
            if (isInAsset(assetsIn, i)) {
                if (liquidity >= 0) {
                    return true;
                }

                AssetInfo memory asset = getAssetInfo(i);
                uint newAmount = mulPrice(
                    userCollateral[account][asset.asset].balance,
                    getPrice(asset.priceFeed),
                    asset.scale
                );
                liquidity += signed256(mulFactor(
                    newAmount,
                    asset.borrowCollateralFactor
                ));
            }
            unchecked { i++; }
        }

        return liquidity >= 0;
    }

    /**
     * @notice Check whether an account has enough collateral to not be liquidated
     * @param account The address to check
     * @return Whether the account is minimally collateralized enough to not be liquidated
     */
    function isLiquidatable(address account) override public view returns (bool) {
        int104 principal = userBasic[account].principal;

        if (principal >= 0) {
            return false;
        }

        uint16 assetsIn = userBasic[account].assetsIn;
        int liquidity = signedMulPrice(
            presentValue(principal),
            getPrice(baseTokenPriceFeed),
            uint64(baseScale)
        );

        for (uint8 i = 0; i < numAssets; ) {
            if (isInAsset(assetsIn, i)) {
                if (liquidity >= 0) {
                    return false;
                }

                AssetInfo memory asset = getAssetInfo(i);
                uint newAmount = mulPrice(
                    userCollateral[account][asset.asset].balance,
                    getPrice(asset.priceFeed),
                    asset.scale
                );
                liquidity += signed256(mulFactor(
                    newAmount,
                    asset.liquidateCollateralFactor
                ));
            }
            unchecked { i++; }
        }

        return liquidity < 0;
    }

    /**
     * @dev The change in principal broken into repay and supply amounts
     */
    function repayAndSupplyAmount(int104 oldPrincipal, int104 newPrincipal) internal pure returns (uint104, uint104) {
        // If the new principal is less than the old principal, then no amount has been repaid or supplied
        if (newPrincipal < oldPrincipal) return (0, 0);

        if (newPrincipal <= 0) {
            return (uint104(newPrincipal - oldPrincipal), 0);
        } else if (oldPrincipal >= 0) {
            return (0, uint104(newPrincipal - oldPrincipal));
        } else {
            return (uint104(-oldPrincipal), uint104(newPrincipal));
        }
    }

    /**
     * @dev The change in principal broken into withdraw and borrow amounts
     */
    function withdrawAndBorrowAmount(int104 oldPrincipal, int104 newPrincipal) internal pure returns (uint104, uint104) {
        // If the new principal is greater than the old principal, then no amount has been withdrawn or borrowed
        if (newPrincipal > oldPrincipal) return (0, 0);

        if (newPrincipal >= 0) {
            return (uint104(oldPrincipal - newPrincipal), 0);
        } else if (oldPrincipal <= 0) {
            return (0, uint104(oldPrincipal - newPrincipal));
        } else {
            return (uint104(oldPrincipal), uint104(-newPrincipal));
        }
    }

    /**
     * @notice Pauses different actions within Comet
     * @param supplyPaused Boolean for pausing supply actions
     * @param transferPaused Boolean for pausing transfer actions
     * @param withdrawPaused Boolean for pausing withdraw actions
     * @param absorbPaused Boolean for pausing absorb actions
     * @param buyPaused Boolean for pausing buy actions
     */
    function pause(
        bool supplyPaused,
        bool transferPaused,
        bool withdrawPaused,
        bool absorbPaused,
        bool buyPaused
    ) override external {
        if (msg.sender != governor && msg.sender != pauseGuardian) revert Unauthorized();

        pauseFlags =
            uint8(0) |
            (toUInt8(supplyPaused) << PAUSE_SUPPLY_OFFSET) |
            (toUInt8(transferPaused) << PAUSE_TRANSFER_OFFSET) |
            (toUInt8(withdrawPaused) << PAUSE_WITHDRAW_OFFSET) |
            (toUInt8(absorbPaused) << PAUSE_ABSORB_OFFSET) |
            (toUInt8(buyPaused) << PAUSE_BUY_OFFSET);

        emit PauseAction(supplyPaused, transferPaused, withdrawPaused, absorbPaused, buyPaused);
    }

    /**
     * @return Whether or not supply actions are paused
     */
    function isSupplyPaused() override public view returns (bool) {
        return toBool(pauseFlags & (uint8(1) << PAUSE_SUPPLY_OFFSET));
    }

    /**
     * @return Whether or not transfer actions are paused
     */
    function isTransferPaused() override public view returns (bool) {
        return toBool(pauseFlags & (uint8(1) << PAUSE_TRANSFER_OFFSET));
    }

    /**
     * @return Whether or not withdraw actions are paused
     */
    function isWithdrawPaused() override public view returns (bool) {
        return toBool(pauseFlags & (uint8(1) << PAUSE_WITHDRAW_OFFSET));
    }

    /**
     * @return Whether or not absorb actions are paused
     */
    function isAbsorbPaused() override public view returns (bool) {
        return toBool(pauseFlags & (uint8(1) << PAUSE_ABSORB_OFFSET));
    }

    /**
     * @return Whether or not buy actions are paused
     */
    function isBuyPaused() override public view returns (bool) {
        return toBool(pauseFlags & (uint8(1) << PAUSE_BUY_OFFSET));
    }

    /**
     * @dev Multiply a number by a factor
     */
    function mulFactor(uint n, uint factor) internal pure returns (uint) {
        return n * factor / FACTOR_SCALE;
    }

    /**
     * @dev Divide a number by an amount of base
     */
    function divBaseWei(uint n, uint baseWei) internal view returns (uint) {
        return n * baseScale / baseWei;
    }

    /**
     * @dev Multiply a `fromScale` quantity by a price, returning a common price quantity
     */
    function mulPrice(uint n, uint price, uint64 fromScale) internal pure returns (uint) {
        return n * price / fromScale;
    }

    /**
     * @dev Multiply a signed `fromScale` quantity by a price, returning a common price quantity
     */
    function signedMulPrice(int n, uint price, uint64 fromScale) internal pure returns (int) {
        return n * signed256(price) / int256(uint256(fromScale));
    }

    /**
     * @dev Divide a common price quantity by a price, returning a `toScale` quantity
     */
    function divPrice(uint n, uint price, uint64 toScale) internal pure returns (uint) {
        return n * toScale / price;
    }

    /**
     * @dev Whether user has a non-zero balance of an asset, given assetsIn flags
     */
    function isInAsset(uint16 assetsIn, uint8 assetOffset) internal pure returns (bool) {
        return (assetsIn & (uint16(1) << assetOffset) != 0);
    }

    /**
     * @dev Update assetsIn bit vector if user has entered or exited an asset
     */
    function updateAssetsIn(
        address account,
        AssetInfo memory assetInfo,
        uint128 initialUserBalance,
        uint128 finalUserBalance
    ) internal {
        if (initialUserBalance == 0 && finalUserBalance != 0) {
            // set bit for asset
            userBasic[account].assetsIn |= (uint16(1) << assetInfo.offset);
        } else if (initialUserBalance != 0 && finalUserBalance == 0) {
            // clear bit for asset
            userBasic[account].assetsIn &= ~(uint16(1) << assetInfo.offset);
        }
    }

    /**
     * @dev Write updated principal to store and tracking participation
     */
    function updateBasePrincipal(address account, UserBasic memory basic, int104 principalNew) internal {
        int104 principal = basic.principal;
        basic.principal = principalNew;

        if (principal >= 0) {
            uint indexDelta = uint256(trackingSupplyIndex - basic.baseTrackingIndex);
            basic.baseTrackingAccrued += safe64(uint104(principal) * indexDelta / trackingIndexScale / accrualDescaleFactor);
        } else {
            uint indexDelta = uint256(trackingBorrowIndex - basic.baseTrackingIndex);
            basic.baseTrackingAccrued += safe64(uint104(-principal) * indexDelta / trackingIndexScale / accrualDescaleFactor);
        }

        if (principalNew >= 0) {
            basic.baseTrackingIndex = trackingSupplyIndex;
        } else {
            basic.baseTrackingIndex = trackingBorrowIndex;
        }

        userBasic[account] = basic;
    }

    /**
     * @dev Safe ERC20 transfer in, assumes no fee is charged and amount is transferred
     */
    function doTransferIn(address asset, address from, uint amount) internal {
        bool success = ERC20(asset).transferFrom(from, address(this), amount);
        if (!success) revert TransferInFailed();
    }

    /**
     * @dev Safe ERC20 transfer out
     */
    function doTransferOut(address asset, address to, uint amount) internal {
        bool success = ERC20(asset).transfer(to, amount);
        if (!success) revert TransferOutFailed();
    }

    /**
     * @notice Supply an amount of asset to the protocol
     * @param asset The asset to supply
     * @param amount The quantity to supply
     */
    function supply(address asset, uint amount) override external {
        return supplyInternal(msg.sender, msg.sender, msg.sender, asset, amount);
    }

    /**
     * @notice Supply an amount of asset to dst
     * @param dst The address which will hold the balance
     * @param asset The asset to supply
     * @param amount The quantity to supply
     */
    function supplyTo(address dst, address asset, uint amount) override external {
        return supplyInternal(msg.sender, msg.sender, dst, asset, amount);
    }

    /**
     * @notice Supply an amount of asset from `from` to dst, if allowed
     * @param from The supplier address
     * @param dst The address which will hold the balance
     * @param asset The asset to supply
     * @param amount The quantity to supply
     */
    function supplyFrom(address from, address dst, address asset, uint amount) override external {
        return supplyInternal(msg.sender, from, dst, asset, amount);
    }

    /**
     * @dev Supply either collateral or base asset, depending on the asset, if operator is allowed
     * @dev Note: Specifying an `amount` of uint256.max will repay all of `dst`'s accrued base borrow balance
     */
    function supplyInternal(address operator, address from, address dst, address asset, uint amount) internal {
        if (isSupplyPaused()) revert Paused();
        if (!hasPermission(from, operator)) revert Unauthorized();

        if (asset == baseToken) {
            if (amount == type(uint256).max) {
                amount = borrowBalanceOf(dst);
            }
            return supplyBase(from, dst, amount);
        } else {
            return supplyCollateral(from, dst, asset, safe128(amount));
        }
    }

    /**
     * @dev Supply an amount of base asset from `from` to dst
     */
    function supplyBase(address from, address dst, uint256 amount) internal {
        doTransferIn(baseToken, from, amount);

        accrueInternal();

        UserBasic memory dstUser = userBasic[dst];
        int104 dstPrincipal = dstUser.principal;
        int256 dstBalance = presentValue(dstPrincipal) + signed256(amount);
        int104 dstPrincipalNew = principalValue(dstBalance);

        (uint104 repayAmount, uint104 supplyAmount) = repayAndSupplyAmount(dstPrincipal, dstPrincipalNew);

        totalSupplyBase += supplyAmount;
        totalBorrowBase -= repayAmount;

        updateBasePrincipal(dst, dstUser, dstPrincipalNew);

        emit Supply(from, dst, amount);

        if (supplyAmount > 0) {
            emit Transfer(address(0), dst, presentValueSupply(baseSupplyIndex, supplyAmount));
        }
    }

    /**
     * @dev Supply an amount of collateral asset from `from` to dst
     */
    function supplyCollateral(address from, address dst, address asset, uint128 amount) internal {
        doTransferIn(asset, from, amount);

        AssetInfo memory assetInfo = getAssetInfoByAddress(asset);
        TotalsCollateral memory totals = totalsCollateral[asset];
        totals.totalSupplyAsset += amount;
        if (totals.totalSupplyAsset > assetInfo.supplyCap) revert SupplyCapExceeded();

        uint128 dstCollateral = userCollateral[dst][asset].balance;
        uint128 dstCollateralNew = dstCollateral + amount;

        totalsCollateral[asset] = totals;
        userCollateral[dst][asset].balance = dstCollateralNew;

        updateAssetsIn(dst, assetInfo, dstCollateral, dstCollateralNew);

        emit SupplyCollateral(from, dst, asset, amount);
    }

    /**
     * @notice ERC20 transfer an amount of base token to dst
     * @param dst The recipient address
     * @param amount The quantity to transfer
     * @return true
     */
    function transfer(address dst, uint amount) override external returns (bool) {
        transferInternal(msg.sender, msg.sender, dst, baseToken, amount);
        return true;
    }

    /**
     * @notice ERC20 transfer an amount of base token from src to dst, if allowed
     * @param src The sender address
     * @param dst The recipient address
     * @param amount The quantity to transfer
     * @return true
     */
    function transferFrom(address src, address dst, uint amount) override external returns (bool) {
        transferInternal(msg.sender, src, dst, baseToken, amount);
        return true;
    }

    /**
     * @notice Transfer an amount of asset to dst
     * @param dst The recipient address
     * @param asset The asset to transfer
     * @param amount The quantity to transfer
     */
    function transferAsset(address dst, address asset, uint amount) override external {
        return transferInternal(msg.sender, msg.sender, dst, asset, amount);
    }

    /**
     * @notice Transfer an amount of asset from src to dst, if allowed
     * @param src The sender address
     * @param dst The recipient address
     * @param asset The asset to transfer
     * @param amount The quantity to transfer
     */
    function transferAssetFrom(address src, address dst, address asset, uint amount) override external {
        return transferInternal(msg.sender, src, dst, asset, amount);
    }

    /**
     * @dev Transfer either collateral or base asset, depending on the asset, if operator is allowed
     * @dev Note: Specifying an `amount` of uint256.max will transfer all of `src`'s accrued base balance
     */
    function transferInternal(address operator, address src, address dst, address asset, uint amount) internal {
        if (isTransferPaused()) revert Paused();
        if (!hasPermission(src, operator)) revert Unauthorized();
        if (src == dst) revert NoSelfTransfer();

        if (asset == baseToken) {
            if (amount == type(uint256).max) {
                amount = balanceOf(src);
            }
            return transferBase(src, dst, amount);
        } else {
            return transferCollateral(src, dst, asset, safe128(amount));
        }
    }

    /**
     * @dev Transfer an amount of base asset from src to dst, borrowing if possible/necessary
     */
    function transferBase(address src, address dst, uint256 amount) internal {
        accrueInternal();

        UserBasic memory srcUser = userBasic[src];
        UserBasic memory dstUser = userBasic[dst];

        int104 srcPrincipal = srcUser.principal;
        int104 dstPrincipal = dstUser.principal;
        int256 srcBalance = presentValue(srcPrincipal) - signed256(amount);
        int256 dstBalance = presentValue(dstPrincipal) + signed256(amount);
        int104 srcPrincipalNew = principalValue(srcBalance);
        int104 dstPrincipalNew = principalValue(dstBalance);

        (uint104 withdrawAmount, uint104 borrowAmount) = withdrawAndBorrowAmount(srcPrincipal, srcPrincipalNew);
        (uint104 repayAmount, uint104 supplyAmount) = repayAndSupplyAmount(dstPrincipal, dstPrincipalNew);

        // Note: Instead of `total += addAmount - subAmount` to avoid underflow errors.
        totalSupplyBase = totalSupplyBase + supplyAmount - withdrawAmount;
        totalBorrowBase = totalBorrowBase + borrowAmount - repayAmount;

        updateBasePrincipal(src, srcUser, srcPrincipalNew);
        updateBasePrincipal(dst, dstUser, dstPrincipalNew);

        if (srcBalance < 0) {
            if (uint256(-srcBalance) < baseBorrowMin) revert BorrowTooSmall();
            if (!isBorrowCollateralized(src)) revert NotCollateralized();
        }

        if (withdrawAmount > 0) {
            emit Transfer(src, address(0), presentValueSupply(baseSupplyIndex, withdrawAmount));
        }

        if (supplyAmount > 0) {
            emit Transfer(address(0), dst, presentValueSupply(baseSupplyIndex, supplyAmount));
        }
    }

    /**
     * @dev Transfer an amount of collateral asset from src to dst
     */
    function transferCollateral(address src, address dst, address asset, uint128 amount) internal {
        uint128 srcCollateral = userCollateral[src][asset].balance;
        uint128 dstCollateral = userCollateral[dst][asset].balance;
        uint128 srcCollateralNew = srcCollateral - amount;
        uint128 dstCollateralNew = dstCollateral + amount;

        userCollateral[src][asset].balance = srcCollateralNew;
        userCollateral[dst][asset].balance = dstCollateralNew;

        AssetInfo memory assetInfo = getAssetInfoByAddress(asset);
        updateAssetsIn(src, assetInfo, srcCollateral, srcCollateralNew);
        updateAssetsIn(dst, assetInfo, dstCollateral, dstCollateralNew);

        // Note: no accrue interest, BorrowCF < LiquidationCF covers small changes
        if (!isBorrowCollateralized(src)) revert NotCollateralized();

        emit TransferCollateral(src, dst, asset, amount);
    }

    /**
     * @notice Withdraw an amount of asset from the protocol
     * @param asset The asset to withdraw
     * @param amount The quantity to withdraw
     */
    function withdraw(address asset, uint amount) override external {
        return withdrawInternal(msg.sender, msg.sender, msg.sender, asset, amount);
    }

    /**
     * @notice Withdraw an amount of asset to `to`
     * @param to The recipient address
     * @param asset The asset to withdraw
     * @param amount The quantity to withdraw
     */
    function withdrawTo(address to, address asset, uint amount) override external {
        return withdrawInternal(msg.sender, msg.sender, to, asset, amount);
    }

    /**
     * @notice Withdraw an amount of asset from src to `to`, if allowed
     * @param src The sender address
     * @param to The recipient address
     * @param asset The asset to withdraw
     * @param amount The quantity to withdraw
     */
    function withdrawFrom(address src, address to, address asset, uint amount) override external {
        return withdrawInternal(msg.sender, src, to, asset, amount);
    }

    /**
     * @dev Withdraw either collateral or base asset, depending on the asset, if operator is allowed
     * @dev Note: Specifying an `amount` of uint256.max will withdraw all of `src`'s accrued base balance
     */
    function withdrawInternal(address operator, address src, address to, address asset, uint amount) internal {
        if (isWithdrawPaused()) revert Paused();
        if (!hasPermission(src, operator)) revert Unauthorized();

        if (asset == baseToken) {
            if (amount == type(uint256).max) {
                amount = balanceOf(src);
            }
            return withdrawBase(src, to, amount);
        } else {
            return withdrawCollateral(src, to, asset, safe128(amount));
        }
    }

    /**
     * @dev Withdraw an amount of base asset from src to `to`, borrowing if possible/necessary
     */
    function withdrawBase(address src, address to, uint256 amount) internal {
        accrueInternal();

        UserBasic memory srcUser = userBasic[src];
        int104 srcPrincipal = srcUser.principal;
        int256 srcBalance = presentValue(srcPrincipal) - signed256(amount);
        int104 srcPrincipalNew = principalValue(srcBalance);

        (uint104 withdrawAmount, uint104 borrowAmount) = withdrawAndBorrowAmount(srcPrincipal, srcPrincipalNew);

        totalSupplyBase -= withdrawAmount;
        totalBorrowBase += borrowAmount;

        updateBasePrincipal(src, srcUser, srcPrincipalNew);

        if (srcBalance < 0) {
            if (uint256(-srcBalance) < baseBorrowMin) revert BorrowTooSmall();
            if (!isBorrowCollateralized(src)) revert NotCollateralized();
        }

        doTransferOut(baseToken, to, amount);

        emit Withdraw(src, to, amount);

        if (withdrawAmount > 0) {
            emit Transfer(src, address(0), presentValueSupply(baseSupplyIndex, withdrawAmount));
        }
    }

    /**
     * @dev Withdraw an amount of collateral asset from src to `to`
     */
    function withdrawCollateral(address src, address to, address asset, uint128 amount) internal {
        uint128 srcCollateral = userCollateral[src][asset].balance;
        uint128 srcCollateralNew = srcCollateral - amount;

        totalsCollateral[asset].totalSupplyAsset -= amount;
        userCollateral[src][asset].balance = srcCollateralNew;

        AssetInfo memory assetInfo = getAssetInfoByAddress(asset);
        updateAssetsIn(src, assetInfo, srcCollateral, srcCollateralNew);

        // Note: no accrue interest, BorrowCF < LiquidationCF covers small changes
        if (!isBorrowCollateralized(src)) revert NotCollateralized();

        doTransferOut(asset, to, amount);

        emit WithdrawCollateral(src, to, asset, amount);
    }

    /**
     * @notice Absorb a list of underwater accounts onto the protocol balance sheet
     * @param absorber The recipient of the incentive paid to the caller of absorb
     * @param accounts The list of underwater accounts to absorb
     */
    function absorb(address absorber, address[] calldata accounts) override external {
        if (isAbsorbPaused()) revert Paused();

        uint startGas = gasleft();
        accrueInternal();
        for (uint i = 0; i < accounts.length; ) {
            absorbInternal(absorber, accounts[i]);
            unchecked { i++; }
        }
        uint gasUsed = startGas - gasleft();

        // Note: liquidator points are an imperfect tool for governance,
        //  to be used while evaluating strategies for incentivizing absorption.
        // Using gas price instead of base fee would more accurately reflect spend,
        //  but is also subject to abuse if refunds were to be given automatically.
        LiquidatorPoints memory points = liquidatorPoints[absorber];
        points.numAbsorbs++;
        points.numAbsorbed += safe64(accounts.length);
        points.approxSpend += safe128(gasUsed * block.basefee);
        liquidatorPoints[absorber] = points;
    }

    /**
     * @dev Transfer user's collateral and debt to the protocol itself.
     */
    function absorbInternal(address absorber, address account) internal {
        if (!isLiquidatable(account)) revert NotLiquidatable();

        UserBasic memory accountUser = userBasic[account];
        int104 oldPrincipal = accountUser.principal;
        int256 oldBalance = presentValue(oldPrincipal);
        uint16 assetsIn = accountUser.assetsIn;

        uint256 basePrice = getPrice(baseTokenPriceFeed);
        uint256 deltaValue = 0;

        for (uint8 i = 0; i < numAssets; ) {
            if (isInAsset(assetsIn, i)) {
                AssetInfo memory assetInfo = getAssetInfo(i);
                address asset = assetInfo.asset;
                uint128 seizeAmount = userCollateral[account][asset].balance;
                userCollateral[account][asset].balance = 0;
                totalsCollateral[asset].totalSupplyAsset -= seizeAmount;

                uint256 value = mulPrice(seizeAmount, getPrice(assetInfo.priceFeed), assetInfo.scale);
                deltaValue += mulFactor(value, assetInfo.liquidationFactor);

                emit AbsorbCollateral(absorber, account, asset, seizeAmount, value);
            }
            unchecked { i++; }
        }

        uint256 deltaBalance = divPrice(deltaValue, basePrice, uint64(baseScale));
        int256 newBalance = oldBalance + signed256(deltaBalance);
        // New balance will not be negative, all excess debt absorbed by reserves
        if (newBalance < 0) {
            newBalance = 0;
        }

        int104 newPrincipal = principalValue(newBalance);
        updateBasePrincipal(account, accountUser, newPrincipal);

        // reset assetsIn
        userBasic[account].assetsIn = 0;

        (uint104 repayAmount, uint104 supplyAmount) = repayAndSupplyAmount(oldPrincipal, newPrincipal);

        // Reserves are decreased by increasing total supply and decreasing borrows
        //  the amount of debt repaid by reserves is `newBalance - oldBalance`
        totalSupplyBase += supplyAmount;
        totalBorrowBase -= repayAmount;

        uint256 basePaidOut = unsigned256(newBalance - oldBalance);
        uint256 valueOfBasePaidOut = mulPrice(basePaidOut, basePrice, uint64(baseScale));
        emit AbsorbDebt(absorber, account, basePaidOut, valueOfBasePaidOut);

        if (newPrincipal > 0) {
            emit Transfer(address(0), account, presentValueSupply(baseSupplyIndex, unsigned104(newPrincipal)));
        }
    }

    /**
     * @notice Buy collateral from the protocol using base tokens, increasing protocol reserves
       A minimum collateral amount should be specified to indicate the maximum slippage acceptable for the buyer.
     * @param asset The asset to buy
     * @param minAmount The minimum amount of collateral tokens that should be received by the buyer
     * @param baseAmount The amount of base tokens used to buy the collateral
     * @param recipient The recipient address
     */
    function buyCollateral(address asset, uint minAmount, uint baseAmount, address recipient) override external {
        if (isBuyPaused()) revert Paused();

        int reserves = getReserves();
        if (reserves >= 0 && uint(reserves) >= targetReserves) revert NotForSale();

        // Note: Re-entrancy can skip the reserves check above on a second buyCollateral call.
        doTransferIn(baseToken, msg.sender, baseAmount);

        uint collateralAmount = quoteCollateral(asset, baseAmount);
        if (collateralAmount < minAmount) revert TooMuchSlippage();
        if (collateralAmount > getCollateralReserves(asset)) revert InsufficientReserves();

        // Note: Pre-transfer hook can re-enter buyCollateral with a stale collateral ERC20 balance.
        //  Assets should not be listed which allow re-entry from pre-transfer now, as too much collateral could be bought.
        //  This is also a problem if quoteCollateral derives its discount from the collateral ERC20 balance.
        doTransferOut(asset, recipient, safe128(collateralAmount));

        emit BuyCollateral(msg.sender, asset, baseAmount, collateralAmount);
    }

    /**
     * @notice Gets the quote for a collateral asset in exchange for an amount of base asset
     * @param asset The collateral asset to get the quote for
     * @param baseAmount The amount of the base asset to get the quote for
     * @return The quote in terms of the collateral asset
     */
    function quoteCollateral(address asset, uint baseAmount) override public view returns (uint) {
        AssetInfo memory assetInfo = getAssetInfoByAddress(asset);
        uint256 assetPrice = getPrice(assetInfo.priceFeed);
        // Store front discount is derived from the collateral asset's liquidationFactor and storeFrontPriceFactor
        // discount = storeFrontPriceFactor * (1e18 - liquidationFactor)
        uint256 discountFactor = mulFactor(storeFrontPriceFactor, FACTOR_SCALE - assetInfo.liquidationFactor);
        uint256 assetPriceDiscounted = mulFactor(assetPrice, FACTOR_SCALE - discountFactor);
        uint256 basePrice = getPrice(baseTokenPriceFeed);
        // # of collateral assets
        // = (TotalValueOfBaseAmount / DiscountedPriceOfCollateralAsset) * assetScale
        // = ((basePrice * baseAmount / baseScale) / assetPriceDiscounted) * assetScale
        return basePrice * baseAmount * assetInfo.scale / assetPriceDiscounted / baseScale;
    }

    /**
     * @notice Withdraws base token reserves if called by the governor
     * @param to An address of the receiver of withdrawn reserves
     * @param amount The amount of reserves to be withdrawn from the protocol
     */
    function withdrawReserves(address to, uint amount) override external {
        if (msg.sender != governor) revert Unauthorized();

        int reserves = getReserves();
        if (reserves < 0 || amount > unsigned256(reserves)) revert InsufficientReserves();

        doTransferOut(baseToken, to, amount);

        emit WithdrawReserves(to, amount);
    }

    /**
     * @notice Sets Comet's ERC20 allowance of an asset for a manager
     * @dev Only callable by governor
     * @dev Note: Setting the `asset` as Comet's address will allow the manager
     * to withdraw from Comet's Comet balance
     * @param asset The asset that the manager will gain approval of
     * @param manager The account which will be allowed or disallowed
     * @param amount The amount of an asset to approve
     */
    function approveThis(address manager, address asset, uint amount) override external {
        if (msg.sender != governor) revert Unauthorized();

        ERC20(asset).approve(manager, amount);
    }

    /**
     * @notice Get the total number of tokens in circulation
     * @dev Note: uses updated interest indices to calculate
     * @return The supply of tokens
     **/
    function totalSupply() override external view returns (uint256) {
        (uint64 baseSupplyIndex_, ) = accruedInterestIndices(getNowInternal() - lastAccrualTime);
        return presentValueSupply(baseSupplyIndex_, totalSupplyBase);
    }

    /**
     * @notice Get the total amount of debt
     * @dev Note: uses updated interest indices to calculate
     * @return The amount of debt
     **/
    function totalBorrow() override external view returns (uint256) {
        (, uint64 baseBorrowIndex_) = accruedInterestIndices(getNowInternal() - lastAccrualTime);
        return presentValueBorrow(baseBorrowIndex_, totalBorrowBase);
    }

    /**
     * @notice Query the current positive base balance of an account or zero
     * @dev Note: uses updated interest indices to calculate
     * @param account The account whose balance to query
     * @return The present day base balance magnitude of the account, if positive
     */
    function balanceOf(address account) override public view returns (uint256) {
        (uint64 baseSupplyIndex_, ) = accruedInterestIndices(getNowInternal() - lastAccrualTime);
        int104 principal = userBasic[account].principal;
        return principal > 0 ? presentValueSupply(baseSupplyIndex_, unsigned104(principal)) : 0;
    }

    /**
     * @notice Query the current negative base balance of an account or zero
     * @dev Note: uses updated interest indices to calculate
     * @param account The account whose balance to query
     * @return The present day base balance magnitude of the account, if negative
     */
    function borrowBalanceOf(address account) override public view returns (uint256) {
        (, uint64 baseBorrowIndex_) = accruedInterestIndices(getNowInternal() - lastAccrualTime);
        int104 principal = userBasic[account].principal;
        return principal < 0 ? presentValueBorrow(baseBorrowIndex_, unsigned104(-principal)) : 0;
    }

    /**
     * @notice Fallback to calling the extension delegate for everything else
     */
    fallback() external payable {
        address delegate = extensionDelegate;
        assembly {
            calldatacopy(0, 0, calldatasize())
            let result := delegatecall(gas(), delegate, 0, calldatasize(), 0, 0)
            returndatacopy(0, 0, returndatasize())
            switch result
            case 0 { revert(0, returndatasize()) }
            default { return(0, returndatasize()) }
        }
    }
} < 

Comet ABI

[{"inputs":[{"components":[{"internalType":"address","name":"governor","type":"address"},{"internalType":"address","name":"pauseGuardian","type":"address"},{"internalType":"address","name":"baseToken","type":"address"},{"internalType":"address","name":"baseTokenPriceFeed","type":"address"},{"internalType":"address","name":"extensionDelegate","type":"address"},{"internalType":"uint64","name":"supplyKink","type":"uint64"},{"internalType":"uint64","name":"supplyPerYearInterestRateSlopeLow","type":"uint64"},{"internalType":"uint64","name":"supplyPerYearInterestRateSlopeHigh","type":"uint64"},{"internalType":"uint64","name":"supplyPerYearInterestRateBase","type":"uint64"},{"internalType":"uint64","name":"borrowKink","type":"uint64"},{"internalType":"uint64","name":"borrowPerYearInterestRateSlopeLow","type":"uint64"},{"internalType":"uint64","name":"borrowPerYearInterestRateSlopeHigh","type":"uint64"},{"internalType":"uint64","name":"borrowPerYearInterestRateBase","type":"uint64"},{"internalType":"uint64","name":"storeFrontPriceFactor","type":"uint64"},{"internalType":"uint64","name":"trackingIndexScale","type":"uint64"},{"internalType":"uint64","name":"baseTrackingSupplySpeed","type":"uint64"},{"internalType":"uint64","name":"baseTrackingBorrowSpeed","type":"uint64"},{"internalType":"uint104","name":"baseMinForRewards","type":"uint104"},{"internalType":"uint104","name":"baseBorrowMin","type":"uint104"},{"internalType":"uint104","name":"targetReserves","type":"uint104"},{"components":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"address","name":"priceFeed","type":"address"},{"internalType":"uint8","name":"decimals","type":"uint8"},{"internalType":"uint64","name":"borrowCollateralFactor","type":"uint64"},{"internalType":"uint64","name":"liquidateCollateralFactor","type":"uint64"},{"internalType":"uint64","name":"liquidationFactor","type":"uint64"},{"internalType":"uint128","name":"supplyCap","type":"uint128"}],"internalType":"struct CometConfiguration.AssetConfig[]","name":"assetConfigs","type":"tuple[]"}],"internalType":"struct CometConfiguration.Configuration","name":"config","type":"tuple"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"Absurd","type":"error"},{"inputs":[],"name":"AlreadyInitialized","type":"error"},{"inputs":[],"name":"BadAsset","type":"error"},{"inputs":[],"name":"BadDecimals","type":"error"},{"inputs":[],"name":"BadDiscount","type":"error"},{"inputs":[],"name":"BadMinimum","type":"error"},{"inputs":[],"name":"BadPrice","type":"error"},{"inputs":[],"name":"BorrowCFTooLarge","type":"error"},{"inputs":[],"name":"BorrowTooSmall","type":"error"},{"inputs":[],"name":"InsufficientReserves","type":"error"},{"inputs":[],"name":"InvalidInt104","type":"error"},{"inputs":[],"name":"InvalidInt256","type":"error"},{"inputs":[],"name":"InvalidUInt104","type":"error"},{"inputs":[],"name":"InvalidUInt128","type":"error"},{"inputs":[],"name":"InvalidUInt64","type":"error"},{"inputs":[],"name":"LiquidateCFTooLarge","type":"error"},{"inputs":[],"name":"NegativeNumber","type":"error"},{"inputs":[],"name":"NoSelfTransfer","type":"error"},{"inputs":[],"name":"NotCollateralized","type":"error"},{"inputs":[],"name":"NotForSale","type":"error"},{"inputs":[],"name":"NotLiquidatable","type":"error"},{"inputs":[],"name":"Paused","type":"error"},{"inputs":[],"name":"SupplyCapExceeded","type":"error"},{"inputs":[],"name":"TimestampTooLarge","type":"error"},{"inputs":[],"name":"TooManyAssets","type":"error"},{"inputs":[],"name":"TooMuchSlippage","type":"error"},{"inputs":[],"name":"TransferInFailed","type":"error"},{"inputs":[],"name":"TransferOutFailed","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"absorber","type":"address"},{"indexed":true,"internalType":"address","name":"borrower","type":"address"},{"indexed":true,"internalType":"address","name":"asset","type":"address"},{"indexed":false,"internalType":"uint256","name":"collateralAbsorbed","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"usdValue","type":"uint256"}],"name":"AbsorbCollateral","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"absorber","type":"address"},{"indexed":true,"internalType":"address","name":"borrower","type":"address"},{"indexed":false,"internalType":"uint256","name":"basePaidOut","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"usdValue","type":"uint256"}],"name":"AbsorbDebt","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"buyer","type":"address"},{"indexed":true,"internalType":"address","name":"asset","type":"address"},{"indexed":false,"internalType":"uint256","name":"baseAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"collateralAmount","type":"uint256"}],"name":"BuyCollateral","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"supplyPaused","type":"bool"},{"indexed":false,"internalType":"bool","name":"transferPaused","type":"bool"},{"indexed":false,"internalType":"bool","name":"withdrawPaused","type":"bool"},{"indexed":false,"internalType":"bool","name":"absorbPaused","type":"bool"},{"indexed":false,"internalType":"bool","name":"buyPaused","type":"bool"}],"name":"PauseAction","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"dst","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Supply","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"dst","type":"address"},{"indexed":true,"internalType":"address","name":"asset","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"SupplyCollateral","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"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"address","name":"asset","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"TransferCollateral","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"src","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Withdraw","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"src","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"address","name":"asset","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"WithdrawCollateral","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"WithdrawReserves","type":"event"},{"stateMutability":"payable","type":"fallback"},{"inputs":[{"internalType":"address","name":"absorber","type":"address"},{"internalType":"address[]","name":"accounts","type":"address[]"}],"name":"absorb","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"accrueAccount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"manager","type":"address"},{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approveThis","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"baseBorrowMin","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"baseMinForRewards","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"baseScale","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"baseToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"baseTokenPriceFeed","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"baseTrackingBorrowSpeed","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"baseTrackingSupplySpeed","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"borrowBalanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"borrowKink","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"borrowPerSecondInterestRateBase","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"borrowPerSecondInterestRateSlopeHigh","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"borrowPerSecondInterestRateSlopeLow","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"minAmount","type":"uint256"},{"internalType":"uint256","name":"baseAmount","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"}],"name":"buyCollateral","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"extensionDelegate","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"i","type":"uint8"}],"name":"getAssetInfo","outputs":[{"components":[{"internalType":"uint8","name":"offset","type":"uint8"},{"internalType":"address","name":"asset","type":"address"},{"internalType":"address","name":"priceFeed","type":"address"},{"internalType":"uint64","name":"scale","type":"uint64"},{"internalType":"uint64","name":"borrowCollateralFactor","type":"uint64"},{"internalType":"uint64","name":"liquidateCollateralFactor","type":"uint64"},{"internalType":"uint64","name":"liquidationFactor","type":"uint64"},{"internalType":"uint128","name":"supplyCap","type":"uint128"}],"internalType":"struct CometCore.AssetInfo","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"}],"name":"getAssetInfoByAddress","outputs":[{"components":[{"internalType":"uint8","name":"offset","type":"uint8"},{"internalType":"address","name":"asset","type":"address"},{"internalType":"address","name":"priceFeed","type":"address"},{"internalType":"uint64","name":"scale","type":"uint64"},{"internalType":"uint64","name":"borrowCollateralFactor","type":"uint64"},{"internalType":"uint64","name":"liquidateCollateralFactor","type":"uint64"},{"internalType":"uint64","name":"liquidationFactor","type":"uint64"},{"internalType":"uint128","name":"supplyCap","type":"uint128"}],"internalType":"struct CometCore.AssetInfo","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"utilization","type":"uint256"}],"name":"getBorrowRate","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"}],"name":"getCollateralReserves","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"priceFeed","type":"address"}],"name":"getPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getReserves","outputs":[{"internalType":"int256","name":"","type":"int256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"utilization","type":"uint256"}],"name":"getSupplyRate","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getUtilization","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"governor","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"manager","type":"address"}],"name":"hasPermission","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"initializeStorage","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"isAbsorbPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"isAllowed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"isBorrowCollateralized","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isBuyPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"isLiquidatable","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isSupplyPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isTransferPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isWithdrawPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"liquidatorPoints","outputs":[{"internalType":"uint32","name":"numAbsorbs","type":"uint32"},{"internalType":"uint64","name":"numAbsorbed","type":"uint64"},{"internalType":"uint128","name":"approxSpend","type":"uint128"},{"internalType":"uint32","name":"_reserved","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"numAssets","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"supplyPaused","type":"bool"},{"internalType":"bool","name":"transferPaused","type":"bool"},{"internalType":"bool","name":"withdrawPaused","type":"bool"},{"internalType":"bool","name":"absorbPaused","type":"bool"},{"internalType":"bool","name":"buyPaused","type":"bool"}],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"pauseGuardian","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"baseAmount","type":"uint256"}],"name":"quoteCollateral","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"storeFrontPriceFactor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"supply","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"dst","type":"address"},{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"supplyFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"supplyKink","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"supplyPerSecondInterestRateBase","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"supplyPerSecondInterestRateSlopeHigh","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"supplyPerSecondInterestRateSlopeLow","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"dst","type":"address"},{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"supplyTo","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"targetReserves","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalBorrow","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":"","type":"address"}],"name":"totalsCollateral","outputs":[{"internalType":"uint128","name":"totalSupplyAsset","type":"uint128"},{"internalType":"uint128","name":"_reserved","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"trackingIndexScale","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":"dst","type":"address"},{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferAsset","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"src","type":"address"},{"internalType":"address","name":"dst","type":"address"},{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferAssetFrom","outputs":[],"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":"","type":"address"}],"name":"userBasic","outputs":[{"internalType":"int104","name":"principal","type":"int104"},{"internalType":"uint64","name":"baseTrackingIndex","type":"uint64"},{"internalType":"uint64","name":"baseTrackingAccrued","type":"uint64"},{"internalType":"uint16","name":"assetsIn","type":"uint16"},{"internalType":"uint8","name":"_reserved","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"userCollateral","outputs":[{"internalType":"uint128","name":"balance","type":"uint128"},{"internalType":"uint128","name":"_reserved","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"userNonce","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"src","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdrawFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdrawReserves","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdrawTo","outputs":[],"stateMutability":"nonpayable","type":"function"}]
[{"inputs":[{"components":[{"internalType":"address","name":"governor","type":"address"},{"internalType":"address","name":"pauseGuardian","type":"address"},{"internalType":"address","name":"baseToken","type":"address"},{"internalType":"address","name":"baseTokenPriceFeed","type":"address"},{"internalType":"address","name":"extensionDelegate","type":"address"},{"internalType":"uint64","name":"supplyKink","type":"uint64"},{"internalType":"uint64","name":"supplyPerYearInterestRateSlopeLow","type":"uint64"},{"internalType":"uint64","name":"supplyPerYearInterestRateSlopeHigh","type":"uint64"},{"internalType":"uint64","name":"supplyPerYearInterestRateBase","type":"uint64"},{"internalType":"uint64","name":"borrowKink","type":"uint64"},{"internalType":"uint64","name":"borrowPerYearInterestRateSlopeLow","type":"uint64"},{"internalType":"uint64","name":"borrowPerYearInterestRateSlopeHigh","type":"uint64"},{"internalType":"uint64","name":"borrowPerYearInterestRateBase","type":"uint64"},{"internalType":"uint64","name":"storeFrontPriceFactor","type":"uint64"},{"internalType":"uint64","name":"trackingIndexScale","type":"uint64"},{"internalType":"uint64","name":"baseTrackingSupplySpeed","type":"uint64"},{"internalType":"uint64","name":"baseTrackingBorrowSpeed","type":"uint64"},{"internalType":"uint104","name":"baseMinForRewards","type":"uint104"},{"internalType":"uint104","name":"baseBorrowMin","type":"uint104"},{"internalType":"uint104","name":"targetReserves","type":"uint104"},{"components":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"address","name":"priceFeed","type":"address"},{"internalType":"uint8","name":"decimals","type":"uint8"},{"internalType":"uint64","name":"borrowCollateralFactor","type":"uint64"},{"internalType":"uint64","name":"liquidateCollateralFactor","type":"uint64"},{"internalType":"uint64","name":"liquidationFactor","type":"uint64"},{"internalType":"uint128","name":"supplyCap","type":"uint128"}],"internalType":"struct CometConfiguration.AssetConfig[]","name":"assetConfigs","type":"tuple[]"}],"internalType":"struct CometConfiguration.Configuration","name":"config","type":"tuple"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"Absurd","type":"error"},{"inputs":[],"name":"AlreadyInitialized","type":"error"},{"inputs":[],"name":"BadAsset","type":"error"},{"inputs":[],"name":"BadDecimals","type":"error"},{"inputs":[],"name":"BadDiscount","type":"error"},{"inputs":[],"name":"BadMinimum","type":"error"},{"inputs":[],"name":"BadPrice","type":"error"},{"inputs":[],"name":"BorrowCFTooLarge","type":"error"},{"inputs":[],"name":"BorrowTooSmall","type":"error"},{"inputs":[],"name":"InsufficientReserves","type":"error"},{"inputs":[],"name":"InvalidInt104","type":"error"},{"inputs":[],"name":"InvalidInt256","type":"error"},{"inputs":[],"name":"InvalidUInt104","type":"error"},{"inputs":[],"name":"InvalidUInt128","type":"error"},{"inputs":[],"name":"InvalidUInt64","type":"error"},{"inputs":[],"name":"LiquidateCFTooLarge","type":"error"},{"inputs":[],"name":"NegativeNumber","type":"error"},{"inputs":[],"name":"NoSelfTransfer","type":"error"},{"inputs":[],"name":"NotCollateralized","type":"error"},{"inputs":[],"name":"NotForSale","type":"error"},{"inputs":[],"name":"NotLiquidatable","type":"error"},{"inputs":[],"name":"Paused","type":"error"},{"inputs":[],"name":"SupplyCapExceeded","type":"error"},{"inputs":[],"name":"TimestampTooLarge","type":"error"},{"inputs":[],"name":"TooManyAssets","type":"error"},{"inputs":[],"name":"TooMuchSlippage","type":"error"},{"inputs":[],"name":"TransferInFailed","type":"error"},{"inputs":[],"name":"TransferOutFailed","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"absorber","type":"address"},{"indexed":true,"internalType":"address","name":"borrower","type":"address"},{"indexed":true,"internalType":"address","name":"asset","type":"address"},{"indexed":false,"internalType":"uint256","name":"collateralAbsorbed","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"usdValue","type":"uint256"}],"name":"AbsorbCollateral","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"absorber","type":"address"},{"indexed":true,"internalType":"address","name":"borrower","type":"address"},{"indexed":false,"internalType":"uint256","name":"basePaidOut","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"usdValue","type":"uint256"}],"name":"AbsorbDebt","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"buyer","type":"address"},{"indexed":true,"internalType":"address","name":"asset","type":"address"},{"indexed":false,"internalType":"uint256","name":"baseAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"collateralAmount","type":"uint256"}],"name":"BuyCollateral","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"supplyPaused","type":"bool"},{"indexed":false,"internalType":"bool","name":"transferPaused","type":"bool"},{"indexed":false,"internalType":"bool","name":"withdrawPaused","type":"bool"},{"indexed":false,"internalType":"bool","name":"absorbPaused","type":"bool"},{"indexed":false,"internalType":"bool","name":"buyPaused","type":"bool"}],"name":"PauseAction","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"dst","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Supply","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"dst","type":"address"},{"indexed":true,"internalType":"address","name":"asset","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"SupplyCollateral","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"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"address","name":"asset","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"TransferCollateral","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"src","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Withdraw","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"src","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"address","name":"asset","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"WithdrawCollateral","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"WithdrawReserves","type":"event"},{"stateMutability":"payable","type":"fallback"},{"inputs":[{"internalType":"address","name":"absorber","type":"address"},{"internalType":"address[]","name":"accounts","type":"address[]"}],"name":"absorb","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"accrueAccount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"manager","type":"address"},{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approveThis","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"baseBorrowMin","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"baseMinForRewards","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"baseScale","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"baseToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"baseTokenPriceFeed","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"baseTrackingBorrowSpeed","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"baseTrackingSupplySpeed","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"borrowBalanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"borrowKink","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"borrowPerSecondInterestRateBase","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"borrowPerSecondInterestRateSlopeHigh","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"borrowPerSecondInterestRateSlopeLow","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"minAmount","type":"uint256"},{"internalType":"uint256","name":"baseAmount","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"}],"name":"buyCollateral","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"extensionDelegate","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"i","type":"uint8"}],"name":"getAssetInfo","outputs":[{"components":[{"internalType":"uint8","name":"offset","type":"uint8"},{"internalType":"address","name":"asset","type":"address"},{"internalType":"address","name":"priceFeed","type":"address"},{"internalType":"uint64","name":"scale","type":"uint64"},{"internalType":"uint64","name":"borrowCollateralFactor","type":"uint64"},{"internalType":"uint64","name":"liquidateCollateralFactor","type":"uint64"},{"internalType":"uint64","name":"liquidationFactor","type":"uint64"},{"internalType":"uint128","name":"supplyCap","type":"uint128"}],"internalType":"struct CometCore.AssetInfo","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"}],"name":"getAssetInfoByAddress","outputs":[{"components":[{"internalType":"uint8","name":"offset","type":"uint8"},{"internalType":"address","name":"asset","type":"address"},{"internalType":"address","name":"priceFeed","type":"address"},{"internalType":"uint64","name":"scale","type":"uint64"},{"internalType":"uint64","name":"borrowCollateralFactor","type":"uint64"},{"internalType":"uint64","name":"liquidateCollateralFactor","type":"uint64"},{"internalType":"uint64","name":"liquidationFactor","type":"uint64"},{"internalType":"uint128","name":"supplyCap","type":"uint128"}],"internalType":"struct CometCore.AssetInfo","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"utilization","type":"uint256"}],"name":"getBorrowRate","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"}],"name":"getCollateralReserves","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"priceFeed","type":"address"}],"name":"getPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getReserves","outputs":[{"internalType":"int256","name":"","type":"int256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"utilization","type":"uint256"}],"name":"getSupplyRate","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getUtilization","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"governor","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"manager","type":"address"}],"name":"hasPermission","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"initializeStorage","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"isAbsorbPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"isAllowed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"isBorrowCollateralized","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isBuyPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"isLiquidatable","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isSupplyPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isTransferPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isWithdrawPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"liquidatorPoints","outputs":[{"internalType":"uint32","name":"numAbsorbs","type":"uint32"},{"internalType":"uint64","name":"numAbsorbed","type":"uint64"},{"internalType":"uint128","name":"approxSpend","type":"uint128"},{"internalType":"uint32","name":"_reserved","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"numAssets","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"supplyPaused","type":"bool"},{"internalType":"bool","name":"transferPaused","type":"bool"},{"internalType":"bool","name":"withdrawPaused","type":"bool"},{"internalType":"bool","name":"absorbPaused","type":"bool"},{"internalType":"bool","name":"buyPaused","type":"bool"}],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"pauseGuardian","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"baseAmount","type":"uint256"}],"name":"quoteCollateral","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"storeFrontPriceFactor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"supply","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"dst","type":"address"},{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"supplyFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"supplyKink","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"supplyPerSecondInterestRateBase","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"supplyPerSecondInterestRateSlopeHigh","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"supplyPerSecondInterestRateSlopeLow","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"dst","type":"address"},{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"supplyTo","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"targetReserves","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalBorrow","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":"","type":"address"}],"name":"totalsCollateral","outputs":[{"internalType":"uint128","name":"totalSupplyAsset","type":"uint128"},{"internalType":"uint128","name":"_reserved","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"trackingIndexScale","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":"dst","type":"address"},{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferAsset","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"src","type":"address"},{"internalType":"address","name":"dst","type":"address"},{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferAssetFrom","outputs":[],"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":"","type":"address"}],"name":"userBasic","outputs":[{"internalType":"int104","name":"principal","type":"int104"},{"internalType":"uint64","name":"baseTrackingIndex","type":"uint64"},{"internalType":"uint64","name":"baseTrackingAccrued","type":"uint64"},{"internalType":"uint16","name":"assetsIn","type":"uint16"},{"internalType":"uint8","name":"_reserved","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"userCollateral","outputs":[{"internalType":"uint128","name":"balance","type":"uint128"},{"internalType":"uint128","name":"_reserved","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"userNonce","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"src","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdrawFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdrawReserves","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdrawTo","outputs":[],"stateMutability":"nonpayable","type":"function"}]

Comet Bytecode

6107406040523462000382576200001f6200001962000563565b620007dc565b604051614a5390816200109d823960805181818161060601528181610d000152818161138e015261173a015260a05181818161082c0152610e06015260c0518181816107e40152818161130f01528181611571015281816117a30152818161185401528181612a36015281816131a80152818161328b0152818161380701528181613c5c0152613d3d015260e05181818161197c01528181612b5e015281816141c401526145b7015261010051818181610e4601526146ef0152610120518181816112c50152612557015261014051818181610ee40152818161259301526125fd015261016051818181610ff3015261262d01526101805181818161114d01526125ba01526101a0518181816110d5015261266701526101c0518181816109bf015281816126a3015261270d01526101e0518181816108c9015261273d015261020051818181610f6f01526126ca01526102205181818161077c015261457b015261024051818181610c7901528181612b8401528181612e25015281816143ae01526145ef01526102605181818161134f01526130910152610280518181816106ba015261244901526102a05181818161118901526123d101526102c051818181611111015261234301526102e051818181610a3901528181613a5e0152613ddc015261030051818181610ab4015261192c01526103205181610a7701526103405181818161128801528181611b3e0152818161215801528181612bbb01528181612d1301526141f4015261036051816130b801526103805181611b8e01526103a05181611b6d01526103c05181611cef01526103e05181611cce01526104005181611d3f01526104205181611d1e01526104405181611d8f01526104605181611d6e01526104805181611ddf01526104a05181611dbe01526104c05181611e2f01526104e05181611e0e01526105005181611e7f01526105205181611e5e01526105405181611ecf01526105605181611eae01526105805181611f1f01526105a05181611efe01526105c05181611f6f01526105e05181611f4e01526106005181611fbf01526106205181611f9e0152610640518161200f01526106605181611fee0152610680518161205f01526106a0518161203e01526106c051816120af01526106e0518161208e015261070051816120fd015261072051816120dc0152f35b600080fd5b50634e487b7160e01b600052604160045260246000fd5b601f909101601f19168101906001600160401b03821190821017620003c257604052565b620003cc62000387565b604052565b90620003e160405192836200039e565b565b51906001600160a01b03821682036200038257565b6001600160401b031690565b51906001600160401b03821682036200038257565b51906001600160681b03821682036200038257565b519060ff821682036200038257565b81601f8201121562000382578051906001600160401b03821162000553575b604080519360209162000475838660051b01876200039e565b848652828601918360e08097028601019481861162000382578401925b858410620004a4575050505050505090565b86848303126200038257825190620004bd88836200039e565b620004c885620003e3565b8252620004d7868601620003e3565b86830152620004e88486016200042e565b848301526060620004fb81870162000404565b9083015260806200050e81870162000404565b9083015260a06200052181870162000404565b9083015260c085810151929091906001600160801b038416840362000382578993889382015281520193019262000492565b6200055d62000387565b6200045c565b62005af080380380604051926200057b82856200039e565b8339810190602081830312620003825780516001600160401b03918282116200038257016102a080828503126200038257620005b790620003d1565b92620005c382620003e3565b8452620005d360208301620003e3565b6020850152620005e660408301620003e3565b6040850152620005f960608301620003e3565b60608501526200060c60808301620003e3565b60808501526200061f60a0830162000404565b60a08501526200063260c0830162000404565b60c08501526200064560e0830162000404565b60e08501526101006200065a81840162000404565b908501526101206200066e81840162000404565b908501526101406200068281840162000404565b908501526101606200069681840162000404565b90850152610180620006aa81840162000404565b908501526101a0620006be81840162000404565b908501526101c0620006d281840162000404565b908501526101e0620006e681840162000404565b90850152610200620006fa81840162000404565b908501526102206200070e81840162000419565b908501526102406200072281840162000419565b908501526102606200073681840162000419565b90850152610280928383015190811162000382576200075692016200043d565b9082015290565b90816020910312620003825762000774906200042e565b90565b506040513d6000823e3d90fd5b50634e487b7160e01b600052601260045260246000fd5b8115620007a6570490565b620007b062000784565b0490565b6001600160401b0391821691908215620007cd57160490565b620007d762000784565b160490565b60408181018051909291906200080990620007fd906001600160a01b031681565b6001600160a01b031690565b90805180938163313ce56760e01b94858252602091829160049889915afa92831562000d75575b60009362000d51575b5060ff8316926012841162000d41576101a0908184019460018060401b0391670de0b6b3a7640000836200086e8951620003f8565b161162000d3157610280948587019b600f8d51511162000d21576102208881018051909c919391906001600160681b03161562000d115790600860ff8f9493838f9160608f0197620008ce620007fd620007fd8b5160018060a01b031690565b93518094819382525afa90811562000d01575b60009162000ccd575b50160362000cbd5789516001600160a01b03908116608052908a0151811660a0529051811660c05290511660e052608087015162000944906200093e906001600160a01b0316996101009a8b5251620003f8565b620003f8565b905261032052600a0a1694610240968688526101c0966200096c6200093e89880151620003f8565b92610260938452620f424080921062000cae57508851906200098e916200079b565b61036052516001600160681b03166001600160681b03166102c0526101e09283850151620009bc90620003f8565b620009c790620003f8565b90526102009687850151620009dc90620003f8565b620009e790620003f8565b6102a0528401516001600160681b039081166102e05290840151166103005260a083015162000a1690620003f8565b62000a2190620003f8565b936101209485526301e13380958680938160c088015162000a4290620003f8565b9062000a4e91620007b4565b62000a5990620003f8565b90610140918252828060e08a015162000a7290620003f8565b9062000a7e91620007b4565b62000a8990620003f8565b9561016096875289015162000a9e90620003f8565b9062000aaa91620007b4565b62000ab590620003f8565b98610180998a5288015162000aca90620003f8565b62000ad590620003f8565b905286015162000ae590620003f8565b9062000af191620007b4565b62000afc90620003f8565b905283015162000b0c90620003f8565b9062000b1891620007b4565b62000b2390620003f8565b9052015162000b3290620003f8565b9062000b3e91620007b4565b62000b4990620003f8565b905281515160ff16610340528151600062000b649162000dc1565b6103a052610380528151600162000b7b9162000dc1565b6103e0526103c0528151600262000b929162000dc1565b61042052610400528151600362000ba99162000dc1565b610460526104405281519062000bbf9162000dc1565b6104a052610480528051600562000bd69162000dc1565b6104e0526104c0528051600662000bed9162000dc1565b61052052610500528051600762000c049162000dc1565b61056052610540528051600862000c1b9162000dc1565b6105a052610580528051600962000c329162000dc1565b6105e0526105c0528051600a62000c499162000dc1565b61062052610600528051600b62000c609162000dc1565b61066052610640528051600c62000c779162000dc1565b6106a052610680528051600d62000c8e9162000dc1565b6106e0526106c05251600e62000ca49162000dc1565b6107205261070052565b51630456c65960e51b81528a90fd5b8b51630456c65960e51b81528e90fd5b62000cf29150843d861162000cf9575b62000ce981836200039e565b8101906200075d565b38620008ea565b503d62000cdd565b62000d0b62000777565b620008e1565b8b51636e77247560e01b81528e90fd5b895163df8153c760e01b81528c90fd5b87516324dc918f60e11b81528a90fd5b8451630456c65960e51b81528790fd5b8162000d6d9294503d851162000cf95762000ce981836200039e565b913862000839565b62000d7f62000777565b62000830565b6040519062000d9660e0836200039e565b8160c06000918281528260208201528260408201528260608201528260808201528260a08201520152565b62000dcb62000d85565b508051821015620010925760059190911b016020015180516001600160a01b0316602082810151909391906001600160a01b031660409362000e108582015160ff1690565b926001600160a01b039081169283156200108457169185519663313ce56760e01b90818952600860ff60049a83818d818b5afa90811562001074575b60009162001052575b501603620010425760ff91885190815281818b81875afa91821562001032575b60009262001010575b505081861691829116036200100057606083019262000e9e8451620003f8565b93608082019462000eb46200093e8751620003f8565b6001600160401b03918216101562000ff057670de0b6b3a7640000809162000edd8851620003f8565b161162000fe05762000f2962000f3862000f2962000f23999a9b9c9d9462000f2362000f3062000f2962000f1861271062000f4899620007b4565b809e819551620003f8565b620007b4565b61ffff1690565b9a51620003f8565b9762000f2360a0860151620003f8565b9961ffff808816908716101562000fd357505060c0015162000f7f916200093e91600a9190910a906001600160801b03166200079b565b60a092831b61ffff60a01b169190911760b09390931b61ffff60b01b169290921760c09690961b61ffff60c01b1695909517949290921b60ff60a01b161760a89190911b600160a81b600160e81b03161790565b5163327d763960e21b8152fd5b89516304d6b32560e41b81528b90fd5b895163327d763960e21b81528b90fd5b8651630456c65960e51b81528890fd5b6200102a9250803d1062000cf95762000ce981836200039e565b388062000e7e565b6200103c62000777565b62000e75565b8751630456c65960e51b81528990fd5b6200106d9150843d861162000cf95762000ce981836200039e565b3862000e55565b6200107e62000777565b62000e4c565b506000965086955050505050565b505060009060009056fe60806040526004361015610018575b6100166146e2565b005b60003560e01c8063042e02cf146105545780630902f1ac1461054b5780630bc47ad1146105425780630c340a241461053957806318160ddd14610530578063189bb2f1146105275780631c9f7fb91461051e5780631f5954bd1461051557806323b872dd1461050c57806324a3d6221461050357806326441318146104fa5780632a48cf12146104f15780632b92a07d146104e85780632d05670b146104df5780632e04b8e7146104d6578063300e6beb146104cd578063313ce567146104c457806332176c49146104bb578063374c49b4146104b257806338aa813f146104a95780633b3bec2e146104a057806341976e09146104975780634232cd631461048e578063439e2e451461048557806344c1e5eb1461047c57806344c35d071461047357806344ff241d1461046a57806359e017bd146104615780635a94b8d11461045857806367800b5f1461044f57806370a08231146104465780637914acc71461043d5780637ac88ed1146104345780637eb711311461042b578063804de71f146104225780638285ef40146104195780638d5d814c1461041057806390323177146104075780639241a561146103fe5780639364e18a146103f557806394920cca146103ec5780639ea99a5a146103e35780639fa83b5a146103da5780639ff567f8146103d1578063a1654379146103c8578063a1a1ef43146103bf578063a46fe83b146103b6578063a5b4ff79146103ad578063a9059cbb146103a4578063aba7f15e1461039b578063ad14777c14610392578063bfe69c8d14610389578063c1ee2c1814610380578063c3b35a7e14610377578063c3cecfd21461036e578063c55dae6314610365578063c5fa15cf1461035c578063c8c7fe6b14610353578063cde680411461034a578063d8e5f61114610341578063d955759d14610338578063dc4abafd1461032f578063e478795d14610326578063e4e6e7791461031d578063e7dad6bd14610314578063f2b9fdb81461030b5763f3fef3a30361000e576103066119d8565b61000e565b506103066119ab565b50610306611965565b506103066117f9565b5061030661170e565b5061030661169b565b5061030661167c565b50610306611655565b50610306611639565b5061030661160e565b506103066115a0565b5061030661155a565b506103066114f5565b506103066114d9565b506103066114bb565b50610306611420565b50610306611372565b50610306611336565b506103066112e8565b506103066112ac565b5061030661126d565b50610306611246565b50610306611205565b506103066111dd565b506103066111ac565b50610306611170565b50610306611134565b506103066110f8565b506103066110bc565b5061030661109e565b50610306611077565b50610306611016565b50610306610fda565b50610306610fbe565b50610306610f92565b50610306610f56565b50610306610f2e565b50610306610f07565b50610306610ecb565b50610306610e75565b50610306610e2f565b50610306610ca6565b50610306610c60565b50610306610c44565b50610306610c28565b50610306610c00565b50610306610bcb565b50610306610aff565b50610306610ad7565b50610306610a9b565b50610306610a5c565b50610306610a20565b506103066109e2565b506103066109a6565b50610306610951565b506103066108b0565b50610306610892565b50610306610815565b506103066107cc565b50610306610763565b506103066106dd565b506103066106a1565b50610306610635565b506103066105ef565b506103066105c9565b506103066105a5565b50610306610573565b6001600160a01b0381160361056e57565b600080fd5b503461056e57602036600319011261056e57602061059b6004356105968161055d565b612cd5565b6040519015158152f35b503461056e57600036600319011261056e5760206105c16129e1565b604051908152f35b503461056e57600036600319011261056e5760206001805460f81c161515604051908152f35b503461056e57600036600319011261056e576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b503461056e57600036600319011261056e57602066038d7ea4c6800061069861065c6121c9565b61067f6001549161067964ffffffffff91828560d01c169061223d565b166124ac565b506001600160401b0316906001600160681b0316611ae0565b04604051908152f35b503461056e57600036600319011261056e5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b503461056e576000806003193601126107605760015464ffffffffff8160d01c1661074f5764ffffffffff60d01b6107136121c9565b64ffffffffff60d01b1990921660d09290921b161760015580546001600160801b0319166e038d7ea4c6800000038d7ea4c68000178155604051f35b60405162dc149f60e41b8152600490fd5b80fd5b503461056e57600036600319011261056e5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b9081606091031261056e5780356107b58161055d565b91604060208301356107c68161055d565b92013590565b503461056e5761080a6107e036600461079f565b91907f000000000000000000000000000000000000000000000000000000000000000091336137cf565b602060405160018152f35b503461056e57600036600319011261056e576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b919082608091031261056e5781356108728161055d565b9160208101356108818161055d565b91606060408301356107c68161055d565b503461056e576100166108a636600461085b565b9291909133613c2d565b503461056e57600036600319011261056e5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b919082604091031261056e57602082356109058161055d565b9201356109118161055d565b90565b9060018060a01b0316600052602052604060002090565b6001600160801b031690565b6001600160801b0391821681529116602082015260400190565b503461056e576109a26109866109683660046108ec565b6001600160a01b039091166000908152600660205260409020610914565b54604051918291608081901c906001600160801b031683610937565b0390f35b503461056e57600036600319011261056e5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b503461056e57602036600319011261056e57600435610a008161055d565b60018060a01b031660005260046020526020604060002054604051908152f35b503461056e57600036600319011261056e5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b503461056e57600036600319011261056e57602060405160ff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b503461056e57600036600319011261056e5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b503461056e57602036600319011261056e5760206105c1600435610afa8161055d565b61469a565b503461056e57602036600319011261056e57602061059b600435610b228161055d565b612b0c565b6001600160401b031690565b610bc99092919260e08061010083019560ff815116845260018060a01b03806020830151166020860152604082015116604085015260018060401b036060820151166060850152610b936080820151608086019060018060401b03169052565b60a0818101516001600160401b03169085015260c0818101516001600160401b03169085015201516001600160801b0316910152565b565b503461056e57602036600319011261056e576109a2610bf4600435610bef8161055d565b612145565b60405191829182610b33565b503461056e57602036600319011261056e5760206105c1600435610c238161055d565b61281f565b503461056e57610016610c3c36600461079f565b913333613176565b503461056e57610016610c5836600461079f565b9133336137cf565b503461056e57600036600319011261056e5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b8015150361056e57565b503461056e5760a036600319011261056e57600435610cc481610c9c565b602435610cd081610c9c565b60443591610cdd83610c9c565b60643592610cea84610c9c565b608435610cf681610c9c565b60018060a01b03807f0000000000000000000000000000000000000000000000000000000000000000163314159081610e02575b50610df1577f3be39979091ae7ca962aa1c44e645f2df3c221b79f324afa5f44aedc8d2f690d94610dec92610db7610d746000610d66886149ef565b9060ff8080931691161b1690565b610d826001610d668a6149ef565b17610d916002610d66856149ef565b17610da06003610d66866149ef565b17610daf6004610d66876149ef565b176001612de9565b6040519586958693909594919260809360a0860197151586521515602086015215156040850152151560608401521515910152565b0390a1005b6040516282b42960e81b8152600490fd5b90507f00000000000000000000000000000000000000000000000000000000000000001633141538610d2a565b503461056e57600036600319011261056e576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b503461056e57602036600319011261056e57600435610e938161055d565b6001600160a01b0316600090815260026020526040908190205490519081906109a290608081901c906001600160801b031683610937565b503461056e57600036600319011261056e5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b503461056e57600036600319011261056e576020600460015460f81c161515604051908152f35b503461056e57602036600319011261056e5760206105c1600435610f518161055d565b61463c565b503461056e57600036600319011261056e5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b503461056e57604036600319011261056e5760206105c1600435610fb58161055d565b6024359061451d565b503461056e57600036600319011261056e5760206105c1612784565b503461056e57600036600319011261056e5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b503461056e57600036600319011261056e57602066038d7ea4c6800061069861103d6121c9565b61105a6001549161067964ffffffffff91828560d01c169061223d565b6001600160401b03169160681c6001600160681b03169050611ae0565b503461056e57600036600319011261056e576020600860015460f81c161515604051908152f35b503461056e576100166110b236600461085b565b9291909133613176565b503461056e57600036600319011261056e5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b503461056e57600036600319011261056e5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b503461056e57600036600319011261056e5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b503461056e57600036600319011261056e5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b503461056e57602036600319011261056e5760206111cb600435612665565b6040516001600160401b039091168152f35b503461056e57602036600319011261056e5760206105c16004356112008161055d565b6128d9565b503461056e57602060ff61123a61121d3660046108ec565b6001600160a01b0390911660009081526003855260409020610914565b54166040519015158152f35b503461056e57600036600319011261056e576020600260015460f81c161515604051908152f35b503461056e57600036600319011261056e57602060405160ff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b503461056e57600036600319011261056e5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b503461056e57604036600319011261056e5761080a6004356113098161055d565b602435907f00000000000000000000000000000000000000000000000000000000000000009033336137cf565b503461056e57600036600319011261056e5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b503461056e5761138336600461079f565b6001600160a01b03917f000000000000000000000000000000000000000000000000000000000000000083163303610df1576113da93600060209460405180978196829563095ea7b360e01b845260048401613e16565b0393165af18015611413575b6113ec57005b6100169060203d811161140c575b6114048183611a05565b810190613482565b503d6113fa565b61141b612812565b6113e6565b503461056e57602036600319011261056e576100166004356114418161055d565b6114496122a3565b60018060a01b038116600052600560205260406000206114b06040519161147160a084611a05565b54600c81900b83526001600160401b03606882901c8116602085015260a882901c16604084015261ffff60e882901c16606084015260f81c6080830152565b8051600c0b91613016565b503461056e576100166114cf36600461085b565b92919091336137cf565b503461056e576100166114ed36600461079f565b913333613c2d565b503461056e57604036600319011261056e576004356115138161055d565b602435906001600160401b039081831161056e573660238401121561056e57826004013591821161056e573660248360051b8501011161056e576024610016930190614038565b503461056e57600036600319011261056e576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b503461056e57602036600319011261056e576004356115be8161055d565b60018060a01b0316600052600760205260806040600020546040519063ffffffff8116825260018060401b038160201c166020830152600180841b038160601c16604083015260e01c6060820152f35b503461056e57602036600319011261056e5760043560ff8116810361056e57610bf46109a291611b2d565b503461056e57602061059b61164f3660046108ec565b90614721565b503461056e57600036600319011261056e576020601060015460f81c161515604051908152f35b503461056e57602036600319011261056e5760206111cb600435612555565b503461056e57602036600319011261056e576004356116b98161055d565b60018060a01b0316600052600560205260a06040600020546040519080600c0b825260018060401b03808260681c1660208401528160a81c16604083015261ffff8160e81c16606083015260f81c6080820152f35b503461056e57604036600319011261056e5760043561172c8161055d565b602435906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000081163303610df1576117696129e1565b600081129081156117e7575b506117d557816117c7847fec4431f2ba1a9382f6b0c4352b888cba6f7db91667d9f776abe5ad8ddc5401b6947f0000000000000000000000000000000000000000000000000000000000000000613e31565b6040519384521691602090a2005b60405163128bd24d60e31b8152600490fd5b6117f191506149e4565b831138611775565b503461056e57608036600319011261056e576004356118178161055d565b604435606435916118278361055d565b601060015460f81c166119535761183c6129e1565b60008112159081611928575b506119165761187882337f0000000000000000000000000000000000000000000000000000000000000000613497565b611882828261451d565b92602435841061190457611895826128d9565b84116117d5577ff891b2a411b0e66a5f0a6ff1368670fefa287a13f541eb633a386a1a9cc7046b916118dc6118ff926118d56118d088614943565b61092b565b9083613e31565b6040805194855260208501959095526001600160a01b0316933393918291820190565b0390a3005b60405163fa6ad35560e01b8152600490fd5b604051631d99ddbf60e01b8152600490fd5b90507f0000000000000000000000000000000000000000000000000000000000000000111538611848565b6040516313d0ff5960e31b8152600490fd5b503461056e57600036600319011261056e576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b503461056e57604036600319011261056e576100166004356119cc8161055d565b60243590333333613176565b503461056e57604036600319011261056e576100166004356119f98161055d565b60243590333333613c2d565b601f909101601f19168101906001600160401b03821190821017611a2857604052565b634e487b7160e01b600052604160045260246000fd5b90610bc96040519283611a05565b60405190611a5c61010083611a05565b8160e06000918281528260208201528260408201528260608201528260808201528260a08201528260c08201520152565b50634e487b7160e01b600052601260045260246000fd5b50634e487b7160e01b600052601160045260246000fd5b6001600160401b0391821691908215611ad357160490565b611adb611a8d565b160490565b8060001904821181151516611af3570290565b611afb611aa4565b0290565b60ff16604d8111611b11575b600a0a90565b611b19611aa4565b611b0b565b6001600160401b039091169052565b611b35611a4c565b5060ff811660ff7f0000000000000000000000000000000000000000000000000000000000000000168110156121335780611cc357507f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000005b6001600160401b0380611bcb612710670de0b6b3a7640000611abb565b1661ffff9181838560a01c1690611be191611ae0565b611bea90610b27565b91611bfb8160b087901c8616611ae0565b611c0490610b27565b938560c01c1690611c1491611ae0565b611c1d90610b27565b938560a01c60ff16611c2e90611aff565b611c3790610b27565b91808316908760a81c1690611c4b91611ae0565b611c549061092b565b95611c60610100611a3e565b60ff9890981688526001600160a01b039182166020890152166040870152611c8b9060608701611b1e565b611c989060808601611b1e565b611ca59060a08501611b1e565b611cb29060c08401611b1e565b6001600160801b031660e082015290565b60018103611d1357507f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000611bae565b60028103611d6357507f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000611bae565b60038103611db357507f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000611bae565b60048103611e0357507f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000611bae565b60058103611e5357507f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000611bae565b60068103611ea357507f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000611bae565b60078103611ef357507f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000611bae565b60088103611f4357507f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000611bae565b60098103611f9357507f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000611bae565b600a8103611fe357507f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000611bae565b600b810361203357507f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000611bae565b600c810361208357507f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000611bae565b600d81036120d357507f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000611bae565b600e03612121577f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000611bae565b60405163971241a160e01b8152600490fd5b604051633640530560e01b8152600490fd5b9061214e611a4c565b5060009060ff92837f000000000000000000000000000000000000000000000000000000000000000016925b838582161061219557604051633640530560e01b8152600490fd5b61219e81611b2d565b60208101516001600160a01b038481169116146121c05750600101841661217a565b93505050915090565b600160281b4210156121e05764ffffffffff421690565b604051633d32ffdb60e01b8152600490fd5b9060405161220160a082611a05565b608081935480600c0b835260018060401b03808260681c1660208501528160a81c16604084015261ffff8160e81c16606084015260f81c910152565b64ffffffffff9182169116818110612253570390565b61225b611aa4565b0390565b6001600160681b031690565b6001600160401b0391821691908116908290038111612288570190565b612290611aa4565b0190565b60681c6001600160681b031690565b6122ab6121c9565b6122d56122cb6122c560015464ffffffffff9060d01c1690565b8361223d565b64ffffffffff1690565b90816122df575050565b816123366122ef610bc9946124ac565b60008054600160401b600160801b03191660409290921b600160401b600160801b0316919091178155919082546001600160401b0319166001600160401b03909116178255565b61234160015461225f565b7f000000000000000000000000000000000000000000000000000000000000000092906001600160681b031683811015612436575b5061238a612385600154612294565b61225f565b928310156123be575b50506001805464ffffffffff60d01b191660d09390931b64ffffffffff60d01b169290921790915550565b6123ff6123fa61242e946123f561240e947f0000000000000000000000000000000000000000000000000000000000000000611ae0565b612e1f565b6148f7565b825460c01c61226b565b61226b565b81546001600160c01b031660c09190911b6001600160c01b031916179055565b388080612393565b61248061246d6123fa6124a6936123f5867f0000000000000000000000000000000000000000000000000000000000000000611ae0565b845460801c6001600160401b031661226b565b8354600160801b600160c01b03191660809190911b600160801b600160c01b0316178355565b38612376565b6000546001600160401b03604082901c811693929181169190816124d1575b50509190565b8161252461251e6124fe97946125306125369761252a8761250561252a996124f7612784565b9e8f612555565b169d612665565b169b61252461251e670de0b6b3a7640000998a93611ae0565b84611ae0565b046148f7565b9061226b565b98611ae0565b9138806124cb565b81198111612288570190565b818110612253570390565b7f00000000000000000000000000000000000000000000000000000000000000008082116125de57506123fa670de0b6b3a76400006125b7610911937f0000000000000000000000000000000000000000000000000000000000000000611ae0565b047f000000000000000000000000000000000000000000000000000000000000000061253e565b610911916123fa91612651670de0b6b3a764000091612621836125b7837f0000000000000000000000000000000000000000000000000000000000000000611ae0565b93818110612658575b037f0000000000000000000000000000000000000000000000000000000000000000611ae0565b049061253e565b612660611aa4565b61262a565b7f00000000000000000000000000000000000000000000000000000000000000008082116126ee57506123fa670de0b6b3a76400006126c7610911937f0000000000000000000000000000000000000000000000000000000000000000611ae0565b047f000000000000000000000000000000000000000000000000000000000000000061253e565b610911916123fa91612651670de0b6b3a764000091612731836126c7837f0000000000000000000000000000000000000000000000000000000000000000611ae0565b93818110612761575b037f0000000000000000000000000000000000000000000000000000000000000000611ae0565b612769611aa4565b61273a565b8115612778570490565b612780611a8d565b0490565b60005460015466038d7ea4c68000906127c6906001600160681b036001600160401b03846127b6828816848616611ae0565b049560401c169160681c16611ae0565b04816127d3575050600090565b670de0b6b3a76400009080600019048211811515166127f157020490565b6127f9611aa4565b020490565b51906001600160501b038216820361056e57565b506040513d6000823e3d90fd5b604051633fabe5a360e21b81529060a090829060049082906001600160a01b03165afa9081156128bd575b600091612871575b50600081131561285f5790565b60405163fd1ee34960e01b8152600490fd5b9060a0823d82116128b5575b8161288a60a09383611a05565b81010312610760575061289c816127fe565b506128ae6080602083015192016127fe565b5038612852565b3d915061287d565b6128c5612812565b61284a565b9081602091031261056e575190565b6040516370a0823160e01b8152306004820152906001600160a01b0316602082602481845afa918215612964575b600092612934575b506000908152600260205260409020546001600160801b031690818110612253570390565b61295691925060203d811161295d575b61294e8183611a05565b8101906128ca565b903861290f565b503d612944565b61296c612812565b612907565b600082128015600160ff1b840183121661299b575b6001600160ff1b038301821316612253570390565b6129a3611aa4565b612986565b6000811280156001600160ff1b038390038413166129d4575b600160ff1b829003831216612288570190565b6129dc611aa4565b6129c1565b6109116129ec6121c9565b612a0f612a0a6122cb6001549364ffffffffff8560d01c169061223d565b6124ac565b6040516370a0823160e01b8152306004820152929091906020846024816001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000165afa938415612aff575b600094612ac7575b5091612abb612ab5612ab593612aae612ac19666038d7ea4c680009260018060401b0384612aa18260018060681b039416848616611ae0565b0497169160681c16611ae0565b0495614998565b91614998565b90612971565b906129a8565b612ac193919450612ab5612ab593612aae612af2612abb9460203d811161295d5761294e8183611a05565b9794965050935050612a68565b612b07612812565b612a60565b90612b22612b1b836005610914565b54600c0b90565b906000928383600c0b1215612ccc57612bb2612b59612b53612b48846005979697610914565b5460e81c61ffff1690565b9361475e565b612b827f000000000000000000000000000000000000000000000000000000000000000061281f565b7f00000000000000000000000000000000000000000000000000000000000000006001600160401b031691612e6a565b90849360ff95867f000000000000000000000000000000000000000000000000000000000000000016955b8781169087821015612cbe5790889161ffff600180931b891616612c03575b0116612bdd565b9590915082811215612cb25790612cac8992612ac1612ca788612ca1612c9c6080612c94612c638f612c42612c3a612c5692611b2d565b976006610914565b60208801516001600160a01b031690610914565b546001600160801b031690565b6040860151612c7a906001600160a01b031661281f565b612c876060880151610b27565b91600180861b0316612e4a565b930151610b27565b610b27565b90612e0c565b614998565b95612bfc565b50955050945050505090565b505094509450509050121590565b50915050600190565b90612ce4612b1b836005610914565b906000928383600c0b1215612de457612d0a612b59612b53612b48846005979697610914565b90849360ff95867f000000000000000000000000000000000000000000000000000000000000000016955b8781169087821015612dd75790889161ffff600180931b891616612d5b575b0116612d35565b9590915082811215612dcb5790612dc58992612ac1612ca788612ca1612c9c60a0612c94612d928f612c42612c3a612c5692611b2d565b6040860151612da9906001600160a01b031661281f565b612db66060880151610b27565b916001600160801b0316612e4a565b95612d54565b50509450945050505090565b5050945094505090501290565b509050565b80546001600160f81b031660f89290921b6001600160f81b031916919091179055565b670de0b6b3a76400009161278091611ae0565b61276e907f000000000000000000000000000000000000000000000000000000000000000090611ae0565b90612e5491611ae0565b6001600160401b03909116908115612778570490565b9190612e7590614998565b6000808413939082136001600160ff1b03858216848204841116612f31575b600160ff1b95600085129185918316858905831216612f24575b60008512938416828905861216612f17575b058312911616612f0a575b6001600160401b03909216929102908215612efd575b8114600019831416612ef1570590565b612ef9611aa4565b0590565b612f05611a8d565b612ee1565b612f12611aa4565b612ecb565b612f1f611aa4565b612ec0565b612f2c611aa4565b612eae565b612f39611aa4565b612e94565b6001600160401b039182169116818110612253570390565b600c0b6001600160671b03198114612f6f575b60000390565b612f77611aa4565b612f69565b805461ffff60e81b191660e89290921b61ffff60e81b16919091179055565b81518154602084015160408501516001600160e81b03199092166001600160681b039093169290921760689290921b600160681b600160a81b03169190911760a89190911b600160a81b600160e81b03161781556060820151610bc99260ff916080919061300d9061ffff1685612f7c565b01511690612de9565b61311f90610bc9936130298451600c0b90565b600c82900b855260009182918683600c83900b8113613136576130b661308f6130f594613080612c9c6130dd9661307a60206130726123fa995460018060401b039060801c1690565b920151610b27565b90612f3e565b906001600160681b0316611ae0565b7f00000000000000000000000000000000000000000000000000000000000000009061276e565b7f00000000000000000000000000000000000000000000000000000000000000009061276e565b6130ef60408901916124098351610b27565b90611b1e565b600c0b1261312457546131189060801c6001600160401b03165b60208501611b1e565b6005610914565b612f9b565b546131319060c01c61310f565b613118565b6130b661308f6131719461316c61238561225f613166612c9c6130dd9961307a60206130726123fa9c5460c01c90565b93612f56565b611ae0565b6130f5565b939290936001805460f81c16611953576131936131979186614721565b1590565b610df1576001600160a01b038181167f0000000000000000000000000000000000000000000000000000000000000000909116036131ee5750610bc992600019830361327f5791506131e88161469a565b9161327f565b906131fc610bc99493614943565b926135d1565b6001600160681b0391821691908116908290038111612288570190565b80546001600160681b0319166001600160681b03909216919091179055565b6001600160681b039182169116818110612253570390565b8054600160681b600160d01b03191660689290921b600160681b600160d01b0316919091179055565b61332d91926132af81837f0000000000000000000000000000000000000000000000000000000000000000613497565b6132b76122a3565b61333a6132cd6132c8866005610914565b6121f2565b8051600c0b906133346132fd6132f66132f16132e88661475e565b612ac189614998565b6147f8565b8094613406565b979061331d6133168a61331160015461225f565b613202565b600161321f565b613328600154612294565b61323e565b6001613256565b86613016565b6040519081526001600160a01b0393841693849216907fd1cf3d156d5f8f0d50f6c122ed609cec09d35c9b9fb3fff6ea0959134dae424e90602090a36001600160681b038116613388575050565b6000805160206149fe8339815191526133bf6133af6000936133aa8554610b27565b6147d3565b6040519081529081906020820190565b0390a3565b600c91820b910b6000821280156001600160671b031984018312166133f9575b6001600160671b038301821316612253570390565b613401611aa4565b6133e4565b91909180600c0b83600c0b81811261347557600012613439575061342a91926133c4565b6001600160681b031690600090565b60001361345c5761344a91926133c4565b6000916001600160681b039190911690565b61346590612f56565b6001600160681b03908116921690565b5050509050600090600090565b9081602091031261056e575161091181610c9c565b6040516323b872dd60e01b81526001600160a01b039283166004820152306024820152604481019390935260209183916064918391600091165af190811561351a575b6000916134fc575b50156134ea57565b60405163073d1efd60e51b8152600490fd5b613514915060203d811161140c576114048183611a05565b386134e2565b613522612812565b6134da565b90604051613536604082611a05565b91546001600160801b038116835260801c6020830152565b6001600160801b0391821691908116908290038111612288570190565b80546001600160801b0319166001600160801b03909216919091179055565b90602060018060801b03916135a2838251168561356b565b0151825490911660809190911b6001600160801b031916179055565b6001600160801b03909116815260200190565b909290916001600160801b03906135eb8284168583613497565b6135f481612145565b90613608613603826002610914565b613527565b9261362c61361f8661361a875161092b565b61354e565b6001600160801b03168552565b613636845161092b565b906136476118d060e086015161092b565b9116116136f1576136cc6136ba856136ec946136c67ffa56f7b24f17183d81894d3ac2ee654e3c26388d17a28dbd9549b8114304e1f4976136c18761369b8e6136a76136a0612c568561369b856006610914565b610914565b988961354e565b9889956136b5856002610914565b61358a565b6006610914565b61356b565b89613703565b6040516001600160a01b03918216968216959091169390918291826135be565b0390a4565b604051637ac7b99d60e11b8152600490fd5b909290916001600160801b039081161580806137c4575b156137655750505061374d613745613736610bc9945160ff1690565b600160ff9091161b61ffff1690565b916005610914565b9061375e825461ffff9060e81c1690565b1790612f7c565b1591826137b9575b5050613777575050565b60ff610bc992600161ffff92839251161b16199160018060a01b031660005260056020526040600020916137b1835461ffff9060e81c1690565b161690612f7c565b16159050388061376d565b50818316151561371a565b93929093600260015460f81c16611953576131936137ed9186614721565b610df1576001600160a01b038481168382161461386557807f000000000000000000000000000000000000000000000000000000000000000016908216146000146138515750610bc992600019830361388857915061384b8261463c565b91613888565b9061385f610bc99493614943565b92613b21565b60405163e397a99b60e01b8152600490fd5b600160ff1b8114612f6f5760000390565b9190916138936122a3565b61389e816005610914565b6138a7906121f2565b6138b2846005610914565b6138bb906121f2565b9281516138c890600c0b90565b9380516138d590600c0b90565b926138df8661475e565b6138e884614998565b6138f191612971565b926138fb8561475e565b9061390590614998565b61390e916129a8565b90613918846147f8565b61392281936147f8565b97889361392e91613abc565b986139399197613406565b98878a6001546139489061225f565b9061395291613202565b9061395c9161323e565b61396790600161321f565b60015461397390612294565b9061397d91613202565b906139879161323e565b613992906001613256565b61399c9187613016565b6139a69187613016565b60008112613a53575b506001600160681b0391818316613a0b575b505081166139cd575050565b6000805160206149fe8339815191526133bf6139ef6000936133aa8554610b27565b6040519081526001600160a01b03909416939081906020820190565b6000805160206149fe833981519152613a49613a2d6000946133aa8654610b27565b6040519081526001600160a01b03909316929081906020820190565b0390a338806139c1565b613a5c90613877565b7f000000000000000000000000000000000000000000000000000000000000000011613aaa57613a8e61319383612b0c565b613a9857386139af565b604051630a62fbdb60e11b8152600490fd5b604051637139da2360e11b8152600490fd5b919082600c0b81600c0b81811361347557600013613adf575061342a91926133c4565b600012613af05761344a91926133c4565b613af990612f56565b6001600160681b03928316921690565b6001600160801b039182169116818110612253570390565b6001600160a01b0380821660008181526006602052604090206001600160801b03959194919391908690613b56908690610914565b54168382169687600052600660205285604060002090613b7591610914565b5416613b818983613b09565b613b8b8a8361354e565b928188613b99886006610914565b90613ba391610914565b90613bad9161356b565b8388613bba876006610914565b90613bc491610914565b90613bce9161356b565b613bd788612145565b91613be3918388613703565b613bec93613703565b613bf590612b0c565b15613a98577f29db89d45e1a802b4d55e202984fce9faf1d30aedf86503ff1ea0ed9ebb64201916136ec6040519283921696826135be565b93929093600460015460f81c1661195357613193613c4b9186614721565b610df1576001600160a01b038181167f000000000000000000000000000000000000000000000000000000000000000090911603613ca25750610bc9926000198303613cb6579150613c9c8261463c565b91613cb6565b90613cb0610bc99493614943565b92613eb9565b909161332d92613cc46122a3565b613cd26132c8846005610914565b613d2c613ce08251600c0b90565b613cf5613cec8261475e565b612abb87614998565b92613334613d0c613d05866147f8565b8094613abc565b613d216133168361332860019e959e5461225f565b613311600154612294565b60008112613dd1575b50613d6182827f0000000000000000000000000000000000000000000000000000000000000000613e31565b6040519182526001600160a01b0392831692169082907f9b1bfa7fa9ee420a16e124f794c35ac9f90472acc99140eb2f6447c714cad8eb90602090a36001600160681b038216613daf575050565b6000805160206149fe8339815191526133bf6133af6000946133aa8654610b27565b613dda90613877565b7f000000000000000000000000000000000000000000000000000000000000000011613aaa57613e0c61319384612b0c565b613a985738613d35565b6001600160a01b039091168152602081019190915260400190565b91613e5792602092600060405180968195829463a9059cbb60e01b845260048401613e16565b03926001600160a01b03165af1908115613eac575b600091613e8e575b5015613e7c57565b60405163cefaffeb60e01b8152600490fd5b613ea6915060203d811161140c576114048183611a05565b38613e74565b613eb4612812565b613e6c565b6001600160a01b038082166000818152600660205260408120909695919491936001600160801b03918290613eef908790610914565b5416613efb8882613b09565b80878781169b8c81526002602052604081208c8882541690613f1c91613b09565b613f259161356b565b8a815260066020526040902090613f3b91610914565b90613f459161356b565b613f4e87612145565b91613f599284613703565b613f6290612b0c565b15613a98577fd6d480d5b3068db003533b170d67561494d72e3bf9fa40a266471351ebba9e169382613f9692881691613e31565b6136ec6040519283921695826135be565b9190811015613fb75760051b0190565b634e487b7160e01b600052603260045260246000fd5b356109118161055d565b90604051613fe6608082611a05565b915463ffffffff81168352602081811c6001600160401b031690840152606081811c6001600160801b0316604085015260e09190911c90830152565b60019063ffffffff809116908114612288570190565b9291909260016008815460f81c16611953575a946140546122a3565b60005b848110614153575050506140fe906140f76140d86140d361407d610bc997985a9061254a565b6140cc6140ba614096614091886007610914565b613fd7565b986123fa6140b06140ab8c5163ffffffff1690565b614022565b63ffffffff168b52565b6130ef60208a01916124098351610b27565b4890611ae0565b614943565b6140ea604086019161361a835161092b565b6001600160801b03169052565b6007610914565b815160208084015160408501516060958601516001600160e01b031960e09190911b16600160601b600160e01b039190961b1663ffffffff909316600160201b600160601b039190921b161717919091179055565b8061417161416b61416686948987613fa7565b613fcd565b86614177565b01614057565b9161418461319383612cd5565b61450b57906141976132c8826005610914565b906141a38251600c0b90565b936141ad8561475e565b926060906141bf8282015161ffff1690565b6141e87f000000000000000000000000000000000000000000000000000000000000000061281f565b96600092839860ff9a8b7f0000000000000000000000000000000000000000000000000000000000000000169a5b8c8116908c82101561438a57908d91898c8c61ffff600180961b8c1616614242575b5050500116614216565b90919a8261424f85611b2d565b60208101519092906001600160a01b0316908160068161426f8683610914565b9061427991610914565b546001600160801b03169461428d91610914565b9061429791610914565b60006142a29161356b565b6142ad826002610914565b8381546142b99061092b565b906142c391613b09565b6142cc9161356b565b604084810151909f906001600160a01b03166142e79061281f565b908501516142f490610b27565b614307916001600160801b038616612e4a565b9360c0015161431590610b27565b61431e90610b27565b6143289085612e0c565b6143319161253e565b9d516001600160801b0392909216825260208201929092526001600160a01b0391821693821692909116907f9850ab1af75177e4a9201c65a2cf7976d5d28e40ef63494b44366f86b2f9412e90604090a4898c8c614238565b50509497969950945096915097506143e06143da612ca7856143d560018060401b037f000000000000000000000000000000000000000000000000000000000000000016809a611ae0565b61276e565b826129a8565b9160008312614502575b6143f3836147f8565b96876143ff9187613016565b8661440b866005610914565b600061441691612f7c565b61441f91613406565b60015461442b9061225f565b9061443591613202565b61444090600161321f565b60015461444c90612294565b906144569161323e565b614461906001613256565b61446a91612971565b614473906149e4565b9261447e9184612e4a565b6040805193845260208401919091526001600160a01b039182169485939216917f1547a878dc89ad3c367b6338b4be6a65a5dd74fb77ae044da1e8747ef1f4f62f9190a380600c0b6000126144d1575050565b6000805160206149fe8339815191526133bf6133af6000936144fc6144f68654610b27565b916149bb565b906147d3565b600092506143ea565b604051636ef5bcdd60e11b8152600490fd5b906145e561452d61091193612145565b6145b060606145db61454a60018060a01b0360408601511661281f565b60c08501516001600160401b0394670de0b6b3a7640000929091839061459f908890811680841061462f575b8303167f0000000000000000000000000000000000000000000000000000000000000000611ae0565b04808410614622575b830390611ae0565b049561316c7f000000000000000000000000000000000000000000000000000000000000000061281f565b9201511690611ae0565b908015614615575b7f0000000000000000000000000000000000000000000000000000000000000000910461276e565b61461d611a8d565b6145ed565b61462a611aa4565b6145a8565b614637611aa4565b614576565b61465f6146476121c9565b61067964ffffffffff918260015460d01c169061223d565b506001600160a01b03909116600090815260056020526040812054600c0b91908083131561469457506144fc610911926149bb565b91505090565b6146a56146476121c9565b6001600160a01b03909216600090815260056020526040812054600c0b9291508083121561469457506144fc6146dd61091193612f56565b6149bb565b50600036818037808036817f00000000000000000000000000000000000000000000000000000000000000005af43d82803e1561471d573d90f35b3d90fd5b6001600160a01b0380831691169081149190821561473e57505090565b60ff9250906147599160005260036020526040600020610914565b541690565b6000600c82900b1261479e576000546109119166038d7ea4c6800091614798916001600160401b03909116906001600160681b0316611ae0565b04614998565b6147ce612ca7610911926147bf60018060401b0360005460401c1691612f56565b6001600160681b0316906147d3565b613877565b66038d7ea4c6800091612780916001600160401b0316906001600160681b0316611ae0565b6000811261481e5760005461091191614819916001600160401b03166148a7565b614969565b61487b6148196109119261483f60018060401b0360005460401c1691613877565b614864826000199266038d7ea4c680009080850482118115151661489a575b0261253e565b6001811061488d575b8215614880575b010461491d565b612f56565b614888611a8d565b614874565b614895611aa4565b61486d565b6148a2611aa4565b61485e565b906109119166038d7ea4c680009082600019048211831515166148ea575b6001600160401b03169182156148dd575b020461491d565b6148e5611a8d565b6148d6565b6148f2611aa4565b6148c5565b6001600160401b039081811161490b571690565b6040516372a1cb5160e11b8152600490fd5b6001600160681b0390818111614931571690565b604051630dc7925560e11b8152600490fd5b6001600160801b0390818111614957571690565b60405163762ea71160e11b8152600490fd5b6001600160681b03166001600160671b03811161498657600c0b90565b604051639369ae3560e01b8152600490fd5b6001600160ff1b0381116149a95790565b60405163e7e828ad60e01b8152600490fd5b600081600c0b126149d2576001600160681b031690565b60405163363b64b760e11b8152600490fd5b600081126149d25790565b60009015610911575060019056feddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa26469706673582212202a7592e3c3182f32b57bc65987dbae689a3af695096cecda4191815467d98a2a64736f6c634300080f003300000000000000000000000000000000000000000000000000000000000000200000000000000000000000006d903f6003cca6255d85cca4d3b5e5146dc33925000000000000000000000000bbf3f1421d886e9b2c5d716b5192ac998af2012c000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000008fffffd4afb6115b954bd326cbe7b4ba576818f6000000000000000000000000285617313887d43256f852cae0ee4de4b68d45b00000000000000000000000000000000000000000000000000b1a2bc2ec50000000000000000000000000000000000000000000000000000000737693eb334000000000000000000000000000000000000000000000000000058d15e17628000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b1a2bc2ec500000000000000000000000000000000000000000000000000000007c58508723800400000000000000000000000000000000000000000000000003782dace9d9000000000000000000000000000000000000000000000000000000354a6ba7a1800000000000000000000000000000000000000000000000000006f05b59d3b2000000000000000000000000000000000000000000000000000000038d7ea4c680000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b2fe95ce6d000000000000000000000000000000000000000000000000000000e8d4a510000000000000000000000000000000000000000000000000000000000005f5e1000000000000000000000000000000000000000000000000000000048c2739500000000000000000000000000000000000000000000000000000000000000002a00000000000000000000000000000000000000000000000000000000000000005000000000000000000000000c00e94cb662c3520282e6f5717214004a7f26888000000000000000000000000dbd020caef83efd542f4de03e3cf0c28a4428bd500000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000905438e6001000000000000000000000000000000000000000000000000000009b6e64a8ec600000000000000000000000000000000000000000000000000000ce80612991d0000000000000000000000000000000000000000000000007f0e10af47c1c70000000000000000000000000000002260fac5e5542a773aa44fbcfedf7c193bc2c599000000000000000000000000f4030086522a5beea4988f8ca5b36dbc97bee88c000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000009b6e64a8ec600000000000000000000000000000000000000000000000000000aaf96eb9d0d00000000000000000000000000000000000000000000000000000d2f13f7789f00000000000000000000000000000000000000000000000000000000008bb2c97000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000005f4ec3df9cbd43714fe2740f5e3616155c5b841900000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000b72fd2103b280000000000000000000000000000000000000000000000000000c6badc211f980000000000000000000000000000000000000000000000000000d2f13f7789f0000000000000000000000000000000000000000000000000fe1c215e8f838e000000000000000000000000000001f9840a85d5af5bf1d1762f925bdaddc4201f984000000000000000000000000553303d460ee0afb37edff9be42922d8ff63220e00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000a688906bd8b00000000000000000000000000000000000000000000000000000b3db2b55c1100000000000000000000000000000000000000000000000000000ce80612991d00000000000000000000000000000000000000000000000108b2a2c2802909400000000000000000000000000000514910771af9ca656af840dff83e8264ecf986ca0000000000000000000000002c1d072e956affc0d435cb7ac38ef18d24d9127c00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000af6a4d07c8f00000000000000000000000000000000000000000000000000000bcbce7f1b1500000000000000000000000000000000000000000000000000000ce80612991d00000000000000000000000000000000000000000000000108b2a2c2802909400000
6107406040523462000382576200001f6200001962000563565b620007dc565b604051614a5390816200109d823960805181818161060601528181610d000152818161138e015261173a015260a05181818161082c0152610e06015260c0518181816107e40152818161130f01528181611571015281816117a30152818161185401528181612a36015281816131a80152818161328b0152818161380701528181613c5c0152613d3d015260e05181818161197c01528181612b5e015281816141c401526145b7015261010051818181610e4601526146ef0152610120518181816112c50152612557015261014051818181610ee40152818161259301526125fd015261016051818181610ff3015261262d01526101805181818161114d01526125ba01526101a0518181816110d5015261266701526101c0518181816109bf015281816126a3015261270d01526101e0518181816108c9015261273d015261020051818181610f6f01526126ca01526102205181818161077c015261457b015261024051818181610c7901528181612b8401528181612e25015281816143ae01526145ef01526102605181818161134f01526130910152610280518181816106ba015261244901526102a05181818161118901526123d101526102c051818181611111015261234301526102e051818181610a3901528181613a5e0152613ddc015261030051818181610ab4015261192c01526103205181610a7701526103405181818161128801528181611b3e0152818161215801528181612bbb01528181612d1301526141f4015261036051816130b801526103805181611b8e01526103a05181611b6d01526103c05181611cef01526103e05181611cce01526104005181611d3f01526104205181611d1e01526104405181611d8f01526104605181611d6e01526104805181611ddf01526104a05181611dbe01526104c05181611e2f01526104e05181611e0e01526105005181611e7f01526105205181611e5e01526105405181611ecf01526105605181611eae01526105805181611f1f01526105a05181611efe01526105c05181611f6f01526105e05181611f4e01526106005181611fbf01526106205181611f9e0152610640518161200f01526106605181611fee0152610680518161205f01526106a0518161203e01526106c051816120af01526106e0518161208e015261070051816120fd015261072051816120dc0152f35b600080fd5b50634e487b7160e01b600052604160045260246000fd5b601f909101601f19168101906001600160401b03821190821017620003c257604052565b620003cc62000387565b604052565b90620003e160405192836200039e565b565b51906001600160a01b03821682036200038257565b6001600160401b031690565b51906001600160401b03821682036200038257565b51906001600160681b03821682036200038257565b519060ff821682036200038257565b81601f8201121562000382578051906001600160401b03821162000553575b604080519360209162000475838660051b01876200039e565b848652828601918360e08097028601019481861162000382578401925b858410620004a4575050505050505090565b86848303126200038257825190620004bd88836200039e565b620004c885620003e3565b8252620004d7868601620003e3565b86830152620004e88486016200042e565b848301526060620004fb81870162000404565b9083015260806200050e81870162000404565b9083015260a06200052181870162000404565b9083015260c085810151929091906001600160801b038416840362000382578993889382015281520193019262000492565b6200055d62000387565b6200045c565b62005af080380380604051926200057b82856200039e565b8339810190602081830312620003825780516001600160401b03918282116200038257016102a080828503126200038257620005b790620003d1565b92620005c382620003e3565b8452620005d360208301620003e3565b6020850152620005e660408301620003e3565b6040850152620005f960608301620003e3565b60608501526200060c60808301620003e3565b60808501526200061f60a0830162000404565b60a08501526200063260c0830162000404565b60c08501526200064560e0830162000404565b60e08501526101006200065a81840162000404565b908501526101206200066e81840162000404565b908501526101406200068281840162000404565b908501526101606200069681840162000404565b90850152610180620006aa81840162000404565b908501526101a0620006be81840162000404565b908501526101c0620006d281840162000404565b908501526101e0620006e681840162000404565b90850152610200620006fa81840162000404565b908501526102206200070e81840162000419565b908501526102406200072281840162000419565b908501526102606200073681840162000419565b90850152610280928383015190811162000382576200075692016200043d565b9082015290565b90816020910312620003825762000774906200042e565b90565b506040513d6000823e3d90fd5b50634e487b7160e01b600052601260045260246000fd5b8115620007a6570490565b620007b062000784565b0490565b6001600160401b0391821691908215620007cd57160490565b620007d762000784565b160490565b60408181018051909291906200080990620007fd906001600160a01b031681565b6001600160a01b031690565b90805180938163313ce56760e01b94858252602091829160049889915afa92831562000d75575b60009362000d51575b5060ff8316926012841162000d41576101a0908184019460018060401b0391670de0b6b3a7640000836200086e8951620003f8565b161162000d3157610280948587019b600f8d51511162000d21576102208881018051909c919391906001600160681b03161562000d115790600860ff8f9493838f9160608f0197620008ce620007fd620007fd8b5160018060a01b031690565b93518094819382525afa90811562000d01575b60009162000ccd575b50160362000cbd5789516001600160a01b03908116608052908a0151811660a0529051811660c05290511660e052608087015162000944906200093e906001600160a01b0316996101009a8b5251620003f8565b620003f8565b905261032052600a0a1694610240968688526101c0966200096c6200093e89880151620003f8565b92610260938452620f424080921062000cae57508851906200098e916200079b565b61036052516001600160681b03166001600160681b03166102c0526101e09283850151620009bc90620003f8565b620009c790620003f8565b90526102009687850151620009dc90620003f8565b620009e790620003f8565b6102a0528401516001600160681b039081166102e05290840151166103005260a083015162000a1690620003f8565b62000a2190620003f8565b936101209485526301e13380958680938160c088015162000a4290620003f8565b9062000a4e91620007b4565b62000a5990620003f8565b90610140918252828060e08a015162000a7290620003f8565b9062000a7e91620007b4565b62000a8990620003f8565b9561016096875289015162000a9e90620003f8565b9062000aaa91620007b4565b62000ab590620003f8565b98610180998a5288015162000aca90620003f8565b62000ad590620003f8565b905286015162000ae590620003f8565b9062000af191620007b4565b62000afc90620003f8565b905283015162000b0c90620003f8565b9062000b1891620007b4565b62000b2390620003f8565b9052015162000b3290620003f8565b9062000b3e91620007b4565b62000b4990620003f8565b905281515160ff16610340528151600062000b649162000dc1565b6103a052610380528151600162000b7b9162000dc1565b6103e0526103c0528151600262000b929162000dc1565b61042052610400528151600362000ba99162000dc1565b610460526104405281519062000bbf9162000dc1565b6104a052610480528051600562000bd69162000dc1565b6104e0526104c0528051600662000bed9162000dc1565b61052052610500528051600762000c049162000dc1565b61056052610540528051600862000c1b9162000dc1565b6105a052610580528051600962000c329162000dc1565b6105e0526105c0528051600a62000c499162000dc1565b61062052610600528051600b62000c609162000dc1565b61066052610640528051600c62000c779162000dc1565b6106a052610680528051600d62000c8e9162000dc1565b6106e0526106c05251600e62000ca49162000dc1565b6107205261070052565b51630456c65960e51b81528a90fd5b8b51630456c65960e51b81528e90fd5b62000cf29150843d861162000cf9575b62000ce981836200039e565b8101906200075d565b38620008ea565b503d62000cdd565b62000d0b62000777565b620008e1565b8b51636e77247560e01b81528e90fd5b895163df8153c760e01b81528c90fd5b87516324dc918f60e11b81528a90fd5b8451630456c65960e51b81528790fd5b8162000d6d9294503d851162000cf95762000ce981836200039e565b913862000839565b62000d7f62000777565b62000830565b6040519062000d9660e0836200039e565b8160c06000918281528260208201528260408201528260608201528260808201528260a08201520152565b62000dcb62000d85565b508051821015620010925760059190911b016020015180516001600160a01b0316602082810151909391906001600160a01b031660409362000e108582015160ff1690565b926001600160a01b039081169283156200108457169185519663313ce56760e01b90818952600860ff60049a83818d818b5afa90811562001074575b60009162001052575b501603620010425760ff91885190815281818b81875afa91821562001032575b60009262001010575b505081861691829116036200100057606083019262000e9e8451620003f8565b93608082019462000eb46200093e8751620003f8565b6001600160401b03918216101562000ff057670de0b6b3a7640000809162000edd8851620003f8565b161162000fe05762000f2962000f3862000f2962000f23999a9b9c9d9462000f2362000f3062000f2962000f1861271062000f4899620007b4565b809e819551620003f8565b620007b4565b61ffff1690565b9a51620003f8565b9762000f2360a0860151620003f8565b9961ffff808816908716101562000fd357505060c0015162000f7f916200093e91600a9190910a906001600160801b03166200079b565b60a092831b61ffff60a01b169190911760b09390931b61ffff60b01b169290921760c09690961b61ffff60c01b1695909517949290921b60ff60a01b161760a89190911b600160a81b600160e81b03161790565b5163327d763960e21b8152fd5b89516304d6b32560e41b81528b90fd5b895163327d763960e21b81528b90fd5b8651630456c65960e51b81528890fd5b6200102a9250803d1062000cf95762000ce981836200039e565b388062000e7e565b6200103c62000777565b62000e75565b8751630456c65960e51b81528990fd5b6200106d9150843d861162000cf95762000ce981836200039e565b3862000e55565b6200107e62000777565b62000e4c565b506000965086955050505050565b505060009060009056fe60806040526004361015610018575b6100166146e2565b005b60003560e01c8063042e02cf146105545780630902f1ac1461054b5780630bc47ad1146105425780630c340a241461053957806318160ddd14610530578063189bb2f1146105275780631c9f7fb91461051e5780631f5954bd1461051557806323b872dd1461050c57806324a3d6221461050357806326441318146104fa5780632a48cf12146104f15780632b92a07d146104e85780632d05670b146104df5780632e04b8e7146104d6578063300e6beb146104cd578063313ce567146104c457806332176c49146104bb578063374c49b4146104b257806338aa813f146104a95780633b3bec2e146104a057806341976e09146104975780634232cd631461048e578063439e2e451461048557806344c1e5eb1461047c57806344c35d071461047357806344ff241d1461046a57806359e017bd146104615780635a94b8d11461045857806367800b5f1461044f57806370a08231146104465780637914acc71461043d5780637ac88ed1146104345780637eb711311461042b578063804de71f146104225780638285ef40146104195780638d5d814c1461041057806390323177146104075780639241a561146103fe5780639364e18a146103f557806394920cca146103ec5780639ea99a5a146103e35780639fa83b5a146103da5780639ff567f8146103d1578063a1654379146103c8578063a1a1ef43146103bf578063a46fe83b146103b6578063a5b4ff79146103ad578063a9059cbb146103a4578063aba7f15e1461039b578063ad14777c14610392578063bfe69c8d14610389578063c1ee2c1814610380578063c3b35a7e14610377578063c3cecfd21461036e578063c55dae6314610365578063c5fa15cf1461035c578063c8c7fe6b14610353578063cde680411461034a578063d8e5f61114610341578063d955759d14610338578063dc4abafd1461032f578063e478795d14610326578063e4e6e7791461031d578063e7dad6bd14610314578063f2b9fdb81461030b5763f3fef3a30361000e576103066119d8565b61000e565b506103066119ab565b50610306611965565b506103066117f9565b5061030661170e565b5061030661169b565b5061030661167c565b50610306611655565b50610306611639565b5061030661160e565b506103066115a0565b5061030661155a565b506103066114f5565b506103066114d9565b506103066114bb565b50610306611420565b50610306611372565b50610306611336565b506103066112e8565b506103066112ac565b5061030661126d565b50610306611246565b50610306611205565b506103066111dd565b506103066111ac565b50610306611170565b50610306611134565b506103066110f8565b506103066110bc565b5061030661109e565b50610306611077565b50610306611016565b50610306610fda565b50610306610fbe565b50610306610f92565b50610306610f56565b50610306610f2e565b50610306610f07565b50610306610ecb565b50610306610e75565b50610306610e2f565b50610306610ca6565b50610306610c60565b50610306610c44565b50610306610c28565b50610306610c00565b50610306610bcb565b50610306610aff565b50610306610ad7565b50610306610a9b565b50610306610a5c565b50610306610a20565b506103066109e2565b506103066109a6565b50610306610951565b506103066108b0565b50610306610892565b50610306610815565b506103066107cc565b50610306610763565b506103066106dd565b506103066106a1565b50610306610635565b506103066105ef565b506103066105c9565b506103066105a5565b50610306610573565b6001600160a01b0381160361056e57565b600080fd5b503461056e57602036600319011261056e57602061059b6004356105968161055d565b612cd5565b6040519015158152f35b503461056e57600036600319011261056e5760206105c16129e1565b604051908152f35b503461056e57600036600319011261056e5760206001805460f81c161515604051908152f35b503461056e57600036600319011261056e576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b503461056e57600036600319011261056e57602066038d7ea4c6800061069861065c6121c9565b61067f6001549161067964ffffffffff91828560d01c169061223d565b166124ac565b506001600160401b0316906001600160681b0316611ae0565b04604051908152f35b503461056e57600036600319011261056e5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b503461056e576000806003193601126107605760015464ffffffffff8160d01c1661074f5764ffffffffff60d01b6107136121c9565b64ffffffffff60d01b1990921660d09290921b161760015580546001600160801b0319166e038d7ea4c6800000038d7ea4c68000178155604051f35b60405162dc149f60e41b8152600490fd5b80fd5b503461056e57600036600319011261056e5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b9081606091031261056e5780356107b58161055d565b91604060208301356107c68161055d565b92013590565b503461056e5761080a6107e036600461079f565b91907f000000000000000000000000000000000000000000000000000000000000000091336137cf565b602060405160018152f35b503461056e57600036600319011261056e576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b919082608091031261056e5781356108728161055d565b9160208101356108818161055d565b91606060408301356107c68161055d565b503461056e576100166108a636600461085b565b9291909133613c2d565b503461056e57600036600319011261056e5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b919082604091031261056e57602082356109058161055d565b9201356109118161055d565b90565b9060018060a01b0316600052602052604060002090565b6001600160801b031690565b6001600160801b0391821681529116602082015260400190565b503461056e576109a26109866109683660046108ec565b6001600160a01b039091166000908152600660205260409020610914565b54604051918291608081901c906001600160801b031683610937565b0390f35b503461056e57600036600319011261056e5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b503461056e57602036600319011261056e57600435610a008161055d565b60018060a01b031660005260046020526020604060002054604051908152f35b503461056e57600036600319011261056e5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b503461056e57600036600319011261056e57602060405160ff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b503461056e57600036600319011261056e5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b503461056e57602036600319011261056e5760206105c1600435610afa8161055d565b61469a565b503461056e57602036600319011261056e57602061059b600435610b228161055d565b612b0c565b6001600160401b031690565b610bc99092919260e08061010083019560ff815116845260018060a01b03806020830151166020860152604082015116604085015260018060401b036060820151166060850152610b936080820151608086019060018060401b03169052565b60a0818101516001600160401b03169085015260c0818101516001600160401b03169085015201516001600160801b0316910152565b565b503461056e57602036600319011261056e576109a2610bf4600435610bef8161055d565b612145565b60405191829182610b33565b503461056e57602036600319011261056e5760206105c1600435610c238161055d565b61281f565b503461056e57610016610c3c36600461079f565b913333613176565b503461056e57610016610c5836600461079f565b9133336137cf565b503461056e57600036600319011261056e5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b8015150361056e57565b503461056e5760a036600319011261056e57600435610cc481610c9c565b602435610cd081610c9c565b60443591610cdd83610c9c565b60643592610cea84610c9c565b608435610cf681610c9c565b60018060a01b03807f0000000000000000000000000000000000000000000000000000000000000000163314159081610e02575b50610df1577f3be39979091ae7ca962aa1c44e645f2df3c221b79f324afa5f44aedc8d2f690d94610dec92610db7610d746000610d66886149ef565b9060ff8080931691161b1690565b610d826001610d668a6149ef565b17610d916002610d66856149ef565b17610da06003610d66866149ef565b17610daf6004610d66876149ef565b176001612de9565b6040519586958693909594919260809360a0860197151586521515602086015215156040850152151560608401521515910152565b0390a1005b6040516282b42960e81b8152600490fd5b90507f00000000000000000000000000000000000000000000000000000000000000001633141538610d2a565b503461056e57600036600319011261056e576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b503461056e57602036600319011261056e57600435610e938161055d565b6001600160a01b0316600090815260026020526040908190205490519081906109a290608081901c906001600160801b031683610937565b503461056e57600036600319011261056e5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b503461056e57600036600319011261056e576020600460015460f81c161515604051908152f35b503461056e57602036600319011261056e5760206105c1600435610f518161055d565b61463c565b503461056e57600036600319011261056e5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b503461056e57604036600319011261056e5760206105c1600435610fb58161055d565b6024359061451d565b503461056e57600036600319011261056e5760206105c1612784565b503461056e57600036600319011261056e5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b503461056e57600036600319011261056e57602066038d7ea4c6800061069861103d6121c9565b61105a6001549161067964ffffffffff91828560d01c169061223d565b6001600160401b03169160681c6001600160681b03169050611ae0565b503461056e57600036600319011261056e576020600860015460f81c161515604051908152f35b503461056e576100166110b236600461085b565b9291909133613176565b503461056e57600036600319011261056e5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b503461056e57600036600319011261056e5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b503461056e57600036600319011261056e5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b503461056e57600036600319011261056e5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b503461056e57602036600319011261056e5760206111cb600435612665565b6040516001600160401b039091168152f35b503461056e57602036600319011261056e5760206105c16004356112008161055d565b6128d9565b503461056e57602060ff61123a61121d3660046108ec565b6001600160a01b0390911660009081526003855260409020610914565b54166040519015158152f35b503461056e57600036600319011261056e576020600260015460f81c161515604051908152f35b503461056e57600036600319011261056e57602060405160ff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b503461056e57600036600319011261056e5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b503461056e57604036600319011261056e5761080a6004356113098161055d565b602435907f00000000000000000000000000000000000000000000000000000000000000009033336137cf565b503461056e57600036600319011261056e5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b503461056e5761138336600461079f565b6001600160a01b03917f000000000000000000000000000000000000000000000000000000000000000083163303610df1576113da93600060209460405180978196829563095ea7b360e01b845260048401613e16565b0393165af18015611413575b6113ec57005b6100169060203d811161140c575b6114048183611a05565b810190613482565b503d6113fa565b61141b612812565b6113e6565b503461056e57602036600319011261056e576100166004356114418161055d565b6114496122a3565b60018060a01b038116600052600560205260406000206114b06040519161147160a084611a05565b54600c81900b83526001600160401b03606882901c8116602085015260a882901c16604084015261ffff60e882901c16606084015260f81c6080830152565b8051600c0b91613016565b503461056e576100166114cf36600461085b565b92919091336137cf565b503461056e576100166114ed36600461079f565b913333613c2d565b503461056e57604036600319011261056e576004356115138161055d565b602435906001600160401b039081831161056e573660238401121561056e57826004013591821161056e573660248360051b8501011161056e576024610016930190614038565b503461056e57600036600319011261056e576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b503461056e57602036600319011261056e576004356115be8161055d565b60018060a01b0316600052600760205260806040600020546040519063ffffffff8116825260018060401b038160201c166020830152600180841b038160601c16604083015260e01c6060820152f35b503461056e57602036600319011261056e5760043560ff8116810361056e57610bf46109a291611b2d565b503461056e57602061059b61164f3660046108ec565b90614721565b503461056e57600036600319011261056e576020601060015460f81c161515604051908152f35b503461056e57602036600319011261056e5760206111cb600435612555565b503461056e57602036600319011261056e576004356116b98161055d565b60018060a01b0316600052600560205260a06040600020546040519080600c0b825260018060401b03808260681c1660208401528160a81c16604083015261ffff8160e81c16606083015260f81c6080820152f35b503461056e57604036600319011261056e5760043561172c8161055d565b602435906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000081163303610df1576117696129e1565b600081129081156117e7575b506117d557816117c7847fec4431f2ba1a9382f6b0c4352b888cba6f7db91667d9f776abe5ad8ddc5401b6947f0000000000000000000000000000000000000000000000000000000000000000613e31565b6040519384521691602090a2005b60405163128bd24d60e31b8152600490fd5b6117f191506149e4565b831138611775565b503461056e57608036600319011261056e576004356118178161055d565b604435606435916118278361055d565b601060015460f81c166119535761183c6129e1565b60008112159081611928575b506119165761187882337f0000000000000000000000000000000000000000000000000000000000000000613497565b611882828261451d565b92602435841061190457611895826128d9565b84116117d5577ff891b2a411b0e66a5f0a6ff1368670fefa287a13f541eb633a386a1a9cc7046b916118dc6118ff926118d56118d088614943565b61092b565b9083613e31565b6040805194855260208501959095526001600160a01b0316933393918291820190565b0390a3005b60405163fa6ad35560e01b8152600490fd5b604051631d99ddbf60e01b8152600490fd5b90507f0000000000000000000000000000000000000000000000000000000000000000111538611848565b6040516313d0ff5960e31b8152600490fd5b503461056e57600036600319011261056e576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b503461056e57604036600319011261056e576100166004356119cc8161055d565b60243590333333613176565b503461056e57604036600319011261056e576100166004356119f98161055d565b60243590333333613c2d565b601f909101601f19168101906001600160401b03821190821017611a2857604052565b634e487b7160e01b600052604160045260246000fd5b90610bc96040519283611a05565b60405190611a5c61010083611a05565b8160e06000918281528260208201528260408201528260608201528260808201528260a08201528260c08201520152565b50634e487b7160e01b600052601260045260246000fd5b50634e487b7160e01b600052601160045260246000fd5b6001600160401b0391821691908215611ad357160490565b611adb611a8d565b160490565b8060001904821181151516611af3570290565b611afb611aa4565b0290565b60ff16604d8111611b11575b600a0a90565b611b19611aa4565b611b0b565b6001600160401b039091169052565b611b35611a4c565b5060ff811660ff7f0000000000000000000000000000000000000000000000000000000000000000168110156121335780611cc357507f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000005b6001600160401b0380611bcb612710670de0b6b3a7640000611abb565b1661ffff9181838560a01c1690611be191611ae0565b611bea90610b27565b91611bfb8160b087901c8616611ae0565b611c0490610b27565b938560c01c1690611c1491611ae0565b611c1d90610b27565b938560a01c60ff16611c2e90611aff565b611c3790610b27565b91808316908760a81c1690611c4b91611ae0565b611c549061092b565b95611c60610100611a3e565b60ff9890981688526001600160a01b039182166020890152166040870152611c8b9060608701611b1e565b611c989060808601611b1e565b611ca59060a08501611b1e565b611cb29060c08401611b1e565b6001600160801b031660e082015290565b60018103611d1357507f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000611bae565b60028103611d6357507f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000611bae565b60038103611db357507f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000611bae565b60048103611e0357507f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000611bae565b60058103611e5357507f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000611bae565b60068103611ea357507f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000611bae565b60078103611ef357507f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000611bae565b60088103611f4357507f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000611bae565b60098103611f9357507f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000611bae565b600a8103611fe357507f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000611bae565b600b810361203357507f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000611bae565b600c810361208357507f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000611bae565b600d81036120d357507f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000611bae565b600e03612121577f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000611bae565b60405163971241a160e01b8152600490fd5b604051633640530560e01b8152600490fd5b9061214e611a4c565b5060009060ff92837f000000000000000000000000000000000000000000000000000000000000000016925b838582161061219557604051633640530560e01b8152600490fd5b61219e81611b2d565b60208101516001600160a01b038481169116146121c05750600101841661217a565b93505050915090565b600160281b4210156121e05764ffffffffff421690565b604051633d32ffdb60e01b8152600490fd5b9060405161220160a082611a05565b608081935480600c0b835260018060401b03808260681c1660208501528160a81c16604084015261ffff8160e81c16606084015260f81c910152565b64ffffffffff9182169116818110612253570390565b61225b611aa4565b0390565b6001600160681b031690565b6001600160401b0391821691908116908290038111612288570190565b612290611aa4565b0190565b60681c6001600160681b031690565b6122ab6121c9565b6122d56122cb6122c560015464ffffffffff9060d01c1690565b8361223d565b64ffffffffff1690565b90816122df575050565b816123366122ef610bc9946124ac565b60008054600160401b600160801b03191660409290921b600160401b600160801b0316919091178155919082546001600160401b0319166001600160401b03909116178255565b61234160015461225f565b7f000000000000000000000000000000000000000000000000000000000000000092906001600160681b031683811015612436575b5061238a612385600154612294565b61225f565b928310156123be575b50506001805464ffffffffff60d01b191660d09390931b64ffffffffff60d01b169290921790915550565b6123ff6123fa61242e946123f561240e947f0000000000000000000000000000000000000000000000000000000000000000611ae0565b612e1f565b6148f7565b825460c01c61226b565b61226b565b81546001600160c01b031660c09190911b6001600160c01b031916179055565b388080612393565b61248061246d6123fa6124a6936123f5867f0000000000000000000000000000000000000000000000000000000000000000611ae0565b845460801c6001600160401b031661226b565b8354600160801b600160c01b03191660809190911b600160801b600160c01b0316178355565b38612376565b6000546001600160401b03604082901c811693929181169190816124d1575b50509190565b8161252461251e6124fe97946125306125369761252a8761250561252a996124f7612784565b9e8f612555565b169d612665565b169b61252461251e670de0b6b3a7640000998a93611ae0565b84611ae0565b046148f7565b9061226b565b98611ae0565b9138806124cb565b81198111612288570190565b818110612253570390565b7f00000000000000000000000000000000000000000000000000000000000000008082116125de57506123fa670de0b6b3a76400006125b7610911937f0000000000000000000000000000000000000000000000000000000000000000611ae0565b047f000000000000000000000000000000000000000000000000000000000000000061253e565b610911916123fa91612651670de0b6b3a764000091612621836125b7837f0000000000000000000000000000000000000000000000000000000000000000611ae0565b93818110612658575b037f0000000000000000000000000000000000000000000000000000000000000000611ae0565b049061253e565b612660611aa4565b61262a565b7f00000000000000000000000000000000000000000000000000000000000000008082116126ee57506123fa670de0b6b3a76400006126c7610911937f0000000000000000000000000000000000000000000000000000000000000000611ae0565b047f000000000000000000000000000000000000000000000000000000000000000061253e565b610911916123fa91612651670de0b6b3a764000091612731836126c7837f0000000000000000000000000000000000000000000000000000000000000000611ae0565b93818110612761575b037f0000000000000000000000000000000000000000000000000000000000000000611ae0565b612769611aa4565b61273a565b8115612778570490565b612780611a8d565b0490565b60005460015466038d7ea4c68000906127c6906001600160681b036001600160401b03846127b6828816848616611ae0565b049560401c169160681c16611ae0565b04816127d3575050600090565b670de0b6b3a76400009080600019048211811515166127f157020490565b6127f9611aa4565b020490565b51906001600160501b038216820361056e57565b506040513d6000823e3d90fd5b604051633fabe5a360e21b81529060a090829060049082906001600160a01b03165afa9081156128bd575b600091612871575b50600081131561285f5790565b60405163fd1ee34960e01b8152600490fd5b9060a0823d82116128b5575b8161288a60a09383611a05565b81010312610760575061289c816127fe565b506128ae6080602083015192016127fe565b5038612852565b3d915061287d565b6128c5612812565b61284a565b9081602091031261056e575190565b6040516370a0823160e01b8152306004820152906001600160a01b0316602082602481845afa918215612964575b600092612934575b506000908152600260205260409020546001600160801b031690818110612253570390565b61295691925060203d811161295d575b61294e8183611a05565b8101906128ca565b903861290f565b503d612944565b61296c612812565b612907565b600082128015600160ff1b840183121661299b575b6001600160ff1b038301821316612253570390565b6129a3611aa4565b612986565b6000811280156001600160ff1b038390038413166129d4575b600160ff1b829003831216612288570190565b6129dc611aa4565b6129c1565b6109116129ec6121c9565b612a0f612a0a6122cb6001549364ffffffffff8560d01c169061223d565b6124ac565b6040516370a0823160e01b8152306004820152929091906020846024816001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000165afa938415612aff575b600094612ac7575b5091612abb612ab5612ab593612aae612ac19666038d7ea4c680009260018060401b0384612aa18260018060681b039416848616611ae0565b0497169160681c16611ae0565b0495614998565b91614998565b90612971565b906129a8565b612ac193919450612ab5612ab593612aae612af2612abb9460203d811161295d5761294e8183611a05565b9794965050935050612a68565b612b07612812565b612a60565b90612b22612b1b836005610914565b54600c0b90565b906000928383600c0b1215612ccc57612bb2612b59612b53612b48846005979697610914565b5460e81c61ffff1690565b9361475e565b612b827f000000000000000000000000000000000000000000000000000000000000000061281f565b7f00000000000000000000000000000000000000000000000000000000000000006001600160401b031691612e6a565b90849360ff95867f000000000000000000000000000000000000000000000000000000000000000016955b8781169087821015612cbe5790889161ffff600180931b891616612c03575b0116612bdd565b9590915082811215612cb25790612cac8992612ac1612ca788612ca1612c9c6080612c94612c638f612c42612c3a612c5692611b2d565b976006610914565b60208801516001600160a01b031690610914565b546001600160801b031690565b6040860151612c7a906001600160a01b031661281f565b612c876060880151610b27565b91600180861b0316612e4a565b930151610b27565b610b27565b90612e0c565b614998565b95612bfc565b50955050945050505090565b505094509450509050121590565b50915050600190565b90612ce4612b1b836005610914565b906000928383600c0b1215612de457612d0a612b59612b53612b48846005979697610914565b90849360ff95867f000000000000000000000000000000000000000000000000000000000000000016955b8781169087821015612dd75790889161ffff600180931b891616612d5b575b0116612d35565b9590915082811215612dcb5790612dc58992612ac1612ca788612ca1612c9c60a0612c94612d928f612c42612c3a612c5692611b2d565b6040860151612da9906001600160a01b031661281f565b612db66060880151610b27565b916001600160801b0316612e4a565b95612d54565b50509450945050505090565b5050945094505090501290565b509050565b80546001600160f81b031660f89290921b6001600160f81b031916919091179055565b670de0b6b3a76400009161278091611ae0565b61276e907f000000000000000000000000000000000000000000000000000000000000000090611ae0565b90612e5491611ae0565b6001600160401b03909116908115612778570490565b9190612e7590614998565b6000808413939082136001600160ff1b03858216848204841116612f31575b600160ff1b95600085129185918316858905831216612f24575b60008512938416828905861216612f17575b058312911616612f0a575b6001600160401b03909216929102908215612efd575b8114600019831416612ef1570590565b612ef9611aa4565b0590565b612f05611a8d565b612ee1565b612f12611aa4565b612ecb565b612f1f611aa4565b612ec0565b612f2c611aa4565b612eae565b612f39611aa4565b612e94565b6001600160401b039182169116818110612253570390565b600c0b6001600160671b03198114612f6f575b60000390565b612f77611aa4565b612f69565b805461ffff60e81b191660e89290921b61ffff60e81b16919091179055565b81518154602084015160408501516001600160e81b03199092166001600160681b039093169290921760689290921b600160681b600160a81b03169190911760a89190911b600160a81b600160e81b03161781556060820151610bc99260ff916080919061300d9061ffff1685612f7c565b01511690612de9565b61311f90610bc9936130298451600c0b90565b600c82900b855260009182918683600c83900b8113613136576130b661308f6130f594613080612c9c6130dd9661307a60206130726123fa995460018060401b039060801c1690565b920151610b27565b90612f3e565b906001600160681b0316611ae0565b7f00000000000000000000000000000000000000000000000000000000000000009061276e565b7f00000000000000000000000000000000000000000000000000000000000000009061276e565b6130ef60408901916124098351610b27565b90611b1e565b600c0b1261312457546131189060801c6001600160401b03165b60208501611b1e565b6005610914565b612f9b565b546131319060c01c61310f565b613118565b6130b661308f6131719461316c61238561225f613166612c9c6130dd9961307a60206130726123fa9c5460c01c90565b93612f56565b611ae0565b6130f5565b939290936001805460f81c16611953576131936131979186614721565b1590565b610df1576001600160a01b038181167f0000000000000000000000000000000000000000000000000000000000000000909116036131ee5750610bc992600019830361327f5791506131e88161469a565b9161327f565b906131fc610bc99493614943565b926135d1565b6001600160681b0391821691908116908290038111612288570190565b80546001600160681b0319166001600160681b03909216919091179055565b6001600160681b039182169116818110612253570390565b8054600160681b600160d01b03191660689290921b600160681b600160d01b0316919091179055565b61332d91926132af81837f0000000000000000000000000000000000000000000000000000000000000000613497565b6132b76122a3565b61333a6132cd6132c8866005610914565b6121f2565b8051600c0b906133346132fd6132f66132f16132e88661475e565b612ac189614998565b6147f8565b8094613406565b979061331d6133168a61331160015461225f565b613202565b600161321f565b613328600154612294565b61323e565b6001613256565b86613016565b6040519081526001600160a01b0393841693849216907fd1cf3d156d5f8f0d50f6c122ed609cec09d35c9b9fb3fff6ea0959134dae424e90602090a36001600160681b038116613388575050565b6000805160206149fe8339815191526133bf6133af6000936133aa8554610b27565b6147d3565b6040519081529081906020820190565b0390a3565b600c91820b910b6000821280156001600160671b031984018312166133f9575b6001600160671b038301821316612253570390565b613401611aa4565b6133e4565b91909180600c0b83600c0b81811261347557600012613439575061342a91926133c4565b6001600160681b031690600090565b60001361345c5761344a91926133c4565b6000916001600160681b039190911690565b61346590612f56565b6001600160681b03908116921690565b5050509050600090600090565b9081602091031261056e575161091181610c9c565b6040516323b872dd60e01b81526001600160a01b039283166004820152306024820152604481019390935260209183916064918391600091165af190811561351a575b6000916134fc575b50156134ea57565b60405163073d1efd60e51b8152600490fd5b613514915060203d811161140c576114048183611a05565b386134e2565b613522612812565b6134da565b90604051613536604082611a05565b91546001600160801b038116835260801c6020830152565b6001600160801b0391821691908116908290038111612288570190565b80546001600160801b0319166001600160801b03909216919091179055565b90602060018060801b03916135a2838251168561356b565b0151825490911660809190911b6001600160801b031916179055565b6001600160801b03909116815260200190565b909290916001600160801b03906135eb8284168583613497565b6135f481612145565b90613608613603826002610914565b613527565b9261362c61361f8661361a875161092b565b61354e565b6001600160801b03168552565b613636845161092b565b906136476118d060e086015161092b565b9116116136f1576136cc6136ba856136ec946136c67ffa56f7b24f17183d81894d3ac2ee654e3c26388d17a28dbd9549b8114304e1f4976136c18761369b8e6136a76136a0612c568561369b856006610914565b610914565b988961354e565b9889956136b5856002610914565b61358a565b6006610914565b61356b565b89613703565b6040516001600160a01b03918216968216959091169390918291826135be565b0390a4565b604051637ac7b99d60e11b8152600490fd5b909290916001600160801b039081161580806137c4575b156137655750505061374d613745613736610bc9945160ff1690565b600160ff9091161b61ffff1690565b916005610914565b9061375e825461ffff9060e81c1690565b1790612f7c565b1591826137b9575b5050613777575050565b60ff610bc992600161ffff92839251161b16199160018060a01b031660005260056020526040600020916137b1835461ffff9060e81c1690565b161690612f7c565b16159050388061376d565b50818316151561371a565b93929093600260015460f81c16611953576131936137ed9186614721565b610df1576001600160a01b038481168382161461386557807f000000000000000000000000000000000000000000000000000000000000000016908216146000146138515750610bc992600019830361388857915061384b8261463c565b91613888565b9061385f610bc99493614943565b92613b21565b60405163e397a99b60e01b8152600490fd5b600160ff1b8114612f6f5760000390565b9190916138936122a3565b61389e816005610914565b6138a7906121f2565b6138b2846005610914565b6138bb906121f2565b9281516138c890600c0b90565b9380516138d590600c0b90565b926138df8661475e565b6138e884614998565b6138f191612971565b926138fb8561475e565b9061390590614998565b61390e916129a8565b90613918846147f8565b61392281936147f8565b97889361392e91613abc565b986139399197613406565b98878a6001546139489061225f565b9061395291613202565b9061395c9161323e565b61396790600161321f565b60015461397390612294565b9061397d91613202565b906139879161323e565b613992906001613256565b61399c9187613016565b6139a69187613016565b60008112613a53575b506001600160681b0391818316613a0b575b505081166139cd575050565b6000805160206149fe8339815191526133bf6139ef6000936133aa8554610b27565b6040519081526001600160a01b03909416939081906020820190565b6000805160206149fe833981519152613a49613a2d6000946133aa8654610b27565b6040519081526001600160a01b03909316929081906020820190565b0390a338806139c1565b613a5c90613877565b7f000000000000000000000000000000000000000000000000000000000000000011613aaa57613a8e61319383612b0c565b613a9857386139af565b604051630a62fbdb60e11b8152600490fd5b604051637139da2360e11b8152600490fd5b919082600c0b81600c0b81811361347557600013613adf575061342a91926133c4565b600012613af05761344a91926133c4565b613af990612f56565b6001600160681b03928316921690565b6001600160801b039182169116818110612253570390565b6001600160a01b0380821660008181526006602052604090206001600160801b03959194919391908690613b56908690610914565b54168382169687600052600660205285604060002090613b7591610914565b5416613b818983613b09565b613b8b8a8361354e565b928188613b99886006610914565b90613ba391610914565b90613bad9161356b565b8388613bba876006610914565b90613bc491610914565b90613bce9161356b565b613bd788612145565b91613be3918388613703565b613bec93613703565b613bf590612b0c565b15613a98577f29db89d45e1a802b4d55e202984fce9faf1d30aedf86503ff1ea0ed9ebb64201916136ec6040519283921696826135be565b93929093600460015460f81c1661195357613193613c4b9186614721565b610df1576001600160a01b038181167f000000000000000000000000000000000000000000000000000000000000000090911603613ca25750610bc9926000198303613cb6579150613c9c8261463c565b91613cb6565b90613cb0610bc99493614943565b92613eb9565b909161332d92613cc46122a3565b613cd26132c8846005610914565b613d2c613ce08251600c0b90565b613cf5613cec8261475e565b612abb87614998565b92613334613d0c613d05866147f8565b8094613abc565b613d216133168361332860019e959e5461225f565b613311600154612294565b60008112613dd1575b50613d6182827f0000000000000000000000000000000000000000000000000000000000000000613e31565b6040519182526001600160a01b0392831692169082907f9b1bfa7fa9ee420a16e124f794c35ac9f90472acc99140eb2f6447c714cad8eb90602090a36001600160681b038216613daf575050565b6000805160206149fe8339815191526133bf6133af6000946133aa8654610b27565b613dda90613877565b7f000000000000000000000000000000000000000000000000000000000000000011613aaa57613e0c61319384612b0c565b613a985738613d35565b6001600160a01b039091168152602081019190915260400190565b91613e5792602092600060405180968195829463a9059cbb60e01b845260048401613e16565b03926001600160a01b03165af1908115613eac575b600091613e8e575b5015613e7c57565b60405163cefaffeb60e01b8152600490fd5b613ea6915060203d811161140c576114048183611a05565b38613e74565b613eb4612812565b613e6c565b6001600160a01b038082166000818152600660205260408120909695919491936001600160801b03918290613eef908790610914565b5416613efb8882613b09565b80878781169b8c81526002602052604081208c8882541690613f1c91613b09565b613f259161356b565b8a815260066020526040902090613f3b91610914565b90613f459161356b565b613f4e87612145565b91613f599284613703565b613f6290612b0c565b15613a98577fd6d480d5b3068db003533b170d67561494d72e3bf9fa40a266471351ebba9e169382613f9692881691613e31565b6136ec6040519283921695826135be565b9190811015613fb75760051b0190565b634e487b7160e01b600052603260045260246000fd5b356109118161055d565b90604051613fe6608082611a05565b915463ffffffff81168352602081811c6001600160401b031690840152606081811c6001600160801b0316604085015260e09190911c90830152565b60019063ffffffff809116908114612288570190565b9291909260016008815460f81c16611953575a946140546122a3565b60005b848110614153575050506140fe906140f76140d86140d361407d610bc997985a9061254a565b6140cc6140ba614096614091886007610914565b613fd7565b986123fa6140b06140ab8c5163ffffffff1690565b614022565b63ffffffff168b52565b6130ef60208a01916124098351610b27565b4890611ae0565b614943565b6140ea604086019161361a835161092b565b6001600160801b03169052565b6007610914565b815160208084015160408501516060958601516001600160e01b031960e09190911b16600160601b600160e01b039190961b1663ffffffff909316600160201b600160601b039190921b161717919091179055565b8061417161416b61416686948987613fa7565b613fcd565b86614177565b01614057565b9161418461319383612cd5565b61450b57906141976132c8826005610914565b906141a38251600c0b90565b936141ad8561475e565b926060906141bf8282015161ffff1690565b6141e87f000000000000000000000000000000000000000000000000000000000000000061281f565b96600092839860ff9a8b7f0000000000000000000000000000000000000000000000000000000000000000169a5b8c8116908c82101561438a57908d91898c8c61ffff600180961b8c1616614242575b5050500116614216565b90919a8261424f85611b2d565b60208101519092906001600160a01b0316908160068161426f8683610914565b9061427991610914565b546001600160801b03169461428d91610914565b9061429791610914565b60006142a29161356b565b6142ad826002610914565b8381546142b99061092b565b906142c391613b09565b6142cc9161356b565b604084810151909f906001600160a01b03166142e79061281f565b908501516142f490610b27565b614307916001600160801b038616612e4a565b9360c0015161431590610b27565b61431e90610b27565b6143289085612e0c565b6143319161253e565b9d516001600160801b0392909216825260208201929092526001600160a01b0391821693821692909116907f9850ab1af75177e4a9201c65a2cf7976d5d28e40ef63494b44366f86b2f9412e90604090a4898c8c614238565b50509497969950945096915097506143e06143da612ca7856143d560018060401b037f000000000000000000000000000000000000000000000000000000000000000016809a611ae0565b61276e565b826129a8565b9160008312614502575b6143f3836147f8565b96876143ff9187613016565b8661440b866005610914565b600061441691612f7c565b61441f91613406565b60015461442b9061225f565b9061443591613202565b61444090600161321f565b60015461444c90612294565b906144569161323e565b614461906001613256565b61446a91612971565b614473906149e4565b9261447e9184612e4a565b6040805193845260208401919091526001600160a01b039182169485939216917f1547a878dc89ad3c367b6338b4be6a65a5dd74fb77ae044da1e8747ef1f4f62f9190a380600c0b6000126144d1575050565b6000805160206149fe8339815191526133bf6133af6000936144fc6144f68654610b27565b916149bb565b906147d3565b600092506143ea565b604051636ef5bcdd60e11b8152600490fd5b906145e561452d61091193612145565b6145b060606145db61454a60018060a01b0360408601511661281f565b60c08501516001600160401b0394670de0b6b3a7640000929091839061459f908890811680841061462f575b8303167f0000000000000000000000000000000000000000000000000000000000000000611ae0565b04808410614622575b830390611ae0565b049561316c7f000000000000000000000000000000000000000000000000000000000000000061281f565b9201511690611ae0565b908015614615575b7f0000000000000000000000000000000000000000000000000000000000000000910461276e565b61461d611a8d565b6145ed565b61462a611aa4565b6145a8565b614637611aa4565b614576565b61465f6146476121c9565b61067964ffffffffff918260015460d01c169061223d565b506001600160a01b03909116600090815260056020526040812054600c0b91908083131561469457506144fc610911926149bb565b91505090565b6146a56146476121c9565b6001600160a01b03909216600090815260056020526040812054600c0b9291508083121561469457506144fc6146dd61091193612f56565b6149bb565b50600036818037808036817f00000000000000000000000000000000000000000000000000000000000000005af43d82803e1561471d573d90f35b3d90fd5b6001600160a01b0380831691169081149190821561473e57505090565b60ff9250906147599160005260036020526040600020610914565b541690565b6000600c82900b1261479e576000546109119166038d7ea4c6800091614798916001600160401b03909116906001600160681b0316611ae0565b04614998565b6147ce612ca7610911926147bf60018060401b0360005460401c1691612f56565b6001600160681b0316906147d3565b613877565b66038d7ea4c6800091612780916001600160401b0316906001600160681b0316611ae0565b6000811261481e5760005461091191614819916001600160401b03166148a7565b614969565b61487b6148196109119261483f60018060401b0360005460401c1691613877565b614864826000199266038d7ea4c680009080850482118115151661489a575b0261253e565b6001811061488d575b8215614880575b010461491d565b612f56565b614888611a8d565b614874565b614895611aa4565b61486d565b6148a2611aa4565b61485e565b906109119166038d7ea4c680009082600019048211831515166148ea575b6001600160401b03169182156148dd575b020461491d565b6148e5611a8d565b6148d6565b6148f2611aa4565b6148c5565b6001600160401b039081811161490b571690565b6040516372a1cb5160e11b8152600490fd5b6001600160681b0390818111614931571690565b604051630dc7925560e11b8152600490fd5b6001600160801b0390818111614957571690565b60405163762ea71160e11b8152600490fd5b6001600160681b03166001600160671b03811161498657600c0b90565b604051639369ae3560e01b8152600490fd5b6001600160ff1b0381116149a95790565b60405163e7e828ad60e01b8152600490fd5b600081600c0b126149d2576001600160681b031690565b60405163363b64b760e11b8152600490fd5b600081126149d25790565b60009015610911575060019056feddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa26469706673582212202a7592e3c3182f32b57bc65987dbae689a3af695096cecda4191815467d98a2a64736f6c634300080f003300000000000000000000000000000000000000000000000000000000000000200000000000000000000000006d903f6003cca6255d85cca4d3b5e5146dc33925000000000000000000000000bbf3f1421d886e9b2c5d716b5192ac998af2012c000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000008fffffd4afb6115b954bd326cbe7b4ba576818f6000000000000000000000000285617313887d43256f852cae0ee4de4b68d45b00000000000000000000000000000000000000000000000000b1a2bc2ec50000000000000000000000000000000000000000000000000000000737693eb334000000000000000000000000000000000000000000000000000058d15e17628000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b1a2bc2ec500000000000000000000000000000000000000000000000000000007c58508723800400000000000000000000000000000000000000000000000003782dace9d9000000000000000000000000000000000000000000000000000000354a6ba7a1800000000000000000000000000000000000000000000000000006f05b59d3b2000000000000000000000000000000000000000000000000000000038d7ea4c680000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b2fe95ce6d000000000000000000000000000000000000000000000000000000e8d4a510000000000000000000000000000000000000000000000000000000000005f5e1000000000000000000000000000000000000000000000000000000048c2739500000000000000000000000000000000000000000000000000000000000000002a00000000000000000000000000000000000000000000000000000000000000005000000000000000000000000c00e94cb662c3520282e6f5717214004a7f26888000000000000000000000000dbd020caef83efd542f4de03e3cf0c28a4428bd500000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000905438e6001000000000000000000000000000000000000000000000000000009b6e64a8ec600000000000000000000000000000000000000000000000000000ce80612991d0000000000000000000000000000000000000000000000007f0e10af47c1c70000000000000000000000000000002260fac5e5542a773aa44fbcfedf7c193bc2c599000000000000000000000000f4030086522a5beea4988f8ca5b36dbc97bee88c000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000009b6e64a8ec600000000000000000000000000000000000000000000000000000aaf96eb9d0d00000000000000000000000000000000000000000000000000000d2f13f7789f00000000000000000000000000000000000000000000000000000000008bb2c97000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000005f4ec3df9cbd43714fe2740f5e3616155c5b841900000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000b72fd2103b280000000000000000000000000000000000000000000000000000c6badc211f980000000000000000000000000000000000000000000000000000d2f13f7789f0000000000000000000000000000000000000000000000000fe1c215e8f838e000000000000000000000000000001f9840a85d5af5bf1d1762f925bdaddc4201f984000000000000000000000000553303d460ee0afb37edff9be42922d8ff63220e00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000a688906bd8b00000000000000000000000000000000000000000000000000000b3db2b55c1100000000000000000000000000000000000000000000000000000ce80612991d00000000000000000000000000000000000000000000000108b2a2c2802909400000000000000000000000000000514910771af9ca656af840dff83e8264ecf986ca0000000000000000000000002c1d072e956affc0d435cb7ac38ef18d24d9127c00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000af6a4d07c8f00000000000000000000000000000000000000000000000000000bcbce7f1b1500000000000000000000000000000000000000000000000000000ce80612991d00000000000000000000000000000000000000000000000108b2a2c2802909400000

Check out more smart contracts

Build blockchain magic with Alchemy

Alchemy combines the most powerful web3 developer products and tools with resources, community and legendary support.