Overview
S Balance
0 S
S Value
$0.00More Info
Private Name Tags
ContractCreator
Loading...
Loading
Contract Name:
RewardsControllerV2
Compiler Version
v0.8.10+commit.fc410830
Optimization Enabled:
Yes with 100000 runs
Other Settings:
berlin EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.10; import {VersionedInitializable} from '@aave/core-v3/contracts/protocol/libraries/aave-upgradeability/VersionedInitializable.sol'; import {SafeCast} from '@aave/core-v3/contracts/dependencies/openzeppelin/contracts/SafeCast.sol'; import {IScaledBalanceToken} from '@aave/core-v3/contracts/interfaces/IScaledBalanceToken.sol'; import {RewardsDistributorV2} from './RewardsDistributorV2.sol'; import {IRewardsControllerV2} from './interfaces/IRewardsControllerV2.sol'; import {ITransferStrategyBase} from '@aave/periphery-v3/contracts/rewards/interfaces/ITransferStrategyBase.sol'; import {RewardsDataTypes} from '@aave/periphery-v3/contracts/rewards/libraries/RewardsDataTypes.sol'; import {IEACAggregatorProxy} from '@aave/periphery-v3/contracts/misc/interfaces/IEACAggregatorProxy.sol'; /** * @title RewardsControllerV2 * @notice Abstract contract template to build Distributors contracts for ERC20 rewards to protocol participants * @author Aave **/ contract RewardsControllerV2 is RewardsDistributorV2, VersionedInitializable, IRewardsControllerV2 { using SafeCast for uint256; uint256 public constant REVISION = 2; // This mapping allows whitelisted addresses to claim on behalf of others // useful for contracts that hold tokens to be rewarded but don't have any native logic to claim Liquidity Mining rewards mapping(address => address) internal _authorizedClaimers; // This mapping allows whitelisted addresses to update on behalf of others // useful for contracts that update user's stats when need mapping(address => bool) internal _authorizedUpdaters; // reward => transfer strategy implementation contract // The TransferStrategy contract abstracts the logic regarding // the source of the reward and how to transfer it to the user. mapping(address => ITransferStrategyBase) internal _transferStrategy; // This mapping contains the price oracle per reward. // A price oracle is enforced for integrators to be able to show incentives at // the current Aave UI without the need to setup an external price registry // At the moment of reward configuration, the Incentives Controller performs // a check to see if the provided reward oracle contains `latestAnswer`. mapping(address => IEACAggregatorProxy) internal _rewardOracle; modifier onlyAuthorizedClaimers(address claimer, address user) { require(_authorizedClaimers[user] == claimer, 'CLAIMER_UNAUTHORIZED'); _; } modifier onlyAuthorizedUpdater() { require(_authorizedUpdaters[msg.sender], 'UPDATER_UNAUTHORIZED'); _; } constructor(address emissionManager) RewardsDistributorV2(emissionManager) {} /** * @dev Initialize for RewardsController * @dev It expects an address as argument since its initialized via PoolAddressesProvider._updateImpl() **/ function initialize(address) external initializer {} /// @inheritdoc IRewardsControllerV2 function getClaimer(address user) external view override returns (address) { return _authorizedClaimers[user]; } /** * @dev Returns the revision of the implementation contract * @return uint256, current revision version */ function getRevision() internal pure override returns (uint256) { return REVISION; } /// @inheritdoc IRewardsControllerV2 function getRewardOracle(address reward) external view override returns (address) { return address(_rewardOracle[reward]); } /// @inheritdoc IRewardsControllerV2 function getTransferStrategy(address reward) external view override returns (address) { return address(_transferStrategy[reward]); } /// @inheritdoc IRewardsControllerV2 function configureAssets( RewardsDataTypes.RewardsConfigInput[] memory config ) external override onlyEmissionManager { for (uint256 i = 0; i < config.length; i++) { // Get the current Scaled Total Supply of AToken or Debt token config[i].totalSupply = IScaledBalanceToken(config[i].asset).scaledTotalSupply(); // Install TransferStrategy logic at IncentivesController _installTransferStrategy(config[i].reward, config[i].transferStrategy); // Set reward oracle, enforces input oracle to have latestPrice function _setRewardOracle(config[i].reward, config[i].rewardOracle); } _configureAssets(config); } /// @inheritdoc IRewardsControllerV2 function setTransferStrategy( address reward, ITransferStrategyBase transferStrategy ) external onlyEmissionManager { _installTransferStrategy(reward, transferStrategy); } /// @inheritdoc IRewardsControllerV2 function setRewardOracle( address reward, IEACAggregatorProxy rewardOracle ) external onlyEmissionManager { _setRewardOracle(reward, rewardOracle); } /// @inheritdoc IRewardsControllerV2 function handleAction(address user, uint256 totalSupply, uint256 userBalance) external override { _updateData(msg.sender, user, userBalance, totalSupply); } function updateAction(address user) external override onlyAuthorizedUpdater { for (uint256 i = 0; i < _assetsList.length; i++) { (uint256 userBalance, uint256 totalSupply) = IScaledBalanceToken( _assetsList[i] ).getScaledUserBalanceAndSupply(user); _updateData(_assetsList[i], user, userBalance, totalSupply); } } /// @inheritdoc IRewardsControllerV2 function claimRewards( address[] calldata assets, uint256 amount, address to, address reward ) external override returns (uint256) { require(to != address(0), 'INVALID_TO_ADDRESS'); return _claimRewards(assets, amount, msg.sender, msg.sender, to, reward); } /// @inheritdoc IRewardsControllerV2 function claimRewardsOnBehalf( address[] calldata assets, uint256 amount, address user, address to, address reward ) external override onlyAuthorizedClaimers(msg.sender, user) returns (uint256) { require(user != address(0), 'INVALID_USER_ADDRESS'); require(to != address(0), 'INVALID_TO_ADDRESS'); return _claimRewards(assets, amount, msg.sender, user, to, reward); } /// @inheritdoc IRewardsControllerV2 function claimRewardsToSelf( address[] calldata assets, uint256 amount, address reward ) external override returns (uint256) { return _claimRewards(assets, amount, msg.sender, msg.sender, msg.sender, reward); } /// @inheritdoc IRewardsControllerV2 function claimAllRewards( address[] calldata assets, address to ) external override returns (address[] memory rewardsList, uint256[] memory claimedAmounts) { require(to != address(0), 'INVALID_TO_ADDRESS'); return _claimAllRewards(assets, msg.sender, msg.sender, to); } /// @inheritdoc IRewardsControllerV2 function claimAllRewardsOnBehalf( address[] calldata assets, address user, address to ) external override onlyAuthorizedClaimers(msg.sender, user) returns (address[] memory rewardsList, uint256[] memory claimedAmounts) { require(user != address(0), 'INVALID_USER_ADDRESS'); require(to != address(0), 'INVALID_TO_ADDRESS'); return _claimAllRewards(assets, msg.sender, user, to); } /// @inheritdoc IRewardsControllerV2 function claimAllRewardsToSelf( address[] calldata assets ) external override returns (address[] memory rewardsList, uint256[] memory claimedAmounts) { return _claimAllRewards(assets, msg.sender, msg.sender, msg.sender); } /// @inheritdoc IRewardsControllerV2 function setClaimer(address user, address caller) external override onlyEmissionManager { _authorizedClaimers[user] = caller; emit ClaimerSet(user, caller); } function setUpdater(address updater, bool status) external override onlyEmissionManager { _authorizedUpdaters[updater] = status; emit UpdaterSet(updater, status); } /** * @dev Get user balances and total supply of all the assets specified by the assets parameter * @param assets List of assets to retrieve user balance and total supply * @param user Address of the user * @return userAssetBalances contains a list of structs with user balance and total supply of the given assets */ function _getUserAssetBalances( address[] calldata assets, address user ) internal view override returns (RewardsDataTypes.UserAssetBalance[] memory userAssetBalances) { userAssetBalances = new RewardsDataTypes.UserAssetBalance[](assets.length); for (uint256 i = 0; i < assets.length; i++) { userAssetBalances[i].asset = assets[i]; (userAssetBalances[i].userBalance, userAssetBalances[i].totalSupply) = IScaledBalanceToken( assets[i] ).getScaledUserBalanceAndSupply(user); } return userAssetBalances; } /** * @dev Claims one type of reward for a user on behalf, on all the assets of the pool, accumulating the pending rewards. * @param assets List of assets to check eligible distributions before claiming rewards * @param amount Amount of rewards to claim * @param claimer Address of the claimer who claims rewards on behalf of user * @param user Address to check and claim rewards * @param to Address that will be receiving the rewards * @param reward Address of the reward token * @return Rewards claimed **/ function _claimRewards( address[] calldata assets, uint256 amount, address claimer, address user, address to, address reward ) internal returns (uint256) { if (amount == 0) { return 0; } uint256 totalRewards; _updateDataMultiple(user, _getUserAssetBalances(assets, user)); for (uint256 i = 0; i < assets.length; i++) { address asset = assets[i]; totalRewards += _assets[asset].rewards[reward].usersData[user].accrued; if (totalRewards <= amount) { _assets[asset].rewards[reward].usersData[user].accrued = 0; } else { uint256 difference = totalRewards - amount; totalRewards -= difference; _assets[asset].rewards[reward].usersData[user].accrued = difference.toUint128(); break; } } if (totalRewards == 0) { return 0; } _transferRewards(to, reward, totalRewards); emit RewardsClaimed(user, reward, to, claimer, totalRewards); return totalRewards; } /** * @dev Claims one type of reward for a user on behalf, on all the assets of the pool, accumulating the pending rewards. * @param assets List of assets to check eligible distributions before claiming rewards * @param claimer Address of the claimer on behalf of user * @param user Address to check and claim rewards * @param to Address that will be receiving the rewards * @return * rewardsList List of reward addresses * claimedAmount List of claimed amounts, follows "rewardsList" items order **/ function _claimAllRewards( address[] calldata assets, address claimer, address user, address to ) internal returns (address[] memory rewardsList, uint256[] memory claimedAmounts) { uint256 rewardsListLength = _rewardsList.length; rewardsList = new address[](rewardsListLength); claimedAmounts = new uint256[](rewardsListLength); _updateDataMultiple(user, _getUserAssetBalances(assets, user)); for (uint256 i = 0; i < assets.length; i++) { address asset = assets[i]; for (uint256 j = 0; j < rewardsListLength; j++) { if (rewardsList[j] == address(0)) { rewardsList[j] = _rewardsList[j]; } uint256 rewardAmount = _assets[asset].rewards[rewardsList[j]].usersData[user].accrued; if (rewardAmount != 0) { claimedAmounts[j] += rewardAmount; _assets[asset].rewards[rewardsList[j]].usersData[user].accrued = 0; } } } for (uint256 i = 0; i < rewardsListLength; i++) { _transferRewards(to, rewardsList[i], claimedAmounts[i]); emit RewardsClaimed(user, rewardsList[i], to, claimer, claimedAmounts[i]); } return (rewardsList, claimedAmounts); } /** * @dev Function to transfer rewards to the desired account using delegatecall and * @param to Account address to send the rewards * @param reward Address of the reward token * @param amount Amount of rewards to transfer */ function _transferRewards(address to, address reward, uint256 amount) internal { ITransferStrategyBase transferStrategy = _transferStrategy[reward]; bool success = transferStrategy.performTransfer(to, reward, amount); require(success == true, 'TRANSFER_ERROR'); } /** * @dev Returns true if `account` is a contract. * @param account The address of the account * @return bool, true if contract, false otherwise */ function _isContract(address account) internal view returns (bool) { // This method relies on extcodesize, which returns 0 for contracts in // construction, since the code is only stored at the end of the // constructor execution. uint256 size; // solhint-disable-next-line no-inline-assembly assembly { size := extcodesize(account) } return size > 0; } /** * @dev Internal function to call the optional install hook at the TransferStrategy * @param reward The address of the reward token * @param transferStrategy The address of the reward TransferStrategy */ function _installTransferStrategy( address reward, ITransferStrategyBase transferStrategy ) internal { require(address(transferStrategy) != address(0), 'STRATEGY_CAN_NOT_BE_ZERO'); require(_isContract(address(transferStrategy)) == true, 'STRATEGY_MUST_BE_CONTRACT'); _transferStrategy[reward] = transferStrategy; emit TransferStrategyInstalled(reward, address(transferStrategy)); } /** * @dev Update the Price Oracle of a reward token. The Price Oracle must follow Chainlink IEACAggregatorProxy interface. * @notice The Price Oracle of a reward is used for displaying correct data about the incentives at the UI frontend. * @param reward The address of the reward token * @param rewardOracle The address of the price oracle */ function _setRewardOracle(address reward, IEACAggregatorProxy rewardOracle) internal { require(rewardOracle.latestAnswer() > 0, 'ORACLE_MUST_RETURN_PRICE'); _rewardOracle[reward] = rewardOracle; emit RewardOracleUpdated(reward, address(rewardOracle)); } }
// SPDX-License-Identifier: AGPL-3.0 pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `recipient`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address recipient, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `sender` to `recipient` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); }
// SPDX-License-Identifier: AGPL-3.0 pragma solidity ^0.8.0; import {IERC20} from './IERC20.sol'; interface IERC20Detailed is IERC20 { function name() external view returns (string memory); function symbol() external view returns (string memory); function decimals() external view returns (uint8); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/math/SafeCast.sol) pragma solidity ^0.8.0; /** * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow * checks. * * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can * easily result in undesired exploitation or bugs, since developers usually * assume that overflows raise errors. `SafeCast` restores this intuition by * reverting the transaction when such an operation overflows. * * Using this library instead of the unchecked operations eliminates an entire * class of bugs, so it's recommended to use it always. * * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing * all math on `uint256` and `int256` and then downcasting. */ library SafeCast { /** * @dev Returns the downcasted uint224 from uint256, reverting on * overflow (when the input is greater than largest uint224). * * Counterpart to Solidity's `uint224` operator. * * Requirements: * * - input must fit into 224 bits */ function toUint224(uint256 value) internal pure returns (uint224) { require(value <= type(uint224).max, "SafeCast: value doesn't fit in 224 bits"); return uint224(value); } /** * @dev Returns the downcasted uint128 from uint256, reverting on * overflow (when the input is greater than largest uint128). * * Counterpart to Solidity's `uint128` operator. * * Requirements: * * - input must fit into 128 bits */ function toUint128(uint256 value) internal pure returns (uint128) { require(value <= type(uint128).max, "SafeCast: value doesn't fit in 128 bits"); return uint128(value); } /** * @dev Returns the downcasted uint96 from uint256, reverting on * overflow (when the input is greater than largest uint96). * * Counterpart to Solidity's `uint96` operator. * * Requirements: * * - input must fit into 96 bits */ function toUint96(uint256 value) internal pure returns (uint96) { require(value <= type(uint96).max, "SafeCast: value doesn't fit in 96 bits"); return uint96(value); } /** * @dev Returns the downcasted uint64 from uint256, reverting on * overflow (when the input is greater than largest uint64). * * Counterpart to Solidity's `uint64` operator. * * Requirements: * * - input must fit into 64 bits */ function toUint64(uint256 value) internal pure returns (uint64) { require(value <= type(uint64).max, "SafeCast: value doesn't fit in 64 bits"); return uint64(value); } /** * @dev Returns the downcasted uint32 from uint256, reverting on * overflow (when the input is greater than largest uint32). * * Counterpart to Solidity's `uint32` operator. * * Requirements: * * - input must fit into 32 bits */ function toUint32(uint256 value) internal pure returns (uint32) { require(value <= type(uint32).max, "SafeCast: value doesn't fit in 32 bits"); return uint32(value); } /** * @dev Returns the downcasted uint16 from uint256, reverting on * overflow (when the input is greater than largest uint16). * * Counterpart to Solidity's `uint16` operator. * * Requirements: * * - input must fit into 16 bits */ function toUint16(uint256 value) internal pure returns (uint16) { require(value <= type(uint16).max, "SafeCast: value doesn't fit in 16 bits"); return uint16(value); } /** * @dev Returns the downcasted uint8 from uint256, reverting on * overflow (when the input is greater than largest uint8). * * Counterpart to Solidity's `uint8` operator. * * Requirements: * * - input must fit into 8 bits. */ function toUint8(uint256 value) internal pure returns (uint8) { require(value <= type(uint8).max, "SafeCast: value doesn't fit in 8 bits"); return uint8(value); } /** * @dev Converts a signed int256 into an unsigned uint256. * * Requirements: * * - input must be greater than or equal to 0. */ function toUint256(int256 value) internal pure returns (uint256) { require(value >= 0, 'SafeCast: value must be positive'); return uint256(value); } /** * @dev Returns the downcasted int128 from int256, reverting on * overflow (when the input is less than smallest int128 or * greater than largest int128). * * Counterpart to Solidity's `int128` operator. * * Requirements: * * - input must fit into 128 bits * * _Available since v3.1._ */ function toInt128(int256 value) internal pure returns (int128) { require( value >= type(int128).min && value <= type(int128).max, "SafeCast: value doesn't fit in 128 bits" ); return int128(value); } /** * @dev Returns the downcasted int64 from int256, reverting on * overflow (when the input is less than smallest int64 or * greater than largest int64). * * Counterpart to Solidity's `int64` operator. * * Requirements: * * - input must fit into 64 bits * * _Available since v3.1._ */ function toInt64(int256 value) internal pure returns (int64) { require( value >= type(int64).min && value <= type(int64).max, "SafeCast: value doesn't fit in 64 bits" ); return int64(value); } /** * @dev Returns the downcasted int32 from int256, reverting on * overflow (when the input is less than smallest int32 or * greater than largest int32). * * Counterpart to Solidity's `int32` operator. * * Requirements: * * - input must fit into 32 bits * * _Available since v3.1._ */ function toInt32(int256 value) internal pure returns (int32) { require( value >= type(int32).min && value <= type(int32).max, "SafeCast: value doesn't fit in 32 bits" ); return int32(value); } /** * @dev Returns the downcasted int16 from int256, reverting on * overflow (when the input is less than smallest int16 or * greater than largest int16). * * Counterpart to Solidity's `int16` operator. * * Requirements: * * - input must fit into 16 bits * * _Available since v3.1._ */ function toInt16(int256 value) internal pure returns (int16) { require( value >= type(int16).min && value <= type(int16).max, "SafeCast: value doesn't fit in 16 bits" ); return int16(value); } /** * @dev Returns the downcasted int8 from int256, reverting on * overflow (when the input is less than smallest int8 or * greater than largest int8). * * Counterpart to Solidity's `int8` operator. * * Requirements: * * - input must fit into 8 bits. * * _Available since v3.1._ */ function toInt8(int256 value) internal pure returns (int8) { require( value >= type(int8).min && value <= type(int8).max, "SafeCast: value doesn't fit in 8 bits" ); return int8(value); } /** * @dev Converts an unsigned uint256 into a signed int256. * * Requirements: * * - input must be less than or equal to maxInt256. */ function toInt256(uint256 value) internal pure returns (int256) { // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive require(value <= uint256(type(int256).max), "SafeCast: value doesn't fit in an int256"); return int256(value); } }
// SPDX-License-Identifier: AGPL-3.0 pragma solidity ^0.8.0; /** * @title IScaledBalanceToken * @author Aave * @notice Defines the basic interface for a scaled-balance token. */ interface IScaledBalanceToken { /** * @dev Emitted after the mint action * @param caller The address performing the mint * @param onBehalfOf The address of the user that will receive the minted tokens * @param value The scaled-up amount being minted (based on user entered amount and balance increase from interest) * @param balanceIncrease The increase in scaled-up balance since the last action of 'onBehalfOf' * @param index The next liquidity index of the reserve */ event Mint( address indexed caller, address indexed onBehalfOf, uint256 value, uint256 balanceIncrease, uint256 index ); /** * @dev Emitted after the burn action * @dev If the burn function does not involve a transfer of the underlying asset, the target defaults to zero address * @param from The address from which the tokens will be burned * @param target The address that will receive the underlying, if any * @param value The scaled-up amount being burned (user entered amount - balance increase from interest) * @param balanceIncrease The increase in scaled-up balance since the last action of 'from' * @param index The next liquidity index of the reserve */ event Burn( address indexed from, address indexed target, uint256 value, uint256 balanceIncrease, uint256 index ); /** * @notice Returns the scaled balance of the user. * @dev The scaled balance is the sum of all the updated stored balance divided by the reserve's liquidity index * at the moment of the update * @param user The user whose balance is calculated * @return The scaled balance of the user */ function scaledBalanceOf(address user) external view returns (uint256); /** * @notice Returns the scaled balance of the user and the scaled total supply. * @param user The address of the user * @return The scaled balance of the user * @return The scaled total supply */ function getScaledUserBalanceAndSupply(address user) external view returns (uint256, uint256); /** * @notice Returns the scaled total supply of the scaled balance token. Represents sum(debt/index) * @return The scaled total supply */ function scaledTotalSupply() external view returns (uint256); /** * @notice Returns last index interest was accrued to the user's balance * @param user The address of the user * @return The last index interest was accrued to the user's balance, expressed in ray */ function getPreviousIndex(address user) external view returns (uint256); }
// SPDX-License-Identifier: AGPL-3.0 pragma solidity ^0.8.0; /** * @title VersionedInitializable * @author Aave, inspired by the OpenZeppelin Initializable contract * @notice Helper contract to implement initializer functions. To use it, replace * the constructor with a function that has the `initializer` modifier. * @dev WARNING: Unlike constructors, initializer functions must be manually * invoked. This applies both to deploying an Initializable contract, as well * as extending an Initializable contract via inheritance. * WARNING: When used with inheritance, manual care must be taken to not invoke * a parent initializer twice, or ensure that all initializers are idempotent, * because this is not dealt with automatically as with constructors. */ abstract contract VersionedInitializable { /** * @dev Indicates that the contract has been initialized. */ uint256 private lastInitializedRevision = 0; /** * @dev Indicates that the contract is in the process of being initialized. */ bool private initializing; /** * @dev Modifier to use in the initializer function of a contract. */ modifier initializer() { uint256 revision = getRevision(); require( initializing || isConstructor() || revision > lastInitializedRevision, 'Contract instance has already been initialized' ); bool isTopLevelCall = !initializing; if (isTopLevelCall) { initializing = true; lastInitializedRevision = revision; } _; if (isTopLevelCall) { initializing = false; } } /** * @notice Returns the revision number of the contract * @dev Needs to be defined in the inherited class as a constant. * @return The revision number */ function getRevision() internal pure virtual returns (uint256); /** * @notice Returns true if and only if the function is running in the constructor * @return True if the function is running in the constructor */ function isConstructor() private view returns (bool) { // extcodesize checks the size of the code stored in an address, and // address returns the current address. Since the code is still not // deployed when running a constructor, any checks on its code size will // yield zero, making it an effective way to detect if a contract is // under construction or not. uint256 cs; //solium-disable-next-line assembly { cs := extcodesize(address()) } return cs == 0; } // Reserved storage space to allow for layout changes in the future. uint256[50] private ______gap; }
// SPDX-License-Identifier: AGPL-3.0 pragma solidity ^0.8.10; interface IEACAggregatorProxy { function decimals() external view returns (uint8); function latestAnswer() external view returns (int256); function latestTimestamp() external view returns (uint256); function latestRound() external view returns (uint256); function getAnswer(uint256 roundId) external view returns (int256); function getTimestamp(uint256 roundId) external view returns (uint256); event AnswerUpdated(int256 indexed current, uint256 indexed roundId, uint256 timestamp); event NewRound(uint256 indexed roundId, address indexed startedBy); }
// SPDX-License-Identifier: AGPL-3.0 pragma solidity ^0.8.10; interface ITransferStrategyBase { event EmergencyWithdrawal( address indexed caller, address indexed token, address indexed to, uint256 amount ); /** * @dev Perform custom transfer logic via delegate call from source contract to a TransferStrategy implementation * @param to Account to transfer rewards * @param reward Address of the reward token * @param amount Amount to transfer to the "to" address parameter * @return Returns true bool if transfer logic succeeds */ function performTransfer(address to, address reward, uint256 amount) external returns (bool); /** * @return Returns the address of the Incentives Controller */ function getIncentivesController() external view returns (address); /** * @return Returns the address of the Rewards admin */ function getRewardsAdmin() external view returns (address); /** * @dev Perform an emergency token withdrawal only callable by the Rewards admin * @param token Address of the token to withdraw funds from this contract * @param to Address of the recipient of the withdrawal * @param amount Amount of the withdrawal */ function emergencyWithdrawal(address token, address to, uint256 amount) external; }
// SPDX-License-Identifier: AGPL-3.0 pragma solidity ^0.8.10; import {ITransferStrategyBase} from '../interfaces/ITransferStrategyBase.sol'; import {IEACAggregatorProxy} from '../../misc/interfaces/IEACAggregatorProxy.sol'; library RewardsDataTypes { struct RewardsConfigInput { uint88 emissionPerSecond; uint256 totalSupply; uint32 distributionEnd; address asset; address reward; ITransferStrategyBase transferStrategy; IEACAggregatorProxy rewardOracle; } struct UserAssetBalance { address asset; uint256 userBalance; uint256 totalSupply; } struct UserData { // Liquidity index of the reward distribution for the user uint104 index; // Amount of accrued rewards for the user since last user index update uint128 accrued; } struct RewardData { // Liquidity index of the reward distribution uint104 index; // Amount of reward tokens distributed per second uint88 emissionPerSecond; // Timestamp of the last reward index update uint32 lastUpdateTimestamp; // The end of the distribution of rewards (in seconds) uint32 distributionEnd; // Map of user addresses and their rewards data (userAddress => userData) mapping(address => UserData) usersData; } struct AssetData { // Map of reward token addresses and their data (rewardTokenAddress => rewardData) mapping(address => RewardData) rewards; // List of reward token addresses for the asset mapping(uint128 => address) availableRewards; // Count of reward tokens for the asset uint128 availableRewardsCount; // Number of decimals of the asset uint8 decimals; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface IBoostConfig { function getBoostRate(address user) external view returns (uint256); function getBoostBasic() external view returns (uint256); function getBoostMaxRate() external view returns (uint256); function updateUser(address user) external; }
// SPDX-License-Identifier: AGPL-3.0 pragma solidity ^0.8.10; import {IRewardsDistributorV2} from './IRewardsDistributorV2.sol'; import {ITransferStrategyBase} from '@aave/periphery-v3/contracts/rewards/interfaces/ITransferStrategyBase.sol'; import {IEACAggregatorProxy} from '@aave/periphery-v3/contracts/misc/interfaces/IEACAggregatorProxy.sol'; import {RewardsDataTypes} from '@aave/periphery-v3/contracts/rewards/libraries/RewardsDataTypes.sol'; /** * @title IRewardsControllerV2 * @author Aave * @notice Defines the basic interface for a Rewards Controller. */ interface IRewardsControllerV2 is IRewardsDistributorV2 { /** * @dev Emitted when a new address is whitelisted as claimer of rewards on behalf of a user * @param user The address of the user * @param claimer The address of the claimer */ event ClaimerSet(address indexed user, address indexed claimer); /** * @dev Emitted when a status of updater change * @param updater The address of the updater * @param status The status of the updater */ event UpdaterSet(address indexed updater, bool indexed status); /** * @dev Emitted when rewards are claimed * @param user The address of the user rewards has been claimed on behalf of * @param reward The address of the token reward is claimed * @param to The address of the receiver of the rewards * @param claimer The address of the claimer * @param amount The amount of rewards claimed */ event RewardsClaimed( address indexed user, address indexed reward, address indexed to, address claimer, uint256 amount ); /** * @dev Emitted when a transfer strategy is installed for the reward distribution * @param reward The address of the token reward * @param transferStrategy The address of TransferStrategy contract */ event TransferStrategyInstalled(address indexed reward, address indexed transferStrategy); /** * @dev Emitted when the reward oracle is updated * @param reward The address of the token reward * @param rewardOracle The address of oracle */ event RewardOracleUpdated(address indexed reward, address indexed rewardOracle); /** * @dev Whitelists an address to claim the rewards on behalf of another address * @param user The address of the user * @param claimer The address of the claimer */ function setClaimer(address user, address claimer) external; /** * @dev Whitelists an address to updater * @param updater The address of the updater * @param status The status of updater */ function setUpdater(address updater, bool status) external; /** * @dev Sets a TransferStrategy logic contract that determines the logic of the rewards transfer * @param reward The address of the reward token * @param transferStrategy The address of the TransferStrategy logic contract */ function setTransferStrategy(address reward, ITransferStrategyBase transferStrategy) external; /** * @dev Sets an Aave Oracle contract to enforce rewards with a source of value. * @notice At the moment of reward configuration, the Incentives Controller performs * a check to see if the reward asset oracle is compatible with IEACAggregator proxy. * This check is enforced for integrators to be able to show incentives at * the current Aave UI without the need to setup an external price registry * @param reward The address of the reward to set the price aggregator * @param rewardOracle The address of price aggregator that follows IEACAggregatorProxy interface */ function setRewardOracle(address reward, IEACAggregatorProxy rewardOracle) external; /** * @dev Get the price aggregator oracle address * @param reward The address of the reward * @return The price oracle of the reward */ function getRewardOracle(address reward) external view returns (address); /** * @dev Returns the whitelisted claimer for a certain address (0x0 if not set) * @param user The address of the user * @return The claimer address */ function getClaimer(address user) external view returns (address); /** * @dev Returns the Transfer Strategy implementation contract address being used for a reward address * @param reward The address of the reward * @return The address of the TransferStrategy contract */ function getTransferStrategy(address reward) external view returns (address); /** * @dev Configure assets to incentivize with an emission of rewards per second until the end of distribution. * @param config The assets configuration input, the list of structs contains the following fields: * uint104 emissionPerSecond: The emission per second following rewards unit decimals. * uint256 totalSupply: The total supply of the asset to incentivize * uint40 distributionEnd: The end of the distribution of the incentives for an asset * address asset: The asset address to incentivize * address reward: The reward token address * ITransferStrategy transferStrategy: The TransferStrategy address with the install hook and claim logic. * IEACAggregatorProxy rewardOracle: The Price Oracle of a reward to visualize the incentives at the UI Frontend. * Must follow Chainlink Aggregator IEACAggregatorProxy interface to be compatible. */ function configureAssets(RewardsDataTypes.RewardsConfigInput[] memory config) external; /** * @dev Called by the corresponding asset on transfer hook in order to update the rewards distribution. * @dev The units of `totalSupply` and `userBalance` should be the same. * @param user The address of the user whose asset balance has changed * @param totalSupply The total supply of the asset prior to user balance change * @param userBalance The previous user balance prior to balance change **/ function handleAction(address user, uint256 totalSupply, uint256 userBalance) external; function updateAction(address user) external; /** * @dev Claims reward for a user to the desired address, on all the assets of the pool, accumulating the pending rewards * @param assets List of assets to check eligible distributions before claiming rewards * @param amount The amount of rewards to claim * @param to The address that will be receiving the rewards * @param reward The address of the reward token * @return The amount of rewards claimed **/ function claimRewards( address[] calldata assets, uint256 amount, address to, address reward ) external returns (uint256); /** * @dev Claims reward for a user on behalf, on all the assets of the pool, accumulating the pending rewards. The * caller must be whitelisted via "allowClaimOnBehalf" function by the RewardsAdmin role manager * @param assets The list of assets to check eligible distributions before claiming rewards * @param amount The amount of rewards to claim * @param user The address to check and claim rewards * @param to The address that will be receiving the rewards * @param reward The address of the reward token * @return The amount of rewards claimed **/ function claimRewardsOnBehalf( address[] calldata assets, uint256 amount, address user, address to, address reward ) external returns (uint256); /** * @dev Claims reward for msg.sender, on all the assets of the pool, accumulating the pending rewards * @param assets The list of assets to check eligible distributions before claiming rewards * @param amount The amount of rewards to claim * @param reward The address of the reward token * @return The amount of rewards claimed **/ function claimRewardsToSelf( address[] calldata assets, uint256 amount, address reward ) external returns (uint256); /** * @dev Claims all rewards for a user to the desired address, on all the assets of the pool, accumulating the pending rewards * @param assets The list of assets to check eligible distributions before claiming rewards * @param to The address that will be receiving the rewards * @return rewardsList List of addresses of the reward tokens * @return claimedAmounts List that contains the claimed amount per reward, following same order as "rewardList" **/ function claimAllRewards( address[] calldata assets, address to ) external returns (address[] memory rewardsList, uint256[] memory claimedAmounts); /** * @dev Claims all rewards for a user on behalf, on all the assets of the pool, accumulating the pending rewards. The caller must * be whitelisted via "allowClaimOnBehalf" function by the RewardsAdmin role manager * @param assets The list of assets to check eligible distributions before claiming rewards * @param user The address to check and claim rewards * @param to The address that will be receiving the rewards * @return rewardsList List of addresses of the reward tokens * @return claimedAmounts List that contains the claimed amount per reward, following same order as "rewardsList" **/ function claimAllRewardsOnBehalf( address[] calldata assets, address user, address to ) external returns (address[] memory rewardsList, uint256[] memory claimedAmounts); /** * @dev Claims all reward for msg.sender, on all the assets of the pool, accumulating the pending rewards * @param assets The list of assets to check eligible distributions before claiming rewards * @return rewardsList List of addresses of the reward tokens * @return claimedAmounts List that contains the claimed amount per reward, following same order as "rewardsList" **/ function claimAllRewardsToSelf( address[] calldata assets ) external returns (address[] memory rewardsList, uint256[] memory claimedAmounts); }
// SPDX-License-Identifier: AGPL-3.0 pragma solidity ^0.8.10; /** * @title IRewardsDistributorV2 * @author Aave * @notice Defines the basic interface for a Rewards Distributor. */ interface IRewardsDistributorV2 { /** * @dev Emitted when the boost config is updated * @param boostConfig The address of the boost config */ event BoostConfigUpdated(address indexed boostConfig); /** * @dev Emitted when the configuration of the rewards of an asset is updated. * @param asset The address of the incentivized asset * @param reward The address of the reward token * @param oldEmission The old emissions per second value of the reward distribution * @param newEmission The new emissions per second value of the reward distribution * @param oldDistributionEnd The old end timestamp of the reward distribution * @param newDistributionEnd The new end timestamp of the reward distribution * @param assetIndex The index of the asset distribution */ event AssetConfigUpdated( address indexed asset, address indexed reward, uint256 oldEmission, uint256 newEmission, uint256 oldDistributionEnd, uint256 newDistributionEnd, uint256 assetIndex ); /** * @dev Emitted when rewards of an asset are accrued on behalf of a user. * @param asset The address of the incentivized asset * @param reward The address of the reward token * @param user The address of the user that rewards are accrued on behalf of * @param assetIndex The index of the asset distribution * @param userIndex The index of the asset distribution on behalf of the user * @param rewardsAccrued The amount of rewards accrued */ event Accrued( address indexed asset, address indexed reward, address indexed user, uint256 assetIndex, uint256 userIndex, uint256 rewardsAccrued ); /** * @dev Sets the end date for the distribution * @param asset The asset to incentivize * @param reward The reward token that incentives the asset * @param newDistributionEnd The end date of the incentivization, in unix time format **/ function setDistributionEnd(address asset, address reward, uint32 newDistributionEnd) external; function setBoostConfig(address boostConfig) external; /** * @dev Sets the emission per second of a set of reward distributions * @param asset The asset is being incentivized * @param rewards List of reward addresses are being distributed * @param newEmissionsPerSecond List of new reward emissions per second */ function setEmissionPerSecond( address asset, address[] calldata rewards, uint88[] calldata newEmissionsPerSecond ) external; /** * @dev Gets the end date for the distribution * @param asset The incentivized asset * @param reward The reward token of the incentivized asset * @return The timestamp with the end of the distribution, in unix time format **/ function getDistributionEnd(address asset, address reward) external view returns (uint256); /** * @dev Returns the index of a user on a reward distribution * @param user Address of the user * @param asset The incentivized asset * @param reward The reward token of the incentivized asset * @return The current user asset index, not including new distributions **/ function getUserAssetIndex( address user, address asset, address reward ) external view returns (uint256); /** * @dev Returns the configuration of the distribution reward for a certain asset * @param asset The incentivized asset * @param reward The reward token of the incentivized asset * @return The index of the asset distribution * @return The emission per second of the reward distribution * @return The timestamp of the last update of the index * @return The timestamp of the distribution end **/ function getRewardsData( address asset, address reward ) external view returns (uint256, uint256, uint256, uint256); /** * @dev Calculates the next value of an specific distribution index, with validations. * @param asset The incentivized asset * @param reward The reward token of the incentivized asset * @return The old index of the asset distribution * @return The new index of the asset distribution **/ function getAssetIndex(address asset, address reward) external view returns (uint256, uint256); /** * @dev Returns the list of available reward token addresses of an incentivized asset * @param asset The incentivized asset * @return List of rewards addresses of the input asset **/ function getRewardsByAsset(address asset) external view returns (address[] memory); /** * @dev Returns the list of available reward addresses * @return List of rewards supported in this contract **/ function getRewardsList() external view returns (address[] memory); /** * @dev Returns the list of all asset addresses * @return List of asset supported in this contract **/ function getAssetsList() external view returns (address[] memory); /** * @dev Returns the accrued rewards balance of a user, not including virtually accrued rewards since last distribution. * @param user The address of the user * @param reward The address of the reward token * @return Unclaimed rewards, not including new distributions **/ function getUserAccruedRewards(address user, address reward) external view returns (uint256); /** * @dev Returns a single rewards balance of a user, including virtually accrued and unrealized claimable rewards. * @param assets List of incentivized assets to check eligible distributions * @param user The address of the user * @param reward The address of the reward token * @return The rewards amount **/ function getUserRewards( address[] calldata assets, address user, address reward ) external view returns (uint256); /** * @dev Returns a list all rewards of a user, including already accrued and unrealized claimable rewards * @param assets List of incentivized assets to check eligible distributions * @param user The address of the user * @return The list of reward addresses * @return The list of unclaimed amount of rewards **/ function getAllUserRewards( address[] calldata assets, address user ) external view returns (address[] memory, uint256[] memory); /** * @dev Returns the decimals of an asset to calculate the distribution delta * @param asset The address to retrieve decimals * @return The decimals of an underlying asset */ function getAssetDecimals(address asset) external view returns (uint8); /** * @dev Returns the address of the emission manager * @return The address of the EmissionManager */ function EMISSION_MANAGER() external view returns (address); /** * @dev Returns the address of the emission manager. * Deprecated: This getter is maintained for compatibility purposes. Use the `EMISSION_MANAGER()` function instead. * @return The address of the EmissionManager */ function getEmissionManager() external view returns (address); }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.10; import {IScaledBalanceToken} from '@aave/core-v3/contracts/interfaces/IScaledBalanceToken.sol'; import {IERC20Detailed} from '@aave/core-v3/contracts/dependencies/openzeppelin/contracts/IERC20Detailed.sol'; import {SafeCast} from '@aave/core-v3/contracts/dependencies/openzeppelin/contracts/SafeCast.sol'; import {IRewardsDistributorV2} from './interfaces/IRewardsDistributorV2.sol'; import {IBoostConfig} from './interfaces/IBoostConfig.sol'; import {RewardsDataTypes} from '@aave/periphery-v3/contracts/rewards/libraries/RewardsDataTypes.sol'; /** * @title RewardsDistributorV2 * @notice Accounting contract to manage multiple staking distributions with multiple rewards * @author Aave **/ abstract contract RewardsDistributorV2 is IRewardsDistributorV2 { using SafeCast for uint256; // Manager of incentives address public immutable EMISSION_MANAGER; // Deprecated: This storage slot is kept for backwards compatibility purposes. address internal _emissionManager; address internal _boostConfig; // Map of rewarded asset addresses and their data (assetAddress => assetData) mapping(address => RewardsDataTypes.AssetData) internal _assets; // Map of reward assets (rewardAddress => enabled) mapping(address => bool) internal _isRewardEnabled; // Rewards list address[] internal _rewardsList; // Assets list address[] internal _assetsList; modifier onlyEmissionManager() { require(msg.sender == EMISSION_MANAGER, 'ONLY_EMISSION_MANAGER'); _; } constructor(address emissionManager) { EMISSION_MANAGER = emissionManager; } /// @inheritdoc IRewardsDistributorV2 function getRewardsData( address asset, address reward ) public view override returns (uint256, uint256, uint256, uint256) { return ( _assets[asset].rewards[reward].index, _assets[asset].rewards[reward].emissionPerSecond, _assets[asset].rewards[reward].lastUpdateTimestamp, _assets[asset].rewards[reward].distributionEnd ); } /// @inheritdoc IRewardsDistributorV2 function getAssetIndex( address asset, address reward ) external view override returns (uint256, uint256) { RewardsDataTypes.RewardData storage rewardData = _assets[asset].rewards[reward]; return _getAssetIndex( rewardData, IScaledBalanceToken(asset).scaledTotalSupply(), 10 ** _assets[asset].decimals ); } /// @inheritdoc IRewardsDistributorV2 function getDistributionEnd( address asset, address reward ) external view override returns (uint256) { return _assets[asset].rewards[reward].distributionEnd; } /// @inheritdoc IRewardsDistributorV2 function getRewardsByAsset(address asset) external view override returns (address[] memory) { uint128 rewardsCount = _assets[asset].availableRewardsCount; address[] memory availableRewards = new address[](rewardsCount); for (uint128 i = 0; i < rewardsCount; i++) { availableRewards[i] = _assets[asset].availableRewards[i]; } return availableRewards; } /// @inheritdoc IRewardsDistributorV2 function getRewardsList() external view override returns (address[] memory) { return _rewardsList; } /// @inheritdoc IRewardsDistributorV2 function getAssetsList() external view override returns (address[] memory) { return _assetsList; } /// @inheritdoc IRewardsDistributorV2 function getUserAssetIndex( address user, address asset, address reward ) public view override returns (uint256) { return _assets[asset].rewards[reward].usersData[user].index; } /// @inheritdoc IRewardsDistributorV2 function getUserAccruedRewards( address user, address reward ) external view override returns (uint256) { uint256 totalAccrued; for (uint256 i = 0; i < _assetsList.length; i++) { totalAccrued += _assets[_assetsList[i]].rewards[reward].usersData[user].accrued; } return totalAccrued; } /// @inheritdoc IRewardsDistributorV2 function getUserRewards( address[] calldata assets, address user, address reward ) external view override returns (uint256) { return _getUserReward(user, reward, _getUserAssetBalances(assets, user)); } /// @inheritdoc IRewardsDistributorV2 function getAllUserRewards( address[] calldata assets, address user ) external view override returns (address[] memory rewardsList, uint256[] memory unclaimedAmounts) { RewardsDataTypes.UserAssetBalance[] memory userAssetBalances = _getUserAssetBalances( assets, user ); rewardsList = new address[](_rewardsList.length); unclaimedAmounts = new uint256[](rewardsList.length); // Add unrealized rewards from user to unclaimedRewards for (uint256 i = 0; i < userAssetBalances.length; i++) { for (uint256 r = 0; r < rewardsList.length; r++) { rewardsList[r] = _rewardsList[r]; unclaimedAmounts[r] += _assets[userAssetBalances[i].asset] .rewards[rewardsList[r]] .usersData[user] .accrued; if (userAssetBalances[i].userBalance == 0) { continue; } unclaimedAmounts[r] += _getPendingRewards(user, rewardsList[r], true, userAssetBalances[i]); } } return (rewardsList, unclaimedAmounts); } /// Get the base reward without boost function getAllUserRewardsWithoutBoost( address[] calldata assets, address user ) external view returns (address[] memory rewardsList, uint256[] memory unclaimedAmounts) { RewardsDataTypes.UserAssetBalance[] memory userAssetBalances = _getUserAssetBalances( assets, user ); rewardsList = new address[](_rewardsList.length); unclaimedAmounts = new uint256[](rewardsList.length); // Add unrealized rewards from user to unclaimedRewards for (uint256 i = 0; i < userAssetBalances.length; i++) { for (uint256 r = 0; r < rewardsList.length; r++) { rewardsList[r] = _rewardsList[r]; unclaimedAmounts[r] += _assets[userAssetBalances[i].asset] .rewards[rewardsList[r]] .usersData[user] .accrued; if (userAssetBalances[i].userBalance == 0) { continue; } unclaimedAmounts[r] += _getPendingRewards(user, rewardsList[r], false, userAssetBalances[i]); } } return (rewardsList, unclaimedAmounts); } /// @inheritdoc IRewardsDistributorV2 function setDistributionEnd( address asset, address reward, uint32 newDistributionEnd ) external override onlyEmissionManager { uint256 oldDistributionEnd = _assets[asset].rewards[reward].distributionEnd; _assets[asset].rewards[reward].distributionEnd = newDistributionEnd; emit AssetConfigUpdated( asset, reward, _assets[asset].rewards[reward].emissionPerSecond, _assets[asset].rewards[reward].emissionPerSecond, oldDistributionEnd, newDistributionEnd, _assets[asset].rewards[reward].index ); } /// @inheritdoc IRewardsDistributorV2 function setEmissionPerSecond( address asset, address[] calldata rewards, uint88[] calldata newEmissionsPerSecond ) external override onlyEmissionManager { require(rewards.length == newEmissionsPerSecond.length, 'INVALID_INPUT'); for (uint256 i = 0; i < rewards.length; i++) { RewardsDataTypes.AssetData storage assetConfig = _assets[asset]; RewardsDataTypes.RewardData storage rewardConfig = _assets[asset].rewards[rewards[i]]; uint256 decimals = assetConfig.decimals; require( decimals != 0 && rewardConfig.lastUpdateTimestamp != 0, 'DISTRIBUTION_DOES_NOT_EXIST' ); (uint256 newIndex, ) = _updateRewardData( rewardConfig, IScaledBalanceToken(asset).scaledTotalSupply(), 10 ** decimals ); uint256 oldEmissionPerSecond = rewardConfig.emissionPerSecond; rewardConfig.emissionPerSecond = newEmissionsPerSecond[i]; emit AssetConfigUpdated( asset, rewards[i], oldEmissionPerSecond, newEmissionsPerSecond[i], rewardConfig.distributionEnd, rewardConfig.distributionEnd, newIndex ); } } function setBoostConfig(address boostConfig) external override onlyEmissionManager { require(boostConfig != address(0), 'CONFIG_MUST_NOT_ZERO'); _boostConfig = boostConfig; emit BoostConfigUpdated(boostConfig); } /** * @dev Configure the _assets for a specific emission * @param rewardsInput The array of each asset configuration **/ function _configureAssets(RewardsDataTypes.RewardsConfigInput[] memory rewardsInput) internal { for (uint256 i = 0; i < rewardsInput.length; i++) { if (_assets[rewardsInput[i].asset].decimals == 0) { //never initialized before, adding to the list of assets _assetsList.push(rewardsInput[i].asset); } uint256 decimals = _assets[rewardsInput[i].asset].decimals = IERC20Detailed( rewardsInput[i].asset ).decimals(); RewardsDataTypes.RewardData storage rewardConfig = _assets[rewardsInput[i].asset].rewards[ rewardsInput[i].reward ]; // Add reward address to asset available rewards if latestUpdateTimestamp is zero if (rewardConfig.lastUpdateTimestamp == 0) { _assets[rewardsInput[i].asset].availableRewards[ _assets[rewardsInput[i].asset].availableRewardsCount ] = rewardsInput[i].reward; _assets[rewardsInput[i].asset].availableRewardsCount++; } // Add reward address to global rewards list if still not enabled if (_isRewardEnabled[rewardsInput[i].reward] == false) { _isRewardEnabled[rewardsInput[i].reward] = true; _rewardsList.push(rewardsInput[i].reward); } // Due emissions is still zero, updates only latestUpdateTimestamp (uint256 newIndex, ) = _updateRewardData( rewardConfig, rewardsInput[i].totalSupply, 10 ** decimals ); // Configure emission and distribution end of the reward per asset uint88 oldEmissionsPerSecond = rewardConfig.emissionPerSecond; uint32 oldDistributionEnd = rewardConfig.distributionEnd; rewardConfig.emissionPerSecond = rewardsInput[i].emissionPerSecond; rewardConfig.distributionEnd = rewardsInput[i].distributionEnd; emit AssetConfigUpdated( rewardsInput[i].asset, rewardsInput[i].reward, oldEmissionsPerSecond, rewardsInput[i].emissionPerSecond, oldDistributionEnd, rewardsInput[i].distributionEnd, newIndex ); } } /** * @dev Updates the state of the distribution for the specified reward * @param rewardData Storage pointer to the distribution reward config * @param totalSupply Current total of underlying assets for this distribution * @param assetUnit One unit of asset (10**decimals) * @return The new distribution index * @return True if the index was updated, false otherwise **/ function _updateRewardData( RewardsDataTypes.RewardData storage rewardData, uint256 totalSupply, uint256 assetUnit ) internal returns (uint256, bool) { (uint256 oldIndex, uint256 newIndex) = _getAssetIndex(rewardData, totalSupply, assetUnit); bool indexUpdated; if (newIndex != oldIndex) { require(newIndex <= type(uint104).max, 'INDEX_OVERFLOW'); indexUpdated = true; //optimization: storing one after another saves one SSTORE rewardData.index = uint104(newIndex); rewardData.lastUpdateTimestamp = block.timestamp.toUint32(); } else { rewardData.lastUpdateTimestamp = block.timestamp.toUint32(); } return (newIndex, indexUpdated); } /** * @dev Updates the state of the distribution for the specific user * @param rewardData Storage pointer to the distribution reward config * @param user The address of the user * @param totalSupply of the asset being rewarded * @param userBalance The user balance of the asset * @param newAssetIndex The new index of the asset distribution * @param assetUnit One unit of asset (10**decimals) * @return The rewards accrued since the last update **/ function _updateUserData( RewardsDataTypes.RewardData storage rewardData, address user, uint256 totalSupply, uint256 userBalance, uint256 newAssetIndex, uint256 assetUnit ) internal returns (uint256, bool) { uint256 userIndex = rewardData.usersData[user].index; uint256 rewardsAccrued; bool dataUpdated; if ((dataUpdated = userIndex != newAssetIndex)) { // already checked for overflow in _updateRewardData rewardData.usersData[user].index = uint104(newAssetIndex); if (userBalance != 0) { rewardsAccrued = _getRewards( user, totalSupply, userBalance, newAssetIndex, userIndex, assetUnit, true ); rewardData.usersData[user].accrued += rewardsAccrued.toUint128(); } } return (rewardsAccrued, dataUpdated); } /** * @dev Iterates and accrues all the rewards for asset of the specific user * @param asset The address of the reference asset of the distribution * @param user The user address * @param userBalance The current user asset balance * @param totalSupply Total supply of the asset **/ function _updateData( address asset, address user, uint256 userBalance, uint256 totalSupply ) internal { uint256 assetUnit; uint256 numAvailableRewards = _assets[asset].availableRewardsCount; unchecked { assetUnit = 10 ** _assets[asset].decimals; } if (numAvailableRewards == 0) { return; } unchecked { for (uint128 r = 0; r < numAvailableRewards; r++) { address reward = _assets[asset].availableRewards[r]; RewardsDataTypes.RewardData storage rewardData = _assets[asset].rewards[reward]; (uint256 newAssetIndex, bool rewardDataUpdated) = _updateRewardData( rewardData, totalSupply, assetUnit ); (uint256 rewardsAccrued, bool userDataUpdated) = _updateUserData( rewardData, user, totalSupply, userBalance, newAssetIndex, assetUnit ); if (rewardDataUpdated || userDataUpdated) { emit Accrued(asset, reward, user, newAssetIndex, newAssetIndex, rewardsAccrued); } } } } /** * @dev Accrues all the rewards of the assets specified in the userAssetBalances list * @param user The address of the user * @param userAssetBalances List of structs with the user balance and total supply of a set of assets **/ function _updateDataMultiple( address user, RewardsDataTypes.UserAssetBalance[] memory userAssetBalances ) internal { for (uint256 i = 0; i < userAssetBalances.length; i++) { _updateData( userAssetBalances[i].asset, user, userAssetBalances[i].userBalance, userAssetBalances[i].totalSupply ); } } /** * @dev Return the accrued unclaimed amount of a reward from a user over a list of distribution * @param user The address of the user * @param reward The address of the reward token * @param userAssetBalances List of structs with the user balance and total supply of a set of assets * @return unclaimedRewards The accrued rewards for the user until the moment **/ function _getUserReward( address user, address reward, RewardsDataTypes.UserAssetBalance[] memory userAssetBalances ) internal view returns (uint256 unclaimedRewards) { // Add unrealized rewards for (uint256 i = 0; i < userAssetBalances.length; i++) { if (userAssetBalances[i].userBalance == 0) { unclaimedRewards += _assets[userAssetBalances[i].asset] .rewards[reward] .usersData[user] .accrued; } else { unclaimedRewards += _getPendingRewards(user, reward, true, userAssetBalances[i]) + _assets[userAssetBalances[i].asset].rewards[reward].usersData[user].accrued; } } return unclaimedRewards; } /** * @dev Calculates the pending (not yet accrued) rewards since the last user action * @param user The address of the user * @param reward The address of the reward token * @param userAssetBalance struct with the user balance and total supply of the incentivized asset * @param isBoost Whether to enable boost * @return The pending rewards for the user since the last user action **/ function _getPendingRewards( address user, address reward, bool isBoost, RewardsDataTypes.UserAssetBalance memory userAssetBalance ) internal view returns (uint256) { RewardsDataTypes.RewardData storage rewardData = _assets[userAssetBalance.asset].rewards[ reward ]; uint256 assetUnit = 10 ** _assets[userAssetBalance.asset].decimals; (, uint256 nextIndex) = _getAssetIndex(rewardData, userAssetBalance.totalSupply, assetUnit); return _getRewards( user, userAssetBalance.totalSupply, userAssetBalance.userBalance, nextIndex, rewardData.usersData[user].index, assetUnit, isBoost ); } /** * @dev Internal function for the calculation of user's rewards on a distribution * @param user The address of the user * @param totalSupply of the asset being rewarded * @param userBalance Balance of the user asset on a distribution * @param reserveIndex Current index of the distribution * @param userIndex Index stored for the user, representation his staking moment * @param assetUnit One unit of asset (10**decimals) * @param isBoost Whether to enable boost * @return The rewards **/ function _getRewards( address user, uint256 totalSupply, uint256 userBalance, uint256 reserveIndex, uint256 userIndex, uint256 assetUnit, bool isBoost ) internal view returns (uint256) { uint256 result = userBalance * (reserveIndex - userIndex); if(_boostConfig!=address(0)){ result += _calcBoosted(user, totalSupply, userBalance, result, isBoost); } assembly { result := div(result, assetUnit) } return result; } /** * @dev Internal function for the calculation of user's boost * @param user The address of the user * @param totalSupply of the asset being rewarded * @param userBalance Balance of the user asset on a distribution * @param baseRewards The base rewards amount * @param isBoost Whether to enable boost **/ function _calcBoosted( address user, uint256 totalSupply, uint256 userBalance, uint256 baseRewards, bool isBoost ) internal view returns (uint256) { if(_boostConfig == address(0) || !isBoost){ return 0; } uint256 boostRate = IBoostConfig(_boostConfig).getBoostRate(user) * totalSupply / userBalance ; uint256 boostMaxRate = IBoostConfig(_boostConfig).getBoostMaxRate(); return (boostRate > boostMaxRate ? boostMaxRate * baseRewards : boostRate * baseRewards) / IBoostConfig(_boostConfig).getBoostBasic(); } /** * @dev Calculates the next value of an specific distribution index, with validations * @param rewardData Storage pointer to the distribution reward config * @param totalSupply of the asset being rewarded * @param assetUnit One unit of asset (10**decimals) * @return The new index. **/ function _getAssetIndex( RewardsDataTypes.RewardData storage rewardData, uint256 totalSupply, uint256 assetUnit ) internal view returns (uint256, uint256) { uint256 oldIndex = rewardData.index; uint256 distributionEnd = rewardData.distributionEnd; uint256 emissionPerSecond = rewardData.emissionPerSecond; uint256 lastUpdateTimestamp = rewardData.lastUpdateTimestamp; if ( emissionPerSecond == 0 || totalSupply == 0 || lastUpdateTimestamp == block.timestamp || lastUpdateTimestamp >= distributionEnd ) { return (oldIndex, oldIndex); } uint256 currentTimestamp = block.timestamp > distributionEnd ? distributionEnd : block.timestamp; uint256 timeDelta = currentTimestamp - lastUpdateTimestamp; uint256 firstTerm = emissionPerSecond * timeDelta * assetUnit; assembly { firstTerm := div(firstTerm, totalSupply) } return (oldIndex, (firstTerm + oldIndex)); } /** * @dev Get user balances and total supply of all the assets specified by the assets parameter * @param assets List of assets to retrieve user balance and total supply * @param user Address of the user * @return userAssetBalances contains a list of structs with user balance and total supply of the given assets */ function _getUserAssetBalances( address[] calldata assets, address user ) internal view virtual returns (RewardsDataTypes.UserAssetBalance[] memory userAssetBalances); /// @inheritdoc IRewardsDistributorV2 function getAssetDecimals(address asset) external view returns (uint8) { return _assets[asset].decimals; } /// @inheritdoc IRewardsDistributorV2 function getEmissionManager() external view returns (address) { return EMISSION_MANAGER; } }
{ "optimizer": { "enabled": true, "runs": 100000 }, "evmVersion": "berlin", "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "metadata": { "useLiteralContent": true }, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"internalType":"address","name":"emissionManager","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"asset","type":"address"},{"indexed":true,"internalType":"address","name":"reward","type":"address"},{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"assetIndex","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"userIndex","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"rewardsAccrued","type":"uint256"}],"name":"Accrued","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"asset","type":"address"},{"indexed":true,"internalType":"address","name":"reward","type":"address"},{"indexed":false,"internalType":"uint256","name":"oldEmission","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newEmission","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"oldDistributionEnd","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newDistributionEnd","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"assetIndex","type":"uint256"}],"name":"AssetConfigUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"boostConfig","type":"address"}],"name":"BoostConfigUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"claimer","type":"address"}],"name":"ClaimerSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"reward","type":"address"},{"indexed":true,"internalType":"address","name":"rewardOracle","type":"address"}],"name":"RewardOracleUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"reward","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"address","name":"claimer","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"RewardsClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"reward","type":"address"},{"indexed":true,"internalType":"address","name":"transferStrategy","type":"address"}],"name":"TransferStrategyInstalled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"updater","type":"address"},{"indexed":true,"internalType":"bool","name":"status","type":"bool"}],"name":"UpdaterSet","type":"event"},{"inputs":[],"name":"EMISSION_MANAGER","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"REVISION","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"assets","type":"address[]"},{"internalType":"address","name":"to","type":"address"}],"name":"claimAllRewards","outputs":[{"internalType":"address[]","name":"rewardsList","type":"address[]"},{"internalType":"uint256[]","name":"claimedAmounts","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"assets","type":"address[]"},{"internalType":"address","name":"user","type":"address"},{"internalType":"address","name":"to","type":"address"}],"name":"claimAllRewardsOnBehalf","outputs":[{"internalType":"address[]","name":"rewardsList","type":"address[]"},{"internalType":"uint256[]","name":"claimedAmounts","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"assets","type":"address[]"}],"name":"claimAllRewardsToSelf","outputs":[{"internalType":"address[]","name":"rewardsList","type":"address[]"},{"internalType":"uint256[]","name":"claimedAmounts","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"assets","type":"address[]"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"address","name":"reward","type":"address"}],"name":"claimRewards","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"assets","type":"address[]"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"user","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"address","name":"reward","type":"address"}],"name":"claimRewardsOnBehalf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"assets","type":"address[]"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"reward","type":"address"}],"name":"claimRewardsToSelf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint88","name":"emissionPerSecond","type":"uint88"},{"internalType":"uint256","name":"totalSupply","type":"uint256"},{"internalType":"uint32","name":"distributionEnd","type":"uint32"},{"internalType":"address","name":"asset","type":"address"},{"internalType":"address","name":"reward","type":"address"},{"internalType":"contract ITransferStrategyBase","name":"transferStrategy","type":"address"},{"internalType":"contract IEACAggregatorProxy","name":"rewardOracle","type":"address"}],"internalType":"struct RewardsDataTypes.RewardsConfigInput[]","name":"config","type":"tuple[]"}],"name":"configureAssets","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"assets","type":"address[]"},{"internalType":"address","name":"user","type":"address"}],"name":"getAllUserRewards","outputs":[{"internalType":"address[]","name":"rewardsList","type":"address[]"},{"internalType":"uint256[]","name":"unclaimedAmounts","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"assets","type":"address[]"},{"internalType":"address","name":"user","type":"address"}],"name":"getAllUserRewardsWithoutBoost","outputs":[{"internalType":"address[]","name":"rewardsList","type":"address[]"},{"internalType":"uint256[]","name":"unclaimedAmounts","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"}],"name":"getAssetDecimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"address","name":"reward","type":"address"}],"name":"getAssetIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getAssetsList","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getClaimer","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"address","name":"reward","type":"address"}],"name":"getDistributionEnd","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getEmissionManager","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"reward","type":"address"}],"name":"getRewardOracle","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"}],"name":"getRewardsByAsset","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"address","name":"reward","type":"address"}],"name":"getRewardsData","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getRewardsList","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"reward","type":"address"}],"name":"getTransferStrategy","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"address","name":"reward","type":"address"}],"name":"getUserAccruedRewards","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"address","name":"asset","type":"address"},{"internalType":"address","name":"reward","type":"address"}],"name":"getUserAssetIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"assets","type":"address[]"},{"internalType":"address","name":"user","type":"address"},{"internalType":"address","name":"reward","type":"address"}],"name":"getUserRewards","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"totalSupply","type":"uint256"},{"internalType":"uint256","name":"userBalance","type":"uint256"}],"name":"handleAction","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"boostConfig","type":"address"}],"name":"setBoostConfig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"address","name":"caller","type":"address"}],"name":"setClaimer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"address","name":"reward","type":"address"},{"internalType":"uint32","name":"newDistributionEnd","type":"uint32"}],"name":"setDistributionEnd","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"address[]","name":"rewards","type":"address[]"},{"internalType":"uint88[]","name":"newEmissionsPerSecond","type":"uint88[]"}],"name":"setEmissionPerSecond","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"reward","type":"address"},{"internalType":"contract IEACAggregatorProxy","name":"rewardOracle","type":"address"}],"name":"setRewardOracle","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"reward","type":"address"},{"internalType":"contract ITransferStrategyBase","name":"transferStrategy","type":"address"}],"name":"setTransferStrategy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"updater","type":"address"},{"internalType":"bool","name":"status","type":"bool"}],"name":"setUpdater","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"updateAction","outputs":[],"stateMutability":"nonpayable","type":"function"}]
Contract Creation Code
60a060405260006006553480156200001657600080fd5b50604051620055523803806200555283398101604081905262000039916200004b565b6001600160a01b03166080526200007d565b6000602082840312156200005e57600080fd5b81516001600160a01b03811681146200007657600080fd5b9392505050565b60805161547a620000d860003960008181610595015281816106c9015281816107440152818161100b015281816116810152818161180c01528181611f20015281816120d60152818161217f015261229a015261547a6000f3fe608060405234801561001057600080fd5b50600436106102415760003560e01c8063886fe70b11610145578063bb492bf5116100bd578063cbcbb5071161008c578063e15ac62311610071578063e15ac623146106f3578063f5cf673b14610706578063f996868b1461071957600080fd5b8063cbcbb507146106c4578063dde43cba146106eb57600080fd5b8063bb492bf514610678578063bf90f63a1461068b578063c4d66de81461069e578063c5a7b538146106b157600080fd5b80639efd6f7211610114578063a73b870c116100f9578063a73b870c14610655578063b022418c1461065d578063b45ac1a91461067057600080fd5b80639efd6f72146105df5780639ff55db91461064257600080fd5b8063886fe70b1461056b57806392074b081461059357806394a9c744146105b9578063955c2ad7146105cc57600080fd5b8063533f542a116101d85780635f130b24116101a757806370674ab91161018c57806370674ab91461044357806374d945ec146104565780637eff4ba81461048f57600080fd5b80635f130b24146103ea5780636657732f1461042357600080fd5b8063533f542a1461034c5780635453ba10146103b157806357b89883146103c45780635c6588f9146103d757600080fd5b806331873e2e1161021457806331873e2e146102f257806333028b99146103055780633eb1e0a7146103185780634c0369c31461032b57600080fd5b80631a153391146102465780631b839c771461025b578063236300dc146102815780632a17bf6014610294575b600080fd5b6102596102543660046149ae565b61072c565b005b61026e6102693660046149e7565b61084f565b6040519081526020015b60405180910390f35b61026e61028f366004614a5a565b6108af565b6102cd6102a2366004614ace565b73ffffffffffffffffffffffffffffffffffffffff9081166000908152603d60205260409020541690565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610278565b610259610300366004614af2565b610949565b61026e610313366004614b27565b61095a565b610259610326366004614ace565b610b06565b61033e610339366004614bac565b610c9b565b604051610278929190614c54565b61026e61035a366004614cab565b73ffffffffffffffffffffffffffffffffffffffff80831660009081526002602090815260408083208585168452825280832093871683526001909301905220546cffffffffffffffffffffffffff169392505050565b6102596103bf3660046149e7565b610ff3565b61026e6103d2366004614ceb565b61109c565b61033e6103e5366004614bac565b6110ad565b6102cd6103f8366004614ace565b73ffffffffffffffffffffffffffffffffffffffff9081166000908152603c60205260409020541690565b610436610431366004614ace565b6113ed565b6040516102789190614d4a565b61026e610451366004614d5d565b611542565b6102cd610464366004614ace565b73ffffffffffffffffffffffffffffffffffffffff9081166000908152603a60205260409020541690565b61054b61049d3660046149e7565b73ffffffffffffffffffffffffffffffffffffffff91821660009081526002602090815260408083209390941682529190915220546cffffffffffffffffffffffffff8116916affffffffffffffffffffff6d01000000000000000000000000008304169163ffffffff780100000000000000000000000000000000000000000000000082048116927c01000000000000000000000000000000000000000000000000000000009092041690565b604080519485526020850193909352918301526060820152608001610278565b61057e6105793660046149e7565b611559565b60408051928352602083019190915201610278565b7f00000000000000000000000000000000000000000000000000000000000000006102cd565b6102596105c7366004614ace565b611669565b6102596105da366004614e95565b6117f4565b6106306105ed366004614ace565b73ffffffffffffffffffffffffffffffffffffffff1660009081526002602081905260409091200154700100000000000000000000000000000000900460ff1690565b60405160ff9091168152602001610278565b61033e610650366004614d5d565b6119f4565b610436611ba3565b61026e61066b3660046149e7565b611c12565b610436611ccb565b61033e610686366004614bac565b611d38565b61033e610699366004614fc3565b611dd1565b6102596106ac366004614ace565b611dec565b6102596106bf366004615005565b611f08565b6102cd7f000000000000000000000000000000000000000000000000000000000000000081565b61026e600281565b6102596107013660046149e7565b6120be565b6102596107143660046149e7565b612167565b61025961072736600461504c565b612282565b3373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016146107d0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4f4e4c595f454d495353494f4e5f4d414e41474552000000000000000000000060448201526064015b60405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff82166000818152603b602052604080822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001685151590811790915590519092917f9e0f3fc17a5f47bd46f3cab56d4d8a9ea9f32ad817657c3ea8e5dff28da3e3e791a35050565b73ffffffffffffffffffffffffffffffffffffffff8281166000908152600260209081526040808320938516835292905220547c0100000000000000000000000000000000000000000000000000000000900463ffffffff165b92915050565b600073ffffffffffffffffffffffffffffffffffffffff831661092e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f494e56414c49445f544f5f41444452455353000000000000000000000000000060448201526064016107c7565b61093d868686333388886126f6565b90505b95945050505050565b6109553384838561298a565b505050565b73ffffffffffffffffffffffffffffffffffffffff8084166000908152603a60205260408120549091339186911682146109f0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f434c41494d45525f554e415554484f52495a454400000000000000000000000060448201526064016107c7565b73ffffffffffffffffffffffffffffffffffffffff8616610a6d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f494e56414c49445f555345525f4144445245535300000000000000000000000060448201526064016107c7565b73ffffffffffffffffffffffffffffffffffffffff8516610aea576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f494e56414c49445f544f5f41444452455353000000000000000000000000000060448201526064016107c7565b610af9898989338a8a8a6126f6565b9998505050505050505050565b336000908152603b602052604090205460ff16610b7f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f555044415445525f554e415554484f52495a454400000000000000000000000060448201526064016107c7565b60005b600554811015610c975760008060058381548110610ba257610ba26150cf565b6000918252602090912001546040517f0afbcdc900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff868116600483015290911690630afbcdc9906024016040805180830381865afa158015610c1a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c3e91906150fe565b91509150610c8260058481548110610c5857610c586150cf565b60009182526020909120015473ffffffffffffffffffffffffffffffffffffffff1685848461298a565b50508080610c8f90615151565b915050610b82565b5050565b6060806000610cab868686612b40565b60045490915067ffffffffffffffff811115610cc957610cc9614dba565b604051908082528060200260200182016040528015610cf2578160200160208202803683370190505b509250825167ffffffffffffffff811115610d0f57610d0f614dba565b604051908082528060200260200182016040528015610d38578160200160208202803683370190505b50915060005b8151811015610fe85760005b8451811015610fd55760048181548110610d6657610d666150cf565b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16858281518110610da357610da36150cf565b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff168152505060026000848481518110610df357610df36150cf565b60200260200101516000015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000016000868381518110610e5057610e506150cf565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060010160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600001600d9054906101000a90046fffffffffffffffffffffffffffffffff166fffffffffffffffffffffffffffffffff16848281518110610f1657610f166150cf565b60200260200101818151610f2a919061518a565b9052508251839083908110610f4157610f416150cf565b60200260200101516020015160001415610f5a57610fc3565b610f9986868381518110610f7057610f706150cf565b60200260200101516001868681518110610f8c57610f8c6150cf565b6020026020010151612d3e565b848281518110610fab57610fab6150cf565b60200260200101818151610fbf919061518a565b9052505b80610fcd81615151565b915050610d4a565b5080610fe081615151565b915050610d3e565b50505b935093915050565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614611092576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4f4e4c595f454d495353494f4e5f4d414e41474552000000000000000000000060448201526064016107c7565b610c978282612e20565b6000610940858585333333886126f6565b60608060006110bd868686612b40565b60045490915067ffffffffffffffff8111156110db576110db614dba565b604051908082528060200260200182016040528015611104578160200160208202803683370190505b509250825167ffffffffffffffff81111561112157611121614dba565b60405190808252806020026020018201604052801561114a578160200160208202803683370190505b50915060005b8151811015610fe85760005b84518110156113da5760048181548110611178576111786150cf565b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff168582815181106111b5576111b56150cf565b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff168152505060026000848481518110611205576112056150cf565b60200260200101516000015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000016000868381518110611262576112626150cf565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060010160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600001600d9054906101000a90046fffffffffffffffffffffffffffffffff166fffffffffffffffffffffffffffffffff16848281518110611328576113286150cf565b6020026020010181815161133c919061518a565b9052508251839083908110611353576113536150cf565b6020026020010151602001516000141561136c576113c8565b61139e86868381518110611382576113826150cf565b60200260200101516000868681518110610f8c57610f8c6150cf565b8482815181106113b0576113b06150cf565b602002602001018181516113c4919061518a565b9052505b806113d281615151565b91505061115c565b50806113e581615151565b915050611150565b73ffffffffffffffffffffffffffffffffffffffff81166000908152600260208190526040822001546060916fffffffffffffffffffffffffffffffff909116908167ffffffffffffffff81111561144757611447614dba565b604051908082528060200260200182016040528015611470578160200160208202803683370190505b50905060005b826fffffffffffffffffffffffffffffffff16816fffffffffffffffffffffffffffffffff16101561153a5773ffffffffffffffffffffffffffffffffffffffff80861660009081526002602090815260408083206fffffffffffffffffffffffffffffffff861680855260019091019092529091205484519216918491908110611503576115036150cf565b73ffffffffffffffffffffffffffffffffffffffff9092166020928302919091019091015280611532816151a2565b915050611476565b509392505050565b60006109408383611554888888612b40565b612f74565b73ffffffffffffffffffffffffffffffffffffffff8083166000818152600260209081526040808320948616835293815283822084517fb1bf962d000000000000000000000000000000000000000000000000000000008152945192948594919361165c9385939263b1bf962d92600480830193928290030181865afa1580156115e7573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061160b91906151d2565b73ffffffffffffffffffffffffffffffffffffffff88166000908152600260208190526040909120015461165790700100000000000000000000000000000000900460ff16600a61530b565b613113565b92509250505b9250929050565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614611708576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4f4e4c595f454d495353494f4e5f4d414e41474552000000000000000000000060448201526064016107c7565b73ffffffffffffffffffffffffffffffffffffffff8116611785576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f434f4e4649475f4d5553545f4e4f545f5a45524f00000000000000000000000060448201526064016107c7565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83169081179091556040517f5f5230011e18591ebb16f1c3d1368e67237b09cac3273a2d03441ada1841110b90600090a250565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614611893576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4f4e4c595f454d495353494f4e5f4d414e41474552000000000000000000000060448201526064016107c7565b60005b81518110156119e7578181815181106118b1576118b16150cf565b60200260200101516060015173ffffffffffffffffffffffffffffffffffffffff1663b1bf962d6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611907573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061192b91906151d2565b82828151811061193d5761193d6150cf565b60200260200101516020018181525050611991828281518110611962576119626150cf565b602002602001015160800151838381518110611980576119806150cf565b602002602001015160a0015161321f565b6119d58282815181106119a6576119a66150cf565b6020026020010151608001518383815181106119c4576119c46150cf565b602002602001015160c00151612e20565b806119df81615151565b915050611896565b506119f181613385565b50565b73ffffffffffffffffffffffffffffffffffffffff8083166000908152603a6020526040902054606091829133918691168214611a8d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f434c41494d45525f554e415554484f52495a454400000000000000000000000060448201526064016107c7565b73ffffffffffffffffffffffffffffffffffffffff8616611b0a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f494e56414c49445f555345525f4144445245535300000000000000000000000060448201526064016107c7565b73ffffffffffffffffffffffffffffffffffffffff8516611b87576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f494e56414c49445f544f5f41444452455353000000000000000000000000000060448201526064016107c7565b611b948888338989613c26565b93509350505094509492505050565b60606005805480602002602001604051908101604052809291908181526020018280548015611c0857602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff168152600190910190602001808311611bdd575b5050505050905090565b60008060005b60055481101561153a576002600060058381548110611c3957611c396150cf565b60009182526020808320919091015473ffffffffffffffffffffffffffffffffffffffff908116845283820194909452604092830182208885168352815282822093891682526001909301909252902054611cb7906d010000000000000000000000000090046fffffffffffffffffffffffffffffffff168361518a565b915080611cc381615151565b915050611c18565b60606004805480602002602001604051908101604052809291908181526020018280548015611c085760200282019190600052602060002090815473ffffffffffffffffffffffffffffffffffffffff168152600190910190602001808311611bdd575050505050905090565b60608073ffffffffffffffffffffffffffffffffffffffff8316611db8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f494e56414c49445f544f5f41444452455353000000000000000000000000000060448201526064016107c7565b611dc58585333387613c26565b91509150935093915050565b606080611de18484333333613c26565b915091509250929050565b60075460029060ff1680611dff5750303b155b80611e0b575060065481115b611e97576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f436f6e747261637420696e7374616e63652068617320616c726561647920626560448201527f656e20696e697469616c697a656400000000000000000000000000000000000060648201526084016107c7565b60075460ff16158015611ed557600780547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905560068290555b801561095557600780547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169055505050565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614611fa7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4f4e4c595f454d495353494f4e5f4d414e41474552000000000000000000000060448201526064016107c7565b73ffffffffffffffffffffffffffffffffffffffff83811660008181526002602090815260408083209487168084529482529182902080547bffffffffffffffffffffffffffffffffffffffffffffffffffffffff81167c010000000000000000000000000000000000000000000000000000000063ffffffff8981168281029384179586905587516d01000000000000000000000000009096046affffffffffffffffffffff16808752968601969096529083041694830185905260608301939093526cffffffffffffffffffffffffff9081169216919091176080820152909291907fac1777479f07f3e7c34da8402139d54027a6a260caaae168bdee825ca5580dc59060a00160405180910390a350505050565b3373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000161461215d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4f4e4c595f454d495353494f4e5f4d414e41474552000000000000000000000060448201526064016107c7565b610c97828261321f565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614612206576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4f4e4c595f454d495353494f4e5f4d414e41474552000000000000000000000060448201526064016107c7565b73ffffffffffffffffffffffffffffffffffffffff8281166000818152603a602052604080822080547fffffffffffffffffffffffff0000000000000000000000000000000000000000169486169485179055517f4925eafc82d0c4d67889898eeed64b18488ab19811e61620f387026dec126a289190a35050565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614612321576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4f4e4c595f454d495353494f4e5f4d414e41474552000000000000000000000060448201526064016107c7565b82811461238a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600d60248201527f494e56414c49445f494e5055540000000000000000000000000000000000000060448201526064016107c7565b60005b838110156126ee5773ffffffffffffffffffffffffffffffffffffffff861660009081526002602052604081209081818888868181106123cf576123cf6150cf565b90506020020160208101906123e49190614ace565b73ffffffffffffffffffffffffffffffffffffffff16815260208101919091526040016000206002830154909150700100000000000000000000000000000000900460ff16801580159061245a575081547801000000000000000000000000000000000000000000000000900463ffffffff1615155b6124c0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f444953545249425554494f4e5f444f45535f4e4f545f4558495354000000000060448201526064016107c7565b6000612545838b73ffffffffffffffffffffffffffffffffffffffff1663b1bf962d6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612511573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061253591906151d2565b61254085600a61531a565b61410e565b5083549091506d010000000000000000000000000090046affffffffffffffffffffff1687878781811061257b5761257b6150cf565b90506020020160208101906125909190615326565b84546affffffffffffffffffffff919091166d0100000000000000000000000000027fffffffffffffffff0000000000000000000000ffffffffffffffffffffffffff9091161784558989878181106125eb576125eb6150cf565b90506020020160208101906126009190614ace565b73ffffffffffffffffffffffffffffffffffffffff168b73ffffffffffffffffffffffffffffffffffffffff167fac1777479f07f3e7c34da8402139d54027a6a260caaae168bdee825ca5580dc5838b8b8b818110612661576126616150cf565b90506020020160208101906126769190615326565b8854604080519384526affffffffffffffffffffff90921660208401527c0100000000000000000000000000000000000000000000000000000000900463ffffffff1690820181905260608201526080810186905260a00160405180910390a3505050505080806126e690615151565b91505061238d565b505050505050565b6000856127055750600061297f565b600061271b856127168b8b89612b40565b61429c565b60005b888110156129055760008a8a8381811061273a5761273a6150cf565b905060200201602081019061274f9190614ace565b73ffffffffffffffffffffffffffffffffffffffff81811660009081526002602090815260408083208a851684528252808320938c1683526001909301905220549091506127c0906d010000000000000000000000000090046fffffffffffffffffffffffffffffffff168461518a565b92508883116128345773ffffffffffffffffffffffffffffffffffffffff808216600090815260026020908152604080832089851684528252808320938b168352600190930190522080547fffffff00000000000000000000000000000000ffffffffffffffffffffffffff1690556128f2565b60006128408a85615341565b905061284c8185615341565b93506128578161431d565b73ffffffffffffffffffffffffffffffffffffffff92831660009081526002602090815260408083208a871684528252808320958c168352600190950190529290922080546fffffffffffffffffffffffffffffffff939093166d0100000000000000000000000000027fffffff00000000000000000000000000000000ffffffffffffffffffffffffff9093169290921790915550612905565b50806128fd81615151565b91505061271e565b508061291557600091505061297f565b6129208484836143c3565b6040805173ffffffffffffffffffffffffffffffffffffffff8881168252602082018490528087169286821692918916917fc052130bc4ef84580db505783484b067ea8b71b3bca78a7e12db7aea8658f004910160405180910390a490505b979650505050505050565b73ffffffffffffffffffffffffffffffffffffffff84166000908152600260208190526040909120015460ff700100000000000000000000000000000000820416600a0a906fffffffffffffffffffffffffffffffff16806129ed575050612b3a565b60005b81816fffffffffffffffffffffffffffffffff161015612b365773ffffffffffffffffffffffffffffffffffffffff80881660009081526002602090815260408083206fffffffffffffffffffffffffffffffff86168452600181018352818420549094168084529390915281209080612a6b83898961410e565b91509150600080612a80858d8c8e888e6144ef565b915091508280612a8d5750805b15612b24578b73ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff168e73ffffffffffffffffffffffffffffffffffffffff167f3303facd24627943a92e9dc87cfbb34b15c49b726eec3ad3487c16be9ab8efe8878887604051612b1b939291909283526020830191909152604082015260600190565b60405180910390a45b5050600190940193506129f092505050565b5050505b50505050565b60608267ffffffffffffffff811115612b5b57612b5b614dba565b604051908082528060200260200182016040528015612bc657816020015b612bb36040518060600160405280600073ffffffffffffffffffffffffffffffffffffffff16815260200160008152602001600081525090565b815260200190600190039081612b795790505b50905060005b8381101561153a57848482818110612be657612be66150cf565b9050602002016020810190612bfb9190614ace565b828281518110612c0d57612c0d6150cf565b602090810291909101015173ffffffffffffffffffffffffffffffffffffffff9091169052848482818110612c4457612c446150cf565b9050602002016020810190612c599190614ace565b6040517f0afbcdc900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85811660048301529190911690630afbcdc9906024016040805180830381865afa158015612cc6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612cea91906150fe565b838381518110612cfc57612cfc6150cf565b6020026020010151602001848481518110612d1957612d196150cf565b6020908102919091010151604001919091525280612d3681615151565b915050612bcc565b805173ffffffffffffffffffffffffffffffffffffffff9081166000908152600260208181526040808420888616855282528084208651909516845290829052822001549091908290612da990700100000000000000000000000000000000900460ff16600a61530b565b90506000612dbc83866040015184613113565b6040808801516020808a015173ffffffffffffffffffffffffffffffffffffffff8e16600090815260018a0190925292902054929450612e1493508b9290919085906cffffffffffffffffffffffffff16878c614653565b98975050505050505050565b60008173ffffffffffffffffffffffffffffffffffffffff166350d25bcd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612e6d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e9191906151d2565b13612ef8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f4f5241434c455f4d5553545f52455455524e5f5052494345000000000000000060448201526064016107c7565b73ffffffffffffffffffffffffffffffffffffffff8281166000818152603d602052604080822080547fffffffffffffffffffffffff0000000000000000000000000000000000000000169486169485179055517f1a1cd5483e52e60b9ff7f3b9d1db3bbd9e9d21c6324ad3a8c79dba9b75e62f4d9190a35050565b6000805b825181101561153a57828181518110612f9357612f936150cf565b602002602001015160200151600014156130425760026000848381518110612fbd57612fbd6150cf565b6020908102919091018101515173ffffffffffffffffffffffffffffffffffffffff908116835282820193909352604091820160009081208885168252825282812093891681526001909301905290205461303b906d010000000000000000000000000090046fffffffffffffffffffffffffffffffff168361518a565b9150613101565b60026000848381518110613058576130586150cf565b6020908102919091018101515173ffffffffffffffffffffffffffffffffffffffff908116835282820193909352604091820160009081208885168252825282812093891681526001938401909152205484516d01000000000000000000000000009091046fffffffffffffffffffffffffffffffff16916130ea9188918891889087908110610f8c57610f8c6150cf565b6130f4919061518a565b6130fe908361518a565b91505b8061310b81615151565b915050612f78565b825460009081906cffffffffffffffffffffffffff81169063ffffffff7c010000000000000000000000000000000000000000000000000000000082048116916affffffffffffffffffffff6d01000000000000000000000000008204169178010000000000000000000000000000000000000000000000009091041681158061319b575087155b806131a557504281145b806131b05750828110155b156131c45783849550955050505050610feb565b60008342116131d357426131d5565b835b905060006131e38383615341565b90506000896131f28387615358565b6131fc9190615358565b8b900490508661320c818361518a565b9850985050505050505050935093915050565b73ffffffffffffffffffffffffffffffffffffffff811661329c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f53545241544547595f43414e5f4e4f545f42455f5a45524f000000000000000060448201526064016107c7565b6001813b151514613309576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f53545241544547595f4d5553545f42455f434f4e54524143540000000000000060448201526064016107c7565b73ffffffffffffffffffffffffffffffffffffffff8281166000818152603c602052604080822080547fffffffffffffffffffffffff0000000000000000000000000000000000000000169486169485179055517f8ca1d928f1d72493a6b78c4f74aabde976bc37ffe2570f2a1ce5a8abd3dde0aa9190a35050565b60005b8151811015610c9757600260008383815181106133a7576133a76150cf565b6020908102919091018101516060015173ffffffffffffffffffffffffffffffffffffffff16825281019190915260400160002060020154700100000000000000000000000000000000900460ff1661347357600582828151811061340e5761340e6150cf565b6020908102919091018101516060015182546001810184556000938452919092200180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff9092169190911790555b6000828281518110613487576134876150cf565b60200260200101516060015173ffffffffffffffffffffffffffffffffffffffff1663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa1580156134dd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135019190615395565b60026000858581518110613517576135176150cf565b60200260200101516060015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060020160106101000a81548160ff021916908360ff160217905560ff169050600060026000858581518110613594576135946150cf565b60200260200101516060015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160008585815181106135f1576135f16150cf565b6020908102919091018101516080015173ffffffffffffffffffffffffffffffffffffffff16825281019190915260400160002080549091507801000000000000000000000000000000000000000000000000900463ffffffff1661386057838381518110613662576136626150cf565b60200260200101516080015160026000868681518110613684576136846150cf565b60200260200101516060015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206001016000600260008888815181106136e5576136e56150cf565b60200260200101516060015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060020160009054906101000a90046fffffffffffffffffffffffffffffffff166fffffffffffffffffffffffffffffffff166fffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550600260008585815181106137d1576137d16150cf565b6020908102919091018101516060015173ffffffffffffffffffffffffffffffffffffffff168252810191909152604001600090812060020180546fffffffffffffffffffffffffffffffff1691613828836151a2565b91906101000a8154816fffffffffffffffffffffffffffffffff02191690836fffffffffffffffffffffffffffffffff160217905550505b60036000858581518110613876576138766150cf565b6020908102919091018101516080015173ffffffffffffffffffffffffffffffffffffffff1682528101919091526040016000205460ff166139a0576001600360008686815181106138ca576138ca6150cf565b60200260200101516080015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550600484848151811061393b5761393b6150cf565b6020908102919091018101516080015182546001810184556000938452919092200180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff9092169190911790555b60006139d1828686815181106139b8576139b86150cf565b60200260200101516020015185600a612540919061531a565b50825486519192506d010000000000000000000000000081046affffffffffffffffffffff16917c010000000000000000000000000000000000000000000000000000000090910463ffffffff1690879087908110613a3257613a326150cf565b60209081029190910101515184546affffffffffffffffffffff9091166d0100000000000000000000000000027fffffffffffffffff0000000000000000000000ffffffffffffffffffffffffff9091161784558651879087908110613a9a57613a9a6150cf565b602090810291909101015160400151845463ffffffff9091167c0100000000000000000000000000000000000000000000000000000000027bffffffffffffffffffffffffffffffffffffffffffffffffffffffff9091161784558651879087908110613b0957613b096150cf565b60200260200101516080015173ffffffffffffffffffffffffffffffffffffffff16878781518110613b3d57613b3d6150cf565b60200260200101516060015173ffffffffffffffffffffffffffffffffffffffff167fac1777479f07f3e7c34da8402139d54027a6a260caaae168bdee825ca5580dc5848a8a81518110613b9357613b936150cf565b602002602001015160000151858c8c81518110613bb257613bb26150cf565b60200260200101516040015189604051613c069594939291906affffffffffffffffffffff958616815293909416602084015263ffffffff9182166040840152166060820152608081019190915260a00190565b60405180910390a350505050508080613c1e90615151565b915050613388565b60045460609081908067ffffffffffffffff811115613c4757613c47614dba565b604051908082528060200260200182016040528015613c70578160200160208202803683370190505b5092508067ffffffffffffffff811115613c8c57613c8c614dba565b604051908082528060200260200182016040528015613cb5578160200160208202803683370190505b509150613cc7856127168a8a89612b40565b60005b87811015613fd6576000898983818110613ce657613ce66150cf565b9050602002016020810190613cfb9190614ace565b905060005b83811015613fc157600073ffffffffffffffffffffffffffffffffffffffff16868281518110613d3257613d326150cf565b602002602001015173ffffffffffffffffffffffffffffffffffffffff161415613de15760048181548110613d6957613d696150cf565b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16868281518110613da657613da66150cf565b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff16815250505b73ffffffffffffffffffffffffffffffffffffffff8216600090815260026020526040812087518290899085908110613e1c57613e1c6150cf565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff90811683528282019390935260409182016000908120938d168152600190930190529020546d010000000000000000000000000090046fffffffffffffffffffffffffffffffff1690508015613fae5780868381518110613ea057613ea06150cf565b60200260200101818151613eb4919061518a565b90525073ffffffffffffffffffffffffffffffffffffffff83166000908152600260205260408120885182908a9086908110613ef257613ef26150cf565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060010160008b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600001600d6101000a8154816fffffffffffffffffffffffffffffffff02191690836fffffffffffffffffffffffffffffffff1602179055505b5080613fb981615151565b915050613d00565b50508080613fce90615151565b915050613cca565b5060005b818110156141025761401f85858381518110613ff857613ff86150cf565b6020026020010151858481518110614012576140126150cf565b60200260200101516143c3565b8473ffffffffffffffffffffffffffffffffffffffff16848281518110614048576140486150cf565b602002602001015173ffffffffffffffffffffffffffffffffffffffff168773ffffffffffffffffffffffffffffffffffffffff167fc052130bc4ef84580db505783484b067ea8b71b3bca78a7e12db7aea8658f0048a8786815181106140b1576140b16150cf565b60200260200101516040516140e892919073ffffffffffffffffffffffffffffffffffffffff929092168252602082015260400190565b60405180910390a4806140fa81615151565b915050613fda565b50509550959350505050565b60008060008061411f878787613113565b915091506000828214614238576cffffffffffffffffffffffffff8211156141a3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f494e4445585f4f564552464c4f5700000000000000000000000000000000000060448201526064016107c7565b5086547fffffffffffffffffffffffffffffffffffffff00000000000000000000000000166cffffffffffffffffffffffffff821617875560016141e6426146b4565b885463ffffffff919091167801000000000000000000000000000000000000000000000000027fffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffffff90911617885561428f565b614241426146b4565b885463ffffffff919091167801000000000000000000000000000000000000000000000000027fffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffffff9091161788555b9097909650945050505050565b60005b81518110156109555761430b8282815181106142bd576142bd6150cf565b602002602001015160000151848484815181106142dc576142dc6150cf565b6020026020010151602001518585815181106142fa576142fa6150cf565b60200260200101516040015161298a565b8061431581615151565b91505061429f565b60006fffffffffffffffffffffffffffffffff8211156143bf576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602760248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203160448201527f323820626974730000000000000000000000000000000000000000000000000060648201526084016107c7565b5090565b73ffffffffffffffffffffffffffffffffffffffff8281166000818152603c60205260408082205490517f16beb9820000000000000000000000000000000000000000000000000000000081528785166004820152602481019390935260448301859052909216919082906316beb982906064016020604051808303816000875af1158015614456573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061447a91906153b8565b90506001811515146144e8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f5452414e534645525f4552524f5200000000000000000000000000000000000060448201526064016107c7565b5050505050565b73ffffffffffffffffffffffffffffffffffffffff8516600090815260018701602052604081205481906cffffffffffffffffffffffffff16818582148015906146435773ffffffffffffffffffffffffffffffffffffffff8a16600090815260018c016020526040902080547fffffffffffffffffffffffffffffffffffffff00000000000000000000000000166cffffffffffffffffffffffffff89161790558715614643576145a78a8a8a8a878b6001614653565b91506145b28261431d565b73ffffffffffffffffffffffffffffffffffffffff8b16600090815260018d01602052604090208054600d9061460c9084906d010000000000000000000000000090046fffffffffffffffffffffffffffffffff166153d5565b92506101000a8154816fffffffffffffffffffffffffffffffff02191690836fffffffffffffffffffffffffffffffff1602179055505b909a909950975050505050505050565b6000806146608587615341565b61466a9088615358565b60015490915073ffffffffffffffffffffffffffffffffffffffff16156146a557614698898989848761474a565b6146a2908261518a565b90505b92909204979650505050505050565b600063ffffffff8211156143bf576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203360448201527f322062697473000000000000000000000000000000000000000000000000000060648201526084016107c7565b60015460009073ffffffffffffffffffffffffffffffffffffffff161580614770575081155b1561477d57506000610940565b6001546040517ff409bbbd00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8881166004830152600092879289929091169063f409bbbd90602401602060405180830381865afa1580156147f4573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061481891906151d2565b6148229190615358565b61482c9190615409565b90506000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16631b0c2cf96040518163ffffffff1660e01b8152600401602060405180830381865afa15801561489d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906148c191906151d2565b9050600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166355564c6d6040518163ffffffff1660e01b8152600401602060405180830381865afa158015614930573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061495491906151d2565b81831161496a576149658684615358565b614974565b6149748683615358565b612e149190615409565b73ffffffffffffffffffffffffffffffffffffffff811681146119f157600080fd5b80151581146119f157600080fd5b600080604083850312156149c157600080fd5b82356149cc8161497e565b915060208301356149dc816149a0565b809150509250929050565b600080604083850312156149fa57600080fd5b8235614a058161497e565b915060208301356149dc8161497e565b60008083601f840112614a2757600080fd5b50813567ffffffffffffffff811115614a3f57600080fd5b6020830191508360208260051b850101111561166257600080fd5b600080600080600060808688031215614a7257600080fd5b853567ffffffffffffffff811115614a8957600080fd5b614a9588828901614a15565b909650945050602086013592506040860135614ab08161497e565b91506060860135614ac08161497e565b809150509295509295909350565b600060208284031215614ae057600080fd5b8135614aeb8161497e565b9392505050565b600080600060608486031215614b0757600080fd5b8335614b128161497e565b95602085013595506040909401359392505050565b60008060008060008060a08789031215614b4057600080fd5b863567ffffffffffffffff811115614b5757600080fd5b614b6389828a01614a15565b909750955050602087013593506040870135614b7e8161497e565b92506060870135614b8e8161497e565b91506080870135614b9e8161497e565b809150509295509295509295565b600080600060408486031215614bc157600080fd5b833567ffffffffffffffff811115614bd857600080fd5b614be486828701614a15565b9094509250506020840135614bf88161497e565b809150509250925092565b600081518084526020808501945080840160005b83811015614c4957815173ffffffffffffffffffffffffffffffffffffffff1687529582019590820190600101614c17565b509495945050505050565b604081526000614c676040830185614c03565b82810360208481019190915284518083528582019282019060005b81811015614c9e57845183529383019391830191600101614c82565b5090979650505050505050565b600080600060608486031215614cc057600080fd5b8335614ccb8161497e565b92506020840135614cdb8161497e565b91506040840135614bf88161497e565b60008060008060608587031215614d0157600080fd5b843567ffffffffffffffff811115614d1857600080fd5b614d2487828801614a15565b909550935050602085013591506040850135614d3f8161497e565b939692955090935050565b602081526000614aeb6020830184614c03565b60008060008060608587031215614d7357600080fd5b843567ffffffffffffffff811115614d8a57600080fd5b614d9687828801614a15565b9095509350506020850135614daa8161497e565b91506040850135614d3f8161497e565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60405160e0810167ffffffffffffffff81118282101715614e0c57614e0c614dba565b60405290565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715614e5957614e59614dba565b604052919050565b80356affffffffffffffffffffff81168114614e7c57600080fd5b919050565b803563ffffffff81168114614e7c57600080fd5b60006020808385031215614ea857600080fd5b823567ffffffffffffffff80821115614ec057600080fd5b818501915085601f830112614ed457600080fd5b813581811115614ee657614ee6614dba565b614ef4848260051b01614e12565b818152848101925060e0918202840185019188831115614f1357600080fd5b938501935b82851015614fb75780858a031215614f305760008081fd5b614f38614de9565b614f4186614e61565b815286860135878201526040614f58818801614e81565b90820152606086810135614f6b8161497e565b90820152608086810135614f7e8161497e565b9082015260a086810135614f918161497e565b9082015260c086810135614fa48161497e565b9082015284529384019392850192614f18565b50979650505050505050565b60008060208385031215614fd657600080fd5b823567ffffffffffffffff811115614fed57600080fd5b614ff985828601614a15565b90969095509350505050565b60008060006060848603121561501a57600080fd5b83356150258161497e565b925060208401356150358161497e565b915061504360408501614e81565b90509250925092565b60008060008060006060868803121561506457600080fd5b853561506f8161497e565b9450602086013567ffffffffffffffff8082111561508c57600080fd5b61509889838a01614a15565b909650945060408801359150808211156150b157600080fd5b506150be88828901614a15565b969995985093965092949392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b6000806040838503121561511157600080fd5b505080516020909101519092909150565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82141561518357615183615122565b5060010190565b6000821982111561519d5761519d615122565b500190565b60006fffffffffffffffffffffffffffffffff808316818114156151c8576151c8615122565b6001019392505050565b6000602082840312156151e457600080fd5b5051919050565b600181815b8085111561524457817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0482111561522a5761522a615122565b8085161561523757918102915b93841c93908002906151f0565b509250929050565b60008261525b575060016108a9565b81615268575060006108a9565b816001811461527e5760028114615288576152a4565b60019150506108a9565b60ff84111561529957615299615122565b50506001821b6108a9565b5060208310610133831016604e8410600b84101617156152c7575081810a6108a9565b6152d183836151eb565b807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0482111561530357615303615122565b029392505050565b6000614aeb60ff84168361524c565b6000614aeb838361524c565b60006020828403121561533857600080fd5b614aeb82614e61565b60008282101561535357615353615122565b500390565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048311821515161561539057615390615122565b500290565b6000602082840312156153a757600080fd5b815160ff81168114614aeb57600080fd5b6000602082840312156153ca57600080fd5b8151614aeb816149a0565b60006fffffffffffffffffffffffffffffffff80831681851680830382111561540057615400615122565b01949350505050565b60008261543f577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b50049056fea2646970667358221220b1e0784893d8879a280406b610f807874c4fa0957c10f9da72b46a263fbb5c5d64736f6c634300080a00330000000000000000000000002a683c0fd6a18a2de50893911b1f5bd6de2365da
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106102415760003560e01c8063886fe70b11610145578063bb492bf5116100bd578063cbcbb5071161008c578063e15ac62311610071578063e15ac623146106f3578063f5cf673b14610706578063f996868b1461071957600080fd5b8063cbcbb507146106c4578063dde43cba146106eb57600080fd5b8063bb492bf514610678578063bf90f63a1461068b578063c4d66de81461069e578063c5a7b538146106b157600080fd5b80639efd6f7211610114578063a73b870c116100f9578063a73b870c14610655578063b022418c1461065d578063b45ac1a91461067057600080fd5b80639efd6f72146105df5780639ff55db91461064257600080fd5b8063886fe70b1461056b57806392074b081461059357806394a9c744146105b9578063955c2ad7146105cc57600080fd5b8063533f542a116101d85780635f130b24116101a757806370674ab91161018c57806370674ab91461044357806374d945ec146104565780637eff4ba81461048f57600080fd5b80635f130b24146103ea5780636657732f1461042357600080fd5b8063533f542a1461034c5780635453ba10146103b157806357b89883146103c45780635c6588f9146103d757600080fd5b806331873e2e1161021457806331873e2e146102f257806333028b99146103055780633eb1e0a7146103185780634c0369c31461032b57600080fd5b80631a153391146102465780631b839c771461025b578063236300dc146102815780632a17bf6014610294575b600080fd5b6102596102543660046149ae565b61072c565b005b61026e6102693660046149e7565b61084f565b6040519081526020015b60405180910390f35b61026e61028f366004614a5a565b6108af565b6102cd6102a2366004614ace565b73ffffffffffffffffffffffffffffffffffffffff9081166000908152603d60205260409020541690565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610278565b610259610300366004614af2565b610949565b61026e610313366004614b27565b61095a565b610259610326366004614ace565b610b06565b61033e610339366004614bac565b610c9b565b604051610278929190614c54565b61026e61035a366004614cab565b73ffffffffffffffffffffffffffffffffffffffff80831660009081526002602090815260408083208585168452825280832093871683526001909301905220546cffffffffffffffffffffffffff169392505050565b6102596103bf3660046149e7565b610ff3565b61026e6103d2366004614ceb565b61109c565b61033e6103e5366004614bac565b6110ad565b6102cd6103f8366004614ace565b73ffffffffffffffffffffffffffffffffffffffff9081166000908152603c60205260409020541690565b610436610431366004614ace565b6113ed565b6040516102789190614d4a565b61026e610451366004614d5d565b611542565b6102cd610464366004614ace565b73ffffffffffffffffffffffffffffffffffffffff9081166000908152603a60205260409020541690565b61054b61049d3660046149e7565b73ffffffffffffffffffffffffffffffffffffffff91821660009081526002602090815260408083209390941682529190915220546cffffffffffffffffffffffffff8116916affffffffffffffffffffff6d01000000000000000000000000008304169163ffffffff780100000000000000000000000000000000000000000000000082048116927c01000000000000000000000000000000000000000000000000000000009092041690565b604080519485526020850193909352918301526060820152608001610278565b61057e6105793660046149e7565b611559565b60408051928352602083019190915201610278565b7f0000000000000000000000002a683c0fd6a18a2de50893911b1f5bd6de2365da6102cd565b6102596105c7366004614ace565b611669565b6102596105da366004614e95565b6117f4565b6106306105ed366004614ace565b73ffffffffffffffffffffffffffffffffffffffff1660009081526002602081905260409091200154700100000000000000000000000000000000900460ff1690565b60405160ff9091168152602001610278565b61033e610650366004614d5d565b6119f4565b610436611ba3565b61026e61066b3660046149e7565b611c12565b610436611ccb565b61033e610686366004614bac565b611d38565b61033e610699366004614fc3565b611dd1565b6102596106ac366004614ace565b611dec565b6102596106bf366004615005565b611f08565b6102cd7f0000000000000000000000002a683c0fd6a18a2de50893911b1f5bd6de2365da81565b61026e600281565b6102596107013660046149e7565b6120be565b6102596107143660046149e7565b612167565b61025961072736600461504c565b612282565b3373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000002a683c0fd6a18a2de50893911b1f5bd6de2365da16146107d0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4f4e4c595f454d495353494f4e5f4d414e41474552000000000000000000000060448201526064015b60405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff82166000818152603b602052604080822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001685151590811790915590519092917f9e0f3fc17a5f47bd46f3cab56d4d8a9ea9f32ad817657c3ea8e5dff28da3e3e791a35050565b73ffffffffffffffffffffffffffffffffffffffff8281166000908152600260209081526040808320938516835292905220547c0100000000000000000000000000000000000000000000000000000000900463ffffffff165b92915050565b600073ffffffffffffffffffffffffffffffffffffffff831661092e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f494e56414c49445f544f5f41444452455353000000000000000000000000000060448201526064016107c7565b61093d868686333388886126f6565b90505b95945050505050565b6109553384838561298a565b505050565b73ffffffffffffffffffffffffffffffffffffffff8084166000908152603a60205260408120549091339186911682146109f0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f434c41494d45525f554e415554484f52495a454400000000000000000000000060448201526064016107c7565b73ffffffffffffffffffffffffffffffffffffffff8616610a6d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f494e56414c49445f555345525f4144445245535300000000000000000000000060448201526064016107c7565b73ffffffffffffffffffffffffffffffffffffffff8516610aea576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f494e56414c49445f544f5f41444452455353000000000000000000000000000060448201526064016107c7565b610af9898989338a8a8a6126f6565b9998505050505050505050565b336000908152603b602052604090205460ff16610b7f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f555044415445525f554e415554484f52495a454400000000000000000000000060448201526064016107c7565b60005b600554811015610c975760008060058381548110610ba257610ba26150cf565b6000918252602090912001546040517f0afbcdc900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff868116600483015290911690630afbcdc9906024016040805180830381865afa158015610c1a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c3e91906150fe565b91509150610c8260058481548110610c5857610c586150cf565b60009182526020909120015473ffffffffffffffffffffffffffffffffffffffff1685848461298a565b50508080610c8f90615151565b915050610b82565b5050565b6060806000610cab868686612b40565b60045490915067ffffffffffffffff811115610cc957610cc9614dba565b604051908082528060200260200182016040528015610cf2578160200160208202803683370190505b509250825167ffffffffffffffff811115610d0f57610d0f614dba565b604051908082528060200260200182016040528015610d38578160200160208202803683370190505b50915060005b8151811015610fe85760005b8451811015610fd55760048181548110610d6657610d666150cf565b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16858281518110610da357610da36150cf565b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff168152505060026000848481518110610df357610df36150cf565b60200260200101516000015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000016000868381518110610e5057610e506150cf565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060010160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600001600d9054906101000a90046fffffffffffffffffffffffffffffffff166fffffffffffffffffffffffffffffffff16848281518110610f1657610f166150cf565b60200260200101818151610f2a919061518a565b9052508251839083908110610f4157610f416150cf565b60200260200101516020015160001415610f5a57610fc3565b610f9986868381518110610f7057610f706150cf565b60200260200101516001868681518110610f8c57610f8c6150cf565b6020026020010151612d3e565b848281518110610fab57610fab6150cf565b60200260200101818151610fbf919061518a565b9052505b80610fcd81615151565b915050610d4a565b5080610fe081615151565b915050610d3e565b50505b935093915050565b3373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000002a683c0fd6a18a2de50893911b1f5bd6de2365da1614611092576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4f4e4c595f454d495353494f4e5f4d414e41474552000000000000000000000060448201526064016107c7565b610c978282612e20565b6000610940858585333333886126f6565b60608060006110bd868686612b40565b60045490915067ffffffffffffffff8111156110db576110db614dba565b604051908082528060200260200182016040528015611104578160200160208202803683370190505b509250825167ffffffffffffffff81111561112157611121614dba565b60405190808252806020026020018201604052801561114a578160200160208202803683370190505b50915060005b8151811015610fe85760005b84518110156113da5760048181548110611178576111786150cf565b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff168582815181106111b5576111b56150cf565b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff168152505060026000848481518110611205576112056150cf565b60200260200101516000015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000016000868381518110611262576112626150cf565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060010160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600001600d9054906101000a90046fffffffffffffffffffffffffffffffff166fffffffffffffffffffffffffffffffff16848281518110611328576113286150cf565b6020026020010181815161133c919061518a565b9052508251839083908110611353576113536150cf565b6020026020010151602001516000141561136c576113c8565b61139e86868381518110611382576113826150cf565b60200260200101516000868681518110610f8c57610f8c6150cf565b8482815181106113b0576113b06150cf565b602002602001018181516113c4919061518a565b9052505b806113d281615151565b91505061115c565b50806113e581615151565b915050611150565b73ffffffffffffffffffffffffffffffffffffffff81166000908152600260208190526040822001546060916fffffffffffffffffffffffffffffffff909116908167ffffffffffffffff81111561144757611447614dba565b604051908082528060200260200182016040528015611470578160200160208202803683370190505b50905060005b826fffffffffffffffffffffffffffffffff16816fffffffffffffffffffffffffffffffff16101561153a5773ffffffffffffffffffffffffffffffffffffffff80861660009081526002602090815260408083206fffffffffffffffffffffffffffffffff861680855260019091019092529091205484519216918491908110611503576115036150cf565b73ffffffffffffffffffffffffffffffffffffffff9092166020928302919091019091015280611532816151a2565b915050611476565b509392505050565b60006109408383611554888888612b40565b612f74565b73ffffffffffffffffffffffffffffffffffffffff8083166000818152600260209081526040808320948616835293815283822084517fb1bf962d000000000000000000000000000000000000000000000000000000008152945192948594919361165c9385939263b1bf962d92600480830193928290030181865afa1580156115e7573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061160b91906151d2565b73ffffffffffffffffffffffffffffffffffffffff88166000908152600260208190526040909120015461165790700100000000000000000000000000000000900460ff16600a61530b565b613113565b92509250505b9250929050565b3373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000002a683c0fd6a18a2de50893911b1f5bd6de2365da1614611708576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4f4e4c595f454d495353494f4e5f4d414e41474552000000000000000000000060448201526064016107c7565b73ffffffffffffffffffffffffffffffffffffffff8116611785576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f434f4e4649475f4d5553545f4e4f545f5a45524f00000000000000000000000060448201526064016107c7565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83169081179091556040517f5f5230011e18591ebb16f1c3d1368e67237b09cac3273a2d03441ada1841110b90600090a250565b3373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000002a683c0fd6a18a2de50893911b1f5bd6de2365da1614611893576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4f4e4c595f454d495353494f4e5f4d414e41474552000000000000000000000060448201526064016107c7565b60005b81518110156119e7578181815181106118b1576118b16150cf565b60200260200101516060015173ffffffffffffffffffffffffffffffffffffffff1663b1bf962d6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611907573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061192b91906151d2565b82828151811061193d5761193d6150cf565b60200260200101516020018181525050611991828281518110611962576119626150cf565b602002602001015160800151838381518110611980576119806150cf565b602002602001015160a0015161321f565b6119d58282815181106119a6576119a66150cf565b6020026020010151608001518383815181106119c4576119c46150cf565b602002602001015160c00151612e20565b806119df81615151565b915050611896565b506119f181613385565b50565b73ffffffffffffffffffffffffffffffffffffffff8083166000908152603a6020526040902054606091829133918691168214611a8d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f434c41494d45525f554e415554484f52495a454400000000000000000000000060448201526064016107c7565b73ffffffffffffffffffffffffffffffffffffffff8616611b0a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f494e56414c49445f555345525f4144445245535300000000000000000000000060448201526064016107c7565b73ffffffffffffffffffffffffffffffffffffffff8516611b87576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f494e56414c49445f544f5f41444452455353000000000000000000000000000060448201526064016107c7565b611b948888338989613c26565b93509350505094509492505050565b60606005805480602002602001604051908101604052809291908181526020018280548015611c0857602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff168152600190910190602001808311611bdd575b5050505050905090565b60008060005b60055481101561153a576002600060058381548110611c3957611c396150cf565b60009182526020808320919091015473ffffffffffffffffffffffffffffffffffffffff908116845283820194909452604092830182208885168352815282822093891682526001909301909252902054611cb7906d010000000000000000000000000090046fffffffffffffffffffffffffffffffff168361518a565b915080611cc381615151565b915050611c18565b60606004805480602002602001604051908101604052809291908181526020018280548015611c085760200282019190600052602060002090815473ffffffffffffffffffffffffffffffffffffffff168152600190910190602001808311611bdd575050505050905090565b60608073ffffffffffffffffffffffffffffffffffffffff8316611db8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f494e56414c49445f544f5f41444452455353000000000000000000000000000060448201526064016107c7565b611dc58585333387613c26565b91509150935093915050565b606080611de18484333333613c26565b915091509250929050565b60075460029060ff1680611dff5750303b155b80611e0b575060065481115b611e97576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f436f6e747261637420696e7374616e63652068617320616c726561647920626560448201527f656e20696e697469616c697a656400000000000000000000000000000000000060648201526084016107c7565b60075460ff16158015611ed557600780547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905560068290555b801561095557600780547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169055505050565b3373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000002a683c0fd6a18a2de50893911b1f5bd6de2365da1614611fa7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4f4e4c595f454d495353494f4e5f4d414e41474552000000000000000000000060448201526064016107c7565b73ffffffffffffffffffffffffffffffffffffffff83811660008181526002602090815260408083209487168084529482529182902080547bffffffffffffffffffffffffffffffffffffffffffffffffffffffff81167c010000000000000000000000000000000000000000000000000000000063ffffffff8981168281029384179586905587516d01000000000000000000000000009096046affffffffffffffffffffff16808752968601969096529083041694830185905260608301939093526cffffffffffffffffffffffffff9081169216919091176080820152909291907fac1777479f07f3e7c34da8402139d54027a6a260caaae168bdee825ca5580dc59060a00160405180910390a350505050565b3373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000002a683c0fd6a18a2de50893911b1f5bd6de2365da161461215d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4f4e4c595f454d495353494f4e5f4d414e41474552000000000000000000000060448201526064016107c7565b610c97828261321f565b3373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000002a683c0fd6a18a2de50893911b1f5bd6de2365da1614612206576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4f4e4c595f454d495353494f4e5f4d414e41474552000000000000000000000060448201526064016107c7565b73ffffffffffffffffffffffffffffffffffffffff8281166000818152603a602052604080822080547fffffffffffffffffffffffff0000000000000000000000000000000000000000169486169485179055517f4925eafc82d0c4d67889898eeed64b18488ab19811e61620f387026dec126a289190a35050565b3373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000002a683c0fd6a18a2de50893911b1f5bd6de2365da1614612321576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4f4e4c595f454d495353494f4e5f4d414e41474552000000000000000000000060448201526064016107c7565b82811461238a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600d60248201527f494e56414c49445f494e5055540000000000000000000000000000000000000060448201526064016107c7565b60005b838110156126ee5773ffffffffffffffffffffffffffffffffffffffff861660009081526002602052604081209081818888868181106123cf576123cf6150cf565b90506020020160208101906123e49190614ace565b73ffffffffffffffffffffffffffffffffffffffff16815260208101919091526040016000206002830154909150700100000000000000000000000000000000900460ff16801580159061245a575081547801000000000000000000000000000000000000000000000000900463ffffffff1615155b6124c0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f444953545249425554494f4e5f444f45535f4e4f545f4558495354000000000060448201526064016107c7565b6000612545838b73ffffffffffffffffffffffffffffffffffffffff1663b1bf962d6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612511573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061253591906151d2565b61254085600a61531a565b61410e565b5083549091506d010000000000000000000000000090046affffffffffffffffffffff1687878781811061257b5761257b6150cf565b90506020020160208101906125909190615326565b84546affffffffffffffffffffff919091166d0100000000000000000000000000027fffffffffffffffff0000000000000000000000ffffffffffffffffffffffffff9091161784558989878181106125eb576125eb6150cf565b90506020020160208101906126009190614ace565b73ffffffffffffffffffffffffffffffffffffffff168b73ffffffffffffffffffffffffffffffffffffffff167fac1777479f07f3e7c34da8402139d54027a6a260caaae168bdee825ca5580dc5838b8b8b818110612661576126616150cf565b90506020020160208101906126769190615326565b8854604080519384526affffffffffffffffffffff90921660208401527c0100000000000000000000000000000000000000000000000000000000900463ffffffff1690820181905260608201526080810186905260a00160405180910390a3505050505080806126e690615151565b91505061238d565b505050505050565b6000856127055750600061297f565b600061271b856127168b8b89612b40565b61429c565b60005b888110156129055760008a8a8381811061273a5761273a6150cf565b905060200201602081019061274f9190614ace565b73ffffffffffffffffffffffffffffffffffffffff81811660009081526002602090815260408083208a851684528252808320938c1683526001909301905220549091506127c0906d010000000000000000000000000090046fffffffffffffffffffffffffffffffff168461518a565b92508883116128345773ffffffffffffffffffffffffffffffffffffffff808216600090815260026020908152604080832089851684528252808320938b168352600190930190522080547fffffff00000000000000000000000000000000ffffffffffffffffffffffffff1690556128f2565b60006128408a85615341565b905061284c8185615341565b93506128578161431d565b73ffffffffffffffffffffffffffffffffffffffff92831660009081526002602090815260408083208a871684528252808320958c168352600190950190529290922080546fffffffffffffffffffffffffffffffff939093166d0100000000000000000000000000027fffffff00000000000000000000000000000000ffffffffffffffffffffffffff9093169290921790915550612905565b50806128fd81615151565b91505061271e565b508061291557600091505061297f565b6129208484836143c3565b6040805173ffffffffffffffffffffffffffffffffffffffff8881168252602082018490528087169286821692918916917fc052130bc4ef84580db505783484b067ea8b71b3bca78a7e12db7aea8658f004910160405180910390a490505b979650505050505050565b73ffffffffffffffffffffffffffffffffffffffff84166000908152600260208190526040909120015460ff700100000000000000000000000000000000820416600a0a906fffffffffffffffffffffffffffffffff16806129ed575050612b3a565b60005b81816fffffffffffffffffffffffffffffffff161015612b365773ffffffffffffffffffffffffffffffffffffffff80881660009081526002602090815260408083206fffffffffffffffffffffffffffffffff86168452600181018352818420549094168084529390915281209080612a6b83898961410e565b91509150600080612a80858d8c8e888e6144ef565b915091508280612a8d5750805b15612b24578b73ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff168e73ffffffffffffffffffffffffffffffffffffffff167f3303facd24627943a92e9dc87cfbb34b15c49b726eec3ad3487c16be9ab8efe8878887604051612b1b939291909283526020830191909152604082015260600190565b60405180910390a45b5050600190940193506129f092505050565b5050505b50505050565b60608267ffffffffffffffff811115612b5b57612b5b614dba565b604051908082528060200260200182016040528015612bc657816020015b612bb36040518060600160405280600073ffffffffffffffffffffffffffffffffffffffff16815260200160008152602001600081525090565b815260200190600190039081612b795790505b50905060005b8381101561153a57848482818110612be657612be66150cf565b9050602002016020810190612bfb9190614ace565b828281518110612c0d57612c0d6150cf565b602090810291909101015173ffffffffffffffffffffffffffffffffffffffff9091169052848482818110612c4457612c446150cf565b9050602002016020810190612c599190614ace565b6040517f0afbcdc900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85811660048301529190911690630afbcdc9906024016040805180830381865afa158015612cc6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612cea91906150fe565b838381518110612cfc57612cfc6150cf565b6020026020010151602001848481518110612d1957612d196150cf565b6020908102919091010151604001919091525280612d3681615151565b915050612bcc565b805173ffffffffffffffffffffffffffffffffffffffff9081166000908152600260208181526040808420888616855282528084208651909516845290829052822001549091908290612da990700100000000000000000000000000000000900460ff16600a61530b565b90506000612dbc83866040015184613113565b6040808801516020808a015173ffffffffffffffffffffffffffffffffffffffff8e16600090815260018a0190925292902054929450612e1493508b9290919085906cffffffffffffffffffffffffff16878c614653565b98975050505050505050565b60008173ffffffffffffffffffffffffffffffffffffffff166350d25bcd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612e6d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e9191906151d2565b13612ef8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f4f5241434c455f4d5553545f52455455524e5f5052494345000000000000000060448201526064016107c7565b73ffffffffffffffffffffffffffffffffffffffff8281166000818152603d602052604080822080547fffffffffffffffffffffffff0000000000000000000000000000000000000000169486169485179055517f1a1cd5483e52e60b9ff7f3b9d1db3bbd9e9d21c6324ad3a8c79dba9b75e62f4d9190a35050565b6000805b825181101561153a57828181518110612f9357612f936150cf565b602002602001015160200151600014156130425760026000848381518110612fbd57612fbd6150cf565b6020908102919091018101515173ffffffffffffffffffffffffffffffffffffffff908116835282820193909352604091820160009081208885168252825282812093891681526001909301905290205461303b906d010000000000000000000000000090046fffffffffffffffffffffffffffffffff168361518a565b9150613101565b60026000848381518110613058576130586150cf565b6020908102919091018101515173ffffffffffffffffffffffffffffffffffffffff908116835282820193909352604091820160009081208885168252825282812093891681526001938401909152205484516d01000000000000000000000000009091046fffffffffffffffffffffffffffffffff16916130ea9188918891889087908110610f8c57610f8c6150cf565b6130f4919061518a565b6130fe908361518a565b91505b8061310b81615151565b915050612f78565b825460009081906cffffffffffffffffffffffffff81169063ffffffff7c010000000000000000000000000000000000000000000000000000000082048116916affffffffffffffffffffff6d01000000000000000000000000008204169178010000000000000000000000000000000000000000000000009091041681158061319b575087155b806131a557504281145b806131b05750828110155b156131c45783849550955050505050610feb565b60008342116131d357426131d5565b835b905060006131e38383615341565b90506000896131f28387615358565b6131fc9190615358565b8b900490508661320c818361518a565b9850985050505050505050935093915050565b73ffffffffffffffffffffffffffffffffffffffff811661329c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f53545241544547595f43414e5f4e4f545f42455f5a45524f000000000000000060448201526064016107c7565b6001813b151514613309576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f53545241544547595f4d5553545f42455f434f4e54524143540000000000000060448201526064016107c7565b73ffffffffffffffffffffffffffffffffffffffff8281166000818152603c602052604080822080547fffffffffffffffffffffffff0000000000000000000000000000000000000000169486169485179055517f8ca1d928f1d72493a6b78c4f74aabde976bc37ffe2570f2a1ce5a8abd3dde0aa9190a35050565b60005b8151811015610c9757600260008383815181106133a7576133a76150cf565b6020908102919091018101516060015173ffffffffffffffffffffffffffffffffffffffff16825281019190915260400160002060020154700100000000000000000000000000000000900460ff1661347357600582828151811061340e5761340e6150cf565b6020908102919091018101516060015182546001810184556000938452919092200180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff9092169190911790555b6000828281518110613487576134876150cf565b60200260200101516060015173ffffffffffffffffffffffffffffffffffffffff1663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa1580156134dd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135019190615395565b60026000858581518110613517576135176150cf565b60200260200101516060015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060020160106101000a81548160ff021916908360ff160217905560ff169050600060026000858581518110613594576135946150cf565b60200260200101516060015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160008585815181106135f1576135f16150cf565b6020908102919091018101516080015173ffffffffffffffffffffffffffffffffffffffff16825281019190915260400160002080549091507801000000000000000000000000000000000000000000000000900463ffffffff1661386057838381518110613662576136626150cf565b60200260200101516080015160026000868681518110613684576136846150cf565b60200260200101516060015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206001016000600260008888815181106136e5576136e56150cf565b60200260200101516060015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060020160009054906101000a90046fffffffffffffffffffffffffffffffff166fffffffffffffffffffffffffffffffff166fffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550600260008585815181106137d1576137d16150cf565b6020908102919091018101516060015173ffffffffffffffffffffffffffffffffffffffff168252810191909152604001600090812060020180546fffffffffffffffffffffffffffffffff1691613828836151a2565b91906101000a8154816fffffffffffffffffffffffffffffffff02191690836fffffffffffffffffffffffffffffffff160217905550505b60036000858581518110613876576138766150cf565b6020908102919091018101516080015173ffffffffffffffffffffffffffffffffffffffff1682528101919091526040016000205460ff166139a0576001600360008686815181106138ca576138ca6150cf565b60200260200101516080015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550600484848151811061393b5761393b6150cf565b6020908102919091018101516080015182546001810184556000938452919092200180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff9092169190911790555b60006139d1828686815181106139b8576139b86150cf565b60200260200101516020015185600a612540919061531a565b50825486519192506d010000000000000000000000000081046affffffffffffffffffffff16917c010000000000000000000000000000000000000000000000000000000090910463ffffffff1690879087908110613a3257613a326150cf565b60209081029190910101515184546affffffffffffffffffffff9091166d0100000000000000000000000000027fffffffffffffffff0000000000000000000000ffffffffffffffffffffffffff9091161784558651879087908110613a9a57613a9a6150cf565b602090810291909101015160400151845463ffffffff9091167c0100000000000000000000000000000000000000000000000000000000027bffffffffffffffffffffffffffffffffffffffffffffffffffffffff9091161784558651879087908110613b0957613b096150cf565b60200260200101516080015173ffffffffffffffffffffffffffffffffffffffff16878781518110613b3d57613b3d6150cf565b60200260200101516060015173ffffffffffffffffffffffffffffffffffffffff167fac1777479f07f3e7c34da8402139d54027a6a260caaae168bdee825ca5580dc5848a8a81518110613b9357613b936150cf565b602002602001015160000151858c8c81518110613bb257613bb26150cf565b60200260200101516040015189604051613c069594939291906affffffffffffffffffffff958616815293909416602084015263ffffffff9182166040840152166060820152608081019190915260a00190565b60405180910390a350505050508080613c1e90615151565b915050613388565b60045460609081908067ffffffffffffffff811115613c4757613c47614dba565b604051908082528060200260200182016040528015613c70578160200160208202803683370190505b5092508067ffffffffffffffff811115613c8c57613c8c614dba565b604051908082528060200260200182016040528015613cb5578160200160208202803683370190505b509150613cc7856127168a8a89612b40565b60005b87811015613fd6576000898983818110613ce657613ce66150cf565b9050602002016020810190613cfb9190614ace565b905060005b83811015613fc157600073ffffffffffffffffffffffffffffffffffffffff16868281518110613d3257613d326150cf565b602002602001015173ffffffffffffffffffffffffffffffffffffffff161415613de15760048181548110613d6957613d696150cf565b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16868281518110613da657613da66150cf565b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff16815250505b73ffffffffffffffffffffffffffffffffffffffff8216600090815260026020526040812087518290899085908110613e1c57613e1c6150cf565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff90811683528282019390935260409182016000908120938d168152600190930190529020546d010000000000000000000000000090046fffffffffffffffffffffffffffffffff1690508015613fae5780868381518110613ea057613ea06150cf565b60200260200101818151613eb4919061518a565b90525073ffffffffffffffffffffffffffffffffffffffff83166000908152600260205260408120885182908a9086908110613ef257613ef26150cf565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060010160008b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600001600d6101000a8154816fffffffffffffffffffffffffffffffff02191690836fffffffffffffffffffffffffffffffff1602179055505b5080613fb981615151565b915050613d00565b50508080613fce90615151565b915050613cca565b5060005b818110156141025761401f85858381518110613ff857613ff86150cf565b6020026020010151858481518110614012576140126150cf565b60200260200101516143c3565b8473ffffffffffffffffffffffffffffffffffffffff16848281518110614048576140486150cf565b602002602001015173ffffffffffffffffffffffffffffffffffffffff168773ffffffffffffffffffffffffffffffffffffffff167fc052130bc4ef84580db505783484b067ea8b71b3bca78a7e12db7aea8658f0048a8786815181106140b1576140b16150cf565b60200260200101516040516140e892919073ffffffffffffffffffffffffffffffffffffffff929092168252602082015260400190565b60405180910390a4806140fa81615151565b915050613fda565b50509550959350505050565b60008060008061411f878787613113565b915091506000828214614238576cffffffffffffffffffffffffff8211156141a3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f494e4445585f4f564552464c4f5700000000000000000000000000000000000060448201526064016107c7565b5086547fffffffffffffffffffffffffffffffffffffff00000000000000000000000000166cffffffffffffffffffffffffff821617875560016141e6426146b4565b885463ffffffff919091167801000000000000000000000000000000000000000000000000027fffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffffff90911617885561428f565b614241426146b4565b885463ffffffff919091167801000000000000000000000000000000000000000000000000027fffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffffff9091161788555b9097909650945050505050565b60005b81518110156109555761430b8282815181106142bd576142bd6150cf565b602002602001015160000151848484815181106142dc576142dc6150cf565b6020026020010151602001518585815181106142fa576142fa6150cf565b60200260200101516040015161298a565b8061431581615151565b91505061429f565b60006fffffffffffffffffffffffffffffffff8211156143bf576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602760248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203160448201527f323820626974730000000000000000000000000000000000000000000000000060648201526084016107c7565b5090565b73ffffffffffffffffffffffffffffffffffffffff8281166000818152603c60205260408082205490517f16beb9820000000000000000000000000000000000000000000000000000000081528785166004820152602481019390935260448301859052909216919082906316beb982906064016020604051808303816000875af1158015614456573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061447a91906153b8565b90506001811515146144e8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f5452414e534645525f4552524f5200000000000000000000000000000000000060448201526064016107c7565b5050505050565b73ffffffffffffffffffffffffffffffffffffffff8516600090815260018701602052604081205481906cffffffffffffffffffffffffff16818582148015906146435773ffffffffffffffffffffffffffffffffffffffff8a16600090815260018c016020526040902080547fffffffffffffffffffffffffffffffffffffff00000000000000000000000000166cffffffffffffffffffffffffff89161790558715614643576145a78a8a8a8a878b6001614653565b91506145b28261431d565b73ffffffffffffffffffffffffffffffffffffffff8b16600090815260018d01602052604090208054600d9061460c9084906d010000000000000000000000000090046fffffffffffffffffffffffffffffffff166153d5565b92506101000a8154816fffffffffffffffffffffffffffffffff02191690836fffffffffffffffffffffffffffffffff1602179055505b909a909950975050505050505050565b6000806146608587615341565b61466a9088615358565b60015490915073ffffffffffffffffffffffffffffffffffffffff16156146a557614698898989848761474a565b6146a2908261518a565b90505b92909204979650505050505050565b600063ffffffff8211156143bf576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203360448201527f322062697473000000000000000000000000000000000000000000000000000060648201526084016107c7565b60015460009073ffffffffffffffffffffffffffffffffffffffff161580614770575081155b1561477d57506000610940565b6001546040517ff409bbbd00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8881166004830152600092879289929091169063f409bbbd90602401602060405180830381865afa1580156147f4573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061481891906151d2565b6148229190615358565b61482c9190615409565b90506000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16631b0c2cf96040518163ffffffff1660e01b8152600401602060405180830381865afa15801561489d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906148c191906151d2565b9050600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166355564c6d6040518163ffffffff1660e01b8152600401602060405180830381865afa158015614930573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061495491906151d2565b81831161496a576149658684615358565b614974565b6149748683615358565b612e149190615409565b73ffffffffffffffffffffffffffffffffffffffff811681146119f157600080fd5b80151581146119f157600080fd5b600080604083850312156149c157600080fd5b82356149cc8161497e565b915060208301356149dc816149a0565b809150509250929050565b600080604083850312156149fa57600080fd5b8235614a058161497e565b915060208301356149dc8161497e565b60008083601f840112614a2757600080fd5b50813567ffffffffffffffff811115614a3f57600080fd5b6020830191508360208260051b850101111561166257600080fd5b600080600080600060808688031215614a7257600080fd5b853567ffffffffffffffff811115614a8957600080fd5b614a9588828901614a15565b909650945050602086013592506040860135614ab08161497e565b91506060860135614ac08161497e565b809150509295509295909350565b600060208284031215614ae057600080fd5b8135614aeb8161497e565b9392505050565b600080600060608486031215614b0757600080fd5b8335614b128161497e565b95602085013595506040909401359392505050565b60008060008060008060a08789031215614b4057600080fd5b863567ffffffffffffffff811115614b5757600080fd5b614b6389828a01614a15565b909750955050602087013593506040870135614b7e8161497e565b92506060870135614b8e8161497e565b91506080870135614b9e8161497e565b809150509295509295509295565b600080600060408486031215614bc157600080fd5b833567ffffffffffffffff811115614bd857600080fd5b614be486828701614a15565b9094509250506020840135614bf88161497e565b809150509250925092565b600081518084526020808501945080840160005b83811015614c4957815173ffffffffffffffffffffffffffffffffffffffff1687529582019590820190600101614c17565b509495945050505050565b604081526000614c676040830185614c03565b82810360208481019190915284518083528582019282019060005b81811015614c9e57845183529383019391830191600101614c82565b5090979650505050505050565b600080600060608486031215614cc057600080fd5b8335614ccb8161497e565b92506020840135614cdb8161497e565b91506040840135614bf88161497e565b60008060008060608587031215614d0157600080fd5b843567ffffffffffffffff811115614d1857600080fd5b614d2487828801614a15565b909550935050602085013591506040850135614d3f8161497e565b939692955090935050565b602081526000614aeb6020830184614c03565b60008060008060608587031215614d7357600080fd5b843567ffffffffffffffff811115614d8a57600080fd5b614d9687828801614a15565b9095509350506020850135614daa8161497e565b91506040850135614d3f8161497e565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60405160e0810167ffffffffffffffff81118282101715614e0c57614e0c614dba565b60405290565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715614e5957614e59614dba565b604052919050565b80356affffffffffffffffffffff81168114614e7c57600080fd5b919050565b803563ffffffff81168114614e7c57600080fd5b60006020808385031215614ea857600080fd5b823567ffffffffffffffff80821115614ec057600080fd5b818501915085601f830112614ed457600080fd5b813581811115614ee657614ee6614dba565b614ef4848260051b01614e12565b818152848101925060e0918202840185019188831115614f1357600080fd5b938501935b82851015614fb75780858a031215614f305760008081fd5b614f38614de9565b614f4186614e61565b815286860135878201526040614f58818801614e81565b90820152606086810135614f6b8161497e565b90820152608086810135614f7e8161497e565b9082015260a086810135614f918161497e565b9082015260c086810135614fa48161497e565b9082015284529384019392850192614f18565b50979650505050505050565b60008060208385031215614fd657600080fd5b823567ffffffffffffffff811115614fed57600080fd5b614ff985828601614a15565b90969095509350505050565b60008060006060848603121561501a57600080fd5b83356150258161497e565b925060208401356150358161497e565b915061504360408501614e81565b90509250925092565b60008060008060006060868803121561506457600080fd5b853561506f8161497e565b9450602086013567ffffffffffffffff8082111561508c57600080fd5b61509889838a01614a15565b909650945060408801359150808211156150b157600080fd5b506150be88828901614a15565b969995985093965092949392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b6000806040838503121561511157600080fd5b505080516020909101519092909150565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82141561518357615183615122565b5060010190565b6000821982111561519d5761519d615122565b500190565b60006fffffffffffffffffffffffffffffffff808316818114156151c8576151c8615122565b6001019392505050565b6000602082840312156151e457600080fd5b5051919050565b600181815b8085111561524457817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0482111561522a5761522a615122565b8085161561523757918102915b93841c93908002906151f0565b509250929050565b60008261525b575060016108a9565b81615268575060006108a9565b816001811461527e5760028114615288576152a4565b60019150506108a9565b60ff84111561529957615299615122565b50506001821b6108a9565b5060208310610133831016604e8410600b84101617156152c7575081810a6108a9565b6152d183836151eb565b807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0482111561530357615303615122565b029392505050565b6000614aeb60ff84168361524c565b6000614aeb838361524c565b60006020828403121561533857600080fd5b614aeb82614e61565b60008282101561535357615353615122565b500390565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048311821515161561539057615390615122565b500290565b6000602082840312156153a757600080fd5b815160ff81168114614aeb57600080fd5b6000602082840312156153ca57600080fd5b8151614aeb816149a0565b60006fffffffffffffffffffffffffffffffff80831681851680830382111561540057615400615122565b01949350505050565b60008261543f577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b50049056fea2646970667358221220b1e0784893d8879a280406b610f807874c4fa0957c10f9da72b46a263fbb5c5d64736f6c634300080a0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000002a683c0fd6a18a2de50893911b1f5bd6de2365da
-----Decoded View---------------
Arg [0] : emissionManager (address): 0x2a683c0fD6A18a2De50893911b1F5Bd6DE2365da
-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 0000000000000000000000002a683c0fd6a18a2de50893911b1f5bd6de2365da
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.