APE Price: $1.12 (+1.56%)

Contract

0x9B31e11BDDb32DA108f7476561f4835D0e113A44

Overview

APE Balance

Apechain LogoApechain LogoApechain Logo0 APE

APE Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
0x6080604046086092024-11-18 20:36:2014 hrs ago1731962180IN
 Create: Rewarder
0 APE0.0592628725.42069

Parent Transaction Hash Block From To
View All Internal Transactions

Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
Rewarder

Compiler Version
v0.7.5+commit.eb77ed08

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion
File 1 of 14 : Rewarder.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.7.5;
pragma experimental ABIEncoderV2;

import {IERC20} from './interfaces/IERC20.sol';
import {SafeERC20} from './libraries/SafeERC20.sol';
import {RewardsController} from './RewardsController.sol';

contract Rewarder is RewardsController {
  using SafeERC20 for IERC20;

  // reward => reward vault
  mapping(address => address) internal _rewardsVault;

  event RewardsVaultUpdated(address indexed vault);

  function setRewardsVault(address vault, address reward) external onlyOwner {
  	_rewardsVault[reward] = vault;
  	emit RewardsVaultUpdated(vault);
  }

  function getRewardsVault(address reward) external view returns (address) {
  	return _rewardsVault[reward];
  }

  function transferRewards(address to, address reward, uint256 amount) internal override returns (bool) {
    IERC20(reward).safeTransferFrom(_rewardsVault[reward], to, amount);
    return true;
  }
}

File 2 of 14 : IERC20.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.7.5;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 * From https://github.com/OpenZeppelin/openzeppelin-contracts
 */
interface IERC20 {
  /**
   * @dev Returns the amount of tokens in existence.
   */
  function totalSupply() external view returns (uint256);

  /**
   * @dev Returns the amount of tokens owned by `account`.
   */
  function balanceOf(address account) external view returns (uint256);

  /**
   * @dev Moves `amount` tokens from the caller's account to `recipient`.
   *
   * Returns a boolean value indicating whether the operation succeeded.
   *
   * Emits a {Transfer} event.
   */
  function transfer(address recipient, uint256 amount) external returns (bool);

  /**
   * @dev Returns the remaining number of tokens that `spender` will be
   * allowed to spend on behalf of `owner` through {transferFrom}. This is
   * zero by default.
   *
   * This value changes when {approve} or {transferFrom} are called.
   */
  function allowance(address owner, address spender) external view returns (uint256);

  /**
   * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
   *
   * Returns a boolean value indicating whether the operation succeeded.
   *
   * IMPORTANT: Beware that changing an allowance with this method brings the risk
   * that someone may use both the old and the new allowance by unfortunate
   * transaction ordering. One possible solution to mitigate this race
   * condition is to first reduce the spender's allowance to 0 and set the
   * desired value afterwards:
   * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
   *
   * Emits an {Approval} event.
   */
  function approve(address spender, uint256 amount) external returns (bool);

  /**
   * @dev Moves `amount` tokens from `sender` to `recipient` using the
   * allowance mechanism. `amount` is then deducted from the caller's
   * allowance.
   *
   * Returns a boolean value indicating whether the operation succeeded.
   *
   * Emits a {Transfer} event.
   */
  function transferFrom(
    address sender,
    address recipient,
    uint256 amount
  ) external returns (bool);

  /**
   * @dev Emitted when `value` tokens are moved from one account (`from`) to
   * another (`to`).
   *
   * Note that `value` may be zero.
   */
  event Transfer(address indexed from, address indexed to, uint256 value);

  /**
   * @dev Emitted when the allowance of a `spender` for an `owner` is set by
   * a call to {approve}. `value` is the new allowance.
   */
  event Approval(address indexed owner, address indexed spender, uint256 value);
}

File 3 of 14 : IERC20Detailed.sol
// SPDX-License-Identifier: agpl-3.0
pragma solidity 0.7.5;

import {IERC20} from './IERC20.sol';

interface IERC20Detailed is IERC20 {
  function name() external view returns (string memory);

  function symbol() external view returns (string memory);

  function decimals() external view returns (uint8);
}

File 4 of 14 : IRewardsController.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.7.5;
pragma experimental ABIEncoderV2;

import {IRewardsDistributor} from './IRewardsDistributor.sol';
import {DistributionTypes} from '../libraries/DistributionTypes.sol';

interface IRewardsController is IRewardsDistributor {
  event RewardsClaimed(
    address indexed user,
    address indexed reward,    
    address indexed to,
    address claimer,
    uint256 amount
  );

  event ClaimerSet(address indexed user, address indexed claimer);

  function setClaimer(address user, address claimer) external;

  function getClaimer(address user) external view returns (address);

  function configureAssets(DistributionTypes.RewardsConfigInput[] memory config) external;

  function handleAction(
    address asset,
    uint256 userBalance,
    uint256 totalSupply
  ) external;

  function claimRewards(
    address[] calldata assets,
    uint256 amount,
    address to,
    address reward
  ) external returns (uint256);

  function claimRewardsOnBehalf(
    address[] calldata assets,
    uint256 amount,
    address user,
    address to,
    address reward
  ) external returns (uint256);


  function claimRewardsToSelf(
    address[] calldata assets,
    uint256 amount,
    address reward
  ) external returns (uint256);

  function claimAllRewards(address[] calldata assets, address to)
    external
    returns (address[] memory rewardsList, uint256[] memory claimedAmounts);

  function claimAllRewardsOnBehalf(
    address[] calldata assets,
    address user,
    address to
  ) external returns (address[] memory rewardsList, uint256[] memory claimedAmounts);

  function claimAllRewardsToSelf(address[] calldata assets)
    external
    returns (address[] memory rewardsList, uint256[] memory claimedAmounts);
}

File 5 of 14 : IRewardsDistributor.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.7.5;
pragma experimental ABIEncoderV2;

import {DistributionTypes} from '../libraries/DistributionTypes.sol';

interface IRewardsDistributor {
  event AssetConfigUpdated(
    address indexed asset,
    address indexed reward,
    uint256 emission,
    uint256 distributionEnd
  );
  event AssetIndexUpdated(address indexed asset, address indexed reward, uint256 index);
  event UserIndexUpdated(
    address indexed user,
    address indexed asset,
    address indexed reward,
    uint256 index
  );

  event RewardsAccrued(address indexed user, address indexed reward, uint256 amount);

  function setDistributionEnd(
    address asset,
    address reward,
    uint32 distributionEnd
  ) external;

  function getDistributionEnd(address asset, address reward) external view returns (uint256);

  function getUserAssetData(
    address user,
    address asset,
    address reward
  ) external view returns (uint256);

  function getRewardsData(address asset, address reward)
    external
    view
    returns (
      uint256,
      uint256,
      uint256,
      uint256
    );

  function getRewardsByAsset(address asset) external view returns (address[] memory);

  function getRewardTokens() external view returns (address[] memory);

  function getUserUnclaimedRewardsFromStorage(address user, address reward)
    external
    view
    returns (uint256);

  function getUserRewardsBalance(
    address[] calldata assets,
    address user,
    address reward
  ) external view returns (uint256);

  function getAllUserRewardsBalance(address[] calldata assets, address user)
    external
    view
    returns (address[] memory, uint256[] memory);

  function getAssetDecimals(address asset) external view returns (uint8);
}

File 6 of 14 : IScaledBalanceToken.sol
// SPDX-License-Identifier: agpl-3.0
pragma solidity 0.7.5;

interface IScaledBalanceToken {
  /**
   * @dev Returns the scaled balance of the user. The scaled balance is the sum of all the
   * updated stored balance divided by the reserve's liquidity index at the moment of the update
   * @param user The user whose balance is calculated
   * @return The scaled balance of the user
   **/
  function scaledBalanceOf(address user) external view returns (uint256);

  /**
   * @dev Returns the scaled balance of the user and the scaled total supply.
   * @param user The address of the user
   * @return The scaled balance of the user
   * @return The scaled balance and the scaled total supply
   **/
  function getScaledUserBalanceAndSupply(address user) external view returns (uint256, uint256);

  /**
   * @dev Returns the scaled total supply of the token. Represents sum(debt/index)
   * @return The scaled total supply
   **/
  function scaledTotalSupply() external view returns (uint256);
}

File 7 of 14 : Address.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.7.5;

/**
 * @dev Collection of functions related to the address type
 * From https://github.com/OpenZeppelin/openzeppelin-contracts
 */
library Address {
  /**
   * @dev Returns true if `account` is a contract.
   *
   * [IMPORTANT]
   * ====
   * It is unsafe to assume that an address for which this function returns
   * false is an externally-owned account (EOA) and not a contract.
   *
   * Among others, `isContract` will return false for the following
   * types of addresses:
   *
   *  - an externally-owned account
   *  - a contract in construction
   *  - an address where a contract will be created
   *  - an address where a contract lived, but was destroyed
   * ====
   */
  function isContract(address account) internal view returns (bool) {
    // According to EIP-1052, 0x0 is the value returned for not-yet created accounts
    // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned
    // for accounts without code, i.e. `keccak256('')`
    bytes32 codehash;
    bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
    // solhint-disable-next-line no-inline-assembly
    assembly {
      codehash := extcodehash(account)
    }
    return (codehash != accountHash && codehash != 0x0);
  }

  /**
   * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
   * `recipient`, forwarding all available gas and reverting on errors.
   *
   * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
   * of certain opcodes, possibly making contracts go over the 2300 gas limit
   * imposed by `transfer`, making them unable to receive funds via
   * `transfer`. {sendValue} removes this limitation.
   *
   * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
   *
   * IMPORTANT: because control is transferred to `recipient`, care must be
   * taken to not create reentrancy vulnerabilities. Consider using
   * {ReentrancyGuard} or the
   * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
   */
  function sendValue(address payable recipient, uint256 amount) internal {
    require(address(this).balance >= amount, 'Address: insufficient balance');

    // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
    (bool success, ) = recipient.call{value: amount}('');
    require(success, 'Address: unable to send value, recipient may have reverted');
  }
}

File 8 of 14 : Context.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.7.5;

