MATIC Price: $0.727822 (-2.29%)
Gas: 30 GWei
 

Overview

MATIC Balance

Polygon PoS Chain LogoPolygon PoS Chain LogoPolygon PoS Chain Logo0 MATIC

MATIC Value

$0.00

Sponsored

Transaction Hash
Method
Block
From
To
Value
Setup414035672023-04-11 10:47:19406 days ago1681210039IN
0x0A4944bc...BE6f58F79
0 MATIC0.01736529185.79266336
0x60806040414029602023-04-11 10:25:18406 days ago1681208718IN
 Create: RewardsController
0 MATIC0.52710062141.66268137

Parent Transaction Hash Block From To Value
View All Internal Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
RewardsController

Compiler Version
v0.8.16+commit.07a7930e

Optimization Enabled:
Yes with 10000 runs

Other Settings:
default evmVersion, MIT license
File 1 of 12 : RewardsController.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;

import "@openzeppelin/contracts/utils/math/SafeCast.sol";

import "./RewardsDistributor.sol";
import "../interfaces/rewards/IRewardsController.sol";
import "../PostConstruct.sol";
import "../libraries/GPv2SafeERC20.sol";

contract RewardsController is IRewardsController, RewardsDistributor, PostConstruct {
    /// @dev user => claimer
    mapping(address => address) internal _authorizedClaimers;

    /// @dev reward => rewardOracle
    mapping(address => IEACAggregatorProxy) internal _rewardOracle;

    /// @dev Account that secures ERC20 rewards.
    /// @dev It must approve `RewardsController` to spend the rewards it holds.
    address internal REWARDS_VAULT;

    modifier onlyAuthorizedClaimers(address claimer, address user) {
        if (_authorizedClaimers[user] != claimer && address(solidStakingViewActions) != claimer) {
            revert UnauthorizedClaimer(claimer, user);
        }
        _;
    }

    function setup(
        address _solidStakingViewActions,
        address rewardsVault,
        address emissionManager
    ) external postConstruct {
        _setSolidStaking(_solidStakingViewActions);
        _setRewardsVault(rewardsVault);
        _setEmissionManager(emissionManager);
    }

    /// @inheritdoc IRewardsController
    function getRewardsVault() external view returns (address) {
        return REWARDS_VAULT;
    }

    /// @inheritdoc IRewardsController
    function getClaimer(address user) external view returns (address) {
        return _authorizedClaimers[user];
    }

    /// @inheritdoc IRewardsController
    function getRewardOracle(address reward) external view returns (address) {
        return address(_rewardOracle[reward]);
    }

    /// @inheritdoc IRewardsController
    function configureAssets(RewardsDataTypes.DistributionConfig[] memory config)
        external
        onlyEmissionManager
    {
        for (uint i; i < config.length; i++) {
            config[i].totalStaked = solidStakingViewActions.totalStaked(config[i].asset);
            _setRewardOracle(config[i].reward, config[i].rewardOracle);
        }
        _configureAssets(config);
    }

    /// @inheritdoc IRewardsController
    function setRewardOracle(address reward, IEACAggregatorProxy rewardOracle) external onlyEmissionManager {
        _setRewardOracle(reward, rewardOracle);
    }

    /// @inheritdoc IRewardsController
    function setClaimer(address user, address caller) external onlyEmissionManager {
        _authorizedClaimers[user] = caller;
        emit ClaimerSet(user, caller);
    }

    /// @inheritdoc IRewardsController
    function setRewardsVault(address rewardsVault) external onlyEmissionManager {
        _setRewardsVault(rewardsVault);
    }

    function setSolidStaking(address solidStaking) external onlyEmissionManager {
        _setSolidStaking(solidStaking);
    }

    /// @inheritdoc IRewardsController
    function handleUserStakeChanged(
        address asset,
        address user,
        uint oldUserStake,
        uint oldTotalStaked
    ) external {
        if (msg.sender != address(solidStakingViewActions)) {
            revert NotSolidStaking(msg.sender);
        }

        _updateAllRewardDistributionsAndUserRewardsForAsset(asset, user, oldUserStake, oldTotalStaked);
    }

    /// @inheritdoc IRewardsController
    function claimAllRewards(address[] calldata assets, address to)
        external
        returns (address[] memory rewardsList, uint[] memory claimedAmounts)
    {
        if (to == address(0)) {
            revert InvalidInput();
        }

        return _claimAllRewards(assets, msg.sender, msg.sender, to);
    }

    /// @inheritdoc IRewardsController
    function claimAllRewardsOnBehalf(
        address[] calldata assets,
        address user,
        address to
    )
        external
        onlyAuthorizedClaimers(msg.sender, user)
        returns (address[] memory rewardsList, uint[] memory claimedAmounts)
    {
        if (to == address(0) || user == address(0)) {
            revert InvalidInput();
        }

        return _claimAllRewards(assets, msg.sender, user, to);
    }

    /// @inheritdoc IRewardsController
    function claimAllRewardsToSelf(address[] calldata assets)
        external
        returns (address[] memory rewardsList, uint[] memory claimedAmounts)
    {
        return _claimAllRewards(assets, msg.sender, msg.sender, msg.sender);
    }

    /// @inheritdoc RewardsDistributor
    function _getAssetStakedAmounts(address[] calldata assets, address user)
        internal
        view
        override
        returns (RewardsDataTypes.AssetStakedAmounts[] memory assetStakedAmounts)
    {
        assetStakedAmounts = new RewardsDataTypes.AssetStakedAmounts[](assets.length);
        for (uint i; i < assets.length; i++) {
            assetStakedAmounts[i].asset = assets[i];
            assetStakedAmounts[i].userStake = solidStakingViewActions.balanceOf(assets[i], user);
            assetStakedAmounts[i].totalStaked = solidStakingViewActions.totalStaked(assets[i]);
        }
        return assetStakedAmounts;
    }

    /// @dev Claims all accrued rewards for a user on behalf, for the specified asset, 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, uint[] memory claimedAmounts) {
        uint rewardsListLength = _rewardsList.length;
        rewardsList = new address[](rewardsListLength);
        claimedAmounts = new uint[](rewardsListLength);

        _updateAllRewardDistributionsAndUserRewardsForAssets(user, _getAssetStakedAmounts(assets, user));

        for (uint i; i < assets.length; i++) {
            address asset = assets[i];
            for (uint j; j < rewardsListLength; j++) {
                if (rewardsList[j] == address(0)) {
                    rewardsList[j] = _rewardsList[j];
                }
                uint rewardAmount = _assetData[asset]
                    .rewardDistribution[rewardsList[j]]
                    .userReward[user]
                    .accrued;
                if (rewardAmount != 0) {
                    claimedAmounts[j] += rewardAmount;
                    _assetData[asset].rewardDistribution[rewardsList[j]].userReward[user].accrued = 0;
                }
            }
        }
        for (uint i; 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
    /// @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,
        uint amount
    ) internal {
        GPv2SafeERC20.safeTransferFrom(IERC20(reward), REWARDS_VAULT, to, amount);
    }

    /// @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 {
        if (rewardOracle.latestAnswer() <= 0) {
            revert InvalidRewardOracle(reward, address(rewardOracle));
        }

        _rewardOracle[reward] = rewardOracle;
        emit RewardOracleUpdated(reward, address(rewardOracle));
    }

    function _setRewardsVault(address rewardsVault) internal {
        REWARDS_VAULT = rewardsVault;
        emit RewardsVaultUpdated(rewardsVault);
    }

    function _setSolidStaking(address solidStaking) internal {
        solidStakingViewActions = ISolidStakingViewActions(solidStaking);
        emit SolidStakingUpdated(solidStaking);
    }
}

File 1 of 12 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @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);

    /**
     * @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 `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, 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 `from` to `to` 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 from,
        address to,
        uint256 amount
    ) external returns (bool);
}

File 1 of 12 : IERC20Metadata.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)

pragma solidity ^0.8.0;

import "../IERC20.sol";

/**
 * @dev Interface for the optional metadata functions from the ERC20 standard.
 *
 * _Available since v4.1._
 */
interface IERC20Metadata is IERC20 {
    /**
     * @dev Returns the name of the token.
     */
    function name() external view returns (string memory);

    /**
     * @dev Returns the symbol of the token.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the decimals places of the token.
     */
    function decimals() external view returns (uint8);
}