/*
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with GSN meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
  function _msgSender() internal view virtual returns (address payable) {
    return msg.sender;
  }

  function _msgData() internal view virtual returns (bytes memory) {
    this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
    return msg.data;
  }
}

File 9 of 14 : DistributionTypes.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.7.5;
pragma experimental ABIEncoderV2;

library DistributionTypes {
  struct RewardsConfigInput {
    uint88 emissionPerSecond;
    uint256 totalSupply;
    uint32 distributionEnd;
    address asset;
    address reward;
  }

  struct UserAssetInput {
    address underlyingAsset;
    uint256 userBalance;
    uint256 totalSupply;
  }
}

File 10 of 14 : Ownable.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.7.5;

import './Context.sol';

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
contract Ownable is Context {
  address private _owner;

  event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

  /**
   * @dev Initializes the contract setting the deployer as the initial owner.
   */
  constructor() {
    address msgSender = _msgSender();
    _owner = msgSender;
    emit OwnershipTransferred(address(0), msgSender);
  }

  /**
   * @dev Returns the address of the current owner.
   */
  function owner() public view returns (address) {
    return _owner;
  }

  /**
   * @dev Throws if called by any account other than the owner.
   */
  modifier onlyOwner() {
    require(_owner == _msgSender(), 'Ownable: caller is not the owner');
    _;
  }

  /**
   * @dev Leaves the contract without owner. It will not be possible to call
   * `onlyOwner` functions anymore. Can only be called by the current owner.
   *
   * NOTE: Renouncing ownership will leave the contract without an owner,
   * thereby removing any functionality that is only available to the owner.
   */
  function renounceOwnership() public virtual onlyOwner {
    emit OwnershipTransferred(_owner, address(0));
    _owner = address(0);
  }

  /**
   * @dev Transfers ownership of the contract to a new account (`newOwner`).
   * Can only be called by the current owner.
   */
  function transferOwnership(address newOwner) public virtual onlyOwner {
    require(newOwner != address(0), 'Ownable: new owner is the zero address');
    emit OwnershipTransferred(_owner, newOwner);
    _owner = newOwner;
  }
}

File 11 of 14 : SafeERC20.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.7.5;

import {IERC20} from '../interfaces/IERC20.sol';
import {SafeMath} from './SafeMath.sol';
import {Address} from './Address.sol';

/**
 * @title SafeERC20
 * @dev From https://github.com/OpenZeppelin/openzeppelin-contracts
 * Wrappers around ERC20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20 {
  using SafeMath for uint256;
  using Address for address;

  function safeTransfer(
    IERC20 token,
    address to,
    uint256 value
  ) internal {
    callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
  }

  function safeTransferFrom(
    IERC20 token,
    address from,
    address to,
    uint256 value
  ) internal {
    callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
  }

  function safeApprove(
    IERC20 token,
    address spender,
    uint256 value
  ) internal {
    require(
      (value == 0) || (token.allowance(address(this), spender) == 0),
      'SafeERC20: approve from non-zero to non-zero allowance'
    );
    callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
  }

  function callOptionalReturn(IERC20 token, bytes memory data) private {
    require(address(token).isContract(), 'SafeERC20: call to non-contract');

    // solhint-disable-next-line avoid-low-level-calls
    (bool success, bytes memory returndata) = address(token).call(data);
    require(success, 'SafeERC20: low-level call failed');

    if (returndata.length > 0) {
      // Return data is optional
      // solhint-disable-next-line max-line-length
      require(abi.decode(returndata, (bool)), 'SafeERC20: ERC20 operation did not succeed');
    }
  }
}

File 12 of 14 : SafeMath.sol
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.7.5;

/**
 * @dev From https://github.com/OpenZeppelin/openzeppelin-contracts
 * Wrappers over Solidity's arithmetic operations with added overflow
 * checks.
 *
 * Arithmetic operations in Solidity wrap on overflow. This can easily result
 * in bugs, because programmers usually assume that an overflow raises an
 * error, which is the standard behavior in high level programming languages.
 * `SafeMath` restores this intuition by reverting the transaction when 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.
 */
library SafeMath {
  /**
   * @dev Returns the addition of two unsigned integers, reverting on
   * overflow.
   *
   * Counterpart to Solidity's `+` operator.
   *
   * Requirements:
   * - Addition cannot overflow.
   */
  function add(uint256 a, uint256 b) internal pure returns (uint256) {
    uint256 c = a + b;
    require(c >= a, 'SafeMath: addition overflow');

    return c;
  }

  /**
   * @dev Returns the subtraction of two unsigned integers, reverting on
   * overflow (when the result is negative).
   *
   * Counterpart to Solidity's `-` operator.
   *
   * Requirements:
   * - Subtraction cannot overflow.
   */
  function sub(uint256 a, uint256 b) internal pure returns (uint256) {
    return sub(a, b, 'SafeMath: subtraction overflow');
  }

  /**
   * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
   * overflow (when the result is negative).
   *
   * Counterpart to Solidity's `-` operator.
   *
   * Requirements:
   * - Subtraction cannot overflow.
   */
  function sub(
    uint256 a,
    uint256 b,
    string memory errorMessage
  ) internal pure returns (uint256) {
    require(b <= a, errorMessage);
    uint256 c = a - b;

    return c;
  }

  /**
   * @dev Returns the multiplication of two unsigned integers, reverting on
   * overflow.
   *
   * Counterpart to Solidity's `*` operator.
   *
   * Requirements:
   * - Multiplication cannot overflow.
   */
  function mul(uint256 a, uint256 b) internal pure returns (uint256) {
    // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
    // benefit is lost if 'b' is also tested.
    // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
    if (a == 0) {
      return 0;
    }

    uint256 c = a * b;
    require(c / a == b, 'SafeMath: multiplication overflow');

    return c;
  }

  /**
   * @dev Returns the integer division of two unsigned integers. Reverts on
   * division by zero. The result is rounded towards zero.
   *
   * Counterpart to Solidity's `/` operator. Note: this function uses a
   * `revert` opcode (which leaves remaining gas untouched) while Solidity
   * uses an invalid opcode to revert (consuming all remaining gas).
   *
   * Requirements:
   * - The divisor cannot be zero.
   */
  function div(uint256 a, uint256 b) internal pure returns (uint256) {
    return div(a, b, 'SafeMath: division by zero');
  }

  /**
   * @dev Returns the integer division of two unsigned integers. Reverts with custom message on
   * division by zero. The result is rounded towards zero.
   *
   * Counterpart to Solidity's `/` operator. Note: this function uses a
   * `revert` opcode (which leaves remaining gas untouched) while Solidity
   * uses an invalid opcode to revert (consuming all remaining gas).
   *
   * Requirements:
   * - The divisor cannot be zero.
   */
  function div(
    uint256 a,
    uint256 b,
    string memory errorMessage
  ) internal pure returns (uint256) {
    // Solidity only automatically asserts when dividing by 0
    require(b > 0, errorMessage);
    uint256 c = a / b;
    // assert(a == b * c + a % b); // There is no case in which this doesn't hold

    return c;
  }

  /**
   * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
   * Reverts when dividing by zero.
   *
   * Counterpart to Solidity's `%` operator. This function uses a `revert`
   * opcode (which leaves remaining gas untouched) while Solidity uses an
   * invalid opcode to revert (consuming all remaining gas).
   *
   * Requirements:
   * - The divisor cannot be zero.
   */
  function mod(uint256 a, uint256 b) internal pure returns (uint256) {
    return mod(a, b, 'SafeMath: modulo by zero');
  }

  /**
   * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
   * Reverts with custom message when dividing by zero.
   *
   * Counterpart to Solidity's `%` operator. This function uses a `revert`
   * opcode (which leaves remaining gas untouched) while Solidity uses an
   * invalid opcode to revert (consuming all remaining gas).
   *
   * Requirements:
   * - The divisor cannot be zero.
   */
  function mod(
    uint256 a,
    uint256 b,
    string memory errorMessage
  ) internal pure returns (uint256) {
    require(b != 0, errorMessage);
    return a % b;
  }
}

File 13 of 14 : RewardsController.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.7.5;
pragma experimental ABIEncoderV2;

import {IRewardsController} from './interfaces/IRewardsController.sol';
import {RewardsDistributor} from './RewardsDistributor.sol';
import {IScaledBalanceToken} from './interfaces/IScaledBalanceToken.sol';
import {DistributionTypes} from './libraries/DistributionTypes.sol';

abstract contract RewardsController is RewardsDistributor, IRewardsController {
	// user => authorized claimer
	mapping(address => address) internal _authorizedClaimers;

  modifier onlyAuthorizedClaimers(address claimer, address user) {
    require(_authorizedClaimers[user] == claimer, 'CLAIMER_UNAUTHORIZED');
    _;
  }

  function getClaimer(address user) external view override returns (address) {
    return _authorizedClaimers[user];
  }

  function setClaimer(address user, address caller) external override onlyOwner {
    _authorizedClaimers[user] = caller;
    emit ClaimerSet(user, caller);
  }

  function configureAssets(DistributionTypes.RewardsConfigInput[] memory config)
    external
    override
    onlyOwner
  {
    for (uint256 i = 0; i < config.length; i++) {
      config[i].totalSupply = IScaledBalanceToken(config[i].asset).scaledTotalSupply();
    }
    _configureAssets(config);
  }

  function handleAction(
    address user,
    uint256 totalSupply,
    uint256 userBalance
  ) external override {
    _updateUserRewardsPerAssetInternal(msg.sender, user, userBalance, totalSupply);
  }

	function claimRewards(
    address[] calldata assets,
    uint256 amount,
    address to,
    address reward
  ) external override returns (uint256) {
    require(to != address(0), 'INVALID_TO_ADDRESS');
    return _claimRewards(assets, amount, msg.sender, msg.sender, to, reward);
  }

  function claimRewardsOnBehalf(
    address[] calldata assets,
    uint256 amount,
    address user,
    address to,
    address reward
  ) external override onlyAuthorizedClaimers(msg.sender, user) returns (uint256) {
    require(user != address(0), 'INVALID_USER_ADDRESS');
    require(to != address(0), 'INVALID_TO_ADDRESS');
    return _claimRewards(assets, amount, msg.sender, user, to, reward);
  }

  function claimRewardsToSelf(
    address[] calldata assets,
    uint256 amount,
    address reward
  ) external override returns (uint256) {
    return _claimRewards(assets, amount, msg.sender, msg.sender, msg.sender, reward);
  }

  function claimAllRewards(address[] calldata assets, address to)
    external
    override
    returns (address[] memory rewardTokens, uint256[] memory claimedAmounts)
  {
    require(to != address(0), 'INVALID_TO_ADDRESS');
    return _claimAllRewards(assets, msg.sender, msg.sender, to);
  }

  function claimAllRewardsOnBehalf(
    address[] calldata assets,
    address user,
    address to
  )
    external
    override
    onlyAuthorizedClaimers(msg.sender, user)
    returns (address[] memory rewardTokens, uint256[] memory claimedAmounts)
  {
    require(user != address(0), 'INVALID_USER_ADDRESS');
    require(to != address(0), 'INVALID_TO_ADDRESS');
    return _claimAllRewards(assets, msg.sender, user, to);
  }

  function claimAllRewardsToSelf(address[] calldata assets)
    external
    override
    returns (address[] memory rewardTokens, uint256[] memory claimedAmounts)
  {
    return _claimAllRewards(assets, msg.sender, msg.sender, msg.sender);
  }

  function _getUserStake(address[] calldata assets, address user)
    internal
    view
    override
    returns (DistributionTypes.UserAssetInput[] memory userState)
  {
    userState = new DistributionTypes.UserAssetInput[](assets.length);
    for (uint256 i = 0; i < assets.length; i++) {
      userState[i].underlyingAsset = assets[i];
      (userState[i].userBalance, userState[i].totalSupply) = IScaledBalanceToken(assets[i])
        .getScaledUserBalanceAndSupply(user);
    }
    return userState;
  }

  function _claimRewards(
    address[] calldata assets,
    uint256 amount,
    address claimer,
    address user,
    address to,
    address reward
  ) internal returns (uint256) {
    if (amount == 0) {
      return 0;
    }
    uint256 unclaimedRewards = _usersUnclaimedRewards[user][reward];

    if (amount > unclaimedRewards) {
      _distributeRewards(user, _getUserStake(assets, user));
      unclaimedRewards = _usersUnclaimedRewards[user][reward];
    }

    if (unclaimedRewards == 0) {
      return 0;
    }

    uint256 amountToClaim = amount > unclaimedRewards ? unclaimedRewards : amount;
    _usersUnclaimedRewards[user][reward] = unclaimedRewards - amountToClaim; // Safe due to the previous line

    _transferRewards(to, reward, amountToClaim);
    emit RewardsClaimed(user, reward, to, claimer, amountToClaim);

    return amountToClaim;
  }

  function _claimAllRewards(
    address[] calldata assets,
    address claimer,
    address user,
    address to
  ) internal returns (address[] memory rewardTokens, uint256[] memory claimedAmounts) {
    _distributeRewards(user, _getUserStake(assets, user));

    rewardTokens = new address[](_rewardTokens.length);
    claimedAmounts = new uint256[](_rewardTokens.length);

    for (uint256 i = 0; i < _rewardTokens.length; i++) {
      address reward = _rewardTokens[i];
      uint256 rewardAmount = _usersUnclaimedRewards[user][reward];

      rewardTokens[i] = reward;
      claimedAmounts[i] = rewardAmount;

      if (rewardAmount != 0) {
        _usersUnclaimedRewards[user][reward] = 0;
        _transferRewards(to, reward, rewardAmount);
        emit RewardsClaimed(user, reward, to, claimer, rewardAmount);
      }
    }
    return (rewardTokens, claimedAmounts);
  }

  function _transferRewards(
    address to,
    address reward,
    uint256 amount
  ) internal {
    bool success = transferRewards(to, reward, amount);
    require(success == true, 'TRANSFER_ERROR');
  }

  function transferRewards(address to, address reward, uint256 amount) internal virtual returns (bool);
}

File 14 of 14 : RewardsDistributor.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.7.5;
pragma experimental ABIEncoderV2;

import {IRewardsDistributor} from './interfaces/IRewardsDistributor.sol';
import {IERC20Detailed} from './interfaces/IERC20Detailed.sol';
import {DistributionTypes} from './libraries/DistributionTypes.sol';
import {Ownable} from './libraries/Ownable.sol';

abstract contract RewardsDistributor is IRewardsDistributor, Ownable {
	struct RewardData {
		uint88 emissionPerSecond;
		uint104 index;
		uint32 lastUpdateTimestamp;
		uint32 distributionEnd;
		mapping(address => uint256) usersIndex;
	}

	struct AssetData {
		// reward => rewardData
		mapping(address => RewardData) rewards;
		address[] availableRewards;
		uint8 decimals;
	}

	// incentivized asset => AssetData
	mapping(address => AssetData) internal _assets;

	// user => reward => unclaimed rewards
	mapping(address => mapping(address => uint256)) internal _usersUnclaimedRewards;

	// reward => isEnabled
	mapping(address => bool) internal _isRewardEnabled;

	address[] internal _rewardTokens;

  function getRewardsData(address asset, address reward)
    public
    view
    override
    returns (
      uint256,
      uint256,
      uint256,
      uint256
    )
  {
    return (
      _assets[asset].rewards[reward].index,
      _assets[asset].rewards[reward].emissionPerSecond,
      _assets[asset].rewards[reward].lastUpdateTimestamp,
      _assets[asset].rewards[reward].distributionEnd
    );
  }

  function getDistributionEnd(address asset, address reward)
    external
    view
    override
    returns (uint256)
  {
    return _assets[asset].rewards[reward].distributionEnd;
  }

  function getRewardsByAsset(address asset) external view override returns (address[] memory) {
    return _assets[asset].availableRewards;
  }

  function getRewardTokens() external view override returns (address[] memory) {
    return _rewardTokens;
  }

  function getUserAssetData(
    address user,
    address asset,
    address reward
  ) public view override returns (uint256) {
    return _assets[asset].rewards[reward].usersIndex[user];
  }

  function getUserUnclaimedRewardsFromStorage(address user, address reward)
    external
    view
    override
    returns (uint256)
  {
    return _usersUnclaimedRewards[user][reward];
  }

  function getUserRewardsBalance(
    address[] calldata assets,
    address user,
    address reward
  ) external view override returns (uint256) {
    return _getUserReward(user, reward, _getUserStake(assets, user));
  }

  function getAllUserRewardsBalance(address[] calldata assets, address user)
    external
    view
    override
    returns (address[] memory rewardTokens, uint256[] memory unclaimedAmounts)
  {
    return _getAllUserRewards(user, _getUserStake(assets, user));
  }

  function setDistributionEnd(
    address asset,
    address reward,
    uint32 distributionEnd
  ) external override onlyOwner {
    _assets[asset].rewards[reward].distributionEnd = distributionEnd;

    emit AssetConfigUpdated(
      asset,
      reward,
      _assets[asset].rewards[reward].emissionPerSecond,
      distributionEnd
    );
  }

  function _configureAssets(DistributionTypes.RewardsConfigInput[] memory rewardsInput)
    internal
  {
    for (uint256 i = 0; i < rewardsInput.length; i++) {
      _assets[rewardsInput[i].asset].decimals = IERC20Detailed(rewardsInput[i].asset).decimals();

      RewardData storage rewardConfig = _assets[rewardsInput[i].asset].rewards[
        rewardsInput[i].reward
      ];

      // Add reward address to asset available rewards if latestUpdateTimestamp is zero
      if (rewardConfig.lastUpdateTimestamp == 0) {
        _assets[rewardsInput[i].asset].availableRewards.push(rewardsInput[i].reward);
      }

      // Add reward address to global rewards list if still not enabled
      if (_isRewardEnabled[rewardsInput[i].reward] == false) {
        _isRewardEnabled[rewardsInput[i].reward] = true;
        _rewardTokens.push(rewardsInput[i].reward);
      }

      // Due emissions is still zero, updates only latestUpdateTimestamp
      _updateAssetStateInternal(
        rewardsInput[i].asset,
        rewardsInput[i].reward,
        rewardConfig,
        rewardsInput[i].totalSupply,
        _assets[rewardsInput[i].asset].decimals
      );

      // Configure emission and distribution end of the reward per asset
      rewardConfig.emissionPerSecond = rewardsInput[i].emissionPerSecond;
      rewardConfig.distributionEnd = rewardsInput[i].distributionEnd;

      emit AssetConfigUpdated(
        rewardsInput[i].asset,
        rewardsInput[i].reward,
        rewardsInput[i].emissionPerSecond,
        rewardsInput[i].distributionEnd
      );
    }
  }

  function _updateAssetStateInternal(
    address asset,
    address reward,
    RewardData storage rewardConfig,
    uint256 totalSupply,
    uint8 decimals
  ) internal returns (uint256) {
    uint256 oldIndex = rewardConfig.index;

    if (block.timestamp == rewardConfig.lastUpdateTimestamp) {
      return oldIndex;
    }

    uint256 newIndex = _getAssetIndex(
      oldIndex,
      rewardConfig.emissionPerSecond,
      rewardConfig.lastUpdateTimestamp,
      rewardConfig.distributionEnd,
      totalSupply,
      decimals
    );

    if (newIndex != oldIndex) {
      require(newIndex <= type(uint104).max, 'Index overflow');
      //optimization: storing one after another saves one SSTORE
      rewardConfig.index = uint104(newIndex);
      rewardConfig.lastUpdateTimestamp = uint32(block.timestamp);
      emit AssetIndexUpdated(asset, reward, newIndex);
    } else {
      rewardConfig.lastUpdateTimestamp = uint32(block.timestamp);
    }

    return newIndex;
  }

  function _updateUserRewardsInternal(
    address user,
    address asset,
    address reward,
    uint256 userBalance,
    uint256 totalSupply
  ) internal returns (uint256) {
    RewardData storage rewardData = _assets[asset].rewards[reward];
    uint256 userIndex = rewardData.usersIndex[user];
    uint256 accruedRewards = 0;

    uint256 newIndex = _updateAssetStateInternal(
      asset,
      reward,
      rewardData,
      totalSupply,
      _assets[asset].decimals
    );

    if (userIndex != newIndex) {
      if (userBalance != 0) {
        accruedRewards = _getRewards(userBalance, newIndex, userIndex, _assets[asset].decimals);
      }

      rewardData.usersIndex[user] = newIndex;
      emit UserIndexUpdated(user, asset, reward, newIndex);
    }

    return accruedRewards;
  }

  function _updateUserRewardsPerAssetInternal(
    address asset,
    address user,
    uint256 userBalance,
    uint256 totalSupply
  ) internal {
    for (uint256 r = 0; r < _assets[asset].availableRewards.length; r++) {
      address reward = _assets[asset].availableRewards[r];
      uint256 accruedRewards = _updateUserRewardsInternal(
        user,
        asset,
        reward,
        userBalance,
        totalSupply
      );
      if (accruedRewards != 0) {
        _usersUnclaimedRewards[user][reward] += accruedRewards;

        emit RewardsAccrued(user, reward, accruedRewards);
      }
    }
  }

  function _distributeRewards(
    address user,
    DistributionTypes.UserAssetInput[] memory userState
  ) internal {
    for (uint256 i = 0; i < userState.length; i++) {
      _updateUserRewardsPerAssetInternal(
        userState[i].underlyingAsset,
        user,
        userState[i].userBalance,
        userState[i].totalSupply
      );
    }
  }

  function _getUserReward(
    address user,
    address reward,
    DistributionTypes.UserAssetInput[] memory userState
  ) internal view returns (uint256 unclaimedRewards) {
    // Add unrealized rewards
    for (uint256 i = 0; i < userState.length; i++) {
      if (userState[i].userBalance == 0) {
        continue;
      }
      unclaimedRewards += _getUnrealizedRewardsFromStake(user, reward, userState[i]);
    }

    // Return unrealized rewards plus stored unclaimed rewardss
    return unclaimedRewards + _usersUnclaimedRewards[user][reward];
  }

  function _getAllUserRewards(
    address user,
    DistributionTypes.UserAssetInput[] memory userState
  ) internal view returns (address[] memory rewardTokens, uint256[] memory unclaimedRewards) {
    rewardTokens = new address[](_rewardTokens.length);
    unclaimedRewards = new uint256[](rewardTokens.length);

    // Add stored rewards from user to unclaimedRewards
    for (uint256 y = 0; y < rewardTokens.length; y++) {
      rewardTokens[y] = _rewardTokens[y];
      unclaimedRewards[y] = _usersUnclaimedRewards[user][rewardTokens[y]];
    }

    // Add unrealized rewards from user to unclaimedRewards
    for (uint256 i = 0; i < userState.length; i++) {
      if (userState[i].userBalance == 0) {
        continue;
      }
      for (uint256 r = 0; r < rewardTokens.length; r++) {
        unclaimedRewards[r] += _getUnrealizedRewardsFromStake(user, rewardTokens[r], userState[i]);
      }
    }
    return (rewardTokens, unclaimedRewards);
  }

  function _getUnrealizedRewardsFromStake(
    address user,
    address reward,
    DistributionTypes.UserAssetInput memory stake
  ) internal view returns (uint256) {
    RewardData storage rewardData = _assets[stake.underlyingAsset].rewards[reward];
    uint8 assetDecimals = _assets[stake.underlyingAsset].decimals;
    uint256 assetIndex = _getAssetIndex(
      rewardData.index,
      rewardData.emissionPerSecond,
      rewardData.lastUpdateTimestamp,
      rewardData.distributionEnd,
      stake.totalSupply,
      assetDecimals
    );

    return _getRewards(stake.userBalance, assetIndex, rewardData.usersIndex[user], assetDecimals);
  }

	function _getRewards(
    uint256 principalUserBalance,
    uint256 reserveIndex,
    uint256 userIndex,
    uint8 decimals
  ) internal pure returns (uint256) {
    return (principalUserBalance * (reserveIndex - userIndex)) / 10**decimals;
  }

  function _getAssetIndex(
    uint256 currentIndex,
    uint256 emissionPerSecond,
    uint128 lastUpdateTimestamp,
    uint256 distributionEnd,
    uint256 totalBalance,
    uint8 decimals
  ) internal view returns (uint256) {
    if (
      emissionPerSecond == 0 ||
      totalBalance == 0 ||
      lastUpdateTimestamp == block.timestamp ||
      lastUpdateTimestamp >= distributionEnd
    ) {
      return currentIndex;
    }

    uint256 currentTimestamp = block.timestamp > distributionEnd
      ? distributionEnd
      : block.timestamp;
    uint256 timeDelta = currentTimestamp - lastUpdateTimestamp;
    return (emissionPerSecond * timeDelta * (10**decimals)) / totalBalance + currentIndex;
  }

  function _getUserStake(address[] calldata assets, address user)
    internal
    view
    virtual
    returns (DistributionTypes.UserAssetInput[] memory userState);

  function getAssetDecimals(address asset) external view override returns (uint8) {
    return _assets[asset].decimals;
  }
}