File 1 of 12 : SafeCast.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SafeCast.sol)
// This file was procedurally generated from scripts/generate/templates/SafeCast.js.

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 uint248 from uint256, reverting on
     * overflow (when the input is greater than largest uint248).
     *
     * Counterpart to Solidity's `uint248` operator.
     *
     * Requirements:
     *
     * - input must fit into 248 bits
     *
     * _Available since v4.7._
     */
    function toUint248(uint256 value) internal pure returns (uint248) {
        require(value <= type(uint248).max, "SafeCast: value doesn't fit in 248 bits");
        return uint248(value);
    }

    /**
     * @dev Returns the downcasted uint240 from uint256, reverting on
     * overflow (when the input is greater than largest uint240).
     *
     * Counterpart to Solidity's `uint240` operator.
     *
     * Requirements:
     *
     * - input must fit into 240 bits
     *
     * _Available since v4.7._
     */
    function toUint240(uint256 value) internal pure returns (uint240) {
        require(value <= type(uint240).max, "SafeCast: value doesn't fit in 240 bits");
        return uint240(value);
    }

    /**
     * @dev Returns the downcasted uint232 from uint256, reverting on
     * overflow (when the input is greater than largest uint232).
     *
     * Counterpart to Solidity's `uint232` operator.
     *
     * Requirements:
     *
     * - input must fit into 232 bits
     *
     * _Available since v4.7._
     */
    function toUint232(uint256 value) internal pure returns (uint232) {
        require(value <= type(uint232).max, "SafeCast: value doesn't fit in 232 bits");
        return uint232(value);
    }

    /**
     * @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
     *
     * _Available since v4.2._
     */
    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 uint216 from uint256, reverting on
     * overflow (when the input is greater than largest uint216).
     *
     * Counterpart to Solidity's `uint216` operator.
     *
     * Requirements:
     *
     * - input must fit into 216 bits
     *
     * _Available since v4.7._
     */
    function toUint216(uint256 value) internal pure returns (uint216) {
        require(value <= type(uint216).max, "SafeCast: value doesn't fit in 216 bits");
        return uint216(value);
    }

    /**
     * @dev Returns the downcasted uint208 from uint256, reverting on
     * overflow (when the input is greater than largest uint208).
     *
     * Counterpart to Solidity's `uint208` operator.
     *
     * Requirements:
     *
     * - input must fit into 208 bits
     *
     * _Available since v4.7._
     */
    function toUint208(uint256 value) internal pure returns (uint208) {
        require(value <= type(uint208).max, "SafeCast: value doesn't fit in 208 bits");
        return uint208(value);
    }

    /**
     * @dev Returns the downcasted uint200 from uint256, reverting on
     * overflow (when the input is greater than largest uint200).
     *
     * Counterpart to Solidity's `uint200` operator.
     *
     * Requirements:
     *
     * - input must fit into 200 bits
     *
     * _Available since v4.7._
     */
    function toUint200(uint256 value) internal pure returns (uint200) {
        require(value <= type(uint200).max, "SafeCast: value doesn't fit in 200 bits");
        return uint200(value);
    }

    /**
     * @dev Returns the downcasted uint192 from uint256, reverting on
     * overflow (when the input is greater than largest uint192).
     *
     * Counterpart to Solidity's `uint192` operator.
     *
     * Requirements:
     *
     * - input must fit into 192 bits
     *
     * _Available since v4.7._
     */
    function toUint192(uint256 value) internal pure returns (uint192) {
        require(value <= type(uint192).max, "SafeCast: value doesn't fit in 192 bits");
        return uint192(value);
    }

    /**
     * @dev Returns the downcasted uint184 from uint256, reverting on
     * overflow (when the input is greater than largest uint184).
     *
     * Counterpart to Solidity's `uint184` operator.
     *
     * Requirements:
     *
     * - input must fit into 184 bits
     *
     * _Available since v4.7._
     */
    function toUint184(uint256 value) internal pure returns (uint184) {
        require(value <= type(uint184).max, "SafeCast: value doesn't fit in 184 bits");
        return uint184(value);
    }

    /**
     * @dev Returns the downcasted uint176 from uint256, reverting on
     * overflow (when the input is greater than largest uint176).
     *
     * Counterpart to Solidity's `uint176` operator.
     *
     * Requirements:
     *
     * - input must fit into 176 bits
     *
     * _Available since v4.7._
     */
    function toUint176(uint256 value) internal pure returns (uint176) {
        require(value <= type(uint176).max, "SafeCast: value doesn't fit in 176 bits");
        return uint176(value);
    }

    /**
     * @dev Returns the downcasted uint168 from uint256, reverting on
     * overflow (when the input is greater than largest uint168).
     *
     * Counterpart to Solidity's `uint168` operator.
     *
     * Requirements:
     *
     * - input must fit into 168 bits
     *
     * _Available since v4.7._
     */
    function toUint168(uint256 value) internal pure returns (uint168) {
        require(value <= type(uint168).max, "SafeCast: value doesn't fit in 168 bits");
        return uint168(value);
    }

    /**
     * @dev Returns the downcasted uint160 from uint256, reverting on
     * overflow (when the input is greater than largest uint160).
     *
     * Counterpart to Solidity's `uint160` operator.
     *
     * Requirements:
     *
     * - input must fit into 160 bits
     *
     * _Available since v4.7._
     */
    function toUint160(uint256 value) internal pure returns (uint160) {
        require(value <= type(uint160).max, "SafeCast: value doesn't fit in 160 bits");
        return uint160(value);
    }

    /**
     * @dev Returns the downcasted uint152 from uint256, reverting on
     * overflow (when the input is greater than largest uint152).
     *
     * Counterpart to Solidity's `uint152` operator.
     *
     * Requirements:
     *
     * - input must fit into 152 bits
     *
     * _Available since v4.7._
     */
    function toUint152(uint256 value) internal pure returns (uint152) {
        require(value <= type(uint152).max, "SafeCast: value doesn't fit in 152 bits");
        return uint152(value);
    }

    /**
     * @dev Returns the downcasted uint144 from uint256, reverting on
     * overflow (when the input is greater than largest uint144).
     *
     * Counterpart to Solidity's `uint144` operator.
     *
     * Requirements:
     *
     * - input must fit into 144 bits
     *
     * _Available since v4.7._
     */
    function toUint144(uint256 value) internal pure returns (uint144) {
        require(value <= type(uint144).max, "SafeCast: value doesn't fit in 144 bits");
        return uint144(value);
    }

    /**
     * @dev Returns the downcasted uint136 from uint256, reverting on
     * overflow (when the input is greater than largest uint136).
     *
     * Counterpart to Solidity's `uint136` operator.
     *
     * Requirements:
     *
     * - input must fit into 136 bits
     *
     * _Available since v4.7._
     */
    function toUint136(uint256 value) internal pure returns (uint136) {
        require(value <= type(uint136).max, "SafeCast: value doesn't fit in 136 bits");
        return uint136(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
     *
     * _Available since v2.5._
     */
    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 uint120 from uint256, reverting on
     * overflow (when the input is greater than largest uint120).
     *
     * Counterpart to Solidity's `uint120` operator.
     *
     * Requirements:
     *
     * - input must fit into 120 bits
     *
     * _Available since v4.7._
     */
    function toUint120(uint256 value) internal pure returns (uint120) {
        require(value <= type(uint120).max, "SafeCast: value doesn't fit in 120 bits");
        return uint120(value);
    }

    /**
     * @dev Returns the downcasted uint112 from uint256, reverting on
     * overflow (when the input is greater than largest uint112).
     *
     * Counterpart to Solidity's `uint112` operator.
     *
     * Requirements:
     *
     * - input must fit into 112 bits
     *
     * _Available since v4.7._
     */
    function toUint112(uint256 value) internal pure returns (uint112) {
        require(value <= type(uint112).max, "SafeCast: value doesn't fit in 112 bits");
        return uint112(value);
    }

    /**
     * @dev Returns the downcasted uint104 from uint256, reverting on
     * overflow (when the input is greater than largest uint104).
     *
     * Counterpart to Solidity's `uint104` operator.
     *
     * Requirements:
     *
     * - input must fit into 104 bits
     *
     * _Available since v4.7._
     */
    function toUint104(uint256 value) internal pure returns (uint104) {
        require(value <= type(uint104).max, "SafeCast: value doesn't fit in 104 bits");
        return uint104(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
     *
     * _Available since v4.2._
     */
    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 uint88 from uint256, reverting on
     * overflow (when the input is greater than largest uint88).
     *
     * Counterpart to Solidity's `uint88` operator.
     *
     * Requirements:
     *
     * - input must fit into 88 bits
     *
     * _Available since v4.7._
     */
    function toUint88(uint256 value) internal pure returns (uint88) {
        require(value <= type(uint88).max, "SafeCast: value doesn't fit in 88 bits");
        return uint88(value);
    }

    /**
     * @dev Returns the downcasted uint80 from uint256, reverting on
     * overflow (when the input is greater than largest uint80).
     *
     * Counterpart to Solidity's `uint80` operator.
     *
     * Requirements:
     *
     * - input must fit into 80 bits
     *
     * _Available since v4.7._
     */
    function toUint80(uint256 value) internal pure returns (uint80) {
        require(value <= type(uint80).max, "SafeCast: value doesn't fit in 80 bits");
        return uint80(value);
    }

    /**
     * @dev Returns the downcasted uint72 from uint256, reverting on
     * overflow (when the input is greater than largest uint72).
     *
     * Counterpart to Solidity's `uint72` operator.
     *
     * Requirements:
     *
     * - input must fit into 72 bits
     *
     * _Available since v4.7._
     */
    function toUint72(uint256 value) internal pure returns (uint72) {
        require(value <= type(uint72).max, "SafeCast: value doesn't fit in 72 bits");
        return uint72(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
     *
     * _Available since v2.5._
     */
    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 uint56 from uint256, reverting on
     * overflow (when the input is greater than largest uint56).
     *
     * Counterpart to Solidity's `uint56` operator.
     *
     * Requirements:
     *
     * - input must fit into 56 bits
     *
     * _Available since v4.7._
     */
    function toUint56(uint256 value) internal pure returns (uint56) {
        require(value <= type(uint56).max, "SafeCast: value doesn't fit in 56 bits");
        return uint56(value);
    }

    /**
     * @dev Returns the downcasted uint48 from uint256, reverting on
     * overflow (when the input is greater than largest uint48).
     *
     * Counterpart to Solidity's `uint48` operator.
     *
     * Requirements:
     *
     * - input must fit into 48 bits
     *
     * _Available since v4.7._
     */
    function toUint48(uint256 value) internal pure returns (uint48) {
        require(value <= type(uint48).max, "SafeCast: value doesn't fit in 48 bits");
        return uint48(value);
    }

    /**
     * @dev Returns the downcasted uint40 from uint256, reverting on
     * overflow (when the input is greater than largest uint40).
     *
     * Counterpart to Solidity's `uint40` operator.
     *
     * Requirements:
     *
     * - input must fit into 40 bits
     *
     * _Available since v4.7._
     */
    function toUint40(uint256 value) internal pure returns (uint40) {
        require(value <= type(uint40).max, "SafeCast: value doesn't fit in 40 bits");
        return uint40(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
     *
     * _Available since v2.5._
     */
    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 uint24 from uint256, reverting on
     * overflow (when the input is greater than largest uint24).
     *
     * Counterpart to Solidity's `uint24` operator.
     *
     * Requirements:
     *
     * - input must fit into 24 bits
     *
     * _Available since v4.7._
     */
    function toUint24(uint256 value) internal pure returns (uint24) {
        require(value <= type(uint24).max, "SafeCast: value doesn't fit in 24 bits");
        return uint24(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
     *
     * _Available since v2.5._
     */
    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
     *
     * _Available since v2.5._
     */
    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.
     *
     * _Available since v3.0._
     */
    function toUint256(int256 value) internal pure returns (uint256) {
        require(value >= 0, "SafeCast: value must be positive");
        return uint256(value);
    }

    /**
     * @dev Returns the downcasted int248 from int256, reverting on
     * overflow (when the input is less than smallest int248 or
     * greater than largest int248).
     *
     * Counterpart to Solidity's `int248` operator.
     *
     * Requirements:
     *
     * - input must fit into 248 bits
     *
     * _Available since v4.7._
     */
    function toInt248(int256 value) internal pure returns (int248 downcasted) {
        downcasted = int248(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 248 bits");
    }

    /**
     * @dev Returns the downcasted int240 from int256, reverting on
     * overflow (when the input is less than smallest int240 or
     * greater than largest int240).
     *
     * Counterpart to Solidity's `int240` operator.
     *
     * Requirements:
     *
     * - input must fit into 240 bits
     *
     * _Available since v4.7._
     */
    function toInt240(int256 value) internal pure returns (int240 downcasted) {
        downcasted = int240(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 240 bits");
    }

    /**
     * @dev Returns the downcasted int232 from int256, reverting on
     * overflow (when the input is less than smallest int232 or
     * greater than largest int232).
     *
     * Counterpart to Solidity's `int232` operator.
     *
     * Requirements:
     *
     * - input must fit into 232 bits
     *
     * _Available since v4.7._
     */
    function toInt232(int256 value) internal pure returns (int232 downcasted) {
        downcasted = int232(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 232 bits");
    }

    /**
     * @dev Returns the downcasted int224 from int256, reverting on
     * overflow (when the input is less than smallest int224 or
     * greater than largest int224).
     *
     * Counterpart to Solidity's `int224` operator.
     *
     * Requirements:
     *
     * - input must fit into 224 bits
     *
     * _Available since v4.7._
     */
    function toInt224(int256 value) internal pure returns (int224 downcasted) {
        downcasted = int224(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 224 bits");
    }

    /**
     * @dev Returns the downcasted int216 from int256, reverting on
     * overflow (when the input is less than smallest int216 or
     * greater than largest int216).
     *
     * Counterpart to Solidity's `int216` operator.
     *
     * Requirements:
     *
     * - input must fit into 216 bits
     *
     * _Available since v4.7._
     */
    function toInt216(int256 value) internal pure returns (int216 downcasted) {
        downcasted = int216(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 216 bits");
    }

    /**
     * @dev Returns the downcasted int208 from int256, reverting on
     * overflow (when the input is less than smallest int208 or
     * greater than largest int208).
     *
     * Counterpart to Solidity's `int208` operator.
     *
     * Requirements:
     *
     * - input must fit into 208 bits
     *
     * _Available since v4.7._
     */
    function toInt208(int256 value) internal pure returns (int208 downcasted) {
        downcasted = int208(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 208 bits");
    }

    /**
     * @dev Returns the downcasted int200 from int256, reverting on
     * overflow (when the input is less than smallest int200 or
     * greater than largest int200).
     *
     * Counterpart to Solidity's `int200` operator.
     *
     * Requirements:
     *
     * - input must fit into 200 bits
     *
     * _Available since v4.7._
     */
    function toInt200(int256 value) internal pure returns (int200 downcasted) {
        downcasted = int200(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 200 bits");
    }

    /**
     * @dev Returns the downcasted int192 from int256, reverting on
     * overflow (when the input is less than smallest int192 or
     * greater than largest int192).
     *
     * Counterpart to Solidity's `int192` operator.
     *
     * Requirements:
     *
     * - input must fit into 192 bits
     *
     * _Available since v4.7._
     */
    function toInt192(int256 value) internal pure returns (int192 downcasted) {
        downcasted = int192(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 192 bits");
    }

    /**
     * @dev Returns the downcasted int184 from int256, reverting on
     * overflow (when the input is less than smallest int184 or
     * greater than largest int184).
     *
     * Counterpart to Solidity's `int184` operator.
     *
     * Requirements:
     *
     * - input must fit into 184 bits
     *
     * _Available since v4.7._
     */
    function toInt184(int256 value) internal pure returns (int184 downcasted) {
        downcasted = int184(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 184 bits");
    }

    /**
     * @dev Returns the downcasted int176 from int256, reverting on
     * overflow (when the input is less than smallest int176 or
     * greater than largest int176).
     *
     * Counterpart to Solidity's `int176` operator.
     *
     * Requirements:
     *
     * - input must fit into 176 bits
     *
     * _Available since v4.7._
     */
    function toInt176(int256 value) internal pure returns (int176 downcasted) {
        downcasted = int176(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 176 bits");
    }

    /**
     * @dev Returns the downcasted int168 from int256, reverting on
     * overflow (when the input is less than smallest int168 or
     * greater than largest int168).
     *
     * Counterpart to Solidity's `int168` operator.
     *
     * Requirements:
     *
     * - input must fit into 168 bits
     *
     * _Available since v4.7._
     */
    function toInt168(int256 value) internal pure returns (int168 downcasted) {
        downcasted = int168(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 168 bits");
    }

    /**
     * @dev Returns the downcasted int160 from int256, reverting on
     * overflow (when the input is less than smallest int160 or
     * greater than largest int160).
     *
     * Counterpart to Solidity's `int160` operator.
     *
     * Requirements:
     *
     * - input must fit into 160 bits
     *
     * _Available since v4.7._
     */
    function toInt160(int256 value) internal pure returns (int160 downcasted) {
        downcasted = int160(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 160 bits");
    }

    /**
     * @dev Returns the downcasted int152 from int256, reverting on
     * overflow (when the input is less than smallest int152 or
     * greater than largest int152).
     *
     * Counterpart to Solidity's `int152` operator.
     *
     * Requirements:
     *
     * - input must fit into 152 bits
     *
     * _Available since v4.7._
     */
    function toInt152(int256 value) internal pure returns (int152 downcasted) {
        downcasted = int152(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 152 bits");
    }

    /**
     * @dev Returns the downcasted int144 from int256, reverting on
     * overflow (when the input is less than smallest int144 or
     * greater than largest int144).
     *
     * Counterpart to Solidity's `int144` operator.
     *
     * Requirements:
     *
     * - input must fit into 144 bits
     *
     * _Available since v4.7._
     */
    function toInt144(int256 value) internal pure returns (int144 downcasted) {
        downcasted = int144(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 144 bits");
    }

    /**
     * @dev Returns the downcasted int136 from int256, reverting on
     * overflow (when the input is less than smallest int136 or
     * greater than largest int136).
     *
     * Counterpart to Solidity's `int136` operator.
     *
     * Requirements:
     *
     * - input must fit into 136 bits
     *
     * _Available since v4.7._
     */
    function toInt136(int256 value) internal pure returns (int136 downcasted) {
        downcasted = int136(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 136 bits");
    }

    /**
     * @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 downcasted) {
        downcasted = int128(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 128 bits");
    }

    /**
     * @dev Returns the downcasted int120 from int256, reverting on
     * overflow (when the input is less than smallest int120 or
     * greater than largest int120).
     *
     * Counterpart to Solidity's `int120` operator.
     *
     * Requirements:
     *
     * - input must fit into 120 bits
     *
     * _Available since v4.7._
     */
    function toInt120(int256 value) internal pure returns (int120 downcasted) {
        downcasted = int120(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 120 bits");
    }

    /**
     * @dev Returns the downcasted int112 from int256, reverting on
     * overflow (when the input is less than smallest int112 or
     * greater than largest int112).
     *
     * Counterpart to Solidity's `int112` operator.
     *
     * Requirements:
     *
     * - input must fit into 112 bits
     *
     * _Available since v4.7._
     */
    function toInt112(int256 value) internal pure returns (int112 downcasted) {
        downcasted = int112(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 112 bits");
    }

    /**
     * @dev Returns the downcasted int104 from int256, reverting on
     * overflow (when the input is less than smallest int104 or
     * greater than largest int104).
     *
     * Counterpart to Solidity's `int104` operator.
     *
     * Requirements:
     *
     * - input must fit into 104 bits
     *
     * _Available since v4.7._
     */
    function toInt104(int256 value) internal pure returns (int104 downcasted) {
        downcasted = int104(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 104 bits");
    }

    /**
     * @dev Returns the downcasted int96 from int256, reverting on
     * overflow (when the input is less than smallest int96 or
     * greater than largest int96).
     *
     * Counterpart to Solidity's `int96` operator.
     *
     * Requirements:
     *
     * - input must fit into 96 bits
     *
     * _Available since v4.7._
     */
    function toInt96(int256 value) internal pure returns (int96 downcasted) {
        downcasted = int96(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 96 bits");
    }

    /**
     * @dev Returns the downcasted int88 from int256, reverting on
     * overflow (when the input is less than smallest int88 or
     * greater than largest int88).
     *
     * Counterpart to Solidity's `int88` operator.
     *
     * Requirements:
     *
     * - input must fit into 88 bits
     *
     * _Available since v4.7._
     */
    function toInt88(int256 value) internal pure returns (int88 downcasted) {
        downcasted = int88(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 88 bits");
    }

    /**
     * @dev Returns the downcasted int80 from int256, reverting on
     * overflow (when the input is less than smallest int80 or
     * greater than largest int80).
     *
     * Counterpart to Solidity's `int80` operator.
     *
     * Requirements:
     *
     * - input must fit into 80 bits
     *
     * _Available since v4.7._
     */
    function toInt80(int256 value) internal pure returns (int80 downcasted) {
        downcasted = int80(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 80 bits");
    }

    /**
     * @dev Returns the downcasted int72 from int256, reverting on
     * overflow (when the input is less than smallest int72 or
     * greater than largest int72).
     *
     * Counterpart to Solidity's `int72` operator.
     *
     * Requirements:
     *
     * - input must fit into 72 bits
     *
     * _Available since v4.7._
     */
    function toInt72(int256 value) internal pure returns (int72 downcasted) {
        downcasted = int72(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 72 bits");
    }

    /**
     * @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 downcasted) {
        downcasted = int64(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 64 bits");
    }

    /**
     * @dev Returns the downcasted int56 from int256, reverting on
     * overflow (when the input is less than smallest int56 or
     * greater than largest int56).
     *
     * Counterpart to Solidity's `int56` operator.
     *
     * Requirements:
     *
     * - input must fit into 56 bits
     *
     * _Available since v4.7._
     */
    function toInt56(int256 value) internal pure returns (int56 downcasted) {
        downcasted = int56(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 56 bits");
    }

    /**
     * @dev Returns the downcasted int48 from int256, reverting on
     * overflow (when the input is less than smallest int48 or
     * greater than largest int48).
     *
     * Counterpart to Solidity's `int48` operator.
     *
     * Requirements:
     *
     * - input must fit into 48 bits
     *
     * _Available since v4.7._
     */
    function toInt48(int256 value) internal pure returns (int48 downcasted) {
        downcasted = int48(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 48 bits");
    }

    /**
     * @dev Returns the downcasted int40 from int256, reverting on
     * overflow (when the input is less than smallest int40 or
     * greater than largest int40).
     *
     * Counterpart to Solidity's `int40` operator.
     *
     * Requirements:
     *
     * - input must fit into 40 bits
     *
     * _Available since v4.7._
     */
    function toInt40(int256 value) internal pure returns (int40 downcasted) {
        downcasted = int40(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 40 bits");
    }

    /**
     * @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 downcasted) {
        downcasted = int32(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 32 bits");
    }

    /**
     * @dev Returns the downcasted int24 from int256, reverting on
     * overflow (when the input is less than smallest int24 or
     * greater than largest int24).
     *
     * Counterpart to Solidity's `int24` operator.
     *
     * Requirements:
     *
     * - input must fit into 24 bits
     *
     * _Available since v4.7._
     */
    function toInt24(int256 value) internal pure returns (int24 downcasted) {
        downcasted = int24(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 24 bits");
    }

    /**
     * @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 downcasted) {
        downcasted = int16(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 16 bits");
    }

    /**
     * @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 downcasted) {
        downcasted = int8(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 8 bits");
    }

    /**
     * @dev Converts an unsigned uint256 into a signed int256.
     *
     * Requirements:
     *
     * - input must be less than or equal to maxInt256.
     *
     * _Available since v3.0._
     */
    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);
    }
}

File 1 of 12 : PostConstruct.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.16;

/// @notice Simple contract exposing a modifier used on setup functions
/// to prevent them from being called more than once
/// @author Solid World DAO
abstract contract PostConstruct {
    error AlreadyInitialized();

    bool private _initialized;

    modifier postConstruct() {
        if (_initialized) {
            revert AlreadyInitialized();
        }
        _initialized = true;
        _;
    }
}

File 1 of 12 : IEACAggregatorProxy.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.7.6 <0.9.0;

interface IEACAggregatorProxy {
    function decimals() external view returns (uint8);

    function latestAnswer() external view returns (int);
}

File 1 of 12 : IRewardsController.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;

import "./IRewardsDistributor.sol";
import "../../libraries/RewardsDataTypes.sol";

/// @title IRewardsController
/// @author Aave
/// @notice Defines the basic interface for a Rewards Controller.
interface IRewardsController is IRewardsDistributor {
    error UnauthorizedClaimer(address claimer, address user);
    error NotSolidStaking(address sender);
    error InvalidRewardOracle(address reward, address rewardOracle);

    /// @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 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,
        uint amount
    );

    /// @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);

    /// @param rewardsVault The address of the account that secures ERC20 rewards.
    event RewardsVaultUpdated(address indexed rewardsVault);

    /// @param solidStaking Used to fetch the total amount staked and the stake of an user for a given asset
    event SolidStakingUpdated(address indexed solidStaking);

    /// @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 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;

    /// @param rewardsVault The address of the account that secures ERC20 rewards.
    function setRewardsVault(address rewardsVault) external;

    /// @param solidStaking Used to fetch the total amount staked and the stake of an user for a given asset
    function setSolidStaking(address solidStaking) 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);

    /// @return Account that secures ERC20 rewards.
    function getRewardsVault() 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 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 totalStaked: The total amount staked of the asset
    ///   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
    ///   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.DistributionConfig[] memory config) external;

    /// @dev Called by the corresponding asset on transfer hook in order to update the rewards distribution.
    /// @param asset The incentivized asset address
    /// @param user The address of the user whose asset balance has changed
    /// @param oldUserStake The amount of assets staked by the user, prior to stake change
    /// @param oldTotalStaked The total amount staked of the asset, prior to stake change
    function handleUserStakeChanged(
        address asset,
        address user,
        uint oldUserStake,
        uint oldTotalStaked
    ) external;

    /// @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, uint[] 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, uint[] 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, uint[] memory claimedAmounts);
}

File 1 of 12 : IRewardsDistributor.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;

/// @title IRewardsDistributor
/// @author Aave
/// @notice Defines the basic interface for a Rewards Distributor.
interface IRewardsDistributor {
    error NotEmissionManager(address sender);
    error InvalidInput();
    error InvalidAssetDecimals(address asset);
    error IndexOverflow(uint newIndex);
    error DistributionNonExistent(address asset, address reward);

    /// @param asset The address of the incentivized asset
    /// @param reward The address of the reward token
    error UpdateDistributionNotApplicable(address asset, address reward);

    /// @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,
        uint oldEmission,
        uint newEmission,
        uint oldDistributionEnd,
        uint newDistributionEnd,
        uint 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,
        uint assetIndex,
        uint userIndex,
        uint rewardsAccrued
    );

    /// @dev Emitted when the emission manager address is updated.
    /// @param oldEmissionManager The address of the old emission manager
    /// @param newEmissionManager The address of the new emission manager
    event EmissionManagerUpdated(address indexed oldEmissionManager, address indexed newEmissionManager);

    /// @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;

    /// @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 Updates weekly reward distributions
    /// @param assets List of incentivized assets getting updated
    /// @param rewards List of reward tokens getting updated
    /// @param rewardAmounts List of carbon reward amounts getting distributed
    function updateCarbonRewardDistribution(
        address[] calldata assets,
        address[] calldata rewards,
        uint[] calldata rewardAmounts
    ) external;

    /// @param asset The incentivized asset
    /// @param reward The reward token of the incentivized asset
    /// @return true, if distribution can be updated for the asset - reward pair
    function canUpdateCarbonRewardDistribution(address asset, address reward) external view returns (bool);

    /// @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 (uint);

    /// @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 getUserIndex(
        address user,
        address asset,
        address reward
    ) external view returns (uint);

    /// @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 getRewardDistribution(address asset, address reward)
        external
        view
        returns (
            uint,
            uint,
            uint,
            uint
        );

    /// @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 getAllRewards() 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 getAccruedRewardAmountForUser(address user, address reward) external view returns (uint);

    /// @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 getUnclaimedRewardAmountForUserAndAssets(
        address[] calldata assets,
        address user,
        address reward
    ) external view returns (uint);

    /// @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 getAllUnclaimedRewardAmountsForUserAndAssets(address[] calldata assets, address user)
        external
        view
        returns (address[] memory, uint[] 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 getEmissionManager() external view returns (address);

    /// @dev Updates the address of the emission manager
    /// @param emissionManager The address of the new EmissionManager
    function setEmissionManager(address emissionManager) external;
}

File 1 of 12 : ISolidStakingViewActions.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.16;

/// @title Permissionless view actions
/// @notice Contains view functions that can be called by anyone
/// @author Solid World DAO
interface ISolidStakingViewActions {
    /// @dev Computes the amount of tokens that the `account` has staked
    /// @param token the token to check
    /// @param account the account to check
    /// @return the amount of `token` tokens that the `account` has staked
    function balanceOf(address token, address account) external view returns (uint);

    /// @dev Computes the total amount of tokens that have been staked
    /// @param token the token to check
    /// @return the total amount of `token` tokens that have been staked
    function totalStaked(address token) external view returns (uint);

    /// @dev Returns the list of tokens that can be staked
    /// @return the list of tokens that can be staked
    function getTokens() external view returns (address[] memory);

    /// @return whether the specified token requires msg.sender to be KYCed before staking
    function isKYCRequired(address token) external view returns (bool);

    /// @return The address controlling timelocked functions (e.g. KYC requirement changes)
    function getTimelockController() external view returns (address);
}

File 1 of 12 : GPv2SafeERC20.sol
// SPDX-License-Identifier: LGPL-3.0-or-later
pragma solidity 0.8.16;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

/// @title Gnosis Protocol v2 Safe ERC20 Transfer Library
/// @author Gnosis Developers
/// @dev Gas-efficient version of Openzeppelin's SafeERC20 contract.
library GPv2SafeERC20 {
    /// @dev Wrapper around a call to the ERC20 function `transfer` that reverts
    /// also when the token returns `false`.
    function safeTransfer(
        IERC20 token,
        address to,
        uint256 value
    ) internal {
        bytes4 selector_ = token.transfer.selector;

        // solhint-disable-next-line no-inline-assembly
        assembly {
            let freeMemoryPointer := mload(0x40)
            mstore(freeMemoryPointer, selector_)
            mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff))
            mstore(add(freeMemoryPointer, 36), value)

            if iszero(call(gas(), token, 0, freeMemoryPointer, 68, 0, 0)) {
                returndatacopy(0, 0, returndatasize())
                revert(0, returndatasize())
            }
        }

        require(getLastTransferResult(token), "GPv2: failed transfer");
    }

    /// @dev Wrapper around a call to the ERC20 function `transferFrom` that
    /// reverts also when the token returns `false`.
    function safeTransferFrom(
        IERC20 token,
        address from,
        address to,
        uint256 value
    ) internal {
        bytes4 selector_ = token.transferFrom.selector;

        // solhint-disable-next-line no-inline-assembly
        assembly {
            let freeMemoryPointer := mload(0x40)
            mstore(freeMemoryPointer, selector_)
            mstore(add(freeMemoryPointer, 4), and(from, 0xffffffffffffffffffffffffffffffffffffffff))
            mstore(add(freeMemoryPointer, 36), and(to, 0xffffffffffffffffffffffffffffffffffffffff))
            mstore(add(freeMemoryPointer, 68), value)

            if iszero(call(gas(), token, 0, freeMemoryPointer, 100, 0, 0)) {
                returndatacopy(0, 0, returndatasize())
                revert(0, returndatasize())
            }
        }

        require(getLastTransferResult(token), "GPv2: failed transferFrom");
    }

    /// @dev Verifies that the last return was a successful `transfer*` call.
    /// This is done by checking that the return data is either empty, or
    /// is a valid ABI encoded boolean.
    function getLastTransferResult(IERC20 token) private view returns (bool success) {
        // NOTE: Inspecting previous return data requires assembly. Note that
        // we write the return data to memory 0 in the case where the return
        // data size is 32, this is OK since the first 64 bytes of memory are
        // reserved by Solidy as a scratch space that can be used within
        // assembly blocks.
        // <https://docs.soliditylang.org/en/v0.7.6/internals/layout_in_memory.html>
        // solhint-disable-next-line no-inline-assembly
        assembly {
            /// @dev Revert with an ABI encoded Solidity error with a message
            /// that fits into 32-bytes.
            ///
            /// An ABI encoded Solidity error has the following memory layout:
            ///
            /// ------------+----------------------------------
            ///  byte range | value
            /// ------------+----------------------------------
            ///  0x00..0x04 |        selector("Error(string)")
            ///  0x04..0x24 |      string offset (always 0x20)
            ///  0x24..0x44 |                    string length
            ///  0x44..0x64 | string value, padded to 32-bytes
            function revertWithMessage(length, message) {
                mstore(0x00, "\x08\xc3\x79\xa0")
                mstore(0x04, 0x20)
                mstore(0x24, length)
                mstore(0x44, message)
                revert(0x00, 0x64)
            }

            switch returndatasize()
            // Non-standard ERC20 transfer without return.
            case 0 {
                // NOTE: When the return data size is 0, verify that there
                // is code at the address. This is done in order to maintain
                // compatibility with Solidity calling conventions.
                // <https://docs.soliditylang.org/en/v0.7.6/control-structures.html#external-function-calls>
                if iszero(extcodesize(token)) {
                    revertWithMessage(20, "GPv2: not a contract")
                }

                success := 1
            }
            // Standard ERC20 transfer returning boolean success value.
            case 32 {
                returndatacopy(0, 0, returndatasize())

                // NOTE: For ABI encoding v1, any non-zero value is accepted
                // as `true` for a boolean. In order to stay compatible with
                // OpenZeppelin's `SafeERC20` library which is known to work
                // with the existing ERC20 implementation we care about,
                // make sure we return success for any non-zero return value
                // from the `transfer*` call.
                success := iszero(iszero(mload(0)))
            }
            default {
                revertWithMessage(31, "GPv2: malformed transfer result")
            }
        }
    }
}

File 1 of 12 : RewardsDataTypes.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;

import "../interfaces/rewards/IEACAggregatorProxy.sol";

library RewardsDataTypes {
    struct DistributionConfig {
        uint88 emissionPerSecond;
        uint totalStaked;
        uint32 distributionEnd;
        address asset;
        address reward;
        IEACAggregatorProxy rewardOracle;
    }

    struct AssetStakedAmounts {
        address asset;
        uint userStake;
        uint totalStaked;
    }

    struct AssetData {
        mapping(address => RewardDistribution) rewardDistribution;
        mapping(uint128 => address) availableRewards;
        uint128 availableRewardsCount;
        uint8 decimals;
    }

    struct RewardDistribution {
        uint104 index;
        uint88 emissionPerSecond;
        uint32 lastUpdateTimestamp;
        uint32 distributionEnd;
        mapping(address => UserReward) userReward;
    }

    struct UserReward {
        uint104 index;
        uint128 accrued;
    }
}

File 2 of 12 : RewardsDistributor.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;

import "@openzeppelin/contracts/utils/math/SafeCast.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";

import "../interfaces/rewards/IRewardsDistributor.sol";
import "../libraries/RewardsDataTypes.sol";
import "../interfaces/staking/ISolidStakingViewActions.sol";

abstract contract RewardsDistributor is IRewardsDistributor {
    using SafeCast for uint;

    // asset => AssetData
    mapping(address => RewardsDataTypes.AssetData) internal _assetData;
    // reward => enabled
    mapping(address => bool) internal _isRewardEnabled;

    address[] internal _rewardsList;
    address[] internal _assetsList;
    address internal _emissionManager;

    /// @dev Used to fetch the total amount staked and the stake of an user for a given asset
    ISolidStakingViewActions public solidStakingViewActions;

    modifier onlyEmissionManager() {
        if (msg.sender != _emissionManager) {
            revert NotEmissionManager(msg.sender);
        }
        _;
    }

    modifier distributionExists(address asset, address reward) {
        RewardsDataTypes.RewardDistribution storage rewardDistribution = _assetData[asset].rewardDistribution[
            reward
        ];
        uint decimals = _assetData[asset].decimals;
        if (decimals == 0 || rewardDistribution.lastUpdateTimestamp == 0) {
            revert DistributionNonExistent(asset, reward);
        }
        _;
    }

    /// @inheritdoc IRewardsDistributor
    function getRewardDistribution(address asset, address reward)
        public
        view
        returns (
            uint,
            uint,
            uint,
            uint
        )
    {
        return (
            _assetData[asset].rewardDistribution[reward].index,
            _assetData[asset].rewardDistribution[reward].emissionPerSecond,
            _assetData[asset].rewardDistribution[reward].lastUpdateTimestamp,
            _assetData[asset].rewardDistribution[reward].distributionEnd
        );
    }

    /// @inheritdoc IRewardsDistributor
    function getDistributionEnd(address asset, address reward) external view returns (uint) {
        return _assetData[asset].rewardDistribution[reward].distributionEnd;
    }

    /// @inheritdoc IRewardsDistributor
    function getRewardsByAsset(address asset) external view returns (address[] memory) {
        uint128 rewardsCount = _assetData[asset].availableRewardsCount;
        address[] memory rewards = new address[](rewardsCount);

        for (uint128 i; i < rewardsCount; i++) {
            rewards[i] = _assetData[asset].availableRewards[i];
        }
        return rewards;
    }

    /// @inheritdoc IRewardsDistributor
    function getAllRewards() external view returns (address[] memory) {
        return _rewardsList;
    }

    /// @inheritdoc IRewardsDistributor
    function getUserIndex(
        address user,
        address asset,
        address reward
    ) public view returns (uint) {
        return _assetData[asset].rewardDistribution[reward].userReward[user].index;
    }

    /// @inheritdoc IRewardsDistributor
    function getAccruedRewardAmountForUser(address user, address reward) external view returns (uint) {
        uint totalAccrued;
        for (uint i; i < _assetsList.length; i++) {
            totalAccrued += _assetData[_assetsList[i]].rewardDistribution[reward].userReward[user].accrued;
        }

        return totalAccrued;
    }

    /// @inheritdoc IRewardsDistributor
    function getUnclaimedRewardAmountForUserAndAssets(
        address[] calldata assets,
        address user,
        address reward
    ) external view returns (uint unclaimedAmount) {
        RewardsDataTypes.AssetStakedAmounts[] memory assetStakedAmounts = _getAssetStakedAmounts(
            assets,
            user
        );

        for (uint i; i < assetStakedAmounts.length; i++) {
            if (assetStakedAmounts[i].userStake == 0) {
                unclaimedAmount += _assetData[assetStakedAmounts[i].asset]
                    .rewardDistribution[reward]
                    .userReward[user]
                    .accrued;
            } else {
                unclaimedAmount +=
                    _computePendingRewardAmountForUser(user, reward, assetStakedAmounts[i]) +
                    _assetData[assetStakedAmounts[i].asset]
                        .rewardDistribution[reward]
                        .userReward[user]
                        .accrued;
            }
        }

        return unclaimedAmount;
    }

    /// @inheritdoc IRewardsDistributor
    function getAllUnclaimedRewardAmountsForUserAndAssets(address[] calldata assets, address user)
        external
        view
        returns (address[] memory rewardsList, uint[] memory unclaimedAmounts)
    {
        RewardsDataTypes.AssetStakedAmounts[] memory assetStakedAmounts = _getAssetStakedAmounts(
            assets,
            user
        );
        rewardsList = new address[](_rewardsList.length);
        unclaimedAmounts = new uint[](rewardsList.length);

        for (uint i; i < assetStakedAmounts.length; i++) {
            for (uint r; r < rewardsList.length; r++) {
                rewardsList[r] = _rewardsList[r];
                unclaimedAmounts[r] += _assetData[assetStakedAmounts[i].asset]
                    .rewardDistribution[rewardsList[r]]
                    .userReward[user]
                    .accrued;

                if (assetStakedAmounts[i].userStake == 0) {
                    continue;
                }
                unclaimedAmounts[r] += _computePendingRewardAmountForUser(
                    user,
                    rewardsList[r],
                    assetStakedAmounts[i]
                );
            }
        }
        return (rewardsList, unclaimedAmounts);
    }

    /// @inheritdoc IRewardsDistributor
    function setDistributionEnd(
        address asset,
        address reward,
        uint32 newDistributionEnd
    ) external onlyEmissionManager distributionExists(asset, reward) {
        uint oldDistributionEnd = _setDistributionEnd(asset, reward, newDistributionEnd);
        uint index = _assetData[asset].rewardDistribution[reward].index;

        emit AssetConfigUpdated(
            asset,
            reward,
            _assetData[asset].rewardDistribution[reward].emissionPerSecond,
            _assetData[asset].rewardDistribution[reward].emissionPerSecond,
            oldDistributionEnd,
            newDistributionEnd,
            index
        );
    }

    /// @inheritdoc IRewardsDistributor
    function setEmissionPerSecond(
        address asset,
        address[] calldata rewards,
        uint88[] calldata newEmissionsPerSecond
    ) external onlyEmissionManager {
        if (rewards.length != newEmissionsPerSecond.length) {
            revert InvalidInput();
        }

        for (uint i; i < rewards.length; i++) {
            (uint oldEmissionPerSecond, uint newIndex, uint distributionEnd) = _setEmissionPerSecond(
                asset,
                rewards[i],
                newEmissionsPerSecond[i]
            );

            emit AssetConfigUpdated(
                asset,
                rewards[i],
                oldEmissionPerSecond,
                newEmissionsPerSecond[i],
                distributionEnd,
                distributionEnd,
                newIndex
            );
        }
    }

    /// @inheritdoc IRewardsDistributor
    function updateCarbonRewardDistribution(
        address[] calldata assets,
        address[] calldata rewards,
        uint[] calldata rewardAmounts
    ) external onlyEmissionManager {
        if (assets.length != rewards.length || rewards.length != rewardAmounts.length) {
            revert InvalidInput();
        }

        for (uint i; i < assets.length; i++) {
            if (!_canUpdateCarbonRewardDistribution(assets[i], rewards[i])) {
                revert UpdateDistributionNotApplicable(assets[i], rewards[i]);
            }

            uint32 newDistributionEnd = _computeNewCarbonRewardDistributionEnd(assets[i], rewards[i]);
            uint88 newEmissionsPerSecond = uint88(rewardAmounts[i] / (newDistributionEnd - block.timestamp));

            (uint oldEmissionPerSecond, uint newIndex, ) = _setEmissionPerSecond(
                assets[i],
                rewards[i],
                newEmissionsPerSecond
            );
            uint oldDistributionEnd = _setDistributionEnd(assets[i], rewards[i], newDistributionEnd);
            emit AssetConfigUpdated(
                assets[i],
                rewards[i],
                oldEmissionPerSecond,
                newEmissionsPerSecond,
                oldDistributionEnd,
                newDistributionEnd,
                newIndex
            );
        }
    }

    /// @inheritdoc IRewardsDistributor
    function getAssetDecimals(address asset) external view returns (uint8) {
        return _assetData[asset].decimals;
    }

    /// @inheritdoc IRewardsDistributor
    function getEmissionManager() external view returns (address) {
        return _emissionManager;
    }

    /// @inheritdoc IRewardsDistributor
    function setEmissionManager(address emissionManager) external onlyEmissionManager {
        _setEmissionManager(emissionManager);
    }

    /// @inheritdoc IRewardsDistributor
    function canUpdateCarbonRewardDistribution(address asset, address reward)
        external
        view
        distributionExists(asset, reward)
        returns (bool)
    {
        return _canUpdateCarbonRewardDistribution(asset, reward);
    }

    function _canUpdateCarbonRewardDistribution(address asset, address reward) internal view returns (bool) {
        uint32 currentDistributionEnd = _assetData[asset].rewardDistribution[reward].distributionEnd;
        uint32 nextDistributionEnd = _computeNewCarbonRewardDistributionEnd(asset, reward);

        bool isInitializedDistribution = currentDistributionEnd != 0;
        bool isBetweenDistributions = block.timestamp >= currentDistributionEnd &&
            block.timestamp < nextDistributionEnd;

        return isInitializedDistribution && isBetweenDistributions;
    }

    function _computeNewCarbonRewardDistributionEnd(address asset, address reward)
        internal
        view
        returns (uint32 newDistributionEnd)
    {
        uint32 currentDistributionEnd = _assetData[asset].rewardDistribution[reward].distributionEnd;

        newDistributionEnd = currentDistributionEnd + 1 weeks;
    }

    /// @dev Configure the _assetData for a specific emission
    /// @param distributionConfig The array of each asset configuration
    function _configureAssets(RewardsDataTypes.DistributionConfig[] memory distributionConfig) internal {
        for (uint i; i < distributionConfig.length; i++) {
            uint8 decimals = IERC20Metadata(distributionConfig[i].asset).decimals();

            if (decimals == 0) {
                revert InvalidAssetDecimals(distributionConfig[i].asset);
            }

            if (_assetData[distributionConfig[i].asset].decimals == 0) {
                _assetsList.push(distributionConfig[i].asset);
            }

            _assetData[distributionConfig[i].asset].decimals = decimals;

            RewardsDataTypes.RewardDistribution storage rewardDistribution = _assetData[
                distributionConfig[i].asset
            ].rewardDistribution[distributionConfig[i].reward];

            if (rewardDistribution.lastUpdateTimestamp == 0) {
                uint128 rewardCount = _assetData[distributionConfig[i].asset].availableRewardsCount;
                _assetData[distributionConfig[i].asset].availableRewards[rewardCount] = distributionConfig[i]
                    .reward;
                _assetData[distributionConfig[i].asset].availableRewardsCount++;
            }

            if (_isRewardEnabled[distributionConfig[i].reward] == false) {
                _isRewardEnabled[distributionConfig[i].reward] = true;
                _rewardsList.push(distributionConfig[i].reward);
            }

            (uint newAssetIndex, ) = _updateRewardDistribution(
                rewardDistribution,
                distributionConfig[i].totalStaked,
                10**decimals
            );

            uint88 oldEmissionsPerSecond = rewardDistribution.emissionPerSecond;
            uint32 oldDistributionEnd = rewardDistribution.distributionEnd;
            rewardDistribution.emissionPerSecond = distributionConfig[i].emissionPerSecond;
            rewardDistribution.distributionEnd = distributionConfig[i].distributionEnd;

            emit AssetConfigUpdated(
                distributionConfig[i].asset,
                distributionConfig[i].reward,
                oldEmissionsPerSecond,
                distributionConfig[i].emissionPerSecond,
                oldDistributionEnd,
                distributionConfig[i].distributionEnd,
                newAssetIndex
            );
        }
    }

    /// @dev Updates rewards distribution and user rewards for all rewards configured for the specified assets
    /// @param user The address of the user
    /// @param assetStakedAmounts List of structs with the user stake and total staked of a set of assets
    function _updateAllRewardDistributionsAndUserRewardsForAssets(
        address user,
        RewardsDataTypes.AssetStakedAmounts[] memory assetStakedAmounts
    ) internal {
        for (uint i; i < assetStakedAmounts.length; i++) {
            _updateAllRewardDistributionsAndUserRewardsForAsset(
                assetStakedAmounts[i].asset,
                user,
                assetStakedAmounts[i].userStake,
                assetStakedAmounts[i].totalStaked
            );
        }
    }

    /// @dev Updates rewards distribution and user rewards for all rewards configured for the specified asset
    /// @dev When call origin is (un)staking, `userStake` and `totalStaked` are prior to the (un)stake action
    /// @dev When call origin is rewards claiming, `userStake` and `totalStaked` are current values
    /// @param asset The address of the incentivized asset
    /// @param user The user address
    /// @param userStake The amount of assets staked by the user
    /// @param totalStaked The total amount staked of the asset
    function _updateAllRewardDistributionsAndUserRewardsForAsset(
        address asset,
        address user,
        uint userStake,
        uint totalStaked
    ) internal {
        uint assetUnit;
        uint numAvailableRewards = _assetData[asset].availableRewardsCount;
        unchecked {
            assetUnit = 10**_assetData[asset].decimals;
        }

        if (numAvailableRewards == 0) {
            return;
        }
        unchecked {
            for (uint128 r; r < numAvailableRewards; r++) {
                address reward = _assetData[asset].availableRewards[r];
                RewardsDataTypes.RewardDistribution storage rewardDistribution = _assetData[asset]
                    .rewardDistribution[reward];

                (uint newAssetIndex, bool rewardDistributionUpdated) = _updateRewardDistribution(
                    rewardDistribution,
                    totalStaked,
                    assetUnit
                );

                (uint rewardsAccrued, bool userRewardUpdated) = _updateUserReward(
                    rewardDistribution,
                    user,
                    userStake,
                    newAssetIndex,
                    assetUnit
                );

                if (rewardDistributionUpdated || userRewardUpdated) {
                    emit Accrued(asset, reward, user, newAssetIndex, newAssetIndex, rewardsAccrued);
                }
            }
        }
    }

    /// @dev Updates the state of the distribution for the specified reward
    /// @param rewardDistribution Storage pointer to the distribution reward config
    /// @param totalStaked The total amount staked of the asset
    /// @param assetUnit One unit of asset (10**decimals)
    /// @return The new distribution index
    /// @return True if the index was updated, false otherwise
    function _updateRewardDistribution(
        RewardsDataTypes.RewardDistribution storage rewardDistribution,
        uint totalStaked,
        uint assetUnit
    ) internal returns (uint, bool) {
        (uint oldIndex, uint newIndex) = _computeNewAssetIndex(rewardDistribution, totalStaked, assetUnit);
        bool indexUpdated;
        if (newIndex != oldIndex) {
            if (newIndex > type(uint104).max) {
                revert IndexOverflow(newIndex);
            }

            indexUpdated = true;

            rewardDistribution.index = uint104(newIndex);
            rewardDistribution.lastUpdateTimestamp = block.timestamp.toUint32();
        } else {
            rewardDistribution.lastUpdateTimestamp = block.timestamp.toUint32();
        }

        return (newIndex, indexUpdated);
    }

    /// @dev Updates the state of the distribution for the specific user
    /// @param rewardDistribution Storage pointer to the distribution reward config
    /// @param user The address of the user
    /// @param userStake The amount of assets staked by the user
    /// @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 _updateUserReward(
        RewardsDataTypes.RewardDistribution storage rewardDistribution,
        address user,
        uint userStake,
        uint newAssetIndex,
        uint assetUnit
    ) internal returns (uint, bool) {
        uint userIndex = rewardDistribution.userReward[user].index;
        uint rewardsAccrued;
        bool dataUpdated;
        if ((dataUpdated = userIndex != newAssetIndex)) {
            if (newAssetIndex > type(uint104).max) {
                revert IndexOverflow(newAssetIndex);
            }

            rewardDistribution.userReward[user].index = uint104(newAssetIndex);
            if (userStake != 0) {
                rewardsAccrued = _computeAccruedRewardAmount(userStake, newAssetIndex, userIndex, assetUnit);

                rewardDistribution.userReward[user].accrued += rewardsAccrued.toUint128();
            }
        }
        return (rewardsAccrued, dataUpdated);
    }

    /// @dev Calculates the pending (not yet accrued) reward amount since the last user action
    /// @param user The address of the user
    /// @param reward The address of the reward token
    /// @param assetStakedAmounts struct with the user stake and total staked of the incentivized asset
    /// @return The pending rewards for the user since the last user action
    function _computePendingRewardAmountForUser(
        address user,
        address reward,
        RewardsDataTypes.AssetStakedAmounts memory assetStakedAmounts
    ) internal view returns (uint) {
        RewardsDataTypes.RewardDistribution storage rewardDistribution = _assetData[assetStakedAmounts.asset]
            .rewardDistribution[reward];
        uint assetUnit = 10**_assetData[assetStakedAmounts.asset].decimals;
        (, uint nextIndex) = _computeNewAssetIndex(
            rewardDistribution,
            assetStakedAmounts.totalStaked,
            assetUnit
        );

        return
            _computeAccruedRewardAmount(
                assetStakedAmounts.userStake,
                nextIndex,
                rewardDistribution.userReward[user].index,
                assetUnit
            );
    }

    /// @dev Internal function for the calculation of user's rewards on a distribution
    /// @param userStake The amount of assets staked by the user on a distribution
    /// @param assetIndex Current index of the asset reward distribution
    /// @param userIndex Index stored for the user, representing his staking moment
    /// @param assetUnit One unit of asset (10**decimals)
    /// @return accruedRewardAmount The accrued reward amount
    function _computeAccruedRewardAmount(
        uint userStake,
        uint assetIndex,
        uint userIndex,
        uint assetUnit
    ) internal pure returns (uint accruedRewardAmount) {
        accruedRewardAmount = userStake * (assetIndex - userIndex);

        assembly {
            accruedRewardAmount := div(accruedRewardAmount, assetUnit)
        }
    }

    /// @dev Calculates the next value of an specific distribution index, with validations
    /// @param totalStaked The total amount staked of the asset
    /// @param assetUnit One unit of asset (10**decimals)
    /// @return The new index.
    function _computeNewAssetIndex(
        RewardsDataTypes.RewardDistribution storage rewardDistribution,
        uint totalStaked,
        uint assetUnit
    ) internal view returns (uint, uint) {
        uint oldIndex = rewardDistribution.index;
        uint distributionEnd = rewardDistribution.distributionEnd;
        uint emissionPerSecond = rewardDistribution.emissionPerSecond;
        uint lastUpdateTimestamp = rewardDistribution.lastUpdateTimestamp;

        if (
            emissionPerSecond == 0 ||
            totalStaked == 0 ||
            lastUpdateTimestamp == block.timestamp ||
            lastUpdateTimestamp >= distributionEnd
        ) {
            return (oldIndex, oldIndex);
        }

        uint currentTimestamp = block.timestamp > distributionEnd ? distributionEnd : block.timestamp;
        uint timeDelta = currentTimestamp - lastUpdateTimestamp;
        uint firstTerm = emissionPerSecond * timeDelta * assetUnit;
        assembly {
            firstTerm := div(firstTerm, totalStaked)
        }
        return (oldIndex, (firstTerm + oldIndex));
    }

    /// @dev Get user stake and total staked of all the assets specified by the assets parameter
    /// @param assets List of assets to retrieve user stake and total staked
    /// @param user Address of the user
    /// @return assetStakedAmounts contains a list of structs with user stake and total staked of the given assets
    function _getAssetStakedAmounts(address[] calldata assets, address user)
        internal
        view
        virtual
        returns (RewardsDataTypes.AssetStakedAmounts[] memory assetStakedAmounts);

    /// @dev Updates the address of the emission manager
    /// @param emissionManager The address of the new EmissionManager
    function _setEmissionManager(address emissionManager) internal {
        address previousEmissionManager = _emissionManager;
        _emissionManager = emissionManager;
        emit EmissionManagerUpdated(previousEmissionManager, emissionManager);
    }

    function _setEmissionPerSecond(
        address asset,
        address reward,
        uint88 newEmissionsPerSecond
    )
        internal
        returns (
            uint oldEmissionPerSecond,
            uint newIndex,
            uint distributionEnd
        )
    {
        RewardsDataTypes.AssetData storage assetConfig = _assetData[asset];
        RewardsDataTypes.RewardDistribution storage rewardDistribution = _assetData[asset].rewardDistribution[
            reward
        ];
        uint decimals = assetConfig.decimals;
        if (decimals == 0 || rewardDistribution.lastUpdateTimestamp == 0) {
            revert DistributionNonExistent(asset, reward);
        }

        distributionEnd = rewardDistribution.distributionEnd;

        (newIndex, ) = _updateRewardDistribution(
            rewardDistribution,
            solidStakingViewActions.totalStaked(asset),
            10**decimals
        );

        oldEmissionPerSecond = rewardDistribution.emissionPerSecond;
        rewardDistribution.emissionPerSecond = newEmissionsPerSecond;
    }

    function _setDistributionEnd(
        address asset,
        address reward,
        uint32 newDistributionEnd
    ) internal returns (uint oldDistributionEnd) {
        oldDistributionEnd = _assetData[asset].rewardDistribution[reward].distributionEnd;
        _assetData[asset].rewardDistribution[reward].distributionEnd = newDistributionEnd;
    }
}

Settings
{
  "evmVersion": "london",
  "libraries": {},
  "metadata": {
    "bytecodeHash": "ipfs",
    "useLiteralContent": true
  },
  "optimizer": {
    "enabled": true,
    "runs": 10000
  },
  "remappings": [],
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  }
}

Contract Security Audit

Contract ABI

[{"inputs":[],"name":"AlreadyInitialized","type":"error"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"address","name":"reward","type":"address"}],"name":"DistributionNonExistent","type":"error"},{"inputs":[{"internalType":"uint256","name":"newIndex","type":"uint256"}],"name":"IndexOverflow","type":"error"},{"inputs":[{"internalType":"address","name":"asset","type":"address"}],"name":"InvalidAssetDecimals","type":"error"},{"inputs":[],"name":"InvalidInput","type":"error"},{"inputs":[{"internalType":"address","name":"reward","type":"address"},{"internalType":"address","name":"rewardOracle","type":"address"}],"name":"InvalidRewardOracle","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"}],"name":"NotEmissionManager","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"}],"name":"NotSolidStaking","type":"error"},{"inputs":[{"internalType":"address","name":"claimer","type":"address"},{"internalType":"address","name":"user","type":"address"}],"name":"UnauthorizedClaimer","type":"error"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"address","name":"reward","type":"address"}],"name":"UpdateDistributionNotApplicable","type":"error"},{"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":"user","type":"address"},{"indexed":true,"internalType":"address","name":"claimer","type":"address"}],"name":"ClaimerSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldEmissionManager","type":"address"},{"indexed":true,"internalType":"address","name":"newEmissionManager","type":"address"}],"name":"EmissionManagerUpdated","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":"rewardsVault","type":"address"}],"name":"RewardsVaultUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"solidStaking","type":"address"}],"name":"SolidStakingUpdated","type":"event"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"address","name":"reward","type":"address"}],"name":"canUpdateCarbonRewardDistribution","outputs":[{"internalType":"bool","name":"","type":"bool"}],"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":[{"components":[{"internalType":"uint88","name":"emissionPerSecond","type":"uint88"},{"internalType":"uint256","name":"totalStaked","type":"uint256"},{"internalType":"uint32","name":"distributionEnd","type":"uint32"},{"internalType":"address","name":"asset","type":"address"},{"internalType":"address","name":"reward","type":"address"},{"internalType":"contract IEACAggregatorProxy","name":"rewardOracle","type":"address"}],"internalType":"struct RewardsDataTypes.DistributionConfig[]","name":"config","type":"tuple[]"}],"name":"configureAssets","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"address","name":"reward","type":"address"}],"name":"getAccruedRewardAmountForUser","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getAllRewards","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"assets","type":"address[]"},{"internalType":"address","name":"user","type":"address"}],"name":"getAllUnclaimedRewardAmountsForUserAndAssets","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":"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":"asset","type":"address"},{"internalType":"address","name":"reward","type":"address"}],"name":"getRewardDistribution","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":[{"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":[],"name":"getRewardsVault","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"assets","type":"address[]"},{"internalType":"address","name":"user","type":"address"},{"internalType":"address","name":"reward","type":"address"}],"name":"getUnclaimedRewardAmountForUserAndAssets","outputs":[{"internalType":"uint256","name":"unclaimedAmount","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":"getUserIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"oldUserStake","type":"uint256"},{"internalType":"uint256","name":"oldTotalStaked","type":"uint256"}],"name":"handleUserStakeChanged","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":"emissionManager","type":"address"}],"name":"setEmissionManager","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":"rewardsVault","type":"address"}],"name":"setRewardsVault","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"solidStaking","type":"address"}],"name":"setSolidStaking","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_solidStakingViewActions","type":"address"},{"internalType":"address","name":"rewardsVault","type":"address"},{"internalType":"address","name":"emissionManager","type":"address"}],"name":"setup","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"solidStakingViewActions","outputs":[{"internalType":"contract ISolidStakingViewActions","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"assets","type":"address[]"},{"internalType":"address[]","name":"rewards","type":"address[]"},{"internalType":"uint256[]","name":"rewardAmounts","type":"uint256[]"}],"name":"updateCarbonRewardDistribution","outputs":[],"stateMutability":"nonpayable","type":"function"}]

608060405234801561001057600080fd5b506142bf806100206000396000f3fe608060405234801561001057600080fd5b50600436106101cf5760003560e01c806388ffcf6111610104578063c040af69116100a2578063ef85093611610071578063ef85093614610569578063f5bb3e021461057c578063f5cf673b1461058f578063f996868b146105a257600080fd5b8063c040af6914610465578063c5a7b53814610478578063e23ddec51461048b578063e3887a4b1461049c57600080fd5b80639ff55db9116100de5780639ff55db91461040b578063ad9f8b2f1461042c578063bb492bf51461043f578063bf90f63a1461045257600080fd5b806388ffcf611461034f57806392074b08146103a55780639efd6f72146103b657600080fd5b80634f7459d511610171578063707199e51161014b578063707199e5146102ea57806374d945ec146102fd57806377b8b1c7146103295780638549f3fd1461033c57600080fd5b80634f7459d5146102b15780635453ba10146102c45780636657732f146102d757600080fd5b806338be719f116101ad57806338be719f1461025357806345b35f56146102665780634a6a60f11461027b5780634e18cced1461028e57600080fd5b8063184dde87146101d45780631b839c77146101e95780632a17bf601461020f575b600080fd5b6101e76101e2366004613916565b6105b5565b005b6101fc6101f736600461395c565b610612565b6040519081526020015b60405180910390f35b61023b61021d366004613995565b6001600160a01b039081166000908152600760205260409020541690565b6040516001600160a01b039091168152602001610206565b6101e76102613660046139fe565b610663565b61026e610a8b565b6040516102069190613adc565b60055461023b906001600160a01b031681565b6102a161029c36600461395c565b610aed565b6040519015158152602001610206565b6101e76102bf366004613995565b610bc5565b6101e76102d236600461395c565b610c17565b61026e6102e5366004613995565b610c6b565b6101e76102f8366004613bca565b610d97565b61023b61030b366004613995565b6001600160a01b039081166000908152600660205260409020541690565b6101e7610337366004613ce5565b610f0c565b6101fc61034a366004613d30565b610fc0565b6101fc61035d366004613ce5565b6001600160a01b038083166000908152602081815260408083208585168452825280832093871683526001909301905220546cffffffffffffffffffffffffff169392505050565b6004546001600160a01b031661023b565b6103f96103c4366004613995565b6001600160a01b0316600090815260208190526040902060020154700100000000000000000000000000000000900460ff1690565b60405160ff9091168152602001610206565b61041e610419366004613d30565b611168565b604051610206929190613d98565b6101fc61043a36600461395c565b611263565b61041e61044d366004613def565b61130e565b61041e610460366004613e3b565b61136b565b6101e7610473366004613995565b611387565b6101e7610486366004613e7d565b6113d6565b6008546001600160a01b031661023b565b6105496104aa36600461395c565b6001600160a01b039182166000908152602081815260408083209390941682529190915220546cffffffffffffffffffffffffff8116916affffffffffffffffffffff6d01000000000000000000000000008304169163ffffffff780100000000000000000000000000000000000000000000000082048116927c01000000000000000000000000000000000000000000000000000000009092041690565b604080519485526020850193909352918301526060820152608001610206565b61041e610577366004613def565b6115ec565b6101e761058a366004613995565b6118b9565b6101e761059d36600461395c565b611908565b6101e76105b0366004613ec4565b6119bd565b6005546001600160a01b03163314610600576040517f4c4365b70000000000000000000000000000000000000000000000000000000081523360048201526024015b60405180910390fd5b61060c84848484611b83565b50505050565b6001600160a01b03828116600090815260208181526040808320938516835292905220547c0100000000000000000000000000000000000000000000000000000000900463ffffffff165b92915050565b6004546001600160a01b031633146106a9576040517f59afb3390000000000000000000000000000000000000000000000000000000081523360048201526024016105f7565b84831415806106b85750828114155b156106ef576040517fb4fa3fb300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b85811015610a825761075087878381811061070f5761070f613f47565b90506020020160208101906107249190613995565b86868481811061073657610736613f47565b905060200201602081019061074b9190613995565b611cef565b6107e75786868281811061076657610766613f47565b905060200201602081019061077b9190613995565b85858381811061078d5761078d613f47565b90506020020160208101906107a29190613995565b6040517f9d0f304c0000000000000000000000000000000000000000000000000000000081526001600160a01b039283166004820152911660248201526044016105f7565b600061083f8888848181106107fe576107fe613f47565b90506020020160208101906108139190613995565b87878581811061082557610825613f47565b905060200201602081019061083a9190613995565b611d7e565b905060006108534263ffffffff8416613fa5565b85858581811061086557610865613f47565b905060200201356108769190613fb8565b90506000806108d28b8b8781811061089057610890613f47565b90506020020160208101906108a59190613995565b8a8a888181106108b7576108b7613f47565b90506020020160208101906108cc9190613995565b85611ddf565b509150915060006109a48c8c888181106108ee576108ee613f47565b90506020020160208101906109039190613995565b8b8b8981811061091557610915613f47565b905060200201602081019061092a9190613995565b6001600160a01b039182166000908152602081815260408083209390941682529190915220805463ffffffff8881167c01000000000000000000000000000000000000000000000000000000009081027bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8416179093559190041690565b90508989878181106109b8576109b8613f47565b90506020020160208101906109cd9190613995565b6001600160a01b03168c8c888181106109e8576109e8613f47565b90506020020160208101906109fd9190613995565b604080518681526affffffffffffffffffffff8816602082015290810184905263ffffffff88166060820152608081018590526001600160a01b0391909116907fac1777479f07f3e7c34da8402139d54027a6a260caaae168bdee825ca5580dc59060a00160405180910390a350505050508080610a7a90613ff3565b9150506106f2565b50505050505050565b60606002805480602002602001604051908101604052809291908181526020018280548015610ae357602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610ac5575b5050505050905090565b6001600160a01b038281166000818152602081815260408083209486168352848252822092825281905260029092015484918491700100000000000000000000000000000000900460ff16801580610b66575081547801000000000000000000000000000000000000000000000000900463ffffffff16155b15610bb0576040517fc8bed79e0000000000000000000000000000000000000000000000000000000081526001600160a01b038086166004830152841660248201526044016105f7565b610bba8787611cef565b979650505050505050565b6004546001600160a01b03163314610c0b576040517f59afb3390000000000000000000000000000000000000000000000000000000081523360048201526024016105f7565b610c1481611fc7565b50565b6004546001600160a01b03163314610c5d576040517f59afb3390000000000000000000000000000000000000000000000000000000081523360048201526024016105f7565b610c678282612031565b5050565b6001600160a01b0381166000908152602081905260408120600201546060916fffffffffffffffffffffffffffffffff909116908167ffffffffffffffff811115610cb857610cb8613aef565b604051908082528060200260200182016040528015610ce1578160200160208202803683370190505b50905060005b826fffffffffffffffffffffffffffffffff16816fffffffffffffffffffffffffffffffff161015610d8f576001600160a01b038086166000908152602081815260408083206fffffffffffffffffffffffffffffffff861680855260019091019092529091205484519216918491908110610d6557610d65613f47565b6001600160a01b039092166020928302919091019091015280610d878161402b565b915050610ce7565b509392505050565b6004546001600160a01b03163314610ddd576040517f59afb3390000000000000000000000000000000000000000000000000000000081523360048201526024016105f7565b60005b8151811015610f025760055482516001600160a01b0390911690639bfd8d6190849084908110610e1257610e12613f47565b6020026020010151606001516040518263ffffffff1660e01b8152600401610e4991906001600160a01b0391909116815260200190565b602060405180830381865afa158015610e66573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e8a919061405a565b828281518110610e9c57610e9c613f47565b60200260200101516020018181525050610ef0828281518110610ec157610ec1613f47565b602002602001015160800151838381518110610edf57610edf613f47565b602002602001015160a00151612031565b80610efa81613ff3565b915050610de0565b50610c148161214e565b60055474010000000000000000000000000000000000000000900460ff1615610f61576040517f0dc149f000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600580547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff1674010000000000000000000000000000000000000000179055610fa98361294c565b610fb2826129ae565b610fbb81611fc7565b505050565b600080610fce868686612a10565b905060005b815181101561115e57818181518110610fee57610fee613f47565b60200260200101516020015160000361108e5760008083838151811061101657611016613f47565b602090810291909101810151516001600160a01b039081168352828201939093526040918201600090812088851682528252828120938916815260019093019052902054611087906d010000000000000000000000000090046fffffffffffffffffffffffffffffffff1684614073565b925061114c565b6000808383815181106110a3576110a3613f47565b602090810291909101810151516001600160a01b03908116835282820193909352604091820160009081208885168252825282812093891681526001909301905290205482516d01000000000000000000000000009091046fffffffffffffffffffffffffffffffff1690611135908790879086908690811061112857611128613f47565b6020026020010151612cab565b61113f9190614073565b6111499084614073565b92505b8061115681613ff3565b915050610fd3565b5050949350505050565b6001600160a01b038083166000908152600660205260409020546060918291339186911682148015906111a957506005546001600160a01b03838116911614155b156111f3576040517f3ebd5fe50000000000000000000000000000000000000000000000000000000081526001600160a01b038084166004830152821660248201526044016105f7565b6001600160a01b038516158061121057506001600160a01b038616155b15611247576040517fb4fa3fb300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6112548888338989612d5e565b93509350505094509492505050565b60008060005b600354811015610d8f576000806003838154811061128957611289613f47565b6000918252602080832091909101546001600160a01b039081168452838201949094526040928301822088851683528152828220938916825260019093019092529020546112fa906d010000000000000000000000000090046fffffffffffffffffffffffffffffffff1683614073565b91508061130681613ff3565b915050611269565b6060806001600160a01b038316611351576040517fb4fa3fb300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61135e8585333387612d5e565b915091505b935093915050565b60608061137b8484333333612d5e565b915091505b9250929050565b6004546001600160a01b031633146113cd576040517f59afb3390000000000000000000000000000000000000000000000000000000081523360048201526024016105f7565b610c148161294c565b6004546001600160a01b0316331461141c576040517f59afb3390000000000000000000000000000000000000000000000000000000081523360048201526024016105f7565b6001600160a01b03838116600081815260208181526040808320948716835284825282209282525260029091015484918491700100000000000000000000000000000000900460ff16801580611493575081547801000000000000000000000000000000000000000000000000900463ffffffff16155b156114dd576040517fc8bed79e0000000000000000000000000000000000000000000000000000000081526001600160a01b038086166004830152841660248201526044016105f7565b6001600160a01b03878116600081815260208181526040808320948b1680845294825291829020805463ffffffff8b81167c01000000000000000000000000000000000000000000000000000000008181027bffffffffffffffffffffffffffffffffffffffffffffffffffffffff851681179586905587516d01000000000000000000000000009096046affffffffffffffffffffff1680875296860196909652830490911683860181905260608401919091526cffffffffffffffffffffffffff918216919093161760808201819052925191949293917fac1777479f07f3e7c34da8402139d54027a6a260caaae168bdee825ca5580dc59160a0908290030190a3505050505050505050565b60608060006115fc868686612a10565b60025490915067ffffffffffffffff81111561161a5761161a613aef565b604051908082528060200260200182016040528015611643578160200160208202803683370190505b509250825167ffffffffffffffff81111561166057611660613aef565b604051908082528060200260200182016040528015611689578160200160208202803683370190505b50915060005b81518110156118af5760005b845181101561189c57600281815481106116b7576116b7613f47565b9060005260206000200160009054906101000a90046001600160a01b03168582815181106116e7576116e7613f47565b60200260200101906001600160a01b031690816001600160a01b03168152505060008084848151811061171c5761171c613f47565b6020026020010151600001516001600160a01b03166001600160a01b03168152602001908152602001600020600001600086838151811061175f5761175f613f47565b60200260200101516001600160a01b03166001600160a01b031681526020019081526020016000206001016000876001600160a01b03166001600160a01b03168152602001908152602001600020600001600d9054906101000a90046fffffffffffffffffffffffffffffffff166fffffffffffffffffffffffffffffffff168482815181106117f1576117f1613f47565b602002602001018181516118059190614073565b905250825183908390811061181c5761181c613f47565b6020026020010151602001516000031561188a576118608686838151811061184657611846613f47565b602002602001015185858151811061112857611128613f47565b84828151811061187257611872613f47565b602002602001018181516118869190614073565b9052505b8061189481613ff3565b91505061169b565b50806118a781613ff3565b91505061168f565b5050935093915050565b6004546001600160a01b031633146118ff576040517f59afb3390000000000000000000000000000000000000000000000000000000081523360048201526024016105f7565b610c14816129ae565b6004546001600160a01b0316331461194e576040517f59afb3390000000000000000000000000000000000000000000000000000000081523360048201526024016105f7565b6001600160a01b0382811660008181526006602052604080822080547fffffffffffffffffffffffff0000000000000000000000000000000000000000169486169485179055517f4925eafc82d0c4d67889898eeed64b18488ab19811e61620f387026dec126a289190a35050565b6004546001600160a01b03163314611a03576040517f59afb3390000000000000000000000000000000000000000000000000000000081523360048201526024016105f7565b828114611a3c576040517fb4fa3fb300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b83811015611b7b576000806000611aa389898987818110611a6257611a62613f47565b9050602002016020810190611a779190613995565b888888818110611a8957611a89613f47565b9050602002016020810190611a9e9190614086565b611ddf565b925092509250878785818110611abb57611abb613f47565b9050602002016020810190611ad09190613995565b6001600160a01b0316896001600160a01b03167fac1777479f07f3e7c34da8402139d54027a6a260caaae168bdee825ca5580dc585898989818110611b1757611b17613f47565b9050602002016020810190611b2c9190614086565b604080519283526affffffffffffffffffffff90911660208301528101859052606081018590526080810186905260a00160405180910390a35050508080611b7390613ff3565b915050611a3f565b505050505050565b6001600160a01b03841660009081526020819052604081206002015460ff700100000000000000000000000000000000820416600a0a916fffffffffffffffffffffffffffffffff90911690819003611bdd57505061060c565b60005b81816fffffffffffffffffffffffffffffffff161015610a82576001600160a01b038088166000908152602081815260408083206fffffffffffffffffffffffffffffffff86168452600181018352818420549094168084529390915281209080611c4c83898961317a565b91509150600080611c60858d8d878d6132db565b915091508280611c6d5750805b15611cdd578b6001600160a01b0316866001600160a01b03168e6001600160a01b03167f3303facd24627943a92e9dc87cfbb34b15c49b726eec3ad3487c16be9ab8efe8878887604051611cd4939291909283526020830191909152604082015260600190565b60405180910390a45b505060019094019350611be092505050565b6001600160a01b038281166000908152602081815260408083209385168352929052908120547c0100000000000000000000000000000000000000000000000000000000900463ffffffff1681611d468585611d7e565b905063ffffffff8216801515906000904210801590611d6a57508263ffffffff1642105b9050818015610bba57509695505050505050565b6001600160a01b038281166000908152602081815260408083209385168352929052908120547c0100000000000000000000000000000000000000000000000000000000900463ffffffff16611dd78162093a806140a1565b949350505050565b6001600160a01b038381166000908152602081815260408083209386168352908390528120600283015491928392839290700100000000000000000000000000000000900460ff16801580611e55575081547801000000000000000000000000000000000000000000000000900463ffffffff16155b15611e9f576040517fc8bed79e0000000000000000000000000000000000000000000000000000000081526001600160a01b03808b166004830152891660248201526044016105f7565b81546005546040517f9bfd8d610000000000000000000000000000000000000000000000000000000081526001600160a01b038c811660048301527c010000000000000000000000000000000000000000000000000000000090930463ffffffff169650611f659285921690639bfd8d6190602401602060405180830381865afa158015611f31573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f55919061405a565b611f6084600a6141e5565b61317a565b5082546affffffffffffffffffffff9889166d01000000000000000000000000009081027fffffffffffffffff0000000000000000000000ffffffffffffffffffffffffff831617909455929092049690961698909750919550909350505050565b600480546001600160a01b038381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681179093556040519116919082907f53271355c244f99d37f622c90fe574eb7c35c7b3548ea276cf9b5b11c601605e90600090a35050565b6000816001600160a01b03166350d25bcd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612071573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612095919061405a565b136120df576040517f4f79ac5c0000000000000000000000000000000000000000000000000000000081526001600160a01b038084166004830152821660248201526044016105f7565b6001600160a01b0382811660008181526007602052604080822080547fffffffffffffffffffffffff0000000000000000000000000000000000000000169486169485179055517f1a1cd5483e52e60b9ff7f3b9d1db3bbd9e9d21c6324ad3a8c79dba9b75e62f4d9190a35050565b60005b8151811015610c6757600082828151811061216e5761216e613f47565b6020026020010151606001516001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa1580156121b7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121db91906141f1565b90508060ff16600003612248578282815181106121fa576121fa613f47565b6020026020010151606001516040517f8f0b74150000000000000000000000000000000000000000000000000000000081526004016105f791906001600160a01b0391909116815260200190565b60008084848151811061225d5761225d613f47565b6020026020010151606001516001600160a01b03166001600160a01b0316815260200190815260200160002060020160109054906101000a900460ff1660ff166000036123105760038383815181106122b8576122b8613f47565b6020908102919091018101516060015182546001810184556000938452919092200180547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b039092169190911790555b8060008085858151811061232657612326613f47565b6020026020010151606001516001600160a01b03166001600160a01b0316815260200190815260200160002060020160106101000a81548160ff021916908360ff160217905550600080600085858151811061238457612384613f47565b6020026020010151606001516001600160a01b03166001600160a01b0316815260200190815260200160002060000160008585815181106123c7576123c7613f47565b6020026020010151608001516001600160a01b03166001600160a01b0316815260200190815260200160002090508060000160189054906101000a900463ffffffff1663ffffffff166000036125cf57600080600086868151811061242e5761242e613f47565b6020026020010151606001516001600160a01b03166001600160a01b0316815260200190815260200160002060020160009054906101000a90046fffffffffffffffffffffffffffffffff16905084848151811061248e5761248e613f47565b6020026020010151608001516000808787815181106124af576124af613f47565b6020026020010151606001516001600160a01b03166001600160a01b031681526020019081526020016000206001016000836fffffffffffffffffffffffffffffffff166fffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060008086868151811061254c5761254c613f47565b602090810291909101810151606001516001600160a01b03168252810191909152604001600090812060020180546fffffffffffffffffffffffffffffffff16916125968361402b565b91906101000a8154816fffffffffffffffffffffffffffffffff02191690836fffffffffffffffffffffffffffffffff16021790555050505b600160008585815181106125e5576125e5613f47565b602090810291909101810151608001516001600160a01b0316825281019190915260400160009081205460ff16151590036126e057600180600086868151811061263157612631613f47565b6020026020010151608001516001600160a01b03166001600160a01b0316815260200190815260200160002060006101000a81548160ff021916908315150217905550600284848151811061268857612688613f47565b6020908102919091018101516080015182546001810184556000938452919092200180547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b039092169190911790555b6000612711828686815181106126f8576126f8613f47565b60200260200101516020015185600a611f609190614214565b50825486519192506d010000000000000000000000000081046affffffffffffffffffffff16917c010000000000000000000000000000000000000000000000000000000090910463ffffffff169087908790811061277257612772613f47565b60209081029190910101515184546affffffffffffffffffffff9091166d0100000000000000000000000000027fffffffffffffffff0000000000000000000000ffffffffffffffffffffffffff90911617845586518790879081106127da576127da613f47565b602090810291909101015160400151845463ffffffff9091167c0100000000000000000000000000000000000000000000000000000000027bffffffffffffffffffffffffffffffffffffffffffffffffffffffff909116178455865187908790811061284957612849613f47565b6020026020010151608001516001600160a01b031687878151811061287057612870613f47565b6020026020010151606001516001600160a01b03167fac1777479f07f3e7c34da8402139d54027a6a260caaae168bdee825ca5580dc5848a8a815181106128b9576128b9613f47565b602002602001015160000151858c8c815181106128d8576128d8613f47565b6020026020010151604001518960405161292c9594939291906affffffffffffffffffffff958616815293909416602084015263ffffffff9182166040840152166060820152608081019190915260a00190565b60405180910390a35050505050808061294490613ff3565b915050612151565b600580547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383169081179091556040517f56ca898af8c1c9c8321dc2ddb32e363c6cd2f38e679340e14a67c810100ce7b790600090a250565b600880547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383169081179091556040517f28a48cbce43190d77247f342cf319b1607bff4ef716cf26b76cf7bb71baebaa590600090a250565b60608267ffffffffffffffff811115612a2b57612a2b613aef565b604051908082528060200260200182016040528015612a8957816020015b612a76604051806060016040528060006001600160a01b0316815260200160008152602001600081525090565b815260200190600190039081612a495790505b50905060005b83811015610d8f57848482818110612aa957612aa9613f47565b9050602002016020810190612abe9190613995565b828281518110612ad057612ad0613f47565b60209081029190910101516001600160a01b0391821690526005541663f7888aec868684818110612b0357612b03613f47565b9050602002016020810190612b189190613995565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b1681526001600160a01b0391821660048201529086166024820152604401602060405180830381865afa158015612b7c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ba0919061405a565b828281518110612bb257612bb2613f47565b60209081029190910181015101526005546001600160a01b0316639bfd8d61868684818110612be357612be3613f47565b9050602002016020810190612bf89190613995565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b1681526001600160a01b039091166004820152602401602060405180830381865afa158015612c54573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c78919061405a565b828281518110612c8a57612c8a613f47565b60209081029190910101516040015280612ca381613ff3565b915050612a8f565b80516001600160a01b039081166000908152602081815260408083208685168452825280832085519094168352908290528120600201549091908290612d0990700100000000000000000000000000000000900460ff16600a614214565b90506000612d1c8386604001518461345d565b6020808801516001600160a01b038b16600090815260018801909252604090912054919350610bba92509083906cffffffffffffffffffffffffff1685613569565b60025460609081908067ffffffffffffffff811115612d7f57612d7f613aef565b604051908082528060200260200182016040528015612da8578160200160208202803683370190505b5092508067ffffffffffffffff811115612dc457612dc4613aef565b604051908082528060200260200182016040528015612ded578160200160208202803683370190505b509150612e0485612dff8a8a89612a10565b61358b565b60005b87811015613076576000898983818110612e2357612e23613f47565b9050602002016020810190612e389190613995565b905060005b838110156130615760006001600160a01b0316868281518110612e6257612e62613f47565b60200260200101516001600160a01b031603612edc5760028181548110612e8b57612e8b613f47565b9060005260206000200160009054906101000a90046001600160a01b0316868281518110612ebb57612ebb613f47565b60200260200101906001600160a01b031690816001600160a01b0316815250505b6001600160a01b038216600090815260208190526040812087518290899085908110612f0a57612f0a613f47565b6020908102919091018101516001600160a01b0390811683528282019390935260409182016000908120938d168152600190930190529020546d010000000000000000000000000090046fffffffffffffffffffffffffffffffff169050801561304e5780868381518110612f8157612f81613f47565b60200260200101818151612f959190614073565b9052506001600160a01b0383166000908152602081905260408120885182908a9086908110612fc657612fc6613f47565b60200260200101516001600160a01b03166001600160a01b0316815260200190815260200160002060010160008b6001600160a01b03166001600160a01b03168152602001908152602001600020600001600d6101000a8154816fffffffffffffffffffffffffffffffff02191690836fffffffffffffffffffffffffffffffff1602179055505b508061305981613ff3565b915050612e3d565b5050808061306e90613ff3565b915050612e07565b5060005b8181101561316e576130bf8585838151811061309857613098613f47565b60200260200101518584815181106130b2576130b2613f47565b602002602001015161360c565b846001600160a01b03168482815181106130db576130db613f47565b60200260200101516001600160a01b0316876001600160a01b03167fc052130bc4ef84580db505783484b067ea8b71b3bca78a7e12db7aea8658f0048a87868151811061312a5761312a613f47565b60200260200101516040516131549291906001600160a01b03929092168252602082015260400190565b60405180910390a48061316681613ff3565b91505061307a565b50509550959350505050565b60008060008061318b87878761345d565b915091506000828214613277576cffffffffffffffffffffffffff8211156131e2576040517fc6c74120000000000000000000000000000000000000000000000000000000008152600481018390526024016105f7565b5086547fffffffffffffffffffffffffffffffffffffff00000000000000000000000000166cffffffffffffffffffffffffff8216178755600161322542613625565b885463ffffffff919091167801000000000000000000000000000000000000000000000000027fffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffffff9091161788556132ce565b61328042613625565b885463ffffffff919091167801000000000000000000000000000000000000000000000000027fffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffffff9091161788555b9097909650945050505050565b6001600160a01b038416600090815260018601602052604081205481906cffffffffffffffffffffffffff168185821480159061344e576cffffffffffffffffffffffffff87111561335c576040517fc6c74120000000000000000000000000000000000000000000000000000000008152600481018890526024016105f7565b6001600160a01b038916600090815260018b016020526040902080547fffffffffffffffffffffffffffffffffffffff00000000000000000000000000166cffffffffffffffffffffffffff8916179055871561344e576133bf88888589613569565b91506133ca826136bf565b6001600160a01b038a16600090815260018c01602052604090208054600d906134179084906d010000000000000000000000000090046fffffffffffffffffffffffffffffffff16614223565b92506101000a8154816fffffffffffffffffffffffffffffffff02191690836fffffffffffffffffffffffffffffffff1602179055505b90999098509650505050505050565b825460009081906cffffffffffffffffffffffffff81169063ffffffff7c010000000000000000000000000000000000000000000000000000000082048116916affffffffffffffffffffff6d0100000000000000000000000000820416917801000000000000000000000000000000000000000000000000909104168115806134e5575087155b806134ef57504281145b806134fa5750828110155b1561350e5783849550955050505050611363565b600083421161351d574261351f565b835b9050600061352d8383613fa5565b905060008961353c838761424c565b613546919061424c565b8b90049050866135568183614073565b9850985050505050505050935093915050565b60006135758385613fa5565b61357f908661424c565b91909104949350505050565b60005b8151811015610fbb576135fa8282815181106135ac576135ac613f47565b602002602001015160000151848484815181106135cb576135cb613f47565b6020026020010151602001518585815181106135e9576135e9613f47565b602002602001015160400151611b83565b8061360481613ff3565b91505061358e565b600854610fbb9083906001600160a01b03168584613761565b600063ffffffff8211156136bb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203360448201527f322062697473000000000000000000000000000000000000000000000000000060648201526084016105f7565b5090565b60006fffffffffffffffffffffffffffffffff8211156136bb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602760248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203160448201527f323820626974730000000000000000000000000000000000000000000000000060648201526084016105f7565b6040517f23b872dd000000000000000000000000000000000000000000000000000000008082526001600160a01b038581166004840152841660248301526044820183905290600080606483828a5af16137bf573d6000803e3d6000fd5b506137c985613836565b61382f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f475076323a206661696c6564207472616e7366657246726f6d0000000000000060448201526064016105f7565b5050505050565b6000613875565b7f08c379a000000000000000000000000000000000000000000000000000000000600052602060045280602452508060445260646000fd5b3d80156138b457602081146138ee576138af7f475076323a206d616c666f726d6564207472616e7366657220726573756c7400601f61383d565b6138fb565b823b6138e5576138e57f475076323a206e6f74206120636f6e7472616374000000000000000000000000601461383d565b600191506138fb565b3d6000803e600051151591505b50919050565b6001600160a01b0381168114610c1457600080fd5b6000806000806080858703121561392c57600080fd5b843561393781613901565b9350602085013561394781613901565b93969395505050506040820135916060013590565b6000806040838503121561396f57600080fd5b823561397a81613901565b9150602083013561398a81613901565b809150509250929050565b6000602082840312156139a757600080fd5b81356139b281613901565b9392505050565b60008083601f8401126139cb57600080fd5b50813567ffffffffffffffff8111156139e357600080fd5b6020830191508360208260051b850101111561138057600080fd5b60008060008060008060608789031215613a1757600080fd5b863567ffffffffffffffff80821115613a2f57600080fd5b613a3b8a838b016139b9565b90985096506020890135915080821115613a5457600080fd5b613a608a838b016139b9565b90965094506040890135915080821115613a7957600080fd5b50613a8689828a016139b9565b979a9699509497509295939492505050565b600081518084526020808501945080840160005b83811015613ad15781516001600160a01b031687529582019590820190600101613aac565b509495945050505050565b6020815260006139b26020830184613a98565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60405160c0810167ffffffffffffffff81118282101715613b4157613b41613aef565b60405290565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715613b8e57613b8e613aef565b604052919050565b80356affffffffffffffffffffff81168114613bb157600080fd5b919050565b803563ffffffff81168114613bb157600080fd5b60006020808385031215613bdd57600080fd5b823567ffffffffffffffff80821115613bf557600080fd5b818501915085601f830112613c0957600080fd5b813581811115613c1b57613c1b613aef565b613c29848260051b01613b47565b818152848101925060c0918202840185019188831115613c4857600080fd5b938501935b82851015613cd95780858a031215613c655760008081fd5b613c6d613b1e565b613c7686613b96565b815286860135878201526040613c8d818801613bb6565b90820152606086810135613ca081613901565b90820152608086810135613cb381613901565b9082015260a086810135613cc681613901565b9082015284529384019392850192613c4d565b50979650505050505050565b600080600060608486031215613cfa57600080fd5b8335613d0581613901565b92506020840135613d1581613901565b91506040840135613d2581613901565b809150509250925092565b60008060008060608587031215613d4657600080fd5b843567ffffffffffffffff811115613d5d57600080fd5b613d69878288016139b9565b9095509350506020850135613d7d81613901565b91506040850135613d8d81613901565b939692955090935050565b604081526000613dab6040830185613a98565b82810360208481019190915284518083528582019282019060005b81811015613de257845183529383019391830191600101613dc6565b5090979650505050505050565b600080600060408486031215613e0457600080fd5b833567ffffffffffffffff811115613e1b57600080fd5b613e27868287016139b9565b9094509250506020840135613d2581613901565b60008060208385031215613e4e57600080fd5b823567ffffffffffffffff811115613e6557600080fd5b613e71858286016139b9565b90969095509350505050565b600080600060608486031215613e9257600080fd5b8335613e9d81613901565b92506020840135613ead81613901565b9150613ebb60408501613bb6565b90509250925092565b600080600080600060608688031215613edc57600080fd5b8535613ee781613901565b9450602086013567ffffffffffffffff80821115613f0457600080fd5b613f1089838a016139b9565b90965094506040880135915080821115613f2957600080fd5b50613f36888289016139b9565b969995985093965092949392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b8181038181111561065d5761065d613f76565b600082613fee577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361402457614024613f76565b5060010190565b60006fffffffffffffffffffffffffffffffff80831681810361405057614050613f76565b6001019392505050565b60006020828403121561406c57600080fd5b5051919050565b8082018082111561065d5761065d613f76565b60006020828403121561409857600080fd5b6139b282613b96565b63ffffffff8181168382160190808211156140be576140be613f76565b5092915050565b600181815b8085111561411e57817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0482111561410457614104613f76565b8085161561411157918102915b93841c93908002906140ca565b509250929050565b6000826141355750600161065d565b816141425750600061065d565b816001811461415857600281146141625761417e565b600191505061065d565b60ff84111561417357614173613f76565b50506001821b61065d565b5060208310610133831016604e8410600b84101617156141a1575081810a61065d565b6141ab83836140c5565b807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048211156141dd576141dd613f76565b029392505050565b60006139b28383614126565b60006020828403121561420357600080fd5b815160ff811681146139b257600080fd5b60006139b260ff841683614126565b6fffffffffffffffffffffffffffffffff8181168382160190808211156140be576140be613f76565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048311821515161561428457614284613f76565b50029056fea26469706673582212204924bea7361fc80e6c58984d72fb7cfd8f717a298b58dfaab3653cb8b31f634064736f6c63430008100033

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106101cf5760003560e01c806388ffcf6111610104578063c040af69116100a2578063ef85093611610071578063ef85093614610569578063f5bb3e021461057c578063f5cf673b1461058f578063f996868b146105a257600080fd5b8063c040af6914610465578063c5a7b53814610478578063e23ddec51461048b578063e3887a4b1461049c57600080fd5b80639ff55db9116100de5780639ff55db91461040b578063ad9f8b2f1461042c578063bb492bf51461043f578063bf90f63a1461045257600080fd5b806388ffcf611461034f57806392074b08146103a55780639efd6f72146103b657600080fd5b80634f7459d511610171578063707199e51161014b578063707199e5146102ea57806374d945ec146102fd57806377b8b1c7146103295780638549f3fd1461033c57600080fd5b80634f7459d5146102b15780635453ba10146102c45780636657732f146102d757600080fd5b806338be719f116101ad57806338be719f1461025357806345b35f56146102665780634a6a60f11461027b5780634e18cced1461028e57600080fd5b8063184dde87146101d45780631b839c77146101e95780632a17bf601461020f575b600080fd5b6101e76101e2366004613916565b6105b5565b005b6101fc6101f736600461395c565b610612565b6040519081526020015b60405180910390f35b61023b61021d366004613995565b6001600160a01b039081166000908152600760205260409020541690565b6040516001600160a01b039091168152602001610206565b6101e76102613660046139fe565b610663565b61026e610a8b565b6040516102069190613adc565b60055461023b906001600160a01b031681565b6102a161029c36600461395c565b610aed565b6040519015158152602001610206565b6101e76102bf366004613995565b610bc5565b6101e76102d236600461395c565b610c17565b61026e6102e5366004613995565b610c6b565b6101e76102f8366004613bca565b610d97565b61023b61030b366004613995565b6001600160a01b039081166000908152600660205260409020541690565b6101e7610337366004613ce5565b610f0c565b6101fc61034a366004613d30565b610fc0565b6101fc61035d366004613ce5565b6001600160a01b038083166000908152602081815260408083208585168452825280832093871683526001909301905220546cffffffffffffffffffffffffff169392505050565b6004546001600160a01b031661023b565b6103f96103c4366004613995565b6001600160a01b0316600090815260208190526040902060020154700100000000000000000000000000000000900460ff1690565b60405160ff9091168152602001610206565b61041e610419366004613d30565b611168565b604051610206929190613d98565b6101fc61043a36600461395c565b611263565b61041e61044d366004613def565b61130e565b61041e610460366004613e3b565b61136b565b6101e7610473366004613995565b611387565b6101e7610486366004613e7d565b6113d6565b6008546001600160a01b031661023b565b6105496104aa36600461395c565b6001600160a01b039182166000908152602081815260408083209390941682529190915220546cffffffffffffffffffffffffff8116916affffffffffffffffffffff6d01000000000000000000000000008304169163ffffffff780100000000000000000000000000000000000000000000000082048116927c01000000000000000000000000000000000000000000000000000000009092041690565b604080519485526020850193909352918301526060820152608001610206565b61041e610577366004613def565b6115ec565b6101e761058a366004613995565b6118b9565b6101e761059d36600461395c565b611908565b6101e76105b0366004613ec4565b6119bd565b6005546001600160a01b03163314610600576040517f4c4365b70000000000000000000000000000000000000000000000000000000081523360048201526024015b60405180910390fd5b61060c84848484611b83565b50505050565b6001600160a01b03828116600090815260208181526040808320938516835292905220547c0100000000000000000000000000000000000000000000000000000000900463ffffffff165b92915050565b6004546001600160a01b031633146106a9576040517f59afb3390000000000000000000000000000000000000000000000000000000081523360048201526024016105f7565b84831415806106b85750828114155b156106ef576040517fb4fa3fb300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b85811015610a825761075087878381811061070f5761070f613f47565b90506020020160208101906107249190613995565b86868481811061073657610736613f47565b905060200201602081019061074b9190613995565b611cef565b6107e75786868281811061076657610766613f47565b905060200201602081019061077b9190613995565b85858381811061078d5761078d613f47565b90506020020160208101906107a29190613995565b6040517f9d0f304c0000000000000000000000000000000000000000000000000000000081526001600160a01b039283166004820152911660248201526044016105f7565b600061083f8888848181106107fe576107fe613f47565b90506020020160208101906108139190613995565b87878581811061082557610825613f47565b905060200201602081019061083a9190613995565b611d7e565b905060006108534263ffffffff8416613fa5565b85858581811061086557610865613f47565b905060200201356108769190613fb8565b90506000806108d28b8b8781811061089057610890613f47565b90506020020160208101906108a59190613995565b8a8a888181106108b7576108b7613f47565b90506020020160208101906108cc9190613995565b85611ddf565b509150915060006109a48c8c888181106108ee576108ee613f47565b90506020020160208101906109039190613995565b8b8b8981811061091557610915613f47565b905060200201602081019061092a9190613995565b6001600160a01b039182166000908152602081815260408083209390941682529190915220805463ffffffff8881167c01000000000000000000000000000000000000000000000000000000009081027bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8416179093559190041690565b90508989878181106109b8576109b8613f47565b90506020020160208101906109cd9190613995565b6001600160a01b03168c8c888181106109e8576109e8613f47565b90506020020160208101906109fd9190613995565b604080518681526affffffffffffffffffffff8816602082015290810184905263ffffffff88166060820152608081018590526001600160a01b0391909116907fac1777479f07f3e7c34da8402139d54027a6a260caaae168bdee825ca5580dc59060a00160405180910390a350505050508080610a7a90613ff3565b9150506106f2565b50505050505050565b60606002805480602002602001604051908101604052809291908181526020018280548015610ae357602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610ac5575b5050505050905090565b6001600160a01b038281166000818152602081815260408083209486168352848252822092825281905260029092015484918491700100000000000000000000000000000000900460ff16801580610b66575081547801000000000000000000000000000000000000000000000000900463ffffffff16155b15610bb0576040517fc8bed79e0000000000000000000000000000000000000000000000000000000081526001600160a01b038086166004830152841660248201526044016105f7565b610bba8787611cef565b979650505050505050565b6004546001600160a01b03163314610c0b576040517f59afb3390000000000000000000000000000000000000000000000000000000081523360048201526024016105f7565b610c1481611fc7565b50565b6004546001600160a01b03163314610c5d576040517f59afb3390000000000000000000000000000000000000000000000000000000081523360048201526024016105f7565b610c678282612031565b5050565b6001600160a01b0381166000908152602081905260408120600201546060916fffffffffffffffffffffffffffffffff909116908167ffffffffffffffff811115610cb857610cb8613aef565b604051908082528060200260200182016040528015610ce1578160200160208202803683370190505b50905060005b826fffffffffffffffffffffffffffffffff16816fffffffffffffffffffffffffffffffff161015610d8f576001600160a01b038086166000908152602081815260408083206fffffffffffffffffffffffffffffffff861680855260019091019092529091205484519216918491908110610d6557610d65613f47565b6001600160a01b039092166020928302919091019091015280610d878161402b565b915050610ce7565b509392505050565b6004546001600160a01b03163314610ddd576040517f59afb3390000000000000000000000000000000000000000000000000000000081523360048201526024016105f7565b60005b8151811015610f025760055482516001600160a01b0390911690639bfd8d6190849084908110610e1257610e12613f47565b6020026020010151606001516040518263ffffffff1660e01b8152600401610e4991906001600160a01b0391909116815260200190565b602060405180830381865afa158015610e66573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e8a919061405a565b828281518110610e9c57610e9c613f47565b60200260200101516020018181525050610ef0828281518110610ec157610ec1613f47565b602002602001015160800151838381518110610edf57610edf613f47565b602002602001015160a00151612031565b80610efa81613ff3565b915050610de0565b50610c148161214e565b60055474010000000000000000000000000000000000000000900460ff1615610f61576040517f0dc149f000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600580547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff1674010000000000000000000000000000000000000000179055610fa98361294c565b610fb2826129ae565b610fbb81611fc7565b505050565b600080610fce868686612a10565b905060005b815181101561115e57818181518110610fee57610fee613f47565b60200260200101516020015160000361108e5760008083838151811061101657611016613f47565b602090810291909101810151516001600160a01b039081168352828201939093526040918201600090812088851682528252828120938916815260019093019052902054611087906d010000000000000000000000000090046fffffffffffffffffffffffffffffffff1684614073565b925061114c565b6000808383815181106110a3576110a3613f47565b602090810291909101810151516001600160a01b03908116835282820193909352604091820160009081208885168252825282812093891681526001909301905290205482516d01000000000000000000000000009091046fffffffffffffffffffffffffffffffff1690611135908790879086908690811061112857611128613f47565b6020026020010151612cab565b61113f9190614073565b6111499084614073565b92505b8061115681613ff3565b915050610fd3565b5050949350505050565b6001600160a01b038083166000908152600660205260409020546060918291339186911682148015906111a957506005546001600160a01b03838116911614155b156111f3576040517f3ebd5fe50000000000000000000000000000000000000000000000000000000081526001600160a01b038084166004830152821660248201526044016105f7565b6001600160a01b038516158061121057506001600160a01b038616155b15611247576040517fb4fa3fb300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6112548888338989612d5e565b93509350505094509492505050565b60008060005b600354811015610d8f576000806003838154811061128957611289613f47565b6000918252602080832091909101546001600160a01b039081168452838201949094526040928301822088851683528152828220938916825260019093019092529020546112fa906d010000000000000000000000000090046fffffffffffffffffffffffffffffffff1683614073565b91508061130681613ff3565b915050611269565b6060806001600160a01b038316611351576040517fb4fa3fb300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61135e8585333387612d5e565b915091505b935093915050565b60608061137b8484333333612d5e565b915091505b9250929050565b6004546001600160a01b031633146113cd576040517f59afb3390000000000000000000000000000000000000000000000000000000081523360048201526024016105f7565b610c148161294c565b6004546001600160a01b0316331461141c576040517f59afb3390000000000000000000000000000000000000000000000000000000081523360048201526024016105f7565b6001600160a01b03838116600081815260208181526040808320948716835284825282209282525260029091015484918491700100000000000000000000000000000000900460ff16801580611493575081547801000000000000000000000000000000000000000000000000900463ffffffff16155b156114dd576040517fc8bed79e0000000000000000000000000000000000000000000000000000000081526001600160a01b038086166004830152841660248201526044016105f7565b6001600160a01b03878116600081815260208181526040808320948b1680845294825291829020805463ffffffff8b81167c01000000000000000000000000000000000000000000000000000000008181027bffffffffffffffffffffffffffffffffffffffffffffffffffffffff851681179586905587516d01000000000000000000000000009096046affffffffffffffffffffff1680875296860196909652830490911683860181905260608401919091526cffffffffffffffffffffffffff918216919093161760808201819052925191949293917fac1777479f07f3e7c34da8402139d54027a6a260caaae168bdee825ca5580dc59160a0908290030190a3505050505050505050565b60608060006115fc868686612a10565b60025490915067ffffffffffffffff81111561161a5761161a613aef565b604051908082528060200260200182016040528015611643578160200160208202803683370190505b509250825167ffffffffffffffff81111561166057611660613aef565b604051908082528060200260200182016040528015611689578160200160208202803683370190505b50915060005b81518110156118af5760005b845181101561189c57600281815481106116b7576116b7613f47565b9060005260206000200160009054906101000a90046001600160a01b03168582815181106116e7576116e7613f47565b60200260200101906001600160a01b031690816001600160a01b03168152505060008084848151811061171c5761171c613f47565b6020026020010151600001516001600160a01b03166001600160a01b03168152602001908152602001600020600001600086838151811061175f5761175f613f47565b60200260200101516001600160a01b03166001600160a01b031681526020019081526020016000206001016000876001600160a01b03166001600160a01b03168152602001908152602001600020600001600d9054906101000a90046fffffffffffffffffffffffffffffffff166fffffffffffffffffffffffffffffffff168482815181106117f1576117f1613f47565b602002602001018181516118059190614073565b905250825183908390811061181c5761181c613f47565b6020026020010151602001516000031561188a576118608686838151811061184657611846613f47565b602002602001015185858151811061112857611128613f47565b84828151811061187257611872613f47565b602002602001018181516118869190614073565b9052505b8061189481613ff3565b91505061169b565b50806118a781613ff3565b91505061168f565b5050935093915050565b6004546001600160a01b031633146118ff576040517f59afb3390000000000000000000000000000000000000000000000000000000081523360048201526024016105f7565b610c14816129ae565b6004546001600160a01b0316331461194e576040517f59afb3390000000000000000000000000000000000000000000000000000000081523360048201526024016105f7565b6001600160a01b0382811660008181526006602052604080822080547fffffffffffffffffffffffff0000000000000000000000000000000000000000169486169485179055517f4925eafc82d0c4d67889898eeed64b18488ab19811e61620f387026dec126a289190a35050565b6004546001600160a01b03163314611a03576040517f59afb3390000000000000000000000000000000000000000000000000000000081523360048201526024016105f7565b828114611a3c576040517fb4fa3fb300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b83811015611b7b576000806000611aa389898987818110611a6257611a62613f47565b9050602002016020810190611a779190613995565b888888818110611a8957611a89613f47565b9050602002016020810190611a9e9190614086565b611ddf565b925092509250878785818110611abb57611abb613f47565b9050602002016020810190611ad09190613995565b6001600160a01b0316896001600160a01b03167fac1777479f07f3e7c34da8402139d54027a6a260caaae168bdee825ca5580dc585898989818110611b1757611b17613f47565b9050602002016020810190611b2c9190614086565b604080519283526affffffffffffffffffffff90911660208301528101859052606081018590526080810186905260a00160405180910390a35050508080611b7390613ff3565b915050611a3f565b505050505050565b6001600160a01b03841660009081526020819052604081206002015460ff700100000000000000000000000000000000820416600a0a916fffffffffffffffffffffffffffffffff90911690819003611bdd57505061060c565b60005b81816fffffffffffffffffffffffffffffffff161015610a82576001600160a01b038088166000908152602081815260408083206fffffffffffffffffffffffffffffffff86168452600181018352818420549094168084529390915281209080611c4c83898961317a565b91509150600080611c60858d8d878d6132db565b915091508280611c6d5750805b15611cdd578b6001600160a01b0316866001600160a01b03168e6001600160a01b03167f3303facd24627943a92e9dc87cfbb34b15c49b726eec3ad3487c16be9ab8efe8878887604051611cd4939291909283526020830191909152604082015260600190565b60405180910390a45b505060019094019350611be092505050565b6001600160a01b038281166000908152602081815260408083209385168352929052908120547c0100000000000000000000000000000000000000000000000000000000900463ffffffff1681611d468585611d7e565b905063ffffffff8216801515906000904210801590611d6a57508263ffffffff1642105b9050818015610bba57509695505050505050565b6001600160a01b038281166000908152602081815260408083209385168352929052908120547c0100000000000000000000000000000000000000000000000000000000900463ffffffff16611dd78162093a806140a1565b949350505050565b6001600160a01b038381166000908152602081815260408083209386168352908390528120600283015491928392839290700100000000000000000000000000000000900460ff16801580611e55575081547801000000000000000000000000000000000000000000000000900463ffffffff16155b15611e9f576040517fc8bed79e0000000000000000000000000000000000000000000000000000000081526001600160a01b03808b166004830152891660248201526044016105f7565b81546005546040517f9bfd8d610000000000000000000000000000000000000000000000000000000081526001600160a01b038c811660048301527c010000000000000000000000000000000000000000000000000000000090930463ffffffff169650611f659285921690639bfd8d6190602401602060405180830381865afa158015611f31573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f55919061405a565b611f6084600a6141e5565b61317a565b5082546affffffffffffffffffffff9889166d01000000000000000000000000009081027fffffffffffffffff0000000000000000000000ffffffffffffffffffffffffff831617909455929092049690961698909750919550909350505050565b600480546001600160a01b038381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681179093556040519116919082907f53271355c244f99d37f622c90fe574eb7c35c7b3548ea276cf9b5b11c601605e90600090a35050565b6000816001600160a01b03166350d25bcd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612071573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612095919061405a565b136120df576040517f4f79ac5c0000000000000000000000000000000000000000000000000000000081526001600160a01b038084166004830152821660248201526044016105f7565b6001600160a01b0382811660008181526007602052604080822080547fffffffffffffffffffffffff0000000000000000000000000000000000000000169486169485179055517f1a1cd5483e52e60b9ff7f3b9d1db3bbd9e9d21c6324ad3a8c79dba9b75e62f4d9190a35050565b60005b8151811015610c6757600082828151811061216e5761216e613f47565b6020026020010151606001516001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa1580156121b7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121db91906141f1565b90508060ff16600003612248578282815181106121fa576121fa613f47565b6020026020010151606001516040517f8f0b74150000000000000000000000000000000000000000000000000000000081526004016105f791906001600160a01b0391909116815260200190565b60008084848151811061225d5761225d613f47565b6020026020010151606001516001600160a01b03166001600160a01b0316815260200190815260200160002060020160109054906101000a900460ff1660ff166000036123105760038383815181106122b8576122b8613f47565b6020908102919091018101516060015182546001810184556000938452919092200180547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b039092169190911790555b8060008085858151811061232657612326613f47565b6020026020010151606001516001600160a01b03166001600160a01b0316815260200190815260200160002060020160106101000a81548160ff021916908360ff160217905550600080600085858151811061238457612384613f47565b6020026020010151606001516001600160a01b03166001600160a01b0316815260200190815260200160002060000160008585815181106123c7576123c7613f47565b6020026020010151608001516001600160a01b03166001600160a01b0316815260200190815260200160002090508060000160189054906101000a900463ffffffff1663ffffffff166000036125cf57600080600086868151811061242e5761242e613f47565b6020026020010151606001516001600160a01b03166001600160a01b0316815260200190815260200160002060020160009054906101000a90046fffffffffffffffffffffffffffffffff16905084848151811061248e5761248e613f47565b6020026020010151608001516000808787815181106124af576124af613f47565b6020026020010151606001516001600160a01b03166001600160a01b031681526020019081526020016000206001016000836fffffffffffffffffffffffffffffffff166fffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060008086868151811061254c5761254c613f47565b602090810291909101810151606001516001600160a01b03168252810191909152604001600090812060020180546fffffffffffffffffffffffffffffffff16916125968361402b565b91906101000a8154816fffffffffffffffffffffffffffffffff02191690836fffffffffffffffffffffffffffffffff16021790555050505b600160008585815181106125e5576125e5613f47565b602090810291909101810151608001516001600160a01b0316825281019190915260400160009081205460ff16151590036126e057600180600086868151811061263157612631613f47565b6020026020010151608001516001600160a01b03166001600160a01b0316815260200190815260200160002060006101000a81548160ff021916908315150217905550600284848151811061268857612688613f47565b6020908102919091018101516080015182546001810184556000938452919092200180547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b039092169190911790555b6000612711828686815181106126f8576126f8613f47565b60200260200101516020015185600a611f609190614214565b50825486519192506d010000000000000000000000000081046affffffffffffffffffffff16917c010000000000000000000000000000000000000000000000000000000090910463ffffffff169087908790811061277257612772613f47565b60209081029190910101515184546affffffffffffffffffffff9091166d0100000000000000000000000000027fffffffffffffffff0000000000000000000000ffffffffffffffffffffffffff90911617845586518790879081106127da576127da613f47565b602090810291909101015160400151845463ffffffff9091167c0100000000000000000000000000000000000000000000000000000000027bffffffffffffffffffffffffffffffffffffffffffffffffffffffff909116178455865187908790811061284957612849613f47565b6020026020010151608001516001600160a01b031687878151811061287057612870613f47565b6020026020010151606001516001600160a01b03167fac1777479f07f3e7c34da8402139d54027a6a260caaae168bdee825ca5580dc5848a8a815181106128b9576128b9613f47565b602002602001015160000151858c8c815181106128d8576128d8613f47565b6020026020010151604001518960405161292c9594939291906affffffffffffffffffffff958616815293909416602084015263ffffffff9182166040840152166060820152608081019190915260a00190565b60405180910390a35050505050808061294490613ff3565b915050612151565b600580547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383169081179091556040517f56ca898af8c1c9c8321dc2ddb32e363c6cd2f38e679340e14a67c810100ce7b790600090a250565b600880547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383169081179091556040517f28a48cbce43190d77247f342cf319b1607bff4ef716cf26b76cf7bb71baebaa590600090a250565b60608267ffffffffffffffff811115612a2b57612a2b613aef565b604051908082528060200260200182016040528015612a8957816020015b612a76604051806060016040528060006001600160a01b0316815260200160008152602001600081525090565b815260200190600190039081612a495790505b50905060005b83811015610d8f57848482818110612aa957612aa9613f47565b9050602002016020810190612abe9190613995565b828281518110612ad057612ad0613f47565b60209081029190910101516001600160a01b0391821690526005541663f7888aec868684818110612b0357612b03613f47565b9050602002016020810190612b189190613995565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b1681526001600160a01b0391821660048201529086166024820152604401602060405180830381865afa158015612b7c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ba0919061405a565b828281518110612bb257612bb2613f47565b60209081029190910181015101526005546001600160a01b0316639bfd8d61868684818110612be357612be3613f47565b9050602002016020810190612bf89190613995565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b1681526001600160a01b039091166004820152602401602060405180830381865afa158015612c54573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c78919061405a565b828281518110612c8a57612c8a613f47565b60209081029190910101516040015280612ca381613ff3565b915050612a8f565b80516001600160a01b039081166000908152602081815260408083208685168452825280832085519094168352908290528120600201549091908290612d0990700100000000000000000000000000000000900460ff16600a614214565b90506000612d1c8386604001518461345d565b6020808801516001600160a01b038b16600090815260018801909252604090912054919350610bba92509083906cffffffffffffffffffffffffff1685613569565b60025460609081908067ffffffffffffffff811115612d7f57612d7f613aef565b604051908082528060200260200182016040528015612da8578160200160208202803683370190505b5092508067ffffffffffffffff811115612dc457612dc4613aef565b604051908082528060200260200182016040528015612ded578160200160208202803683370190505b509150612e0485612dff8a8a89612a10565b61358b565b60005b87811015613076576000898983818110612e2357612e23613f47565b9050602002016020810190612e389190613995565b905060005b838110156130615760006001600160a01b0316868281518110612e6257612e62613f47565b60200260200101516001600160a01b031603612edc5760028181548110612e8b57612e8b613f47565b9060005260206000200160009054906101000a90046001600160a01b0316868281518110612ebb57612ebb613f47565b60200260200101906001600160a01b031690816001600160a01b0316815250505b6001600160a01b038216600090815260208190526040812087518290899085908110612f0a57612f0a613f47565b6020908102919091018101516001600160a01b0390811683528282019390935260409182016000908120938d168152600190930190529020546d010000000000000000000000000090046fffffffffffffffffffffffffffffffff169050801561304e5780868381518110612f8157612f81613f47565b60200260200101818151612f959190614073565b9052506001600160a01b0383166000908152602081905260408120885182908a9086908110612fc657612fc6613f47565b60200260200101516001600160a01b03166001600160a01b0316815260200190815260200160002060010160008b6001600160a01b03166001600160a01b03168152602001908152602001600020600001600d6101000a8154816fffffffffffffffffffffffffffffffff02191690836fffffffffffffffffffffffffffffffff1602179055505b508061305981613ff3565b915050612e3d565b5050808061306e90613ff3565b915050612e07565b5060005b8181101561316e576130bf8585838151811061309857613098613f47565b60200260200101518584815181106130b2576130b2613f47565b602002602001015161360c565b846001600160a01b03168482815181106130db576130db613f47565b60200260200101516001600160a01b0316876001600160a01b03167fc052130bc4ef84580db505783484b067ea8b71b3bca78a7e12db7aea8658f0048a87868151811061312a5761312a613f47565b60200260200101516040516131549291906001600160a01b03929092168252602082015260400190565b60405180910390a48061316681613ff3565b91505061307a565b50509550959350505050565b60008060008061318b87878761345d565b915091506000828214613277576cffffffffffffffffffffffffff8211156131e2576040517fc6c74120000000000000000000000000000000000000000000000000000000008152600481018390526024016105f7565b5086547fffffffffffffffffffffffffffffffffffffff00000000000000000000000000166cffffffffffffffffffffffffff8216178755600161322542613625565b885463ffffffff919091167801000000000000000000000000000000000000000000000000027fffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffffff9091161788556132ce565b61328042613625565b885463ffffffff919091167801000000000000000000000000000000000000000000000000027fffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffffff9091161788555b9097909650945050505050565b6001600160a01b038416600090815260018601602052604081205481906cffffffffffffffffffffffffff168185821480159061344e576cffffffffffffffffffffffffff87111561335c576040517fc6c74120000000000000000000000000000000000000000000000000000000008152600481018890526024016105f7565b6001600160a01b038916600090815260018b016020526040902080547fffffffffffffffffffffffffffffffffffffff00000000000000000000000000166cffffffffffffffffffffffffff8916179055871561344e576133bf88888589613569565b91506133ca826136bf565b6001600160a01b038a16600090815260018c01602052604090208054600d906134179084906d010000000000000000000000000090046fffffffffffffffffffffffffffffffff16614223565b92506101000a8154816fffffffffffffffffffffffffffffffff02191690836fffffffffffffffffffffffffffffffff1602179055505b90999098509650505050505050565b825460009081906cffffffffffffffffffffffffff81169063ffffffff7c010000000000000000000000000000000000000000000000000000000082048116916affffffffffffffffffffff6d0100000000000000000000000000820416917801000000000000000000000000000000000000000000000000909104168115806134e5575087155b806134ef57504281145b806134fa5750828110155b1561350e5783849550955050505050611363565b600083421161351d574261351f565b835b9050600061352d8383613fa5565b905060008961353c838761424c565b613546919061424c565b8b90049050866135568183614073565b9850985050505050505050935093915050565b60006135758385613fa5565b61357f908661424c565b91909104949350505050565b60005b8151811015610fbb576135fa8282815181106135ac576135ac613f47565b602002602001015160000151848484815181106135cb576135cb613f47565b6020026020010151602001518585815181106135e9576135e9613f47565b602002602001015160400151611b83565b8061360481613ff3565b91505061358e565b600854610fbb9083906001600160a01b03168584613761565b600063ffffffff8211156136bb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203360448201527f322062697473000000000000000000000000000000000000000000000000000060648201526084016105f7565b5090565b60006fffffffffffffffffffffffffffffffff8211156136bb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602760248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203160448201527f323820626974730000000000000000000000000000000000000000000000000060648201526084016105f7565b6040517f23b872dd000000000000000000000000000000000000000000000000000000008082526001600160a01b038581166004840152841660248301526044820183905290600080606483828a5af16137bf573d6000803e3d6000fd5b506137c985613836565b61382f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f475076323a206661696c6564207472616e7366657246726f6d0000000000000060448201526064016105f7565b5050505050565b6000613875565b7f08c379a000000000000000000000000000000000000000000000000000000000600052602060045280602452508060445260646000fd5b3d80156138b457602081146138ee576138af7f475076323a206d616c666f726d6564207472616e7366657220726573756c7400601f61383d565b6138fb565b823b6138e5576138e57f475076323a206e6f74206120636f6e7472616374000000000000000000000000601461383d565b600191506138fb565b3d6000803e600051151591505b50919050565b6001600160a01b0381168114610c1457600080fd5b6000806000806080858703121561392c57600080fd5b843561393781613901565b9350602085013561394781613901565b93969395505050506040820135916060013590565b6000806040838503121561396f57600080fd5b823561397a81613901565b9150602083013561398a81613901565b809150509250929050565b6000602082840312156139a757600080fd5b81356139b281613901565b9392505050565b60008083601f8401126139cb57600080fd5b50813567ffffffffffffffff8111156139e357600080fd5b6020830191508360208260051b850101111561138057600080fd5b60008060008060008060608789031215613a1757600080fd5b863567ffffffffffffffff80821115613a2f57600080fd5b613a3b8a838b016139b9565b90985096506020890135915080821115613a5457600080fd5b613a608a838b016139b9565b90965094506040890135915080821115613a7957600080fd5b50613a8689828a016139b9565b979a9699509497509295939492505050565b600081518084526020808501945080840160005b83811015613ad15781516001600160a01b031687529582019590820190600101613aac565b509495945050505050565b6020815260006139b26020830184613a98565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60405160c0810167ffffffffffffffff81118282101715613b4157613b41613aef565b60405290565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715613b8e57613b8e613aef565b604052919050565b80356affffffffffffffffffffff81168114613bb157600080fd5b919050565b803563ffffffff81168114613bb157600080fd5b60006020808385031215613bdd57600080fd5b823567ffffffffffffffff80821115613bf557600080fd5b818501915085601f830112613c0957600080fd5b813581811115613c1b57613c1b613aef565b613c29848260051b01613b47565b818152848101925060c0918202840185019188831115613c4857600080fd5b938501935b82851015613cd95780858a031215613c655760008081fd5b613c6d613b1e565b613c7686613b96565b815286860135878201526040613c8d818801613bb6565b90820152606086810135613ca081613901565b90820152608086810135613cb381613901565b9082015260a086810135613cc681613901565b9082015284529384019392850192613c4d565b50979650505050505050565b600080600060608486031215613cfa57600080fd5b8335613d0581613901565b92506020840135613d1581613901565b91506040840135613d2581613901565b809150509250925092565b60008060008060608587031215613d4657600080fd5b843567ffffffffffffffff811115613d5d57600080fd5b613d69878288016139b9565b9095509350506020850135613d7d81613901565b91506040850135613d8d81613901565b939692955090935050565b604081526000613dab6040830185613a98565b82810360208481019190915284518083528582019282019060005b81811015613de257845183529383019391830191600101613dc6565b5090979650505050505050565b600080600060408486031215613e0457600080fd5b833567ffffffffffffffff811115613e1b57600080fd5b613e27868287016139b9565b9094509250506020840135613d2581613901565b60008060208385031215613e4e57600080fd5b823567ffffffffffffffff811115613e6557600080fd5b613e71858286016139b9565b90969095509350505050565b600080600060608486031215613e9257600080fd5b8335613e9d81613901565b92506020840135613ead81613901565b9150613ebb60408501613bb6565b90509250925092565b600080600080600060608688031215613edc57600080fd5b8535613ee781613901565b9450602086013567ffffffffffffffff80821115613f0457600080fd5b613f1089838a016139b9565b90965094506040880135915080821115613f2957600080fd5b50613f36888289016139b9565b969995985093965092949392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b8181038181111561065d5761065d613f76565b600082613fee577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361402457614024613f76565b5060010190565b60006fffffffffffffffffffffffffffffffff80831681810361405057614050613f76565b6001019392505050565b60006020828403121561406c57600080fd5b5051919050565b8082018082111561065d5761065d613f76565b60006020828403121561409857600080fd5b6139b282613b96565b63ffffffff8181168382160190808211156140be576140be613f76565b5092915050565b600181815b8085111561411e57817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0482111561410457614104613f76565b8085161561411157918102915b93841c93908002906140ca565b509250929050565b6000826141355750600161065d565b816141425750600061065d565b816001811461415857600281146141625761417e565b600191505061065d565b60ff84111561417357614173613f76565b50506001821b61065d565b5060208310610133831016604e8410600b84101617156141a1575081810a61065d565b6141ab83836140c5565b807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048211156141dd576141dd613f76565b029392505050565b60006139b28383614126565b60006020828403121561420357600080fd5b815160ff811681146139b257600080fd5b60006139b260ff841683614126565b6fffffffffffffffffffffffffffffffff8181168382160190808211156140be576140be613f76565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048311821515161561428457614284613f76565b50029056fea26469706673582212204924bea7361fc80e6c58984d72fb7cfd8f717a298b58dfaab3653cb8b31f634064736f6c63430008100033

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
[ Download: CSV Export  ]

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.