Settings
{
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"asset","type":"address"},{"indexed":true,"internalType":"address","name":"reward","type":"address"},{"indexed":false,"internalType":"uint256","name":"emission","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"distributionEnd","type":"uint256"}],"name":"AssetConfigUpdated","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":"index","type":"uint256"}],"name":"AssetIndexUpdated","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":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"reward","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"RewardsAccrued","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":"vault","type":"address"}],"name":"RewardsVaultUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"asset","type":"address"},{"indexed":true,"internalType":"address","name":"reward","type":"address"},{"indexed":false,"internalType":"uint256","name":"index","type":"uint256"}],"name":"UserIndexUpdated","type":"event"},{"inputs":[{"internalType":"address[]","name":"assets","type":"address[]"},{"internalType":"address","name":"to","type":"address"}],"name":"claimAllRewards","outputs":[{"internalType":"address[]","name":"rewardTokens","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":"rewardTokens","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":"rewardTokens","type":"address[]"},{"internalType":"uint256[]","name":"claimedAmounts","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"assets","type":"address[]"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"address","name":"reward","type":"address"}],"name":"claimRewards","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"assets","type":"address[]"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"user","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"address","name":"reward","type":"address"}],"name":"claimRewardsOnBehalf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"assets","type":"address[]"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"reward","type":"address"}],"name":"claimRewardsToSelf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint88","name":"emissionPerSecond","type":"uint88"},{"internalType":"uint256","name":"totalSupply","type":"uint256"},{"internalType":"uint32","name":"distributionEnd","type":"uint32"},{"internalType":"address","name":"asset","type":"address"},{"internalType":"address","name":"reward","type":"address"}],"internalType":"struct DistributionTypes.RewardsConfigInput[]","name":"config","type":"tuple[]"}],"name":"configureAssets","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"assets","type":"address[]"},{"internalType":"address","name":"user","type":"address"}],"name":"getAllUserRewardsBalance","outputs":[{"internalType":"address[]","name":"rewardTokens","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":"getRewardTokens","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"}],"name":"getRewardsByAsset","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"address","name":"reward","type":"address"}],"name":"getRewardsData","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"reward","type":"address"}],"name":"getRewardsVault","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"address","name":"asset","type":"address"},{"internalType":"address","name":"reward","type":"address"}],"name":"getUserAssetData","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"assets","type":"address[]"},{"internalType":"address","name":"user","type":"address"},{"internalType":"address","name":"reward","type":"address"}],"name":"getUserRewardsBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"address","name":"reward","type":"address"}],"name":"getUserUnclaimedRewardsFromStorage","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"totalSupply","type":"uint256"},{"internalType":"uint256","name":"userBalance","type":"uint256"}],"name":"handleAction","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","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":"distributionEnd","type":"uint32"}],"name":"setDistributionEnd","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"vault","type":"address"},{"internalType":"address","name":"reward","type":"address"}],"name":"setRewardsVault","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"}]

608060405234801561001057600080fd5b50600061001b61006a565b600080546001600160a01b0319166001600160a01b0383169081178255604051929350917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a35061006e565b3390565b6128b78061007d6000396000f3fe608060405234801561001057600080fd5b50600436106101735760003560e01c80637eff4ba8116100de578063bb492bf511610097578063c5a7b53811610071578063c5a7b53814610343578063c664493a14610356578063f2fde38b14610369578063f5cf673b1461037c57610173565b8063bb492bf514610315578063bf90f63a14610328578063c4f59f9b1461033b57610173565b80637eff4ba814610283578063866bc096146102a65780638da5cb5b146102b95780639efd6f72146102c15780639ff55db9146102e1578063af15ec5a1461030257610173565b80635077215511610130578063507721551461020257806357b89883146102155780636657732f1461022857806367eabbc914610248578063715018a61461026857806374d945ec1461027057610173565b80630b0db655146101785780631b839c771461018d578063236300dc146101b657806331873e2e146101c957806333028b99146101dc57806344fd1400146101ef575b600080fd5b61018b6101863660046124e3565b61038f565b005b6101a061019b3660046121e7565b6104aa565b6040516101ad919061279d565b60405180910390f35b6101a06101c43660046123fe565b6104e2565b61018b6101d7366004612294565b61052e565b6101a06101ea366004612469565b61053f565b61018b6101fd3660046121e7565b6105e6565b6101a0610210366004612219565b610695565b6101a06102233660046123af565b6106cf565b61023b6102363660046121c6565b6106e0565b6040516101ad919061265c565b61025b6102563660046121c6565b61075a565b6040516101ad919061262f565b61018b610778565b61025b61027e3660046121c6565b61081a565b6102966102913660046121e7565b610838565b6040516101ad94939291906127a6565b6101a06102b43660046121e7565b610897565b61025b6108c2565b6102d46102cf3660046121c6565b6108d1565b6040516101ad91906127e0565b6102f46102ef36600461234d565b6108f2565b6040516101ad92919061266f565b6101a061031036600461234d565b61099c565b6102f4610323366004612305565b6109b3565b6102f46103363660046122c6565b6109f5565b61023b610a11565b61018b61035136600461225b565b610a73565b6102f4610364366004612305565b610b5a565b61018b6103773660046121c6565b610b71565b61018b61038a3660046121e7565b610c69565b610397610d18565b6000546001600160a01b039081169116146103e7576040805162461bcd60e51b81526020600482018190526024820152600080516020612838833981519152604482015290519081900360640190fd5b60005b815181101561049d578181815181106103ff57fe5b6020026020010151606001516001600160a01b031663b1bf962d6040518163ffffffff1660e01b815260040160206040518083038186803b15801561044357600080fd5b505afa158015610457573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061047b9190612590565b82828151811061048757fe5b60209081029190910181015101526001016103ea565b506104a781610d1c565b50565b6001600160a01b039182166000908152600160209081526040808320939094168252919091522054600160e01b900463ffffffff1690565b60006001600160a01b0383166105135760405162461bcd60e51b815260040161050a9061271b565b60405180910390fd5b610522868686333388886111b9565b90505b95945050505050565b61053a338483856112ef565b505050565b6001600160a01b0380841660009081526005602052604081205490913391869116821461057e5760405162461bcd60e51b815260040161050a9061276f565b6001600160a01b0386166105a45760405162461bcd60e51b815260040161050a906126c5565b6001600160a01b0385166105ca5760405162461bcd60e51b815260040161050a9061271b565b6105d9898989338a8a8a6111b9565b9998505050505050505050565b6105ee610d18565b6000546001600160a01b0390811691161461063e576040805162461bcd60e51b81526020600482018190526024820152600080516020612838833981519152604482015290519081900360640190fd5b6001600160a01b0381811660009081526006602052604080822080546001600160a01b0319169386169384179055517f28a48cbce43190d77247f342cf319b1607bff4ef716cf26b76cf7bb71baebaa59190a25050565b6001600160a01b03808316600090815260016020818152604080842086861685528252808420948816845293909101905220549392505050565b6000610525858585333333886111b9565b6001600160a01b03811660009081526001602081815260409283902090910180548351818402810184019094528084526060939283018282801561074d57602002820191906000526020600020905b81546001600160a01b0316815260019091019060200180831161072f575b505050505090505b919050565b6001600160a01b039081166000908152600660205260409020541690565b610780610d18565b6000546001600160a01b039081169116146107d0576040805162461bcd60e51b81526020600482018190526024820152600080516020612838833981519152604482015290519081900360640190fd5b600080546040516001600160a01b03909116907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3600080546001600160a01b0319169055565b6001600160a01b039081166000908152600560205260409020541690565b6001600160a01b0391821660009081526001602090815260408083209390941682529190915220546001600160681b03600160581b820416916001600160581b0382169163ffffffff600160c01b8204811692600160e01b9092041690565b6001600160a01b03918216600090815260026020908152604080832093909416825291909152205490565b6000546001600160a01b031690565b6001600160a01b031660009081526001602052604090206002015460ff1690565b6001600160a01b038083166000908152600560205260409020546060918291339186911682146109345760405162461bcd60e51b815260040161050a9061276f565b6001600160a01b03861661095a5760405162461bcd60e51b815260040161050a906126c5565b6001600160a01b0385166109805760405162461bcd60e51b815260040161050a9061271b565b61098d88883389896113e0565b93509350505094509492505050565b600061052583836109ae8888886115bc565b611742565b6060806001600160a01b0383166109dc5760405162461bcd60e51b815260040161050a9061271b565b6109e985853333876113e0565b91509150935093915050565b606080610a0584843333336113e0565b915091505b9250929050565b60606004805480602002602001604051908101604052809291908181526020018280548015610a6957602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610a4b575b5050505050905090565b610a7b610d18565b6000546001600160a01b03908116911614610acb576040805162461bcd60e51b81526020600482018190526024820152600080516020612838833981519152604482015290519081900360640190fd5b6001600160a01b038381166000818152600160209081526040808320948716808452949091529081902080546001600160e01b0316600160e01b63ffffffff871602179081905590517fec44514a2743afadd78647150195ef92fec31e96087fa5cbbcd72e8d131d9a1c91610b4d916001600160581b039091169086906127c1565b60405180910390a3505050565b6060806109e983610b6c8787876115bc565b6117ce565b610b79610d18565b6000546001600160a01b03908116911614610bc9576040805162461bcd60e51b81526020600482018190526024820152600080516020612838833981519152604482015290519081900360640190fd5b6001600160a01b038116610c0e5760405162461bcd60e51b81526004018080602001828103825260268152602001806128126026913960400191505060405180910390fd5b600080546040516001600160a01b03808516939216917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3600080546001600160a01b0319166001600160a01b0392909216919091179055565b610c71610d18565b6000546001600160a01b03908116911614610cc1576040805162461bcd60e51b81526020600482018190526024820152600080516020612838833981519152604482015290519081900360640190fd5b6001600160a01b0382811660008181526005602052604080822080546001600160a01b0319169486169485179055517f4925eafc82d0c4d67889898eeed64b18488ab19811e61620f387026dec126a289190a35050565b3390565b60005b81518110156111b557818181518110610d3457fe5b6020026020010151606001516001600160a01b031663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b158015610d7857600080fd5b505afa158015610d8c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610db091906125cb565b60016000848481518110610dc057fe5b6020026020010151606001516001600160a01b03166001600160a01b0316815260200190815260200160002060020160006101000a81548160ff021916908360ff160217905550600060016000848481518110610e1957fe5b6020026020010151606001516001600160a01b03166001600160a01b031681526020019081526020016000206000016000848481518110610e5657fe5b602090810291909101810151608001516001600160a01b031682528101919091526040016000208054909150600160c01b900463ffffffff16610f1e5760016000848481518110610ea357fe5b6020026020010151606001516001600160a01b03166001600160a01b03168152602001908152602001600020600101838381518110610ede57fe5b6020908102919091018101516080015182546001810184556000938452919092200180546001600160a01b0319166001600160a01b039092169190911790555b60036000848481518110610f2e57fe5b602090810291909101810151608001516001600160a01b031682528101919091526040016000205460ff1661100057600160036000858581518110610f6f57fe5b6020026020010151608001516001600160a01b03166001600160a01b0316815260200190815260200160002060006101000a81548160ff0219169083151502179055506004838381518110610fc057fe5b6020908102919091018101516080015182546001810184556000938452919092200180546001600160a01b0319166001600160a01b039092169190911790555b61108f83838151811061100f57fe5b60200260200101516060015184848151811061102757fe5b6020026020010151608001518386868151811061104057fe5b6020026020010151602001516001600089898151811061105c57fe5b602090810291909101810151606001516001600160a01b031682528101919091526040016000206002015460ff166119d3565b5082828151811061109c57fe5b60209081029190910101515181546affffffffffffffffffffff19166001600160581b0390911617815582518390839081106110d457fe5b602090810291909101015160400151815463ffffffff909116600160e01b026001600160e01b03909116178155825183908390811061110f57fe5b6020026020010151608001516001600160a01b031683838151811061113057fe5b6020026020010151606001516001600160a01b03167fec44514a2743afadd78647150195ef92fec31e96087fa5cbbcd72e8d131d9a1c85858151811061117257fe5b60200260200101516000015186868151811061118a57fe5b6020026020010151604001516040516111a49291906127c1565b60405180910390a350600101610d1f565b5050565b6000856111c8575060006112e4565b6001600160a01b03808516600090815260026020908152604080832093861683529290522054808711156112325761120a856112058b8b896115bc565b611b1c565b506001600160a01b038085166000908152600260209081526040808320938616835292905220545b806112415760009150506112e4565b60008188116112505787611252565b815b6001600160a01b03808816600090815260026020908152604080832093891683529290522081840390559050611289858583611b81565b846001600160a01b0316846001600160a01b0316876001600160a01b03167fc052130bc4ef84580db505783484b067ea8b71b3bca78a7e12db7aea8658f0048a856040516112d8929190612643565b60405180910390a49150505b979650505050505050565b60005b6001600160a01b038516600090815260016020819052604090912001548110156113d9576001600160a01b038516600090815260016020819052604082200180548390811061133d57fe5b60009182526020822001546001600160a01b031691506113608688848888611bb8565b905080156113cf576001600160a01b0380871660008181526002602090815260408083209487168084529490915290819020805485019055517f68ac6b4d0eeedccf3df154188dfac62d18d2ac5eaac52ecd6e728e33ce75a1d4906113c690859061279d565b60405180910390a35b50506001016112f2565b5050505050565b6060806113f2846112058989886115bc565b6004546001600160401b038111801561140a57600080fd5b50604051908082528060200260200182016040528015611434578160200160208202803683370190505b506004549092506001600160401b038111801561145057600080fd5b5060405190808252806020026020018201604052801561147a578160200160208202803683370190505b50905060005b6004548110156115b15760006004828154811061149957fe5b60009182526020808320909101546001600160a01b0389811684526002835260408085209190921680855292529091205485519192509082908690859081106114de57fe5b60200260200101906001600160a01b031690816001600160a01b0316815250508084848151811061150b57fe5b602090810291909101015280156115a7576001600160a01b03808816600090815260026020908152604080832093861683529290529081205561154f868383611b81565b856001600160a01b0316826001600160a01b0316886001600160a01b03167fc052130bc4ef84580db505783484b067ea8b71b3bca78a7e12db7aea8658f0048b8560405161159e929190612643565b60405180910390a45b5050600101611480565b509550959350505050565b6060826001600160401b03811180156115d457600080fd5b5060405190808252806020026020018201604052801561160e57816020015b6115fb6120a0565b8152602001906001900390816115f35790505b50905060005b8381101561173a5784848281811061162857fe5b905060200201602081019061163d91906121c6565b82828151811061164957fe5b60209081029190910101516001600160a01b03909116905284848281811061166d57fe5b905060200201602081019061168291906121c6565b6001600160a01b0316630afbcdc9846040518263ffffffff1660e01b81526004016116ad919061262f565b604080518083038186803b1580156116c457600080fd5b505afa1580156116d8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116fc91906125a8565b83838151811061170857fe5b602002602001015160200184848151811061171f57fe5b60209081029190910101516040019190915252600101611614565b509392505050565b6000805b825181101561179f5782818151811061175b57fe5b6020026020010151602001516000141561177457611797565b611792858585848151811061178557fe5b6020026020010151611cbf565b820191505b600101611746565b506001600160a01b03808516600090815260026020908152604080832093871683529290522054019392505050565b60045460609081906001600160401b03811180156117eb57600080fd5b50604051908082528060200260200182016040528015611815578160200160208202803683370190505b50915081516001600160401b038111801561182f57600080fd5b50604051908082528060200260200182016040528015611859578160200160208202803683370190505b50905060005b825181101561193a576004818154811061187557fe5b9060005260206000200160009054906101000a90046001600160a01b031683828151811061189f57fe5b60200260200101906001600160a01b031690816001600160a01b03168152505060026000866001600160a01b03166001600160a01b0316815260200190815260200160002060008483815181106118f257fe5b60200260200101516001600160a01b03166001600160a01b031681526020019081526020016000205482828151811061192757fe5b602090810291909101015260010161185f565b5060005b83518110156119cb5783818151811061195357fe5b6020026020010151602001516000141561196c576119c3565b60005b83518110156119c15761199c8685838151811061198857fe5b602002602001015187858151811061178557fe5b8382815181106119a857fe5b602090810291909101018051909101905260010161196f565b505b60010161193e565b509250929050565b8254600090600160581b81046001600160681b031690600160c01b900463ffffffff16421415611a04579050610525565b8454600090611a389083906001600160581b0381169063ffffffff600160c01b8204811691600160e01b9004168989611d81565b9050818114611af5576001600160681b03811115611a685760405162461bcd60e51b815260040161050a90612747565b85546cffffffffffffffffffffffffff60581b1916600160581b6001600160681b038316021763ffffffff60c01b1916600160c01b4263ffffffff16021786556040516001600160a01b0388811691908a16907f7ecfb468c92138c74e6034d3e9d76f99150e483bd53bcc43524ce7056698751f90611ae890859061279d565b60405180910390a36112e4565b855463ffffffff60c01b1916600160c01b4263ffffffff1602178655979650505050505050565b60005b815181101561053a57611b79828281518110611b3757fe5b60200260200101516000015184848481518110611b5057fe5b602002602001015160200151858581518110611b6857fe5b6020026020010151604001516112ef565b600101611b1f565b6000611b8e848484611e05565b9050600181151514611bb25760405162461bcd60e51b815260040161050a906126f3565b50505050565b6001600160a01b0380851660008181526001602081815260408084208987168552808352818520968c168552868401835290842054948452919052600201549092919083908190611c13908a908a9087908a9060ff166119d3565b9050808314611cb2578615611c53576001600160a01b038916600090815260016020526040902060020154611c509088908390869060ff16611e38565b91505b6001600160a01b03808b16600081815260018701602052604090819020849055518a8316928c1691907ff5674975ec2736b27b8d52ea7cafb8bc2761685f31f109e028e1498aa2b2f3e590611ca990869061279d565b60405180910390a45b5098975050505050505050565b80516001600160a01b03908116600090815260016020818152604080842087861685528252808420865190951684529190528082206002015483549185015192939260ff909116918491611d48916001600160681b03600160581b820416916001600160581b0382169163ffffffff600160c01b8204811692600160e01b909204169087611d81565b90506112e48560200151828560010160008b6001600160a01b03166001600160a01b031681526020019081526020016000205485611e38565b6000851580611d8e575082155b80611da1575042856001600160801b0316145b80611db5575083856001600160801b031610155b15611dc1575085611dfb565b6000844211611dd05742611dd2565b845b90506001600160801b0386168103888589830260ff8716600a0a0281611df457fe5b0401925050505b9695505050505050565b6001600160a01b038083166000818152600660205260408120549092611e2e9291168685611e57565b5060019392505050565b60008160ff16600a0a838503860281611e4d57fe5b0495945050505050565b604080516001600160a01b0380861660248301528416604482015260648082018490528251808303909101815260849091019091526020810180516001600160e01b03166323b872dd60e01b179052611bb2908590611ebe826001600160a01b0316612064565b611f0f576040805162461bcd60e51b815260206004820152601f60248201527f5361666545524332303a2063616c6c20746f206e6f6e2d636f6e747261637400604482015290519081900360640190fd5b60006060836001600160a01b0316836040518082805190602001908083835b60208310611f4d5780518252601f199092019160209182019101611f2e565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d8060008114611faf576040519150601f19603f3d011682016040523d82523d6000602084013e611fb4565b606091505b50915091508161200b576040805162461bcd60e51b815260206004820181905260248201527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564604482015290519081900360640190fd5b805115611bb25780806020019051602081101561202757600080fd5b5051611bb25760405162461bcd60e51b815260040180806020018281038252602a815260200180612858602a913960400191505060405180910390fd5b6000813f7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47081811480159061209857508115155b949350505050565b604051806060016040528060006001600160a01b0316815260200160008152602001600081525090565b80356001600160a01b038116811461075557600080fd5b60008083601f8401126120f2578182fd5b5081356001600160401b03811115612108578182fd5b6020830191508360208083028501011115610a0a57600080fd5b600060a08284031215612133578081fd5b60405160a081018181106001600160401b038211171561214f57fe5b60405290508082356001600160581b038116811461216c57600080fd5b815260208381013590820152612184604084016121b2565b6040820152612195606084016120ca565b60608201526121a6608084016120ca565b60808201525092915050565b803563ffffffff8116811461075557600080fd5b6000602082840312156121d7578081fd5b6121e0826120ca565b9392505050565b600080604083850312156121f9578081fd5b612202836120ca565b9150612210602084016120ca565b90509250929050565b60008060006060848603121561222d578081fd5b612236846120ca565b9250612244602085016120ca565b9150612252604085016120ca565b90509250925092565b60008060006060848603121561226f578283fd5b612278846120ca565b9250612286602085016120ca565b9150612252604085016121b2565b6000806000606084860312156122a8578283fd5b6122b1846120ca565b95602085013595506040909401359392505050565b600080602083850312156122d8578182fd5b82356001600160401b038111156122ed578283fd5b6122f9858286016120e1565b90969095509350505050565b600080600060408486031215612319578283fd5b83356001600160401b0381111561232e578384fd5b61233a868287016120e1565b90945092506122529050602085016120ca565b60008060008060608587031215612362578081fd5b84356001600160401b03811115612377578182fd5b612383878288016120e1565b90955093506123969050602086016120ca565b91506123a4604086016120ca565b905092959194509250565b600080600080606085870312156123c4578384fd5b84356001600160401b038111156123d9578485fd5b6123e5878288016120e1565b909550935050602085013591506123a4604086016120ca565b600080600080600060808688031215612415578081fd5b85356001600160401b0381111561242a578182fd5b612436888289016120e1565b9096509450506020860135925061244f604087016120ca565b915061245d606087016120ca565b90509295509295909350565b60008060008060008060a08789031215612481578384fd5b86356001600160401b03811115612496578485fd5b6124a289828a016120e1565b909750955050602087013593506124bb604088016120ca565b92506124c9606088016120ca565b91506124d7608088016120ca565b90509295509295509295565b600060208083850312156124f5578182fd5b82356001600160401b038082111561250b578384fd5b818501915085601f83011261251e578384fd5b81358181111561252a57fe5b61253784858302016127ee565b818152848101925083850160a0808402860187018a1015612556578788fd5b8795505b838610156125825761256c8a83612122565b855260019590950194938601939081019061255a565b509098975050505050505050565b6000602082840312156125a1578081fd5b5051919050565b600080604083850312156125ba578182fd5b505080516020909101519092909150565b6000602082840312156125dc578081fd5b815160ff811681146121e0578182fd5b6000815180845260208085019450808401835b838110156126245781516001600160a01b0316875295820195908201906001016125ff565b509495945050505050565b6001600160a01b0391909116815260200190565b6001600160a01b03929092168252602082015260400190565b6000602082526121e060208301846125ec565b60006040825261268260408301856125ec565b828103602084810191909152845180835285820192820190845b818110156126b85784518352938301939183019160010161269c565b5090979650505050505050565b602080825260149082015273494e56414c49445f555345525f4144445245535360601b604082015260600190565b6020808252600e908201526d2a2920a729a322a92fa2a92927a960911b604082015260600190565b602080825260129082015271494e56414c49445f544f5f4144445245535360701b604082015260600190565b6020808252600e908201526d496e646578206f766572666c6f7760901b604082015260600190565b60208082526014908201527310d3105253515497d5539055551213d49256915160621b604082015260600190565b90815260200190565b93845260208401929092526040830152606082015260800190565b6001600160581b0392909216825263ffffffff16602082015260400190565b60ff91909116815260200190565b6040518181016001600160401b038111828210171561280957fe5b60405291905056fe4f776e61626c653a206e6577206f776e657220697320746865207a65726f20616464726573734f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65725361666545524332303a204552433230206f7065726174696f6e20646964206e6f742073756363656564a2646970667358221220ec125919d2e26d0c53b9727952496a47da726b6381dfb9da84ec16851746a87364736f6c63430007050033

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106101735760003560e01c80637eff4ba8116100de578063bb492bf511610097578063c5a7b53811610071578063c5a7b53814610343578063c664493a14610356578063f2fde38b14610369578063f5cf673b1461037c57610173565b8063bb492bf514610315578063bf90f63a14610328578063c4f59f9b1461033b57610173565b80637eff4ba814610283578063866bc096146102a65780638da5cb5b146102b95780639efd6f72146102c15780639ff55db9146102e1578063af15ec5a1461030257610173565b80635077215511610130578063507721551461020257806357b89883146102155780636657732f1461022857806367eabbc914610248578063715018a61461026857806374d945ec1461027057610173565b80630b0db655146101785780631b839c771461018d578063236300dc146101b657806331873e2e146101c957806333028b99146101dc57806344fd1400146101ef575b600080fd5b61018b6101863660046124e3565b61038f565b005b6101a061019b3660046121e7565b6104aa565b6040516101ad919061279d565b60405180910390f35b6101a06101c43660046123fe565b6104e2565b61018b6101d7366004612294565b61052e565b6101a06101ea366004612469565b61053f565b61018b6101fd3660046121e7565b6105e6565b6101a0610210366004612219565b610695565b6101a06102233660046123af565b6106cf565b61023b6102363660046121c6565b6106e0565b6040516101ad919061265c565b61025b6102563660046121c6565b61075a565b6040516101ad919061262f565b61018b610778565b61025b61027e3660046121c6565b61081a565b6102966102913660046121e7565b610838565b6040516101ad94939291906127a6565b6101a06102b43660046121e7565b610897565b61025b6108c2565b6102d46102cf3660046121c6565b6108d1565b6040516101ad91906127e0565b6102f46102ef36600461234d565b6108f2565b6040516101ad92919061266f565b6101a061031036600461234d565b61099c565b6102f4610323366004612305565b6109b3565b6102f46103363660046122c6565b6109f5565b61023b610a11565b61018b61035136600461225b565b610a73565b6102f4610364366004612305565b610b5a565b61018b6103773660046121c6565b610b71565b61018b61038a3660046121e7565b610c69565b610397610d18565b6000546001600160a01b039081169116146103e7576040805162461bcd60e51b81526020600482018190526024820152600080516020612838833981519152604482015290519081900360640190fd5b60005b815181101561049d578181815181106103ff57fe5b6020026020010151606001516001600160a01b031663b1bf962d6040518163ffffffff1660e01b815260040160206040518083038186803b15801561044357600080fd5b505afa158015610457573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061047b9190612590565b82828151811061048757fe5b60209081029190910181015101526001016103ea565b506104a781610d1c565b50565b6001600160a01b039182166000908152600160209081526040808320939094168252919091522054600160e01b900463ffffffff1690565b60006001600160a01b0383166105135760405162461bcd60e51b815260040161050a9061271b565b60405180910390fd5b610522868686333388886111b9565b90505b95945050505050565b61053a338483856112ef565b505050565b6001600160a01b0380841660009081526005602052604081205490913391869116821461057e5760405162461bcd60e51b815260040161050a9061276f565b6001600160a01b0386166105a45760405162461bcd60e51b815260040161050a906126c5565b6001600160a01b0385166105ca5760405162461bcd60e51b815260040161050a9061271b565b6105d9898989338a8a8a6111b9565b9998505050505050505050565b6105ee610d18565b6000546001600160a01b0390811691161461063e576040805162461bcd60e51b81526020600482018190526024820152600080516020612838833981519152604482015290519081900360640190fd5b6001600160a01b0381811660009081526006602052604080822080546001600160a01b0319169386169384179055517f28a48cbce43190d77247f342cf319b1607bff4ef716cf26b76cf7bb71baebaa59190a25050565b6001600160a01b03808316600090815260016020818152604080842086861685528252808420948816845293909101905220549392505050565b6000610525858585333333886111b9565b6001600160a01b03811660009081526001602081815260409283902090910180548351818402810184019094528084526060939283018282801561074d57602002820191906000526020600020905b81546001600160a01b0316815260019091019060200180831161072f575b505050505090505b919050565b6001600160a01b039081166000908152600660205260409020541690565b610780610d18565b6000546001600160a01b039081169116146107d0576040805162461bcd60e51b81526020600482018190526024820152600080516020612838833981519152604482015290519081900360640190fd5b600080546040516001600160a01b03909116907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3600080546001600160a01b0319169055565b6001600160a01b039081166000908152600560205260409020541690565b6001600160a01b0391821660009081526001602090815260408083209390941682529190915220546001600160681b03600160581b820416916001600160581b0382169163ffffffff600160c01b8204811692600160e01b9092041690565b6001600160a01b03918216600090815260026020908152604080832093909416825291909152205490565b6000546001600160a01b031690565b6001600160a01b031660009081526001602052604090206002015460ff1690565b6001600160a01b038083166000908152600560205260409020546060918291339186911682146109345760405162461bcd60e51b815260040161050a9061276f565b6001600160a01b03861661095a5760405162461bcd60e51b815260040161050a906126c5565b6001600160a01b0385166109805760405162461bcd60e51b815260040161050a9061271b565b61098d88883389896113e0565b93509350505094509492505050565b600061052583836109ae8888886115bc565b611742565b6060806001600160a01b0383166109dc5760405162461bcd60e51b815260040161050a9061271b565b6109e985853333876113e0565b91509150935093915050565b606080610a0584843333336113e0565b915091505b9250929050565b60606004805480602002602001604051908101604052809291908181526020018280548015610a6957602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610a4b575b5050505050905090565b610a7b610d18565b6000546001600160a01b03908116911614610acb576040805162461bcd60e51b81526020600482018190526024820152600080516020612838833981519152604482015290519081900360640190fd5b6001600160a01b038381166000818152600160209081526040808320948716808452949091529081902080546001600160e01b0316600160e01b63ffffffff871602179081905590517fec44514a2743afadd78647150195ef92fec31e96087fa5cbbcd72e8d131d9a1c91610b4d916001600160581b039091169086906127c1565b60405180910390a3505050565b6060806109e983610b6c8787876115bc565b6117ce565b610b79610d18565b6000546001600160a01b03908116911614610bc9576040805162461bcd60e51b81526020600482018190526024820152600080516020612838833981519152604482015290519081900360640190fd5b6001600160a01b038116610c0e5760405162461bcd60e51b81526004018080602001828103825260268152602001806128126026913960400191505060405180910390fd5b600080546040516001600160a01b03808516939216917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3600080546001600160a01b0319166001600160a01b0392909216919091179055565b610c71610d18565b6000546001600160a01b03908116911614610cc1576040805162461bcd60e51b81526020600482018190526024820152600080516020612838833981519152604482015290519081900360640190fd5b6001600160a01b0382811660008181526005602052604080822080546001600160a01b0319169486169485179055517f4925eafc82d0c4d67889898eeed64b18488ab19811e61620f387026dec126a289190a35050565b3390565b60005b81518110156111b557818181518110610d3457fe5b6020026020010151606001516001600160a01b031663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b158015610d7857600080fd5b505afa158015610d8c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610db091906125cb565b60016000848481518110610dc057fe5b6020026020010151606001516001600160a01b03166001600160a01b0316815260200190815260200160002060020160006101000a81548160ff021916908360ff160217905550600060016000848481518110610e1957fe5b6020026020010151606001516001600160a01b03166001600160a01b031681526020019081526020016000206000016000848481518110610e5657fe5b602090810291909101810151608001516001600160a01b031682528101919091526040016000208054909150600160c01b900463ffffffff16610f1e5760016000848481518110610ea357fe5b6020026020010151606001516001600160a01b03166001600160a01b03168152602001908152602001600020600101838381518110610ede57fe5b6020908102919091018101516080015182546001810184556000938452919092200180546001600160a01b0319166001600160a01b039092169190911790555b60036000848481518110610f2e57fe5b602090810291909101810151608001516001600160a01b031682528101919091526040016000205460ff1661100057600160036000858581518110610f6f57fe5b6020026020010151608001516001600160a01b03166001600160a01b0316815260200190815260200160002060006101000a81548160ff0219169083151502179055506004838381518110610fc057fe5b6020908102919091018101516080015182546001810184556000938452919092200180546001600160a01b0319166001600160a01b039092169190911790555b61108f83838151811061100f57fe5b60200260200101516060015184848151811061102757fe5b6020026020010151608001518386868151811061104057fe5b6020026020010151602001516001600089898151811061105c57fe5b602090810291909101810151606001516001600160a01b031682528101919091526040016000206002015460ff166119d3565b5082828151811061109c57fe5b60209081029190910101515181546affffffffffffffffffffff19166001600160581b0390911617815582518390839081106110d457fe5b602090810291909101015160400151815463ffffffff909116600160e01b026001600160e01b03909116178155825183908390811061110f57fe5b6020026020010151608001516001600160a01b031683838151811061113057fe5b6020026020010151606001516001600160a01b03167fec44514a2743afadd78647150195ef92fec31e96087fa5cbbcd72e8d131d9a1c85858151811061117257fe5b60200260200101516000015186868151811061118a57fe5b6020026020010151604001516040516111a49291906127c1565b60405180910390a350600101610d1f565b5050565b6000856111c8575060006112e4565b6001600160a01b03808516600090815260026020908152604080832093861683529290522054808711156112325761120a856112058b8b896115bc565b611b1c565b506001600160a01b038085166000908152600260209081526040808320938616835292905220545b806112415760009150506112e4565b60008188116112505787611252565b815b6001600160a01b03808816600090815260026020908152604080832093891683529290522081840390559050611289858583611b81565b846001600160a01b0316846001600160a01b0316876001600160a01b03167fc052130bc4ef84580db505783484b067ea8b71b3bca78a7e12db7aea8658f0048a856040516112d8929190612643565b60405180910390a49150505b979650505050505050565b60005b6001600160a01b038516600090815260016020819052604090912001548110156113d9576001600160a01b038516600090815260016020819052604082200180548390811061133d57fe5b60009182526020822001546001600160a01b031691506113608688848888611bb8565b905080156113cf576001600160a01b0380871660008181526002602090815260408083209487168084529490915290819020805485019055517f68ac6b4d0eeedccf3df154188dfac62d18d2ac5eaac52ecd6e728e33ce75a1d4906113c690859061279d565b60405180910390a35b50506001016112f2565b5050505050565b6060806113f2846112058989886115bc565b6004546001600160401b038111801561140a57600080fd5b50604051908082528060200260200182016040528015611434578160200160208202803683370190505b506004549092506001600160401b038111801561145057600080fd5b5060405190808252806020026020018201604052801561147a578160200160208202803683370190505b50905060005b6004548110156115b15760006004828154811061149957fe5b60009182526020808320909101546001600160a01b0389811684526002835260408085209190921680855292529091205485519192509082908690859081106114de57fe5b60200260200101906001600160a01b031690816001600160a01b0316815250508084848151811061150b57fe5b602090810291909101015280156115a7576001600160a01b03808816600090815260026020908152604080832093861683529290529081205561154f868383611b81565b856001600160a01b0316826001600160a01b0316886001600160a01b03167fc052130bc4ef84580db505783484b067ea8b71b3bca78a7e12db7aea8658f0048b8560405161159e929190612643565b60405180910390a45b5050600101611480565b509550959350505050565b6060826001600160401b03811180156115d457600080fd5b5060405190808252806020026020018201604052801561160e57816020015b6115fb6120a0565b8152602001906001900390816115f35790505b50905060005b8381101561173a5784848281811061162857fe5b905060200201602081019061163d91906121c6565b82828151811061164957fe5b60209081029190910101516001600160a01b03909116905284848281811061166d57fe5b905060200201602081019061168291906121c6565b6001600160a01b0316630afbcdc9846040518263ffffffff1660e01b81526004016116ad919061262f565b604080518083038186803b1580156116c457600080fd5b505afa1580156116d8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116fc91906125a8565b83838151811061170857fe5b602002602001015160200184848151811061171f57fe5b60209081029190910101516040019190915252600101611614565b509392505050565b6000805b825181101561179f5782818151811061175b57fe5b6020026020010151602001516000141561177457611797565b611792858585848151811061178557fe5b6020026020010151611cbf565b820191505b600101611746565b506001600160a01b03808516600090815260026020908152604080832093871683529290522054019392505050565b60045460609081906001600160401b03811180156117eb57600080fd5b50604051908082528060200260200182016040528015611815578160200160208202803683370190505b50915081516001600160401b038111801561182f57600080fd5b50604051908082528060200260200182016040528015611859578160200160208202803683370190505b50905060005b825181101561193a576004818154811061187557fe5b9060005260206000200160009054906101000a90046001600160a01b031683828151811061189f57fe5b60200260200101906001600160a01b031690816001600160a01b03168152505060026000866001600160a01b03166001600160a01b0316815260200190815260200160002060008483815181106118f257fe5b60200260200101516001600160a01b03166001600160a01b031681526020019081526020016000205482828151811061192757fe5b602090810291909101015260010161185f565b5060005b83518110156119cb5783818151811061195357fe5b6020026020010151602001516000141561196c576119c3565b60005b83518110156119c15761199c8685838151811061198857fe5b602002602001015187858151811061178557fe5b8382815181106119a857fe5b602090810291909101018051909101905260010161196f565b505b60010161193e565b509250929050565b8254600090600160581b81046001600160681b031690600160c01b900463ffffffff16421415611a04579050610525565b8454600090611a389083906001600160581b0381169063ffffffff600160c01b8204811691600160e01b9004168989611d81565b9050818114611af5576001600160681b03811115611a685760405162461bcd60e51b815260040161050a90612747565b85546cffffffffffffffffffffffffff60581b1916600160581b6001600160681b038316021763ffffffff60c01b1916600160c01b4263ffffffff16021786556040516001600160a01b0388811691908a16907f7ecfb468c92138c74e6034d3e9d76f99150e483bd53bcc43524ce7056698751f90611ae890859061279d565b60405180910390a36112e4565b855463ffffffff60c01b1916600160c01b4263ffffffff1602178655979650505050505050565b60005b815181101561053a57611b79828281518110611b3757fe5b60200260200101516000015184848481518110611b5057fe5b602002602001015160200151858581518110611b6857fe5b6020026020010151604001516112ef565b600101611b1f565b6000611b8e848484611e05565b9050600181151514611bb25760405162461bcd60e51b815260040161050a906126f3565b50505050565b6001600160a01b0380851660008181526001602081815260408084208987168552808352818520968c168552868401835290842054948452919052600201549092919083908190611c13908a908a9087908a9060ff166119d3565b9050808314611cb2578615611c53576001600160a01b038916600090815260016020526040902060020154611c509088908390869060ff16611e38565b91505b6001600160a01b03808b16600081815260018701602052604090819020849055518a8316928c1691907ff5674975ec2736b27b8d52ea7cafb8bc2761685f31f109e028e1498aa2b2f3e590611ca990869061279d565b60405180910390a45b5098975050505050505050565b80516001600160a01b03908116600090815260016020818152604080842087861685528252808420865190951684529190528082206002015483549185015192939260ff909116918491611d48916001600160681b03600160581b820416916001600160581b0382169163ffffffff600160c01b8204811692600160e01b909204169087611d81565b90506112e48560200151828560010160008b6001600160a01b03166001600160a01b031681526020019081526020016000205485611e38565b6000851580611d8e575082155b80611da1575042856001600160801b0316145b80611db5575083856001600160801b031610155b15611dc1575085611dfb565b6000844211611dd05742611dd2565b845b90506001600160801b0386168103888589830260ff8716600a0a0281611df457fe5b0401925050505b9695505050505050565b6001600160a01b038083166000818152600660205260408120549092611e2e9291168685611e57565b5060019392505050565b60008160ff16600a0a838503860281611e4d57fe5b0495945050505050565b604080516001600160a01b0380861660248301528416604482015260648082018490528251808303909101815260849091019091526020810180516001600160e01b03166323b872dd60e01b179052611bb2908590611ebe826001600160a01b0316612064565b611f0f576040805162461bcd60e51b815260206004820152601f60248201527f5361666545524332303a2063616c6c20746f206e6f6e2d636f6e747261637400604482015290519081900360640190fd5b60006060836001600160a01b0316836040518082805190602001908083835b60208310611f4d5780518252601f199092019160209182019101611f2e565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d8060008114611faf576040519150601f19603f3d011682016040523d82523d6000602084013e611fb4565b606091505b50915091508161200b576040805162461bcd60e51b815260206004820181905260248201527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564604482015290519081900360640190fd5b805115611bb25780806020019051602081101561202757600080fd5b5051611bb25760405162461bcd60e51b815260040180806020018281038252602a815260200180612858602a913960400191505060405180910390fd5b6000813f7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47081811480159061209857508115155b949350505050565b604051806060016040528060006001600160a01b0316815260200160008152602001600081525090565b80356001600160a01b038116811461075557600080fd5b60008083601f8401126120f2578182fd5b5081356001600160401b03811115612108578182fd5b6020830191508360208083028501011115610a0a57600080fd5b600060a08284031215612133578081fd5b60405160a081018181106001600160401b038211171561214f57fe5b60405290508082356001600160581b038116811461216c57600080fd5b815260208381013590820152612184604084016121b2565b6040820152612195606084016120ca565b60608201526121a6608084016120ca565b60808201525092915050565b803563ffffffff8116811461075557600080fd5b6000602082840312156121d7578081fd5b6121e0826120ca565b9392505050565b600080604083850312156121f9578081fd5b612202836120ca565b9150612210602084016120ca565b90509250929050565b60008060006060848603121561222d578081fd5b612236846120ca565b9250612244602085016120ca565b9150612252604085016120ca565b90509250925092565b60008060006060848603121561226f578283fd5b612278846120ca565b9250612286602085016120ca565b9150612252604085016121b2565b6000806000606084860312156122a8578283fd5b6122b1846120ca565b95602085013595506040909401359392505050565b600080602083850312156122d8578182fd5b82356001600160401b038111156122ed578283fd5b6122f9858286016120e1565b90969095509350505050565b600080600060408486031215612319578283fd5b83356001600160401b0381111561232e578384fd5b61233a868287016120e1565b90945092506122529050602085016120ca565b60008060008060608587031215612362578081fd5b84356001600160401b03811115612377578182fd5b612383878288016120e1565b90955093506123969050602086016120ca565b91506123a4604086016120ca565b905092959194509250565b600080600080606085870312156123c4578384fd5b84356001600160401b038111156123d9578485fd5b6123e5878288016120e1565b909550935050602085013591506123a4604086016120ca565b600080600080600060808688031215612415578081fd5b85356001600160401b0381111561242a578182fd5b612436888289016120e1565b9096509450506020860135925061244f604087016120ca565b915061245d606087016120ca565b90509295509295909350565b60008060008060008060a08789031215612481578384fd5b86356001600160401b03811115612496578485fd5b6124a289828a016120e1565b909750955050602087013593506124bb604088016120ca565b92506124c9606088016120ca565b91506124d7608088016120ca565b90509295509295509295565b600060208083850312156124f5578182fd5b82356001600160401b038082111561250b578384fd5b818501915085601f83011261251e578384fd5b81358181111561252a57fe5b61253784858302016127ee565b818152848101925083850160a0808402860187018a1015612556578788fd5b8795505b838610156125825761256c8a83612122565b855260019590950194938601939081019061255a565b509098975050505050505050565b6000602082840312156125a1578081fd5b5051919050565b600080604083850312156125ba578182fd5b505080516020909101519092909150565b6000602082840312156125dc578081fd5b815160ff811681146121e0578182fd5b6000815180845260208085019450808401835b838110156126245781516001600160a01b0316875295820195908201906001016125ff565b509495945050505050565b6001600160a01b0391909116815260200190565b6001600160a01b03929092168252602082015260400190565b6000602082526121e060208301846125ec565b60006040825261268260408301856125ec565b828103602084810191909152845180835285820192820190845b818110156126b85784518352938301939183019160010161269c565b5090979650505050505050565b602080825260149082015273494e56414c49445f555345525f4144445245535360601b604082015260600190565b6020808252600e908201526d2a2920a729a322a92fa2a92927a960911b604082015260600190565b602080825260129082015271494e56414c49445f544f5f4144445245535360701b604082015260600190565b6020808252600e908201526d496e646578206f766572666c6f7760901b604082015260600190565b60208082526014908201527310d3105253515497d5539055551213d49256915160621b604082015260600190565b90815260200190565b93845260208401929092526040830152606082015260800190565b6001600160581b0392909216825263ffffffff16602082015260400190565b60ff91909116815260200190565b6040518181016001600160401b038111828210171561280957fe5b60405291905056fe4f776e61626c653a206e6577206f776e657220697320746865207a65726f20616464726573734f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65725361666545524332303a204552433230206f7065726174696f6e20646964206e6f742073756363656564a2646970667358221220ec125919d2e26d0c53b9727952496a47da726b6381dfb9da84ec16851746a87364736f6c63430007050033

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
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.