ETH Price: $3,186.20 (-7.43%)
Gas: 2 Gwei

Token

Staked Lyra (stkLYRA)
 

Overview

Max Total Supply

0 stkLYRA

Holders

0

Total Transfers

-

Market

Onchain Market Cap

$0.00

Circulating Supply Market Cap

-

Other Info

Token Contract (WITH 18 Decimals)

Loading...
Loading
Loading...
Loading
Loading...
Loading

Click here to update the token information / general information
# Exchange Pair Price  24H Volume % Volume

Contract Source Code Verified (Exact Match)

Contract Name:
LyraSafetyModule

Compiler Version
v0.7.5+commit.eb77ed08

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion
File 1 of 23 : LyraSafetyModule.sol
// SPDX-License-Identifier: agpl-3.0
pragma solidity 0.7.5;
pragma experimental ABIEncoderV2;

import { StakedTokenV3 } from "@aave/aave-stake-v2/contracts/stake/StakedTokenV3.sol";
import { IERC20 } from "@aave/aave-stake-v2/contracts/interfaces/IERC20.sol";

/**
 * @title LyraSafetyModule
 * @notice Contract to stake Lyra token, tokenize the position and get rewards, inheriting from AAVE StakedTokenV3
 * @author Lyra
 **/
contract LyraSafetyModule is StakedTokenV3 {
  string internal constant NAME = "Staked Lyra";
  string internal constant SYMBOL = "stkLYRA";
  uint8 internal constant DECIMALS = 18;

  constructor(
    IERC20 stakedToken,
    IERC20 rewardToken,
    uint256 cooldownSeconds,
    uint256 unstakeWindow,
    address rewardsVault,
    address emissionManager,
    uint128 distributionDuration
  )
    public
    StakedTokenV3(
      stakedToken,
      rewardToken,
      cooldownSeconds,
      unstakeWindow,
      rewardsVault,
      emissionManager,
      distributionDuration,
      NAME,
      SYMBOL,
      DECIMALS,
      address(0)
    )
  {}
}

File 2 of 23 : StakedTokenV3.sol
// SPDX-License-Identifier: agpl-3.0
pragma solidity 0.7.5;
pragma experimental ABIEncoderV2;

import {ERC20} from '@aave/aave-token/contracts/open-zeppelin/ERC20.sol';

import {IERC20} from '../interfaces/IERC20.sol';
import {IStakedAave} from '../interfaces/IStakedAave.sol';
import {ITransferHook} from '../interfaces/ITransferHook.sol';

import {DistributionTypes} from '../lib/DistributionTypes.sol';
import {SafeMath} from '../lib/SafeMath.sol';
import {SafeERC20} from '../lib/SafeERC20.sol';

import {VersionedInitializable} from '../utils/VersionedInitializable.sol';
import {AaveDistributionManager} from './AaveDistributionManager.sol';
import {GovernancePowerWithSnapshot} from '../lib/GovernancePowerWithSnapshot.sol';

/**
 * @title StakedToken V3
 * @notice Contract to stake Aave token, tokenize the position and get rewards, inheriting from a distribution manager contract
 * @author Aave
 **/
contract StakedTokenV3 is
  IStakedAave,
  GovernancePowerWithSnapshot,
  VersionedInitializable,
  AaveDistributionManager
{
  using SafeMath for uint256;
  using SafeERC20 for IERC20;

  /// @dev Start of Storage layout from StakedToken v1
  uint256 public constant REVISION = 1;

  IERC20 public immutable STAKED_TOKEN;
  IERC20 public immutable REWARD_TOKEN;
  uint256 public immutable COOLDOWN_SECONDS;

  /// @notice Seconds available to redeem once the cooldown period is fullfilled
  uint256 public immutable UNSTAKE_WINDOW;

  /// @notice Address to pull from the rewards, needs to have approved this contract
  address public immutable REWARDS_VAULT;

  mapping(address => uint256) public stakerRewardsToClaim;
  mapping(address => uint256) public stakersCooldowns;

  /// @dev End of Storage layout from StakedToken v1

  /// @dev To see the voting mappings, go to GovernancePowerWithSnapshot.sol
  mapping(address => address) internal _votingDelegates;

  mapping(address => mapping(uint256 => Snapshot)) internal _propositionPowerSnapshots;
  mapping(address => uint256) internal _propositionPowerSnapshotsCounts;
  mapping(address => address) internal _propositionPowerDelegates;

  bytes32 public DOMAIN_SEPARATOR;
  bytes public constant EIP712_REVISION = bytes('1');
  bytes32 internal constant EIP712_DOMAIN =
    keccak256('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)');
  bytes32 public constant PERMIT_TYPEHASH =
    keccak256('Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)');

  /// @dev owner => next valid nonce to submit with permit()
  mapping(address => uint256) public _nonces;

  event Staked(address indexed from, address indexed onBehalfOf, uint256 amount);
  event Redeem(address indexed from, address indexed to, uint256 amount);

  event RewardsAccrued(address user, uint256 amount);
  event RewardsClaimed(address indexed from, address indexed to, uint256 amount);

  event Cooldown(address indexed user);

  constructor(
    IERC20 stakedToken,
    IERC20 rewardToken,
    uint256 cooldownSeconds,
    uint256 unstakeWindow,
    address rewardsVault,
    address emissionManager,
    uint128 distributionDuration,
    string memory name,
    string memory symbol,
    uint8 decimals,
    address governance
  ) public ERC20(name, symbol) AaveDistributionManager(emissionManager, distributionDuration) {
    STAKED_TOKEN = stakedToken;
    REWARD_TOKEN = rewardToken;
    COOLDOWN_SECONDS = cooldownSeconds;
    UNSTAKE_WINDOW = unstakeWindow;
    REWARDS_VAULT = rewardsVault;
    _aaveGovernance = ITransferHook(governance);
    ERC20._setupDecimals(decimals);
  }

  /**
   * @dev Called by the proxy contract
   **/
  function initialize(
    string calldata name,
    string calldata symbol,
    uint8 decimals
  ) external initializer {
    uint256 chainId;

    //solium-disable-next-line
    assembly {
      chainId := chainid()
    }

    DOMAIN_SEPARATOR = keccak256(
      abi.encode(
        EIP712_DOMAIN,
        keccak256(bytes(super.name())),
        keccak256(EIP712_REVISION),
        chainId,
        address(this)
      )
    );
    if (REVISION == 1) {
      _name = name;
      _symbol = symbol;
      _setupDecimals(decimals);
    }
  }

  function stake(address onBehalfOf, uint256 amount) external override {
    require(amount != 0, 'INVALID_ZERO_AMOUNT');
    uint256 balanceOfUser = balanceOf(onBehalfOf);

    uint256 accruedRewards =
      _updateUserAssetInternal(onBehalfOf, address(this), balanceOfUser, totalSupply());
    if (accruedRewards != 0) {
      emit RewardsAccrued(onBehalfOf, accruedRewards);
      stakerRewardsToClaim[onBehalfOf] = stakerRewardsToClaim[onBehalfOf].add(accruedRewards);
    }

    stakersCooldowns[onBehalfOf] = getNextCooldownTimestamp(0, amount, onBehalfOf, balanceOfUser);

    _mint(onBehalfOf, amount);
    IERC20(STAKED_TOKEN).safeTransferFrom(msg.sender, address(this), amount);

    emit Staked(msg.sender, onBehalfOf, amount);
  }

  /**
   * @dev Redeems staked tokens, and stop earning rewards
   * @param to Address to redeem to
   * @param amount Amount to redeem
   **/
  function redeem(address to, uint256 amount) external override {
    require(amount != 0, 'INVALID_ZERO_AMOUNT');
    //solium-disable-next-line
    uint256 cooldownStartTimestamp = stakersCooldowns[msg.sender];
    require(
      block.timestamp > cooldownStartTimestamp.add(COOLDOWN_SECONDS),
      'INSUFFICIENT_COOLDOWN'
    );
    require(
      block.timestamp.sub(cooldownStartTimestamp.add(COOLDOWN_SECONDS)) <= UNSTAKE_WINDOW,
      'UNSTAKE_WINDOW_FINISHED'
    );
    uint256 balanceOfMessageSender = balanceOf(msg.sender);

    uint256 amountToRedeem = (amount > balanceOfMessageSender) ? balanceOfMessageSender : amount;

    _updateCurrentUnclaimedRewards(msg.sender, balanceOfMessageSender, true);

    _burn(msg.sender, amountToRedeem);

    if (balanceOfMessageSender.sub(amountToRedeem) == 0) {
      stakersCooldowns[msg.sender] = 0;
    }

    IERC20(STAKED_TOKEN).safeTransfer(to, amountToRedeem);

    emit Redeem(msg.sender, to, amountToRedeem);
  }

  /**
   * @dev Activates the cooldown period to unstake
   * - It can't be called if the user is not staking
   **/
  function cooldown() external override {
    require(balanceOf(msg.sender) != 0, 'INVALID_BALANCE_ON_COOLDOWN');
    //solium-disable-next-line
    stakersCooldowns[msg.sender] = block.timestamp;

    emit Cooldown(msg.sender);
  }

  /**
   * @dev Claims an `amount` of `REWARD_TOKEN` to the address `to`
   * @param to Address to stake for
   * @param amount Amount to stake
   **/
  function claimRewards(address to, uint256 amount) external override {
    uint256 newTotalRewards =
      _updateCurrentUnclaimedRewards(msg.sender, balanceOf(msg.sender), false);
    uint256 amountToClaim = (amount == type(uint256).max) ? newTotalRewards : amount;

    stakerRewardsToClaim[msg.sender] = newTotalRewards.sub(amountToClaim, 'INVALID_AMOUNT');

    REWARD_TOKEN.safeTransferFrom(REWARDS_VAULT, to, amountToClaim);

    emit RewardsClaimed(msg.sender, to, amountToClaim);
  }

  /**
   * @dev Internal ERC20 _transfer of the tokenized staked tokens
   * @param from Address to transfer from
   * @param to Address to transfer to
   * @param amount Amount to transfer
   **/
  function _transfer(
    address from,
    address to,
    uint256 amount
  ) internal override {
    uint256 balanceOfFrom = balanceOf(from);
    // Sender
    _updateCurrentUnclaimedRewards(from, balanceOfFrom, true);

    // Recipient
    if (from != to) {
      uint256 balanceOfTo = balanceOf(to);
      _updateCurrentUnclaimedRewards(to, balanceOfTo, true);

      uint256 previousSenderCooldown = stakersCooldowns[from];
      stakersCooldowns[to] = getNextCooldownTimestamp(
        previousSenderCooldown,
        amount,
        to,
        balanceOfTo
      );
      // if cooldown was set and whole balance of sender was transferred - clear cooldown
      if (balanceOfFrom == amount && previousSenderCooldown != 0) {
        stakersCooldowns[from] = 0;
      }
    }

    super._transfer(from, to, amount);
  }

  /**
   * @dev Updates the user state related with his accrued rewards
   * @param user Address of the user
   * @param userBalance The current balance of the user
   * @param updateStorage Boolean flag used to update or not the stakerRewardsToClaim of the user
   * @return The unclaimed rewards that were added to the total accrued
   **/
  function _updateCurrentUnclaimedRewards(
    address user,
    uint256 userBalance,
    bool updateStorage
  ) internal returns (uint256) {
    uint256 accruedRewards =
      _updateUserAssetInternal(user, address(this), userBalance, totalSupply());
    uint256 unclaimedRewards = stakerRewardsToClaim[user].add(accruedRewards);

    if (accruedRewards != 0) {
      if (updateStorage) {
        stakerRewardsToClaim[user] = unclaimedRewards;
      }
      emit RewardsAccrued(user, accruedRewards);
    }

    return unclaimedRewards;
  }

  /**
   * @dev Calculates the how is gonna be a new cooldown timestamp depending on the sender/receiver situation
   *  - If the timestamp of the sender is "better" or the timestamp of the recipient is 0, we take the one of the recipient
   *  - Weighted average of from/to cooldown timestamps if:
   *    # The sender doesn't have the cooldown activated (timestamp 0).
   *    # The sender timestamp is expired
   *    # The sender has a "worse" timestamp
   *  - If the receiver's cooldown timestamp expired (too old), the next is 0
   * @param fromCooldownTimestamp Cooldown timestamp of the sender
   * @param amountToReceive Amount
   * @param toAddress Address of the recipient
   * @param toBalance Current balance of the receiver
   * @return The new cooldown timestamp
   **/
  function getNextCooldownTimestamp(
    uint256 fromCooldownTimestamp,
    uint256 amountToReceive,
    address toAddress,
    uint256 toBalance
  ) public view returns (uint256) {
    uint256 toCooldownTimestamp = stakersCooldowns[toAddress];
    if (toCooldownTimestamp == 0) {
      return 0;
    }

    uint256 minimalValidCooldownTimestamp =
      block.timestamp.sub(COOLDOWN_SECONDS).sub(UNSTAKE_WINDOW);

    if (minimalValidCooldownTimestamp > toCooldownTimestamp) {
      toCooldownTimestamp = 0;
    } else {
      uint256 fromCooldownTimestamp =
        (minimalValidCooldownTimestamp > fromCooldownTimestamp)
          ? block.timestamp
          : fromCooldownTimestamp;

      if (fromCooldownTimestamp < toCooldownTimestamp) {
        return toCooldownTimestamp;
      } else {
        toCooldownTimestamp = (
          amountToReceive.mul(fromCooldownTimestamp).add(toBalance.mul(toCooldownTimestamp))
        )
          .div(amountToReceive.add(toBalance));
      }
    }

    return toCooldownTimestamp;
  }

  /**
   * @dev Return the total rewards pending to claim by an staker
   * @param staker The staker address
   * @return The rewards
   */
  function getTotalRewardsBalance(address staker) external view returns (uint256) {
    DistributionTypes.UserStakeInput[] memory userStakeInputs =
      new DistributionTypes.UserStakeInput[](1);
    userStakeInputs[0] = DistributionTypes.UserStakeInput({
      underlyingAsset: address(this),
      stakedByUser: balanceOf(staker),
      totalStaked: totalSupply()
    });
    return stakerRewardsToClaim[staker].add(_getUnclaimedRewards(staker, userStakeInputs));
  }

  /**
   * @dev returns the revision of the implementation contract
   * @return The revision
   */
  function getRevision() internal pure override returns (uint256) {
    return REVISION;
  }

  /**
   * @dev implements the permit function as for https://github.com/ethereum/EIPs/blob/8a34d644aacf0f9f8f00815307fd7dd5da07655f/EIPS/eip-2612.md
   * @param owner the owner of the funds
   * @param spender the spender
   * @param value the amount
   * @param deadline the deadline timestamp, type(uint256).max for no deadline
   * @param v signature param
   * @param s signature param
   * @param r signature param
   */

  function permit(
    address owner,
    address spender,
    uint256 value,
    uint256 deadline,
    uint8 v,
    bytes32 r,
    bytes32 s
  ) external {
    require(owner != address(0), 'INVALID_OWNER');
    //solium-disable-next-line
    require(block.timestamp <= deadline, 'INVALID_EXPIRATION');
    uint256 currentValidNonce = _nonces[owner];
    bytes32 digest =
      keccak256(
        abi.encodePacked(
          '\x19\x01',
          DOMAIN_SEPARATOR,
          keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, currentValidNonce, deadline))
        )
      );

    require(owner == ecrecover(digest, v, r, s), 'INVALID_SIGNATURE');
    _nonces[owner] = currentValidNonce.add(1);
    _approve(owner, spender, value);
  }

  /**
   * @dev Writes a snapshot before any operation involving transfer of value: _transfer, _mint and _burn
   * - On _transfer, it writes snapshots for both "from" and "to"
   * - On _mint, only for _to
   * - On _burn, only for _from
   * @param from the from address
   * @param to the to address
   * @param amount the amount to transfer
   */
  function _beforeTokenTransfer(
    address from,
    address to,
    uint256 amount
  ) internal override {
    address votingFromDelegatee = _votingDelegates[from];
    address votingToDelegatee = _votingDelegates[to];

    if (votingFromDelegatee == address(0)) {
      votingFromDelegatee = from;
    }
    if (votingToDelegatee == address(0)) {
      votingToDelegatee = to;
    }

    _moveDelegatesByType(
      votingFromDelegatee,
      votingToDelegatee,
      amount,
      DelegationType.VOTING_POWER
    );

    address propPowerFromDelegatee = _propositionPowerDelegates[from];
    address propPowerToDelegatee = _propositionPowerDelegates[to];

    if (propPowerFromDelegatee == address(0)) {
      propPowerFromDelegatee = from;
    }
    if (propPowerToDelegatee == address(0)) {
      propPowerToDelegatee = to;
    }

    _moveDelegatesByType(
      propPowerFromDelegatee,
      propPowerToDelegatee,
      amount,
      DelegationType.PROPOSITION_POWER
    );

    // caching the aave governance address to avoid multiple state loads
    ITransferHook aaveGovernance = _aaveGovernance;
    if (aaveGovernance != ITransferHook(0)) {
      aaveGovernance.onTransfer(from, to, amount);
    }
  }

  function _getDelegationDataByType(DelegationType delegationType)
    internal
    view
    override
    returns (
      mapping(address => mapping(uint256 => Snapshot)) storage, //snapshots
      mapping(address => uint256) storage, //snapshots count
      mapping(address => address) storage //delegatees list
    )
  {
    if (delegationType == DelegationType.VOTING_POWER) {
      return (_votingSnapshots, _votingSnapshotsCounts, _votingDelegates);
    } else {
      return (
        _propositionPowerSnapshots,
        _propositionPowerSnapshotsCounts,
        _propositionPowerDelegates
      );
    }
  }

  /**
   * @dev Delegates power from signatory to `delegatee`
   * @param delegatee The address to delegate votes to
   * @param delegationType the type of delegation (VOTING_POWER, PROPOSITION_POWER)
   * @param nonce The contract state required to match the signature
   * @param expiry The time at which to expire the signature
   * @param v The recovery byte of the signature
   * @param r Half of the ECDSA signature pair
   * @param s Half of the ECDSA signature pair
   */
  function delegateByTypeBySig(
    address delegatee,
    DelegationType delegationType,
    uint256 nonce,
    uint256 expiry,
    uint8 v,
    bytes32 r,
    bytes32 s
  ) public {
    bytes32 structHash =
      keccak256(
        abi.encode(DELEGATE_BY_TYPE_TYPEHASH, delegatee, uint256(delegationType), nonce, expiry)
      );
    bytes32 digest = keccak256(abi.encodePacked('\x19\x01', DOMAIN_SEPARATOR, structHash));
    address signatory = ecrecover(digest, v, r, s);
    require(signatory != address(0), 'INVALID_SIGNATURE');
    require(nonce == _nonces[signatory]++, 'INVALID_NONCE');
    require(block.timestamp <= expiry, 'INVALID_EXPIRATION');
    _delegateByType(signatory, delegatee, delegationType);
  }

  /**
   * @dev Delegates power from signatory to `delegatee`
   * @param delegatee The address to delegate votes to
   * @param nonce The contract state required to match the signature
   * @param expiry The time at which to expire the signature
   * @param v The recovery byte of the signature
   * @param r Half of the ECDSA signature pair
   * @param s Half of the ECDSA signature pair
   */
  function delegateBySig(
    address delegatee,
    uint256 nonce,
    uint256 expiry,
    uint8 v,
    bytes32 r,
    bytes32 s
  ) public {
    bytes32 structHash = keccak256(abi.encode(DELEGATE_TYPEHASH, delegatee, nonce, expiry));
    bytes32 digest = keccak256(abi.encodePacked('\x19\x01', DOMAIN_SEPARATOR, structHash));
    address signatory = ecrecover(digest, v, r, s);
    require(signatory != address(0), 'INVALID_SIGNATURE');
    require(nonce == _nonces[signatory]++, 'INVALID_NONCE');
    require(block.timestamp <= expiry, 'INVALID_EXPIRATION');
    _delegateByType(signatory, delegatee, DelegationType.VOTING_POWER);
    _delegateByType(signatory, delegatee, DelegationType.PROPOSITION_POWER);
  }
}

File 3 of 23 : 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 4 of 23 : ERC20.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.7.5;

import "./Context.sol";
import "../interfaces/IERC20.sol";
import "./SafeMath.sol";
import "./Address.sol";

/**
 * @dev Implementation of the {IERC20} interface.
 *
 * This implementation is agnostic to the way tokens are created. This means
 * that a supply mechanism has to be added in a derived contract using {_mint}.
 * For a generic mechanism see {ERC20PresetMinterPauser}.
 *
 * TIP: For a detailed writeup see our guide
 * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
 * to implement supply mechanisms].
 *
 * We have followed general OpenZeppelin guidelines: functions revert instead
 * of returning `false` on failure. This behavior is nonetheless conventional
 * and does not conflict with the expectations of ERC20 applications.
 *
 * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
 * This allows applications to reconstruct the allowance for all accounts just
 * by listening to said events. Other implementations of the EIP may not emit
 * these events, as it isn't required by the specification.
 *
 * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
 * functions have been added to mitigate the well-known issues around setting
 * allowances. See {IERC20-approve}.
 */
contract ERC20 is Context, IERC20 {
    using SafeMath for uint256;
    using Address for address;

    mapping (address => uint256) private _balances;

    mapping (address => mapping (address => uint256)) private _allowances;

    uint256 private _totalSupply;

    string internal _name;
    string internal _symbol;
    uint8 private _decimals;

    /**
     * @dev Sets the values for {name} and {symbol}, initializes {decimals} with
     * a default value of 18.
     *
     * To select a different value for {decimals}, use {_setupDecimals}.
     *
     * All three of these values are immutable: they can only be set once during
     * construction.
     */
    constructor (string memory name, string memory symbol) public {
        _name = name;
        _symbol = symbol;
        _decimals = 18;
    }

    /**
     * @dev Returns the name of the token.
     */
    function name() public view returns (string memory) {
        return _name;
    }

    /**
     * @dev Returns the symbol of the token, usually a shorter version of the
     * name.
     */
    function symbol() public view returns (string memory) {
        return _symbol;
    }

    /**
     * @dev Returns the number of decimals used to get its user representation.
     * For example, if `decimals` equals `2`, a balance of `505` tokens should
     * be displayed to a user as `5,05` (`505 / 10 ** 2`).
     *
     * Tokens usually opt for a value of 18, imitating the relationship between
     * Ether and Wei. This is the value {ERC20} uses, unless {_setupDecimals} is
     * called.
     *
     * NOTE: This information is only used for _display_ purposes: it in
     * no way affects any of the arithmetic of the contract, including
     * {IERC20-balanceOf} and {IERC20-transfer}.
     */
    function decimals() public view returns (uint8) {
        return _decimals;
    }

    /**
     * @dev See {IERC20-totalSupply}.
     */
    function totalSupply() public view override returns (uint256) {
        return _totalSupply;
    }

    /**
     * @dev See {IERC20-balanceOf}.
     */
    function balanceOf(address account) public view override returns (uint256) {
        return _balances[account];
    }

    /**
     * @dev See {IERC20-transfer}.
     *
     * Requirements:
     *
     * - `recipient` cannot be the zero address.
     * - the caller must have a balance of at least `amount`.
     */
    function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
        _transfer(_msgSender(), recipient, amount);
        return true;
    }

    /**
     * @dev See {IERC20-allowance}.
     */
    function allowance(address owner, address spender) public view virtual override returns (uint256) {
        return _allowances[owner][spender];
    }

    /**
     * @dev See {IERC20-approve}.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function approve(address spender, uint256 amount) public virtual override returns (bool) {
        _approve(_msgSender(), spender, amount);
        return true;
    }

    /**
     * @dev See {IERC20-transferFrom}.
     *
     * Emits an {Approval} event indicating the updated allowance. This is not
     * required by the EIP. See the note at the beginning of {ERC20};
     *
     * Requirements:
     * - `sender` and `recipient` cannot be the zero address.
     * - `sender` must have a balance of at least `amount`.
     * - the caller must have allowance for ``sender``'s tokens of at least
     * `amount`.
     */
    function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) {
        _transfer(sender, recipient, amount);
        _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance"));
        return true;
    }

    /**
     * @dev Atomically increases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
        _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue));
        return true;
    }

    /**
     * @dev Atomically decreases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `spender` must have allowance for the caller of at least
     * `subtractedValue`.
     */
    function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
        _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero"));
        return true;
    }

    /**
     * @dev Moves tokens `amount` from `sender` to `recipient`.
     *
     * This is internal function is equivalent to {transfer}, and can be used to
     * e.g. implement automatic token fees, slashing mechanisms, etc.
     *
     * Emits a {Transfer} event.
     *
     * Requirements:
     *
     * - `sender` cannot be the zero address.
     * - `recipient` cannot be the zero address.
     * - `sender` must have a balance of at least `amount`.
     */
    function _transfer(address sender, address recipient, uint256 amount) internal virtual {
        require(sender != address(0), "ERC20: transfer from the zero address");
        require(recipient != address(0), "ERC20: transfer to the zero address");

        _beforeTokenTransfer(sender, recipient, amount);

        _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance");
        _balances[recipient] = _balances[recipient].add(amount);
        emit Transfer(sender, recipient, amount);
    }

    /** @dev Creates `amount` tokens and assigns them to `account`, increasing
     * the total supply.
     *
     * Emits a {Transfer} event with `from` set to the zero address.
     *
     * Requirements
     *
     * - `to` cannot be the zero address.
     */
    function _mint(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: mint to the zero address");

        _beforeTokenTransfer(address(0), account, amount);

        _totalSupply = _totalSupply.add(amount);
        _balances[account] = _balances[account].add(amount);
        emit Transfer(address(0), account, amount);
    }

    /**
     * @dev Destroys `amount` tokens from `account`, reducing the
     * total supply.
     *
     * Emits a {Transfer} event with `to` set to the zero address.
     *
     * Requirements
     *
     * - `account` cannot be the zero address.
     * - `account` must have at least `amount` tokens.
     */
    function _burn(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: burn from the zero address");

        _beforeTokenTransfer(account, address(0), amount);

        _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance");
        _totalSupply = _totalSupply.sub(amount);
        emit Transfer(account, address(0), amount);
    }

    /**
     * @dev Sets `amount` as the allowance of `spender` over the `owner`s tokens.
     *
     * This is internal function is equivalent to `approve`, and can be used to
     * e.g. set automatic allowances for certain subsystems, etc.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `owner` cannot be the zero address.
     * - `spender` cannot be the zero address.
     */
    function _approve(address owner, address spender, uint256 amount) internal virtual {
        require(owner != address(0), "ERC20: approve from the zero address");
        require(spender != address(0), "ERC20: approve to the zero address");

        _allowances[owner][spender] = amount;
        emit Approval(owner, spender, amount);
    }

    /**
     * @dev Sets {decimals} to a value other than the default one of 18.
     *
     * WARNING: This function should only be called from the constructor. Most
     * applications that interact with token contracts will not expect
     * {decimals} to ever change, and may work incorrectly if it does.
     */
    function _setupDecimals(uint8 decimals_) internal {
        _decimals = decimals_;
    }

    /**
     * @dev Hook that is called before any transfer of tokens. This includes
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * will be to transferred to `to`.
     * - when `from` is zero, `amount` tokens will be minted for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { }
}

File 5 of 23 : IStakedAave.sol
// SPDX-License-Identifier: agpl-3.0
pragma solidity 0.7.5;

interface IStakedAave {
  function stake(address to, uint256 amount) external;

  function redeem(address to, uint256 amount) external;

  function cooldown() external;

  function claimRewards(address to, uint256 amount) external;
}

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

interface ITransferHook {
  function onTransfer(
    address from,
    address to,
    uint256 amount
  ) external;
}

File 7 of 23 : DistributionTypes.sol
// SPDX-License-Identifier: agpl-3.0
pragma solidity 0.7.5;
pragma experimental ABIEncoderV2;

library DistributionTypes {
  struct AssetConfigInput {
    uint128 emissionPerSecond;
    uint256 totalStaked;
    address underlyingAsset;
  }

  struct UserStakeInput {
    address underlyingAsset;
    uint256 stakedByUser;
    uint256 totalStaked;
  }
}

File 8 of 23 : 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 9 of 23 : 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 10 of 23 : VersionedInitializable.sol
// SPDX-License-Identifier: agpl-3.0
pragma solidity 0.7.5;

/**
 * @title VersionedInitializable
 *
 * @dev Helper contract to support initializer functions. To use it, replace
 * the constructor with a function that has the `initializer` modifier.
 * WARNING: Unlike constructors, initializer functions must be manually
 * invoked. This applies both to deploying an Initializable contract, as well
 * as extending an Initializable contract via inheritance.
 * WARNING: When used with inheritance, manual care must be taken to not invoke
 * a parent initializer twice, or ensure that all initializers are idempotent,
 * because this is not dealt with automatically as with constructors.
 *
 * @author Aave, inspired by the OpenZeppelin Initializable contract
 */
abstract contract VersionedInitializable {
  /**
   * @dev Indicates that the contract has been initialized.
   */
  uint256 internal lastInitializedRevision = 0;

  /**
   * @dev Modifier to use in the initializer function of a contract.
   */
  modifier initializer() {
    uint256 revision = getRevision();
    require(revision > lastInitializedRevision, 'Contract instance has already been initialized');

    lastInitializedRevision = revision;

    _;
  }

  /// @dev returns the revision number of the contract.
  /// Needs to be defined in the inherited class as a constant.
  function getRevision() internal pure virtual returns (uint256);

  // Reserved storage space to allow for layout changes in the future.
  uint256[50] private ______gap;
}

File 11 of 23 : AaveDistributionManager.sol
// SPDX-License-Identifier: agpl-3.0
pragma solidity 0.7.5;
pragma experimental ABIEncoderV2;

import {SafeMath} from '../lib/SafeMath.sol';
import {DistributionTypes} from '../lib/DistributionTypes.sol';
import {IAaveDistributionManager} from '../interfaces/IAaveDistributionManager.sol';

/**
 * @title AaveDistributionManager
 * @notice Accounting contract to manage multiple staking distributions
 * @author Aave
 **/
contract AaveDistributionManager is IAaveDistributionManager {
  using SafeMath for uint256;

  struct AssetData {
    uint128 emissionPerSecond;
    uint128 lastUpdateTimestamp;
    uint256 index;
    mapping(address => uint256) users;
  }

  uint256 public immutable DISTRIBUTION_END;

  address public immutable EMISSION_MANAGER;

  uint8 public constant PRECISION = 18;

  mapping(address => AssetData) public assets;

  event AssetConfigUpdated(address indexed asset, uint256 emission);
  event AssetIndexUpdated(address indexed asset, uint256 index);
  event UserIndexUpdated(address indexed user, address indexed asset, uint256 index);

  constructor(address emissionManager, uint256 distributionDuration) public {
    DISTRIBUTION_END = block.timestamp.add(distributionDuration);
    EMISSION_MANAGER = emissionManager;
  }

  /**
   * @dev Configures the distribution of rewards for a list of assets
   * @param assetsConfigInput The list of configurations to apply
   **/
  function configureAssets(DistributionTypes.AssetConfigInput[] calldata assetsConfigInput)
    external
    override
  {
    require(msg.sender == EMISSION_MANAGER, 'ONLY_EMISSION_MANAGER');

    for (uint256 i = 0; i < assetsConfigInput.length; i++) {
      AssetData storage assetConfig = assets[assetsConfigInput[i].underlyingAsset];

      _updateAssetStateInternal(
        assetsConfigInput[i].underlyingAsset,
        assetConfig,
        assetsConfigInput[i].totalStaked
      );

      assetConfig.emissionPerSecond = assetsConfigInput[i].emissionPerSecond;

      emit AssetConfigUpdated(
        assetsConfigInput[i].underlyingAsset,
        assetsConfigInput[i].emissionPerSecond
      );
    }
  }

  /**
   * @dev Updates the state of one distribution, mainly rewards index and timestamp
   * @param underlyingAsset The address used as key in the distribution, for example sAAVE or the aTokens addresses on Aave
   * @param assetConfig Storage pointer to the distribution's config
   * @param totalStaked Current total of staked assets for this distribution
   * @return The new distribution index
   **/
  function _updateAssetStateInternal(
    address underlyingAsset,
    AssetData storage assetConfig,
    uint256 totalStaked
  ) internal returns (uint256) {
    uint256 oldIndex = assetConfig.index;
    uint128 lastUpdateTimestamp = assetConfig.lastUpdateTimestamp;

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

    uint256 newIndex =
      _getAssetIndex(oldIndex, assetConfig.emissionPerSecond, lastUpdateTimestamp, totalStaked);

    if (newIndex != oldIndex) {
      assetConfig.index = newIndex;
      emit AssetIndexUpdated(underlyingAsset, newIndex);
    }

    assetConfig.lastUpdateTimestamp = uint128(block.timestamp);

    return newIndex;
  }

  /**
   * @dev Updates the state of an user in a distribution
   * @param user The user's address
   * @param asset The address of the reference asset of the distribution
   * @param stakedByUser Amount of tokens staked by the user in the distribution at the moment
   * @param totalStaked Total tokens staked in the distribution
   * @return The accrued rewards for the user until the moment
   **/
  function _updateUserAssetInternal(
    address user,
    address asset,
    uint256 stakedByUser,
    uint256 totalStaked
  ) internal returns (uint256) {
    AssetData storage assetData = assets[asset];
    uint256 userIndex = assetData.users[user];
    uint256 accruedRewards = 0;

    uint256 newIndex = _updateAssetStateInternal(asset, assetData, totalStaked);

    if (userIndex != newIndex) {
      if (stakedByUser != 0) {
        accruedRewards = _getRewards(stakedByUser, newIndex, userIndex);
      }

      assetData.users[user] = newIndex;
      emit UserIndexUpdated(user, asset, newIndex);
    }

    return accruedRewards;
  }

  /**
   * @dev Used by "frontend" stake contracts to update the data of an user when claiming rewards from there
   * @param user The address of the user
   * @param stakes List of structs of the user data related with his stake
   * @return The accrued rewards for the user until the moment
   **/
  function _claimRewards(address user, DistributionTypes.UserStakeInput[] memory stakes)
    internal
    returns (uint256)
  {
    uint256 accruedRewards = 0;

    for (uint256 i = 0; i < stakes.length; i++) {
      accruedRewards = accruedRewards.add(
        _updateUserAssetInternal(
          user,
          stakes[i].underlyingAsset,
          stakes[i].stakedByUser,
          stakes[i].totalStaked
        )
      );
    }

    return accruedRewards;
  }

  /**
   * @dev Return the accrued rewards for an user over a list of distribution
   * @param user The address of the user
   * @param stakes List of structs of the user data related with his stake
   * @return The accrued rewards for the user until the moment
   **/
  function _getUnclaimedRewards(address user, DistributionTypes.UserStakeInput[] memory stakes)
    internal
    view
    returns (uint256)
  {
    uint256 accruedRewards = 0;

    for (uint256 i = 0; i < stakes.length; i++) {
      AssetData storage assetConfig = assets[stakes[i].underlyingAsset];
      uint256 assetIndex =
        _getAssetIndex(
          assetConfig.index,
          assetConfig.emissionPerSecond,
          assetConfig.lastUpdateTimestamp,
          stakes[i].totalStaked
        );

      accruedRewards = accruedRewards.add(
        _getRewards(stakes[i].stakedByUser, assetIndex, assetConfig.users[user])
      );
    }
    return accruedRewards;
  }

  /**
   * @dev Internal function for the calculation of user's rewards on a distribution
   * @param principalUserBalance Amount staked by the user on a distribution
   * @param reserveIndex Current index of the distribution
   * @param userIndex Index stored for the user, representation his staking moment
   * @return The rewards
   **/
  function _getRewards(
    uint256 principalUserBalance,
    uint256 reserveIndex,
    uint256 userIndex
  ) internal pure returns (uint256) {
    return principalUserBalance.mul(reserveIndex.sub(userIndex)).div(10**uint256(PRECISION));
  }

  /**
   * @dev Calculates the next value of an specific distribution index, with validations
   * @param currentIndex Current index of the distribution
   * @param emissionPerSecond Representing the total rewards distributed per second per asset unit, on the distribution
   * @param lastUpdateTimestamp Last moment this distribution was updated
   * @param totalBalance of tokens considered for the distribution
   * @return The new index.
   **/
  function _getAssetIndex(
    uint256 currentIndex,
    uint256 emissionPerSecond,
    uint128 lastUpdateTimestamp,
    uint256 totalBalance
  ) internal view returns (uint256) {
    if (
      emissionPerSecond == 0 ||
      totalBalance == 0 ||
      lastUpdateTimestamp == block.timestamp ||
      lastUpdateTimestamp >= DISTRIBUTION_END
    ) {
      return currentIndex;
    }

    uint256 currentTimestamp =
      block.timestamp > DISTRIBUTION_END ? DISTRIBUTION_END : block.timestamp;
    uint256 timeDelta = currentTimestamp.sub(lastUpdateTimestamp);
    return
      emissionPerSecond.mul(timeDelta).mul(10**uint256(PRECISION)).div(totalBalance).add(
        currentIndex
      );
  }

  /**
   * @dev Returns the data of an user on a distribution
   * @param user Address of the user
   * @param asset The address of the reference asset of the distribution
   * @return The new index
   **/
  function getUserAssetData(address user, address asset) public view returns (uint256) {
    return assets[asset].users[user];
  }
}

File 12 of 23 : GovernancePowerWithSnapshot.sol
// SPDX-License-Identifier: agpl-3.0
pragma solidity 0.7.5;

import {ERC20} from '../lib/ERC20.sol';
import {ITransferHook} from '../interfaces/ITransferHook.sol';
import {SafeMath} from '../lib/SafeMath.sol';
import {
  GovernancePowerDelegationERC20
} from '@aave/aave-token/contracts/token/base/GovernancePowerDelegationERC20.sol';

/**
 * @title ERC20WithSnapshot
 * @notice ERC20 including snapshots of balances on transfer-related actions
 * @author Aave
 **/
abstract contract GovernancePowerWithSnapshot is GovernancePowerDelegationERC20 {
  using SafeMath for uint256;

  /**
   * @dev The following storage layout points to the prior StakedToken.sol implementation:
   * _snapshots => _votingSnapshots
   * _snapshotsCounts =>  _votingSnapshotsCounts
   * _aaveGovernance => _aaveGovernance
   */
  mapping(address => mapping(uint256 => Snapshot)) public _votingSnapshots;
  mapping(address => uint256) public _votingSnapshotsCounts;

  /// @dev reference to the Aave governance contract to call (if initialized) on _beforeTokenTransfer
  /// !!! IMPORTANT The Aave governance is considered a trustable contract, being its responsibility
  /// to control all potential reentrancies by calling back the this contract
  ITransferHook public _aaveGovernance;

  function _setAaveGovernance(ITransferHook aaveGovernance) internal virtual {
    _aaveGovernance = aaveGovernance;
  }
}

File 13 of 23 : 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 14 of 23 : IERC20.sol
// SPDX-License-Identifier: agpl-3.0
pragma solidity 0.7.5;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

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

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

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

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

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

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

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

File 15 of 23 : SafeMath.sol
pragma solidity ^0.7.5;

/**
 * @dev 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 16 of 23 : Address.sol
pragma solidity ^0.7.5;

/**
 * @dev Collection of functions related to the address type
 */
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 17 of 23 : 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 18 of 23 : IAaveDistributionManager.sol
// SPDX-License-Identifier: agpl-3.0
pragma solidity 0.7.5;
pragma experimental ABIEncoderV2;

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

interface IAaveDistributionManager {
  function configureAssets(DistributionTypes.AssetConfigInput[] calldata assetsConfigInput)
    external;
}

File 19 of 23 : ERC20.sol
// SPDX-License-Identifier: agpl-3.0
pragma solidity 0.7.5;

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

/**
 * @title ERC20
 * @notice Basic ERC20 implementation
 * @author Aave
 **/
contract ERC20 is Context, IERC20, IERC20Detailed {
  using SafeMath for uint256;

  mapping(address => uint256) private _balances;
  mapping(address => mapping(address => uint256)) private _allowances;
  uint256 private _totalSupply;
  string private _name;
  string private _symbol;
  uint8 private _decimals;

  constructor(
    string memory name,
    string memory symbol,
    uint8 decimals
  ) public {
    _name = name;
    _symbol = symbol;
    _decimals = decimals;
  }

  /**
   * @return the name of the token
   **/
  function name() public view override returns (string memory) {
    return _name;
  }

  /**
   * @return the symbol of the token
   **/
  function symbol() public view override returns (string memory) {
    return _symbol;
  }

  /**
   * @return the decimals of the token
   **/
  function decimals() public view override returns (uint8) {
    return _decimals;
  }

  /**
   * @return the total supply of the token
   **/
  function totalSupply() public view override returns (uint256) {
    return _totalSupply;
  }

  /**
   * @return the balance of the token
   **/
  function balanceOf(address account) public view override returns (uint256) {
    return _balances[account];
  }

  /**
   * @dev executes a transfer of tokens from msg.sender to recipient
   * @param recipient the recipient of the tokens
   * @param amount the amount of tokens being transferred
   * @return true if the transfer succeeds, false otherwise
   **/
  function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
    _transfer(_msgSender(), recipient, amount);
    return true;
  }

  /**
   * @dev returns the allowance of spender on the tokens owned by owner
   * @param owner the owner of the tokens
   * @param spender the user allowed to spend the owner's tokens
   * @return the amount of owner's tokens spender is allowed to spend
   **/
  function allowance(address owner, address spender)
    public
    view
    virtual
    override
    returns (uint256)
  {
    return _allowances[owner][spender];
  }

  /**
   * @dev allows spender to spend the tokens owned by msg.sender
   * @param spender the user allowed to spend msg.sender tokens
   * @return true
   **/
  function approve(address spender, uint256 amount) public virtual override returns (bool) {
    _approve(_msgSender(), spender, amount);
    return true;
  }

  /**
   * @dev executes a transfer of token from sender to recipient, if msg.sender is allowed to do so
   * @param sender the owner of the tokens
   * @param recipient the recipient of the tokens
   * @param amount the amount of tokens being transferred
   * @return true if the transfer succeeds, false otherwise
   **/
  function transferFrom(
    address sender,
    address recipient,
    uint256 amount
  ) public virtual override returns (bool) {
    _transfer(sender, recipient, amount);
    _approve(
      sender,
      _msgSender(),
      _allowances[sender][_msgSender()].sub(amount, 'ERC20: transfer amount exceeds allowance')
    );
    return true;
  }

  /**
   * @dev increases the allowance of spender to spend msg.sender tokens
   * @param spender the user allowed to spend on behalf of msg.sender
   * @param addedValue the amount being added to the allowance
   * @return true
   **/
  function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
    _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue));
    return true;
  }

  /**
   * @dev decreases the allowance of spender to spend msg.sender tokens
   * @param spender the user allowed to spend on behalf of msg.sender
   * @param subtractedValue the amount being subtracted to the allowance
   * @return true
   **/
  function decreaseAllowance(address spender, uint256 subtractedValue)
    public
    virtual
    returns (bool)
  {
    _approve(
      _msgSender(),
      spender,
      _allowances[_msgSender()][spender].sub(
        subtractedValue,
        'ERC20: decreased allowance below zero'
      )
    );
    return true;
  }

  function _transfer(
    address sender,
    address recipient,
    uint256 amount
  ) internal virtual {
    require(sender != address(0), 'ERC20: transfer from the zero address');
    require(recipient != address(0), 'ERC20: transfer to the zero address');

    _beforeTokenTransfer(sender, recipient, amount);

    _balances[sender] = _balances[sender].sub(amount, 'ERC20: transfer amount exceeds balance');
    _balances[recipient] = _balances[recipient].add(amount);
    emit Transfer(sender, recipient, amount);
  }

  function _mint(address account, uint256 amount) internal virtual {
    require(account != address(0), 'ERC20: mint to the zero address');

    _beforeTokenTransfer(address(0), account, amount);

    _totalSupply = _totalSupply.add(amount);
    _balances[account] = _balances[account].add(amount);
    emit Transfer(address(0), account, amount);
  }

  function _burn(address account, uint256 amount) internal virtual {
    require(account != address(0), 'ERC20: burn from the zero address');

    _beforeTokenTransfer(account, address(0), amount);

    _balances[account] = _balances[account].sub(amount, 'ERC20: burn amount exceeds balance');
    _totalSupply = _totalSupply.sub(amount);
    emit Transfer(account, address(0), amount);
  }

  function _approve(
    address owner,
    address spender,
    uint256 amount
  ) internal virtual {
    require(owner != address(0), 'ERC20: approve from the zero address');
    require(spender != address(0), 'ERC20: approve to the zero address');

    _allowances[owner][spender] = amount;
    emit Approval(owner, spender, amount);
  }

  function _setName(string memory newName) internal {
    _name = newName;
  }

  function _setSymbol(string memory newSymbol) internal {
    _symbol = newSymbol;
  }

  function _setDecimals(uint8 newDecimals) internal {
    _decimals = newDecimals;
  }

  function _beforeTokenTransfer(
    address from,
    address to,
    uint256 amount
  ) internal virtual {}
}

File 20 of 23 : GovernancePowerDelegationERC20.sol
// SPDX-License-Identifier: agpl-3.0
pragma solidity 0.7.5;

import {SafeMath} from '../../open-zeppelin/SafeMath.sol';
import {ERC20} from '../../open-zeppelin/ERC20.sol';
import {
  IGovernancePowerDelegationToken
} from '../../interfaces/IGovernancePowerDelegationToken.sol';

/**
 * @notice implementation of the AAVE token contract
 * @author Aave
 */
abstract contract GovernancePowerDelegationERC20 is ERC20, IGovernancePowerDelegationToken {
  using SafeMath for uint256;
  /// @notice The EIP-712 typehash for the delegation struct used by the contract
  bytes32 public constant DELEGATE_BY_TYPE_TYPEHASH = keccak256(
    'DelegateByType(address delegatee,uint256 type,uint256 nonce,uint256 expiry)'
  );

  bytes32 public constant DELEGATE_TYPEHASH = keccak256(
    'Delegate(address delegatee,uint256 nonce,uint256 expiry)'
  );

  /// @dev snapshot of a value on a specific block, used for votes
  struct Snapshot {
    uint128 blockNumber;
    uint128 value;
  }

  /**
   * @dev delegates one specific power to a delegatee
   * @param delegatee the user which delegated power has changed
   * @param delegationType the type of delegation (VOTING_POWER, PROPOSITION_POWER)
   **/
  function delegateByType(address delegatee, DelegationType delegationType) external override {
    _delegateByType(msg.sender, delegatee, delegationType);
  }

  /**
   * @dev delegates all the powers to a specific user
   * @param delegatee the user to which the power will be delegated
   **/
  function delegate(address delegatee) external override {
    _delegateByType(msg.sender, delegatee, DelegationType.VOTING_POWER);
    _delegateByType(msg.sender, delegatee, DelegationType.PROPOSITION_POWER);
  }

  /**
   * @dev returns the delegatee of an user
   * @param delegator the address of the delegator
   **/
  function getDelegateeByType(address delegator, DelegationType delegationType)
    external
    override
    view
    returns (address)
  {
    (, , mapping(address => address) storage delegates) = _getDelegationDataByType(delegationType);

    return _getDelegatee(delegator, delegates);
  }

  /**
   * @dev returns the current delegated power of a user. The current power is the
   * power delegated at the time of the last snapshot
   * @param user the user
   **/
  function getPowerCurrent(address user, DelegationType delegationType)
    external
    override
    view
    returns (uint256)
  {
    (
      mapping(address => mapping(uint256 => Snapshot)) storage snapshots,
      mapping(address => uint256) storage snapshotsCounts,

    ) = _getDelegationDataByType(delegationType);

    return _searchByBlockNumber(snapshots, snapshotsCounts, user, block.number);
  }

  /**
   * @dev returns the delegated power of a user at a certain block
   * @param user the user
   **/
  function getPowerAtBlock(
    address user,
    uint256 blockNumber,
    DelegationType delegationType
  ) external override view returns (uint256) {
    (
      mapping(address => mapping(uint256 => Snapshot)) storage snapshots,
      mapping(address => uint256) storage snapshotsCounts,

    ) = _getDelegationDataByType(delegationType);

    return _searchByBlockNumber(snapshots, snapshotsCounts, user, blockNumber);
  }

  /**
   * @dev returns the total supply at a certain block number
   * used by the voting strategy contracts to calculate the total votes needed for threshold/quorum
   * In this initial implementation with no AAVE minting, simply returns the current supply
   * A snapshots mapping will need to be added in case a mint function is added to the AAVE token in the future
   **/
  function totalSupplyAt(uint256 blockNumber) external override view returns (uint256) {
    return super.totalSupply();
  }

  /**
   * @dev delegates the specific power to a delegatee
   * @param delegatee the user which delegated power has changed
   * @param delegationType the type of delegation (VOTING_POWER, PROPOSITION_POWER)
   **/
  function _delegateByType(
    address delegator,
    address delegatee,
    DelegationType delegationType
  ) internal {
    require(delegatee != address(0), 'INVALID_DELEGATEE');

    (, , mapping(address => address) storage delegates) = _getDelegationDataByType(delegationType);

    uint256 delegatorBalance = balanceOf(delegator);

    address previousDelegatee = _getDelegatee(delegator, delegates);

    delegates[delegator] = delegatee;

    _moveDelegatesByType(previousDelegatee, delegatee, delegatorBalance, delegationType);
    emit DelegateChanged(delegator, delegatee, delegationType);
  }

  /**
   * @dev moves delegated power from one user to another
   * @param from the user from which delegated power is moved
   * @param to the user that will receive the delegated power
   * @param amount the amount of delegated power to be moved
   * @param delegationType the type of delegation (VOTING_POWER, PROPOSITION_POWER)
   **/
  function _moveDelegatesByType(
    address from,
    address to,
    uint256 amount,
    DelegationType delegationType
  ) internal {
    if (from == to) {
      return;
    }

    (
      mapping(address => mapping(uint256 => Snapshot)) storage snapshots,
      mapping(address => uint256) storage snapshotsCounts,

    ) = _getDelegationDataByType(delegationType);

    if (from != address(0)) {
      uint256 previous = 0;
      uint256 fromSnapshotsCount = snapshotsCounts[from];

      if (fromSnapshotsCount != 0) {
        previous = snapshots[from][fromSnapshotsCount - 1].value;
      } else {
        previous = balanceOf(from);
      }

      _writeSnapshot(
        snapshots,
        snapshotsCounts,
        from,
        uint128(previous),
        uint128(previous.sub(amount))
      );

      emit DelegatedPowerChanged(from, previous.sub(amount), delegationType);
    }
    if (to != address(0)) {
      uint256 previous = 0;
      uint256 toSnapshotsCount = snapshotsCounts[to];
      if (toSnapshotsCount != 0) {
        previous = snapshots[to][toSnapshotsCount - 1].value;
      } else {
        previous = balanceOf(to);
      }

      _writeSnapshot(
        snapshots,
        snapshotsCounts,
        to,
        uint128(previous),
        uint128(previous.add(amount))
      );

      emit DelegatedPowerChanged(to, previous.add(amount), delegationType);
    }
  }

  /**
   * @dev searches a snapshot by block number. Uses binary search.
   * @param snapshots the snapshots mapping
   * @param snapshotsCounts the number of snapshots
   * @param user the user for which the snapshot is being searched
   * @param blockNumber the block number being searched
   **/
  function _searchByBlockNumber(
    mapping(address => mapping(uint256 => Snapshot)) storage snapshots,
    mapping(address => uint256) storage snapshotsCounts,
    address user,
    uint256 blockNumber
  ) internal view returns (uint256) {
    require(blockNumber <= block.number, 'INVALID_BLOCK_NUMBER');

    uint256 snapshotsCount = snapshotsCounts[user];

    if (snapshotsCount == 0) {
      return balanceOf(user);
    }

    // First check most recent balance
    if (snapshots[user][snapshotsCount - 1].blockNumber <= blockNumber) {
      return snapshots[user][snapshotsCount - 1].value;
    }

    // Next check implicit zero balance
    if (snapshots[user][0].blockNumber > blockNumber) {
      return 0;
    }

    uint256 lower = 0;
    uint256 upper = snapshotsCount - 1;
    while (upper > lower) {
      uint256 center = upper - (upper - lower) / 2; // ceil, avoiding overflow
      Snapshot memory snapshot = snapshots[user][center];
      if (snapshot.blockNumber == blockNumber) {
        return snapshot.value;
      } else if (snapshot.blockNumber < blockNumber) {
        lower = center;
      } else {
        upper = center - 1;
      }
    }
    return snapshots[user][lower].value;
  }

  /**
   * @dev returns the delegation data (snapshot, snapshotsCount, list of delegates) by delegation type
   * NOTE: Ideal implementation would have mapped this in a struct by delegation type. Unfortunately,
   * the AAVE token and StakeToken already include a mapping for the snapshots, so we require contracts
   * who inherit from this to provide access to the delegation data by overriding this method.
   * @param delegationType the type of delegation
   **/
  function _getDelegationDataByType(DelegationType delegationType)
    internal
    virtual
    view
    returns (
      mapping(address => mapping(uint256 => Snapshot)) storage, //snapshots
      mapping(address => uint256) storage, //snapshots count
      mapping(address => address) storage //delegatees list
    );

  /**
   * @dev Writes a snapshot for an owner of tokens
   * @param owner The owner of the tokens
   * @param oldValue The value before the operation that is gonna be executed after the snapshot
   * @param newValue The value after the operation
   */
  function _writeSnapshot(
    mapping(address => mapping(uint256 => Snapshot)) storage snapshots,
    mapping(address => uint256) storage snapshotsCounts,
    address owner,
    uint128 oldValue,
    uint128 newValue
  ) internal {
    uint128 currentBlock = uint128(block.number);

    uint256 ownerSnapshotsCount = snapshotsCounts[owner];
    mapping(uint256 => Snapshot) storage snapshotsOwner = snapshots[owner];

    // Doing multiple operations in the same block
    if (
      ownerSnapshotsCount != 0 &&
      snapshotsOwner[ownerSnapshotsCount - 1].blockNumber == currentBlock
    ) {
      snapshotsOwner[ownerSnapshotsCount - 1].value = newValue;
    } else {
      snapshotsOwner[ownerSnapshotsCount] = Snapshot(currentBlock, newValue);
      snapshotsCounts[owner] = ownerSnapshotsCount + 1;
    }
  }

  /**
   * @dev returns the user delegatee. If a user never performed any delegation,
   * his delegated address will be 0x0. In that case we simply return the user itself
   * @param delegator the address of the user for which return the delegatee
   * @param delegates the array of delegates for a particular type of delegation
   **/
  function _getDelegatee(address delegator, mapping(address => address) storage delegates)
    internal
    view
    returns (address)
  {
    address previousDelegatee = delegates[delegator];

    if (previousDelegatee == address(0)) {
      return delegator;
    }

    return previousDelegatee;
  }
}

File 21 of 23 : Context.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.7.5;

/**
 * @dev From https://github.com/OpenZeppelin/openzeppelin-contracts
 * 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 22 of 23 : IERC20Detailed.sol
// SPDX-License-Identifier: agpl-3.0
pragma solidity 0.7.5;

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

/**
 * @dev Interface for ERC20 including metadata
 **/
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 23 of 23 : IGovernancePowerDelegationToken.sol
// SPDX-License-Identifier: agpl-3.0
pragma solidity 0.7.5;

interface IGovernancePowerDelegationToken {
  
  enum DelegationType {VOTING_POWER, PROPOSITION_POWER}

  /**
   * @dev emitted when a user delegates to another
   * @param delegator the delegator
   * @param delegatee the delegatee
   * @param delegationType the type of delegation (VOTING_POWER, PROPOSITION_POWER)
   **/
  event DelegateChanged(
    address indexed delegator,
    address indexed delegatee,
    DelegationType delegationType
  );

  /**
   * @dev emitted when an action changes the delegated power of a user
   * @param user the user which delegated power has changed
   * @param amount the amount of delegated power for the user
   * @param delegationType the type of delegation (VOTING_POWER, PROPOSITION_POWER)
   **/
  event DelegatedPowerChanged(address indexed user, uint256 amount, DelegationType delegationType);

  /**
   * @dev delegates the specific power to a delegatee
   * @param delegatee the user which delegated power has changed
   * @param delegationType the type of delegation (VOTING_POWER, PROPOSITION_POWER)
   **/
  function delegateByType(address delegatee, DelegationType delegationType) external virtual;
  /**
   * @dev delegates all the powers to a specific user
   * @param delegatee the user to which the power will be delegated
   **/
  function delegate(address delegatee) external virtual;
  /**
   * @dev returns the delegatee of an user
   * @param delegator the address of the delegator
   **/
  function getDelegateeByType(address delegator, DelegationType delegationType)
    external
    virtual
    view
    returns (address);

  /**
   * @dev returns the current delegated power of a user. The current power is the
   * power delegated at the time of the last snapshot
   * @param user the user
   **/
  function getPowerCurrent(address user, DelegationType delegationType)
    external
    virtual
    view
    returns (uint256);

  /**
   * @dev returns the delegated power of a user at a certain block
   * @param user the user
   **/
  function getPowerAtBlock(
    address user,
    uint256 blockNumber,
    DelegationType delegationType
  ) external virtual view returns (uint256);
 
  /**
  * @dev returns the total supply at a certain block number
  **/
  function totalSupplyAt(uint256 blockNumber) external virtual view returns (uint256);
}

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

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"contract IERC20","name":"stakedToken","type":"address"},{"internalType":"contract IERC20","name":"rewardToken","type":"address"},{"internalType":"uint256","name":"cooldownSeconds","type":"uint256"},{"internalType":"uint256","name":"unstakeWindow","type":"uint256"},{"internalType":"address","name":"rewardsVault","type":"address"},{"internalType":"address","name":"emissionManager","type":"address"},{"internalType":"uint128","name":"distributionDuration","type":"uint128"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"asset","type":"address"},{"indexed":false,"internalType":"uint256","name":"emission","type":"uint256"}],"name":"AssetConfigUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"asset","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"}],"name":"Cooldown","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"delegator","type":"address"},{"indexed":true,"internalType":"address","name":"delegatee","type":"address"},{"indexed":false,"internalType":"enum IGovernancePowerDelegationToken.DelegationType","name":"delegationType","type":"uint8"}],"name":"DelegateChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"enum IGovernancePowerDelegationToken.DelegationType","name":"delegationType","type":"uint8"}],"name":"DelegatedPowerChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Redeem","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"RewardsAccrued","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"RewardsClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"onBehalfOf","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Staked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"asset","type":"address"},{"indexed":false,"internalType":"uint256","name":"index","type":"uint256"}],"name":"UserIndexUpdated","type":"event"},{"inputs":[],"name":"COOLDOWN_SECONDS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DELEGATE_BY_TYPE_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DELEGATE_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DISTRIBUTION_END","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"EIP712_REVISION","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"EMISSION_MANAGER","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PERMIT_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PRECISION","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"REVISION","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"REWARDS_VAULT","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"REWARD_TOKEN","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"STAKED_TOKEN","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"UNSTAKE_WINDOW","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_aaveGovernance","outputs":[{"internalType":"contract ITransferHook","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"_nonces","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"_votingSnapshots","outputs":[{"internalType":"uint128","name":"blockNumber","type":"uint128"},{"internalType":"uint128","name":"value","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"_votingSnapshotsCounts","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"assets","outputs":[{"internalType":"uint128","name":"emissionPerSecond","type":"uint128"},{"internalType":"uint128","name":"lastUpdateTimestamp","type":"uint128"},{"internalType":"uint256","name":"index","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"claimRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint128","name":"emissionPerSecond","type":"uint128"},{"internalType":"uint256","name":"totalStaked","type":"uint256"},{"internalType":"address","name":"underlyingAsset","type":"address"}],"internalType":"struct DistributionTypes.AssetConfigInput[]","name":"assetsConfigInput","type":"tuple[]"}],"name":"configureAssets","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"cooldown","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"subtractedValue","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"delegatee","type":"address"}],"name":"delegate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"delegatee","type":"address"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint256","name":"expiry","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"delegateBySig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"delegatee","type":"address"},{"internalType":"enum IGovernancePowerDelegationToken.DelegationType","name":"delegationType","type":"uint8"}],"name":"delegateByType","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"delegatee","type":"address"},{"internalType":"enum IGovernancePowerDelegationToken.DelegationType","name":"delegationType","type":"uint8"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint256","name":"expiry","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"delegateByTypeBySig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"delegator","type":"address"},{"internalType":"enum IGovernancePowerDelegationToken.DelegationType","name":"delegationType","type":"uint8"}],"name":"getDelegateeByType","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"fromCooldownTimestamp","type":"uint256"},{"internalType":"uint256","name":"amountToReceive","type":"uint256"},{"internalType":"address","name":"toAddress","type":"address"},{"internalType":"uint256","name":"toBalance","type":"uint256"}],"name":"getNextCooldownTimestamp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"blockNumber","type":"uint256"},{"internalType":"enum IGovernancePowerDelegationToken.DelegationType","name":"delegationType","type":"uint8"}],"name":"getPowerAtBlock","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"enum IGovernancePowerDelegationToken.DelegationType","name":"delegationType","type":"uint8"}],"name":"getPowerCurrent","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"staker","type":"address"}],"name":"getTotalRewardsBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"address","name":"asset","type":"address"}],"name":"getUserAssetData","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"addedValue","type":"uint256"}],"name":"increaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"symbol","type":"string"},{"internalType":"uint8","name":"decimals","type":"uint8"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"permit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"redeem","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"onBehalfOf","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"stake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"stakerRewardsToClaim","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"stakersCooldowns","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"blockNumber","type":"uint256"}],"name":"totalSupplyAt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"}]

61016060405260006009553480156200001757600080fd5b5060405162003dce38038062003dce8339810160408190526200003a9162000299565b868686868686866040518060400160405280600b81526020016a5374616b6564204c79726160a81b8152506040518060400160405280600781526020016673746b4c59524160c81b8152506012600085856001600160801b031685858160039080519060200190620000ae929190620001ed565b508051620000c4906004906020840190620001ed565b505060058054601260ff1990911617905550620000ee428262000175602090811b6200199e17901c565b60805250606090811b6001600160601b031990811660a0528c821b811660c0528b821b811660e0526101008b90526101208a90529088901b1661014052600880546001600160a01b0319166001600160a01b0383161790556200015d82620001d7602090811b620019f817901c565b5050505050505050505050505050505050506200034f565b600082820183811015620001d0576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b9392505050565b6005805460ff191660ff92909216919091179055565b828054600181600116156101000203166002900490600052602060002090601f01602090048101928262000225576000855562000270565b82601f106200024057805160ff191683800117855562000270565b8280016001018555821562000270579182015b828111156200027057825182559160200191906001019062000253565b506200027e92915062000282565b5090565b5b808211156200027e576000815560010162000283565b600080600080600080600060e0888a031215620002b4578283fd5b8751620002c18162000336565b6020890151909750620002d48162000336565b8096505060408801519450606088015193506080880151620002f68162000336565b60a0890151909350620003098162000336565b60c08901519092506001600160801b038116811462000326578182fd5b8091505092959891949750929550565b6001600160a01b03811681146200034c57600080fd5b50565b60805160a05160601c60c05160601c60e05160601c61010051610120516101405160601c6139de620003f060003980610d945280610eec52508061087c5280610ad4528061175b52508061083852806108a45280610bfd5280611780525080610e235280610ec25250806109505280610a7752806111025250806111785280611510525080610d705280612ce45280612d205280612d4d52506139de6000f3fe608060405234801561001057600080fd5b50600436106102d65760003560e01c8063919cd40f11610182578063b2f4201d116100e9578063d505accf116100a2578063dde43cba1161007c578063dde43cba146105ce578063f11b8188146105d6578063f1cc432a146105f8578063f713d8a81461060b576102d6565b8063d505accf14610595578063dc937e1c146105a8578063dd62ed3e146105bb576102d6565b8063b2f4201d14610539578063b9844d8d1461054c578063c2ffbb911461055f578063c3863ada14610572578063c3cda5201461057a578063cbcbb5071461058d576102d6565b8063a457c2d71161013b578063a457c2d7146104dd578063a9059cbb146104f0578063aa9fbe0214610503578063aaf5eb681461050b578063adc9772e14610513578063b2a5dbfa14610526576102d6565b8063919cd40f14610497578063946776cd1461049f57806395d89b41146104a7578063981b24d0146104af57806399248ea7146104c25780639a99b4f0146104ca576102d6565b80633644e5151161024157806370a08231116101fa578063787a08a6116101d4578063787a08a6146104565780637bb73c971461045e5780637e90d7ef146104715780638dbefee214610484576102d6565b806370a082311461043357806372b49d6314610446578063781603761461044e576102d6565b80633644e515146103c957806339509351146103d157806341cbf54a146103e45780635b3cc0cf146103ec5780635c19a95c1461040d5780636f50458d14610420576102d6565b806323b872dd1161029357806323b872dd1461036957806330adf81f1461037c578063312f6b8314610384578063313ce567146103995780633373ee4c146103ae578063359c4a96146103c1576102d6565b806306fdde03146102db578063091030c3146102f9578063095ea7b3146103195780631624f6c61461033957806318160ddd1461034e5780631e9a695014610356575b600080fd5b6102e361061e565b6040516102f091906135da565b60405180910390f35b61030c6103073660046130d3565b6106b4565b6040516102f09190613503565b61032c61032736600461321d565b6106c6565b6040516102f091906134f8565b61034c610347366004613348565b6106e4565b005b61030c6107f6565b61034c61036436600461321d565b6107fc565b61032c61037736600461311f565b6109c9565b61030c610a51565b61038c610a75565b6040516102f091906134a7565b6103a1610a99565b6040516102f091906137dd565b61030c6103bc3660046130ed565b610aa2565b61030c610ad2565b61030c610af6565b61032c6103df36600461321d565b610afc565b61030c610b4a565b6103ff6103fa36600461321d565b610b6e565b6040516102f092919061379f565b61034c61041b3660046130d3565b610b9f565b61038c61042e3660046131c3565b610bba565b61030c6104413660046130d3565b610bdc565b61030c610bfb565b6102e3610c1f565b61034c610c3c565b61030c61046c3660046130d3565b610c9b565b61030c61047f3660046130d3565b610cad565b61030c6104923660046130d3565b610cbf565b61030c610d6e565b61038c610d92565b6102e3610db6565b61030c6104bd3660046133ed565b610e17565b61038c610e21565b61034c6104d836600461321d565b610e45565b61032c6104eb36600461321d565b610f63565b61032c6104fe36600461321d565b610fcb565b61030c610fdf565b6103a1611003565b61034c61052136600461321d565b611008565b61034c6105343660046132d8565b61116d565b61030c6105473660046131c3565b611320565b61030c61055a3660046130d3565b611348565b61030c61056d366004613246565b61135a565b61038c611383565b61034c610588366004613281565b611392565b61038c61150e565b61034c6105a336600461315a565b611532565b61034c6105b63660046131c3565b6116bf565b61030c6105c93660046130ed565b6116ce565b61030c6116f9565b6105e96105e43660046130d3565b6116fe565b6040516102f0939291906137b9565b61030c610606366004613405565b61172c565b61034c6106193660046131ec565b611821565b60038054604080516020601f60026000196101006001881615020190951694909404938401819004810282018101909252828152606093909290918301828280156106aa5780601f1061067f576101008083540402835291602001916106aa565b820191906000526020600020905b81548152906001019060200180831161068d57829003601f168201915b5050505050905090565b603e6020526000908152604090205481565b60006106da6106d3611a0e565b8484611a12565b5060015b92915050565b60006106ee611afe565b905060095481116107305760405162461bcd60e51b815260040180806020018281038252602e8152602001806138c2602e913960400191505060405180910390fd5b6009819055467f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f61075f61061e565b805160209182012060408051808201825260018152603160f81b90840152516107af93927fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6918691309101613590565b60408051601f1981840301815291905280516020909101206043556107d660038888612f73565b506107e360048686612f73565b506107ed836119f8565b50505050505050565b60025490565b806108225760405162461bcd60e51b8152600401610819906135ed565b60405180910390fd5b336000908152603e602052604090205461085c817f000000000000000000000000000000000000000000000000000000000000000061199e565b421161087a5760405162461bcd60e51b815260040161081990613645565b7f00000000000000000000000000000000000000000000000000000000000000006108cf6108c8837f000000000000000000000000000000000000000000000000000000000000000061199e565b4290611b03565b11156108ed5760405162461bcd60e51b81526004016108199061371d565b60006108f833610bdc565b90506000818411610909578361090b565b815b905061091933836001611b45565b506109243382611be9565b61092e8282611b03565b61094357336000908152603e60205260408120555b6109776001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168683611ce5565b846001600160a01b0316336001600160a01b03167fd12200efa34901b99367694174c3b0d32c99585fdf37c7c26892136ddd0836d9836040516109ba9190613503565b60405180910390a35050505050565b60006109d6848484611d37565b610a46846109e2611a0e565b610a418560405180606001604052806028815260200161389a602891396001600160a01b038a16600090815260016020526040812090610a20611a0e565b6001600160a01b031681526020810191909152604001600020549190611e03565b611a12565b5060015b9392505050565b7f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c981565b7f000000000000000000000000000000000000000000000000000000000000000081565b60055460ff1690565b6001600160a01b038082166000908152603c60209081526040808320938616835260029093019052205492915050565b7f000000000000000000000000000000000000000000000000000000000000000081565b60435481565b60006106da610b09611a0e565b84610a418560016000610b1a611a0e565b6001600160a01b03908116825260208083019390935260409182016000908120918c16815292529020549061199e565b7f9a9a49b990ba9bb39f8048c490a40ab25c18f55d208d5fbcf958261a9b48716d81565b60066020908152600092835260408084209091529082529020546001600160801b0380821691600160801b90041682565b610bab33826000611e9a565b610bb733826001611e9a565b50565b600080610bc683611fab565b92505050610bd48482611fe5565b949350505050565b6001600160a01b0381166000908152602081905260409020545b919050565b7f000000000000000000000000000000000000000000000000000000000000000081565b604051806040016040528060018152602001603160f81b81525081565b610c4533610bdc565b610c615760405162461bcd60e51b815260040161081990613754565b336000818152603e6020526040808220429055517ff52f50426b32362d3e6bb8cb36b7074756b224622def6352a59eac7f66ebe6e89190a2565b60076020526000908152604090205481565b603d6020526000908152604090205481565b60408051600180825281830190925260009160609190816020015b610ce2612fff565b815260200190600190039081610cda5790505090506040518060600160405280306001600160a01b03168152602001610d1a85610bdc565b8152602001610d276107f6565b81525081600081518110610d3757fe5b6020026020010181905250610a4a610d4f8483612010565b6001600160a01b0385166000908152603d60205260409020549061199e565b7f000000000000000000000000000000000000000000000000000000000000000081565b7f000000000000000000000000000000000000000000000000000000000000000081565b60048054604080516020601f60026000196101006001881615020190951694909404938401819004810282018101909252828152606093909290918301828280156106aa5780601f1061067f576101008083540402835291602001916106aa565b60006106de6107f6565b7f000000000000000000000000000000000000000000000000000000000000000081565b6000610e5b33610e5433610bdc565b6000611b45565b905060006000198314610e6e5782610e70565b815b9050610ead816040518060400160405280600e81526020016d1253959053125117d05353d5539560921b81525084611e039092919063ffffffff16565b336000908152603d6020526040902055610f127f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03167f00000000000000000000000000000000000000000000000000000000000000008684612101565b836001600160a01b0316336001600160a01b03167f9310ccfcb8de723f578a9e4282ea9f521f05ae40dc08f3068dfad528a65ee3c783604051610f559190613503565b60405180910390a350505050565b60006106da610f70611a0e565b84610a41856040518060600160405280602581526020016139846025913960016000610f9a611a0e565b6001600160a01b03908116825260208083019390935260409182016000908120918d16815292529020549190611e03565b60006106da610fd8611a0e565b8484611d37565b7f10d8d059343739efce7dad10d09f0806da52b252b3e6a7951920d2d6ec4102e581565b601281565b806110255760405162461bcd60e51b8152600401610819906135ed565b600061103083610bdc565b905060006110478430846110426107f6565b61215b565b905080156110c5577f2468f9268c60ad90e2d49edb0032c8a001e733ae888b3ab8e982edf535be1a7684826040516110809291906134df565b60405180910390a16001600160a01b0384166000908152603d60205260409020546110ab908261199e565b6001600160a01b0385166000908152603d60205260409020555b6110d2600084868561172c565b6001600160a01b0385166000908152603e60205260409020556110f5848461221a565b61112a6001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016333086612101565b836001600160a01b0316336001600160a01b03167f5dac0c1b1112564a045ba943c9d50270893e8e826c49be8e7073adc713ab7bd785604051610f559190613503565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146111b55760405162461bcd60e51b8152600401610819906136c7565b60005b8181101561131b576000603c60008585858181106111d257fe5b90506060020160400160208101906111ea91906130d3565b6001600160a01b03166001600160a01b03168152602001908152602001600020905061124f84848481811061121b57fe5b905060600201604001602081019061123391906130d3565b8286868681811061124057fe5b9050606002016020013561230a565b5083838381811061125c57fe5b61127292602060609092020190810191506133c6565b81546001600160801b0319166001600160801b039190911617815583838381811061129957fe5b90506060020160400160208101906112b191906130d3565b6001600160a01b03167f87fa03892a0556cb6b8f97e6d533a150d4d55fcbf275fff5fa003fa636bcc7fa8585858181106112e757fe5b6112fd92602060609092020190810191506133c6565b60405161130a919061378b565b60405180910390a2506001016111b8565b505050565b600080600061132e84611fab565b509150915061133f828287436123c7565b95945050505050565b60446020526000908152604090205481565b600080600061136884611fab565b5091509150611379828288886123c7565b9695505050505050565b6008546001600160a01b031681565b60007f9a9a49b990ba9bb39f8048c490a40ab25c18f55d208d5fbcf958261a9b48716d8787876040516020016113cb9493929190613540565b6040516020818303038152906040528051906020012090506000604354826040516020016113fa92919061348c565b60405160208183030381529060405280519060200120905060006001828787876040516000815260200160405260405161143794939291906135bc565b6020604051602081039080840390855afa158015611459573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b03811661148c5760405162461bcd60e51b81526004016108199061361a565b6001600160a01b038116600090815260446020526040902080546001810190915588146114cb5760405162461bcd60e51b8152600401610819906136a0565b864211156114eb5760405162461bcd60e51b815260040161081990613674565b6114f7818a6000611e9a565b611503818a6001611e9a565b505050505050505050565b7f000000000000000000000000000000000000000000000000000000000000000081565b6001600160a01b0387166115585760405162461bcd60e51b8152600401610819906136f6565b834211156115785760405162461bcd60e51b815260040161081990613674565b6001600160a01b03871660009081526044602090815260408083205460435491519093926115d2917f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9918d918d918d9189918e910161350c565b604051602081830303815290604052805190602001206040516020016115f992919061348c565b6040516020818303038152906040528051906020012090506001818686866040516000815260200160405260405161163494939291906135bc565b6020604051602081039080840390855afa158015611656573d6000803e3d6000fd5b505050602060405103516001600160a01b0316896001600160a01b0316146116905760405162461bcd60e51b81526004016108199061361a565b61169b82600161199e565b6001600160a01b038a16600090815260446020526040902055611503898989611a12565b6116ca338383611e9a565b5050565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b600181565b603c60205260009081526040902080546001909101546001600160801b0380831692600160801b9004169083565b6001600160a01b0382166000908152603e602052604081205480611754576000915050610bd4565b60006117aa7f00000000000000000000000000000000000000000000000000000000000000006117a4427f0000000000000000000000000000000000000000000000000000000000000000611b03565b90611b03565b9050818111156117bd5760009150611817565b60008782116117cc57876117ce565b425b9050828110156117e357829350505050610bd4565b6118136117f0888761199e565b61180d6117fd88876125e3565b6118078b866125e3565b9061199e565b9061263c565b9250505b5095945050505050565b60007f10d8d059343739efce7dad10d09f0806da52b252b3e6a7951920d2d6ec4102e58888600181111561185157fe5b8888604051602001611867959493929190613564565b60405160208183030381529060405280519060200120905060006043548260405160200161189692919061348c565b6040516020818303038152906040528051906020012090506000600182878787604051600081526020016040526040516118d394939291906135bc565b6020604051602081039080840390855afa1580156118f5573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b0381166119285760405162461bcd60e51b81526004016108199061361a565b6001600160a01b038116600090815260446020526040902080546001810190915588146119675760405162461bcd60e51b8152600401610819906136a0565b864211156119875760405162461bcd60e51b815260040161081990613674565b611992818b8b611e9a565b50505050505050505050565b600082820183811015610a4a576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b6005805460ff191660ff92909216919091179055565b3390565b6001600160a01b038316611a575760405162461bcd60e51b81526004018080602001828103825260248152602001806139366024913960400191505060405180910390fd5b6001600160a01b038216611a9c5760405162461bcd60e51b81526004018080602001828103825260228152602001806138316022913960400191505060405180910390fd5b6001600160a01b03808416600081815260016020908152604080832094871680845294825291829020859055815185815291517f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259281900390910190a3505050565b600190565b6000610a4a83836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f770000815250611e03565b600080611b568530866110426107f6565b6001600160a01b0386166000908152603d602052604081205491925090611b7d908361199e565b9050811561133f578315611ba7576001600160a01b0386166000908152603d602052604090208190555b7f2468f9268c60ad90e2d49edb0032c8a001e733ae888b3ab8e982edf535be1a768683604051611bd89291906134df565b60405180910390a195945050505050565b6001600160a01b038216611c2e5760405162461bcd60e51b81526004018080602001828103825260218152602001806138f06021913960400191505060405180910390fd5b611c3a8260008361267e565b611c778160405180606001604052806022815260200161380f602291396001600160a01b0385166000908152602081905260409020549190611e03565b6001600160a01b038316600090815260208190526040902055600254611c9d9082611b03565b6002556040805182815290516000916001600160a01b038516917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9181900360200190a35050565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b17905261131b90849061279b565b6000611d4284610bdc565b9050611d5084826001611b45565b50826001600160a01b0316846001600160a01b031614611df2576000611d7584610bdc565b9050611d8384826001611b45565b506001600160a01b0385166000908152603e6020526040902054611da98185878561172c565b6001600160a01b0386166000908152603e60205260409020558284148015611dd057508015155b15611def576001600160a01b0386166000908152603e60205260408120555b50505b611dfd848484612953565b50505050565b60008184841115611e925760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b83811015611e57578181015183820152602001611e3f565b50505050905090810190601f168015611e845780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b505050900390565b6001600160a01b038216611ee9576040805162461bcd60e51b8152602060048201526011602482015270494e56414c49445f44454c45474154454560781b604482015290519081900360640190fd5b6000611ef482611fab565b925050506000611f0385610bdc565b90506000611f118684611fe5565b6001600160a01b03878116600090815260208690526040902080546001600160a01b0319169188169190911790559050611f4d81868487612aae565b846001600160a01b0316866001600160a01b03167fe8d51c8e11bd570db1734c8ec775785330e77007feed45c43b608ef33ff914bd8660405180826001811115611f9357fe5b815260200191505060405180910390a3505050505050565b6000808080846001811115611fbc57fe5b1415611fd257506006915060079050603f611fde565b50604091506041905060425b9193909250565b6001600160a01b0380831660009081526020839052604081205490911680610a4a57839150506106de565b600080805b83518110156120f9576000603c600086848151811061203057fe5b602090810291909101810151516001600160a01b031682528101919091526040016000908120600181015481548851929450612098926001600160801b0380831692600160801b900416908a908890811061208757fe5b602002602001015160400151612cbc565b90506120ed6120e68785815181106120ac57fe5b602002602001015160200151838560020160008c6001600160a01b03166001600160a01b0316815260200190815260200160002054612db5565b859061199e565b93505050600101612015565b509392505050565b604080516001600160a01b0380861660248301528416604482015260648082018490528251808303909101815260849091019091526020810180516001600160e01b03166323b872dd60e01b179052611dfd90859061279b565b6001600160a01b038084166000908152603c60209081526040808320938816835260028401909152812054909190828061219688858861230a565b905080831461220e5786156121b3576121b0878285612db5565b91505b6001600160a01b03808a1660008181526002870160205260409081902084905551918a16917fbb123b5c06d5408bbea3c4fef481578175cfb432e3b482c6186f02ed9086585b90612205908590613503565b60405180910390a35b50979650505050505050565b6001600160a01b038216612275576040805162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f206164647265737300604482015290519081900360640190fd5b6122816000838361267e565b60025461228e908261199e565b6002556001600160a01b0382166000908152602081905260409020546122b4908261199e565b6001600160a01b0383166000818152602081815260408083209490945583518581529351929391927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a35050565b6001820154825460009190600160801b90046001600160801b03164281141561233557509050610a4a565b84546000906123509084906001600160801b03168488612cbc565b90508281146123a457808660010181905550866001600160a01b03167f5777ca300dfe5bead41006fbce4389794dbc0ed8d6cccebfaf94630aa04184bc8260405161239b9190613503565b60405180910390a25b85546001600160801b03428116600160801b029116178655925050509392505050565b600043821115612415576040805162461bcd60e51b815260206004820152601460248201527324a72b20a624a22fa12627a1a5afa72aa6a122a960611b604482015290519081900360640190fd5b6001600160a01b038316600090815260208590526040902054806124445761243c84610bdc565b915050610bd4565b6001600160a01b038416600090815260208781526040808320600019850184529091529020546001600160801b031683106124b7576001600160a01b038416600090815260208781526040808320600019909401835292905220546001600160801b03600160801b909104169050610bd4565b6001600160a01b0384166000908152602087815260408083208380529091529020546001600160801b03168310156124f3576000915050610bd4565b600060001982015b818111156125a5576002828203048103612513613029565b506001600160a01b038716600090815260208a815260408083208484528252918290208251808401909352546001600160801b03808216808552600160801b909204169183019190915287141561257d57602001516001600160801b03169450610bd49350505050565b80516001600160801b03168711156125975781935061259e565b6001820392505b50506124fb565b506001600160a01b03851660009081526020888152604080832093835292905220546001600160801b03600160801b90910416915050949350505050565b6000826125f2575060006106de565b828202828482816125ff57fe5b0414610a4a5760405162461bcd60e51b81526004018080602001828103825260218152602001806138796021913960400191505060405180910390fd5b6000610a4a83836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f000000000000815250612dd7565b6001600160a01b038084166000908152603f60205260408082205485841683529120549082169116816126af578491505b6001600160a01b0381166126c05750825b6126cd8282856000612aae565b6001600160a01b038086166000908152604260205260408082205487841683529120549082169116816126fe578691505b6001600160a01b03811661270f5750845b61271c8282876001612aae565b6008546001600160a01b0316801561279157604051634a39314960e01b81526001600160a01b03821690634a3931499061275e908b908b908b906004016134bb565b600060405180830381600087803b15801561277857600080fd5b505af115801561278c573d6000803e3d6000fd5b505050505b5050505050505050565b6127ad826001600160a01b0316612e3c565b6127fe576040805162461bcd60e51b815260206004820152601f60248201527f5361666545524332303a2063616c6c20746f206e6f6e2d636f6e747261637400604482015290519081900360640190fd5b60006060836001600160a01b0316836040518082805190602001908083835b6020831061283c5780518252601f19909201916020918201910161281d565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d806000811461289e576040519150601f19603f3d011682016040523d82523d6000602084013e6128a3565b606091505b5091509150816128fa576040805162461bcd60e51b815260206004820181905260248201527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564604482015290519081900360640190fd5b805115611dfd5780806020019051602081101561291657600080fd5b5051611dfd5760405162461bcd60e51b815260040180806020018281038252602a81526020018061395a602a913960400191505060405180910390fd5b6001600160a01b0383166129985760405162461bcd60e51b81526004018080602001828103825260258152602001806139116025913960400191505060405180910390fd5b6001600160a01b0382166129dd5760405162461bcd60e51b81526004018080602001828103825260238152602001806137ec6023913960400191505060405180910390fd5b6129e883838361267e565b612a2581604051806060016040528060268152602001613853602691396001600160a01b0386166000908152602081905260409020549190611e03565b6001600160a01b038085166000908152602081905260408082209390935590841681522054612a54908261199e565b6001600160a01b038084166000818152602081815260409182902094909455805185815290519193928716927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef92918290030190a3505050565b826001600160a01b0316846001600160a01b03161415612acd57611dfd565b600080612ad983611fab565b5090925090506001600160a01b03861615612bcc576001600160a01b0386166000908152602082905260408120548015612b4a576001600160a01b03881660009081526020858152604080832060001985018452909152902054600160801b90046001600160801b03169150612b56565b612b5388610bdc565b91505b612b6c84848a85612b67818c611b03565b612e75565b6001600160a01b0388167fa0a19463ee116110c9b282012d9b65cc5522dc38a9520340cbaf3142e550127f612ba18489611b03565b8760405180838152602001826001811115612bb857fe5b81526020019250505060405180910390a250505b6001600160a01b03851615612cb4576001600160a01b0385166000908152602082905260408120548015612c37576001600160a01b03871660009081526020858152604080832060001985018452909152902054600160801b90046001600160801b03169150612c43565b612c4087610bdc565b91505b612c5484848985612b67818c61199e565b6001600160a01b0387167fa0a19463ee116110c9b282012d9b65cc5522dc38a9520340cbaf3142e550127f612c89848961199e565b8760405180838152602001826001811115612ca057fe5b81526020019250505060405180910390a250505b505050505050565b6000831580612cc9575081155b80612cdc575042836001600160801b0316145b80612d1057507f0000000000000000000000000000000000000000000000000000000000000000836001600160801b031610155b15612d1c575083610bd4565b60007f00000000000000000000000000000000000000000000000000000000000000004211612d4b5742612d6d565b7f00000000000000000000000000000000000000000000000000000000000000005b90506000612d84826001600160801b038716611b03565b9050612daa876118078661180d670de0b6b3a7640000612da48c886125e3565b906125e3565b979650505050505050565b6000610bd4670de0b6b3a764000061180d612dd08686611b03565b87906125e3565b60008183612e265760405162461bcd60e51b8152602060048201818152835160248401528351909283926044909101919085019080838360008315611e57578181015183820152602001611e3f565b506000838581612e3257fe5b0495945050505050565b6000813f7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470818114801590610bd4575050151592915050565b6001600160a01b03831660009081526020858152604080832054918890529091204391908115801590612ec6575060001982016000908152602082905260409020546001600160801b038481169116145b15612efb576000198201600090815260208290526040902080546001600160801b03808716600160801b029116179055612791565b6040805180820182526001600160801b038086168252868116602080840191825260008781528682528581209451855493518516600160801b029085166001600160801b031990941693909317909316919091179092556001600160a01b038916815290899052206001830190555050505050505050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282612fa95760008555612fef565b82601f10612fc25782800160ff19823516178555612fef565b82800160010185558215612fef579182015b82811115612fef578235825591602001919060010190612fd4565b50612ffb929150613040565b5090565b604051806060016040528060006001600160a01b0316815260200160008152602001600081525090565b604080518082019091526000808252602082015290565b5b80821115612ffb5760008155600101613041565b80356001600160a01b0381168114610bf657600080fd5b803560028110610bf657600080fd5b60008083601f84011261308c578182fd5b50813567ffffffffffffffff8111156130a3578182fd5b6020830191508360208285010111156130bb57600080fd5b9250929050565b803560ff81168114610bf657600080fd5b6000602082840312156130e4578081fd5b610a4a82613055565b600080604083850312156130ff578081fd5b61310883613055565b915061311660208401613055565b90509250929050565b600080600060608486031215613133578081fd5b61313c84613055565b925061314a60208501613055565b9150604084013590509250925092565b600080600080600080600060e0888a031215613174578283fd5b61317d88613055565b965061318b60208901613055565b955060408801359450606088013593506131a7608089016130c2565b925060a0880135915060c0880135905092959891949750929550565b600080604083850312156131d5578182fd5b6131de83613055565b91506131166020840161306c565b600080600080600080600060e0888a031215613206578283fd5b61320f88613055565b965061318b6020890161306c565b6000806040838503121561322f578182fd5b61323883613055565b946020939093013593505050565b60008060006060848603121561325a578283fd5b61326384613055565b9250602084013591506132786040850161306c565b90509250925092565b60008060008060008060c08789031215613299578182fd5b6132a287613055565b955060208701359450604087013593506132be606088016130c2565b92506080870135915060a087013590509295509295509295565b600080602083850312156132ea578081fd5b823567ffffffffffffffff80821115613301578283fd5b818501915085601f830112613314578283fd5b813581811115613322578384fd5b866020606083028501011115613336578384fd5b60209290920196919550909350505050565b60008060008060006060868803121561335f578283fd5b853567ffffffffffffffff80821115613376578485fd5b61338289838a0161307b565b9097509550602088013591508082111561339a578485fd5b506133a78882890161307b565b90945092506133ba9050604087016130c2565b90509295509295909350565b6000602082840312156133d7578081fd5b81356001600160801b0381168114610a4a578182fd5b6000602082840312156133fe578081fd5b5035919050565b6000806000806080858703121561341a578182fd5b843593506020850135925061343160408601613055565b9396929550929360600135925050565b60008151808452815b818110156134665760208185018101518683018201520161344a565b818111156134775782602083870101525b50601f01601f19169290920160200192915050565b61190160f01b81526002810192909252602282015260420190565b6001600160a01b0391909116815260200190565b6001600160a01b039384168152919092166020820152604081019190915260600190565b6001600160a01b03929092168252602082015260400190565b901515815260200190565b90815260200190565b9586526001600160a01b0394851660208701529290931660408501526060840152608083019190915260a082015260c00190565b9384526001600160a01b039290921660208401526040830152606082015260800190565b9485526001600160a01b0393909316602085015260408401919091526060830152608082015260a00190565b9485526020850193909352604084019190915260608301526001600160a01b0316608082015260a00190565b93845260ff9290921660208401526040830152606082015260800190565b600060208252610a4a6020830184613441565b6020808252601390820152721253959053125117d6915493d7d05353d55395606a1b604082015260600190565b602080825260119082015270494e56414c49445f5349474e415455524560781b604082015260600190565b60208082526015908201527424a729aaa32324a1a4a2a72a2fa1a7a7a62227aba760591b604082015260600190565b60208082526012908201527124a72b20a624a22fa2ac2824a920aa24a7a760711b604082015260600190565b6020808252600d908201526c494e56414c49445f4e4f4e434560981b604082015260600190565b60208082526015908201527427a7262cafa2a6a4a9a9a4a7a72fa6a0a720a3a2a960591b604082015260600190565b6020808252600d908201526c24a72b20a624a22fa7aba722a960991b604082015260600190565b60208082526017908201527f554e5354414b455f57494e444f575f46494e4953484544000000000000000000604082015260600190565b6020808252601b908201527f494e56414c49445f42414c414e43455f4f4e5f434f4f4c444f574e0000000000604082015260600190565b6001600160801b0391909116815260200190565b6001600160801b0392831681529116602082015260400190565b6001600160801b039384168152919092166020820152604081019190915260600190565b60ff9190911681526020019056fe45524332303a207472616e7366657220746f20746865207a65726f206164647265737345524332303a206275726e20616d6f756e7420657863656564732062616c616e636545524332303a20617070726f766520746f20746865207a65726f206164647265737345524332303a207472616e7366657220616d6f756e7420657863656564732062616c616e6365536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f7745524332303a207472616e7366657220616d6f756e74206578636565647320616c6c6f77616e6365436f6e747261637420696e7374616e63652068617320616c7265616479206265656e20696e697469616c697a656445524332303a206275726e2066726f6d20746865207a65726f206164647265737345524332303a207472616e736665722066726f6d20746865207a65726f206164647265737345524332303a20617070726f76652066726f6d20746865207a65726f20616464726573735361666545524332303a204552433230206f7065726174696f6e20646964206e6f74207375636365656445524332303a2064656372656173656420616c6c6f77616e63652062656c6f77207a65726fa26469706673582212200c4f0888820d9e117b76e7a1a597eb3afa35dc8ba043094cd2cad6dfa1967c3f64736f6c6343000705003300000000000000000000000001ba67aac7f75f647d94220cc98fb30fcc5105bf00000000000000000000000001ba67aac7f75f647d94220cc98fb30fcc5105bf00000000000000000000000000000000000000000000000000000000000d2f00000000000000000000000000000000000000000000000000000000000002a3000000000000000000000000008ef8eeeb39b21ecdde451e6c539017df24d14a190000000000000000000000008ef8eeeb39b21ecdde451e6c539017df24d14a1900000000000000000000000000000000000000000000000000000000bbf81e00

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106102d65760003560e01c8063919cd40f11610182578063b2f4201d116100e9578063d505accf116100a2578063dde43cba1161007c578063dde43cba146105ce578063f11b8188146105d6578063f1cc432a146105f8578063f713d8a81461060b576102d6565b8063d505accf14610595578063dc937e1c146105a8578063dd62ed3e146105bb576102d6565b8063b2f4201d14610539578063b9844d8d1461054c578063c2ffbb911461055f578063c3863ada14610572578063c3cda5201461057a578063cbcbb5071461058d576102d6565b8063a457c2d71161013b578063a457c2d7146104dd578063a9059cbb146104f0578063aa9fbe0214610503578063aaf5eb681461050b578063adc9772e14610513578063b2a5dbfa14610526576102d6565b8063919cd40f14610497578063946776cd1461049f57806395d89b41146104a7578063981b24d0146104af57806399248ea7146104c25780639a99b4f0146104ca576102d6565b80633644e5151161024157806370a08231116101fa578063787a08a6116101d4578063787a08a6146104565780637bb73c971461045e5780637e90d7ef146104715780638dbefee214610484576102d6565b806370a082311461043357806372b49d6314610446578063781603761461044e576102d6565b80633644e515146103c957806339509351146103d157806341cbf54a146103e45780635b3cc0cf146103ec5780635c19a95c1461040d5780636f50458d14610420576102d6565b806323b872dd1161029357806323b872dd1461036957806330adf81f1461037c578063312f6b8314610384578063313ce567146103995780633373ee4c146103ae578063359c4a96146103c1576102d6565b806306fdde03146102db578063091030c3146102f9578063095ea7b3146103195780631624f6c61461033957806318160ddd1461034e5780631e9a695014610356575b600080fd5b6102e361061e565b6040516102f091906135da565b60405180910390f35b61030c6103073660046130d3565b6106b4565b6040516102f09190613503565b61032c61032736600461321d565b6106c6565b6040516102f091906134f8565b61034c610347366004613348565b6106e4565b005b61030c6107f6565b61034c61036436600461321d565b6107fc565b61032c61037736600461311f565b6109c9565b61030c610a51565b61038c610a75565b6040516102f091906134a7565b6103a1610a99565b6040516102f091906137dd565b61030c6103bc3660046130ed565b610aa2565b61030c610ad2565b61030c610af6565b61032c6103df36600461321d565b610afc565b61030c610b4a565b6103ff6103fa36600461321d565b610b6e565b6040516102f092919061379f565b61034c61041b3660046130d3565b610b9f565b61038c61042e3660046131c3565b610bba565b61030c6104413660046130d3565b610bdc565b61030c610bfb565b6102e3610c1f565b61034c610c3c565b61030c61046c3660046130d3565b610c9b565b61030c61047f3660046130d3565b610cad565b61030c6104923660046130d3565b610cbf565b61030c610d6e565b61038c610d92565b6102e3610db6565b61030c6104bd3660046133ed565b610e17565b61038c610e21565b61034c6104d836600461321d565b610e45565b61032c6104eb36600461321d565b610f63565b61032c6104fe36600461321d565b610fcb565b61030c610fdf565b6103a1611003565b61034c61052136600461321d565b611008565b61034c6105343660046132d8565b61116d565b61030c6105473660046131c3565b611320565b61030c61055a3660046130d3565b611348565b61030c61056d366004613246565b61135a565b61038c611383565b61034c610588366004613281565b611392565b61038c61150e565b61034c6105a336600461315a565b611532565b61034c6105b63660046131c3565b6116bf565b61030c6105c93660046130ed565b6116ce565b61030c6116f9565b6105e96105e43660046130d3565b6116fe565b6040516102f0939291906137b9565b61030c610606366004613405565b61172c565b61034c6106193660046131ec565b611821565b60038054604080516020601f60026000196101006001881615020190951694909404938401819004810282018101909252828152606093909290918301828280156106aa5780601f1061067f576101008083540402835291602001916106aa565b820191906000526020600020905b81548152906001019060200180831161068d57829003601f168201915b5050505050905090565b603e6020526000908152604090205481565b60006106da6106d3611a0e565b8484611a12565b5060015b92915050565b60006106ee611afe565b905060095481116107305760405162461bcd60e51b815260040180806020018281038252602e8152602001806138c2602e913960400191505060405180910390fd5b6009819055467f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f61075f61061e565b805160209182012060408051808201825260018152603160f81b90840152516107af93927fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6918691309101613590565b60408051601f1981840301815291905280516020909101206043556107d660038888612f73565b506107e360048686612f73565b506107ed836119f8565b50505050505050565b60025490565b806108225760405162461bcd60e51b8152600401610819906135ed565b60405180910390fd5b336000908152603e602052604090205461085c817f00000000000000000000000000000000000000000000000000000000000d2f0061199e565b421161087a5760405162461bcd60e51b815260040161081990613645565b7f000000000000000000000000000000000000000000000000000000000002a3006108cf6108c8837f00000000000000000000000000000000000000000000000000000000000d2f0061199e565b4290611b03565b11156108ed5760405162461bcd60e51b81526004016108199061371d565b60006108f833610bdc565b90506000818411610909578361090b565b815b905061091933836001611b45565b506109243382611be9565b61092e8282611b03565b61094357336000908152603e60205260408120555b6109776001600160a01b037f00000000000000000000000001ba67aac7f75f647d94220cc98fb30fcc5105bf168683611ce5565b846001600160a01b0316336001600160a01b03167fd12200efa34901b99367694174c3b0d32c99585fdf37c7c26892136ddd0836d9836040516109ba9190613503565b60405180910390a35050505050565b60006109d6848484611d37565b610a46846109e2611a0e565b610a418560405180606001604052806028815260200161389a602891396001600160a01b038a16600090815260016020526040812090610a20611a0e565b6001600160a01b031681526020810191909152604001600020549190611e03565b611a12565b5060015b9392505050565b7f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c981565b7f00000000000000000000000001ba67aac7f75f647d94220cc98fb30fcc5105bf81565b60055460ff1690565b6001600160a01b038082166000908152603c60209081526040808320938616835260029093019052205492915050565b7f000000000000000000000000000000000000000000000000000000000002a30081565b60435481565b60006106da610b09611a0e565b84610a418560016000610b1a611a0e565b6001600160a01b03908116825260208083019390935260409182016000908120918c16815292529020549061199e565b7f9a9a49b990ba9bb39f8048c490a40ab25c18f55d208d5fbcf958261a9b48716d81565b60066020908152600092835260408084209091529082529020546001600160801b0380821691600160801b90041682565b610bab33826000611e9a565b610bb733826001611e9a565b50565b600080610bc683611fab565b92505050610bd48482611fe5565b949350505050565b6001600160a01b0381166000908152602081905260409020545b919050565b7f00000000000000000000000000000000000000000000000000000000000d2f0081565b604051806040016040528060018152602001603160f81b81525081565b610c4533610bdc565b610c615760405162461bcd60e51b815260040161081990613754565b336000818152603e6020526040808220429055517ff52f50426b32362d3e6bb8cb36b7074756b224622def6352a59eac7f66ebe6e89190a2565b60076020526000908152604090205481565b603d6020526000908152604090205481565b60408051600180825281830190925260009160609190816020015b610ce2612fff565b815260200190600190039081610cda5790505090506040518060600160405280306001600160a01b03168152602001610d1a85610bdc565b8152602001610d276107f6565b81525081600081518110610d3757fe5b6020026020010181905250610a4a610d4f8483612010565b6001600160a01b0385166000908152603d60205260409020549061199e565b7f000000000000000000000000000000000000000000000000000000011dc6e98e81565b7f0000000000000000000000008ef8eeeb39b21ecdde451e6c539017df24d14a1981565b60048054604080516020601f60026000196101006001881615020190951694909404938401819004810282018101909252828152606093909290918301828280156106aa5780601f1061067f576101008083540402835291602001916106aa565b60006106de6107f6565b7f00000000000000000000000001ba67aac7f75f647d94220cc98fb30fcc5105bf81565b6000610e5b33610e5433610bdc565b6000611b45565b905060006000198314610e6e5782610e70565b815b9050610ead816040518060400160405280600e81526020016d1253959053125117d05353d5539560921b81525084611e039092919063ffffffff16565b336000908152603d6020526040902055610f127f00000000000000000000000001ba67aac7f75f647d94220cc98fb30fcc5105bf6001600160a01b03167f0000000000000000000000008ef8eeeb39b21ecdde451e6c539017df24d14a198684612101565b836001600160a01b0316336001600160a01b03167f9310ccfcb8de723f578a9e4282ea9f521f05ae40dc08f3068dfad528a65ee3c783604051610f559190613503565b60405180910390a350505050565b60006106da610f70611a0e565b84610a41856040518060600160405280602581526020016139846025913960016000610f9a611a0e565b6001600160a01b03908116825260208083019390935260409182016000908120918d16815292529020549190611e03565b60006106da610fd8611a0e565b8484611d37565b7f10d8d059343739efce7dad10d09f0806da52b252b3e6a7951920d2d6ec4102e581565b601281565b806110255760405162461bcd60e51b8152600401610819906135ed565b600061103083610bdc565b905060006110478430846110426107f6565b61215b565b905080156110c5577f2468f9268c60ad90e2d49edb0032c8a001e733ae888b3ab8e982edf535be1a7684826040516110809291906134df565b60405180910390a16001600160a01b0384166000908152603d60205260409020546110ab908261199e565b6001600160a01b0385166000908152603d60205260409020555b6110d2600084868561172c565b6001600160a01b0385166000908152603e60205260409020556110f5848461221a565b61112a6001600160a01b037f00000000000000000000000001ba67aac7f75f647d94220cc98fb30fcc5105bf16333086612101565b836001600160a01b0316336001600160a01b03167f5dac0c1b1112564a045ba943c9d50270893e8e826c49be8e7073adc713ab7bd785604051610f559190613503565b336001600160a01b037f0000000000000000000000008ef8eeeb39b21ecdde451e6c539017df24d14a1916146111b55760405162461bcd60e51b8152600401610819906136c7565b60005b8181101561131b576000603c60008585858181106111d257fe5b90506060020160400160208101906111ea91906130d3565b6001600160a01b03166001600160a01b03168152602001908152602001600020905061124f84848481811061121b57fe5b905060600201604001602081019061123391906130d3565b8286868681811061124057fe5b9050606002016020013561230a565b5083838381811061125c57fe5b61127292602060609092020190810191506133c6565b81546001600160801b0319166001600160801b039190911617815583838381811061129957fe5b90506060020160400160208101906112b191906130d3565b6001600160a01b03167f87fa03892a0556cb6b8f97e6d533a150d4d55fcbf275fff5fa003fa636bcc7fa8585858181106112e757fe5b6112fd92602060609092020190810191506133c6565b60405161130a919061378b565b60405180910390a2506001016111b8565b505050565b600080600061132e84611fab565b509150915061133f828287436123c7565b95945050505050565b60446020526000908152604090205481565b600080600061136884611fab565b5091509150611379828288886123c7565b9695505050505050565b6008546001600160a01b031681565b60007f9a9a49b990ba9bb39f8048c490a40ab25c18f55d208d5fbcf958261a9b48716d8787876040516020016113cb9493929190613540565b6040516020818303038152906040528051906020012090506000604354826040516020016113fa92919061348c565b60405160208183030381529060405280519060200120905060006001828787876040516000815260200160405260405161143794939291906135bc565b6020604051602081039080840390855afa158015611459573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b03811661148c5760405162461bcd60e51b81526004016108199061361a565b6001600160a01b038116600090815260446020526040902080546001810190915588146114cb5760405162461bcd60e51b8152600401610819906136a0565b864211156114eb5760405162461bcd60e51b815260040161081990613674565b6114f7818a6000611e9a565b611503818a6001611e9a565b505050505050505050565b7f0000000000000000000000008ef8eeeb39b21ecdde451e6c539017df24d14a1981565b6001600160a01b0387166115585760405162461bcd60e51b8152600401610819906136f6565b834211156115785760405162461bcd60e51b815260040161081990613674565b6001600160a01b03871660009081526044602090815260408083205460435491519093926115d2917f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9918d918d918d9189918e910161350c565b604051602081830303815290604052805190602001206040516020016115f992919061348c565b6040516020818303038152906040528051906020012090506001818686866040516000815260200160405260405161163494939291906135bc565b6020604051602081039080840390855afa158015611656573d6000803e3d6000fd5b505050602060405103516001600160a01b0316896001600160a01b0316146116905760405162461bcd60e51b81526004016108199061361a565b61169b82600161199e565b6001600160a01b038a16600090815260446020526040902055611503898989611a12565b6116ca338383611e9a565b5050565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b600181565b603c60205260009081526040902080546001909101546001600160801b0380831692600160801b9004169083565b6001600160a01b0382166000908152603e602052604081205480611754576000915050610bd4565b60006117aa7f000000000000000000000000000000000000000000000000000000000002a3006117a4427f00000000000000000000000000000000000000000000000000000000000d2f00611b03565b90611b03565b9050818111156117bd5760009150611817565b60008782116117cc57876117ce565b425b9050828110156117e357829350505050610bd4565b6118136117f0888761199e565b61180d6117fd88876125e3565b6118078b866125e3565b9061199e565b9061263c565b9250505b5095945050505050565b60007f10d8d059343739efce7dad10d09f0806da52b252b3e6a7951920d2d6ec4102e58888600181111561185157fe5b8888604051602001611867959493929190613564565b60405160208183030381529060405280519060200120905060006043548260405160200161189692919061348c565b6040516020818303038152906040528051906020012090506000600182878787604051600081526020016040526040516118d394939291906135bc565b6020604051602081039080840390855afa1580156118f5573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b0381166119285760405162461bcd60e51b81526004016108199061361a565b6001600160a01b038116600090815260446020526040902080546001810190915588146119675760405162461bcd60e51b8152600401610819906136a0565b864211156119875760405162461bcd60e51b815260040161081990613674565b611992818b8b611e9a565b50505050505050505050565b600082820183811015610a4a576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b6005805460ff191660ff92909216919091179055565b3390565b6001600160a01b038316611a575760405162461bcd60e51b81526004018080602001828103825260248152602001806139366024913960400191505060405180910390fd5b6001600160a01b038216611a9c5760405162461bcd60e51b81526004018080602001828103825260228152602001806138316022913960400191505060405180910390fd5b6001600160a01b03808416600081815260016020908152604080832094871680845294825291829020859055815185815291517f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259281900390910190a3505050565b600190565b6000610a4a83836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f770000815250611e03565b600080611b568530866110426107f6565b6001600160a01b0386166000908152603d602052604081205491925090611b7d908361199e565b9050811561133f578315611ba7576001600160a01b0386166000908152603d602052604090208190555b7f2468f9268c60ad90e2d49edb0032c8a001e733ae888b3ab8e982edf535be1a768683604051611bd89291906134df565b60405180910390a195945050505050565b6001600160a01b038216611c2e5760405162461bcd60e51b81526004018080602001828103825260218152602001806138f06021913960400191505060405180910390fd5b611c3a8260008361267e565b611c778160405180606001604052806022815260200161380f602291396001600160a01b0385166000908152602081905260409020549190611e03565b6001600160a01b038316600090815260208190526040902055600254611c9d9082611b03565b6002556040805182815290516000916001600160a01b038516917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9181900360200190a35050565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b17905261131b90849061279b565b6000611d4284610bdc565b9050611d5084826001611b45565b50826001600160a01b0316846001600160a01b031614611df2576000611d7584610bdc565b9050611d8384826001611b45565b506001600160a01b0385166000908152603e6020526040902054611da98185878561172c565b6001600160a01b0386166000908152603e60205260409020558284148015611dd057508015155b15611def576001600160a01b0386166000908152603e60205260408120555b50505b611dfd848484612953565b50505050565b60008184841115611e925760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b83811015611e57578181015183820152602001611e3f565b50505050905090810190601f168015611e845780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b505050900390565b6001600160a01b038216611ee9576040805162461bcd60e51b8152602060048201526011602482015270494e56414c49445f44454c45474154454560781b604482015290519081900360640190fd5b6000611ef482611fab565b925050506000611f0385610bdc565b90506000611f118684611fe5565b6001600160a01b03878116600090815260208690526040902080546001600160a01b0319169188169190911790559050611f4d81868487612aae565b846001600160a01b0316866001600160a01b03167fe8d51c8e11bd570db1734c8ec775785330e77007feed45c43b608ef33ff914bd8660405180826001811115611f9357fe5b815260200191505060405180910390a3505050505050565b6000808080846001811115611fbc57fe5b1415611fd257506006915060079050603f611fde565b50604091506041905060425b9193909250565b6001600160a01b0380831660009081526020839052604081205490911680610a4a57839150506106de565b600080805b83518110156120f9576000603c600086848151811061203057fe5b602090810291909101810151516001600160a01b031682528101919091526040016000908120600181015481548851929450612098926001600160801b0380831692600160801b900416908a908890811061208757fe5b602002602001015160400151612cbc565b90506120ed6120e68785815181106120ac57fe5b602002602001015160200151838560020160008c6001600160a01b03166001600160a01b0316815260200190815260200160002054612db5565b859061199e565b93505050600101612015565b509392505050565b604080516001600160a01b0380861660248301528416604482015260648082018490528251808303909101815260849091019091526020810180516001600160e01b03166323b872dd60e01b179052611dfd90859061279b565b6001600160a01b038084166000908152603c60209081526040808320938816835260028401909152812054909190828061219688858861230a565b905080831461220e5786156121b3576121b0878285612db5565b91505b6001600160a01b03808a1660008181526002870160205260409081902084905551918a16917fbb123b5c06d5408bbea3c4fef481578175cfb432e3b482c6186f02ed9086585b90612205908590613503565b60405180910390a35b50979650505050505050565b6001600160a01b038216612275576040805162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f206164647265737300604482015290519081900360640190fd5b6122816000838361267e565b60025461228e908261199e565b6002556001600160a01b0382166000908152602081905260409020546122b4908261199e565b6001600160a01b0383166000818152602081815260408083209490945583518581529351929391927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a35050565b6001820154825460009190600160801b90046001600160801b03164281141561233557509050610a4a565b84546000906123509084906001600160801b03168488612cbc565b90508281146123a457808660010181905550866001600160a01b03167f5777ca300dfe5bead41006fbce4389794dbc0ed8d6cccebfaf94630aa04184bc8260405161239b9190613503565b60405180910390a25b85546001600160801b03428116600160801b029116178655925050509392505050565b600043821115612415576040805162461bcd60e51b815260206004820152601460248201527324a72b20a624a22fa12627a1a5afa72aa6a122a960611b604482015290519081900360640190fd5b6001600160a01b038316600090815260208590526040902054806124445761243c84610bdc565b915050610bd4565b6001600160a01b038416600090815260208781526040808320600019850184529091529020546001600160801b031683106124b7576001600160a01b038416600090815260208781526040808320600019909401835292905220546001600160801b03600160801b909104169050610bd4565b6001600160a01b0384166000908152602087815260408083208380529091529020546001600160801b03168310156124f3576000915050610bd4565b600060001982015b818111156125a5576002828203048103612513613029565b506001600160a01b038716600090815260208a815260408083208484528252918290208251808401909352546001600160801b03808216808552600160801b909204169183019190915287141561257d57602001516001600160801b03169450610bd49350505050565b80516001600160801b03168711156125975781935061259e565b6001820392505b50506124fb565b506001600160a01b03851660009081526020888152604080832093835292905220546001600160801b03600160801b90910416915050949350505050565b6000826125f2575060006106de565b828202828482816125ff57fe5b0414610a4a5760405162461bcd60e51b81526004018080602001828103825260218152602001806138796021913960400191505060405180910390fd5b6000610a4a83836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f000000000000815250612dd7565b6001600160a01b038084166000908152603f60205260408082205485841683529120549082169116816126af578491505b6001600160a01b0381166126c05750825b6126cd8282856000612aae565b6001600160a01b038086166000908152604260205260408082205487841683529120549082169116816126fe578691505b6001600160a01b03811661270f5750845b61271c8282876001612aae565b6008546001600160a01b0316801561279157604051634a39314960e01b81526001600160a01b03821690634a3931499061275e908b908b908b906004016134bb565b600060405180830381600087803b15801561277857600080fd5b505af115801561278c573d6000803e3d6000fd5b505050505b5050505050505050565b6127ad826001600160a01b0316612e3c565b6127fe576040805162461bcd60e51b815260206004820152601f60248201527f5361666545524332303a2063616c6c20746f206e6f6e2d636f6e747261637400604482015290519081900360640190fd5b60006060836001600160a01b0316836040518082805190602001908083835b6020831061283c5780518252601f19909201916020918201910161281d565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d806000811461289e576040519150601f19603f3d011682016040523d82523d6000602084013e6128a3565b606091505b5091509150816128fa576040805162461bcd60e51b815260206004820181905260248201527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564604482015290519081900360640190fd5b805115611dfd5780806020019051602081101561291657600080fd5b5051611dfd5760405162461bcd60e51b815260040180806020018281038252602a81526020018061395a602a913960400191505060405180910390fd5b6001600160a01b0383166129985760405162461bcd60e51b81526004018080602001828103825260258152602001806139116025913960400191505060405180910390fd5b6001600160a01b0382166129dd5760405162461bcd60e51b81526004018080602001828103825260238152602001806137ec6023913960400191505060405180910390fd5b6129e883838361267e565b612a2581604051806060016040528060268152602001613853602691396001600160a01b0386166000908152602081905260409020549190611e03565b6001600160a01b038085166000908152602081905260408082209390935590841681522054612a54908261199e565b6001600160a01b038084166000818152602081815260409182902094909455805185815290519193928716927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef92918290030190a3505050565b826001600160a01b0316846001600160a01b03161415612acd57611dfd565b600080612ad983611fab565b5090925090506001600160a01b03861615612bcc576001600160a01b0386166000908152602082905260408120548015612b4a576001600160a01b03881660009081526020858152604080832060001985018452909152902054600160801b90046001600160801b03169150612b56565b612b5388610bdc565b91505b612b6c84848a85612b67818c611b03565b612e75565b6001600160a01b0388167fa0a19463ee116110c9b282012d9b65cc5522dc38a9520340cbaf3142e550127f612ba18489611b03565b8760405180838152602001826001811115612bb857fe5b81526020019250505060405180910390a250505b6001600160a01b03851615612cb4576001600160a01b0385166000908152602082905260408120548015612c37576001600160a01b03871660009081526020858152604080832060001985018452909152902054600160801b90046001600160801b03169150612c43565b612c4087610bdc565b91505b612c5484848985612b67818c61199e565b6001600160a01b0387167fa0a19463ee116110c9b282012d9b65cc5522dc38a9520340cbaf3142e550127f612c89848961199e565b8760405180838152602001826001811115612ca057fe5b81526020019250505060405180910390a250505b505050505050565b6000831580612cc9575081155b80612cdc575042836001600160801b0316145b80612d1057507f000000000000000000000000000000000000000000000000000000011dc6e98e836001600160801b031610155b15612d1c575083610bd4565b60007f000000000000000000000000000000000000000000000000000000011dc6e98e4211612d4b5742612d6d565b7f000000000000000000000000000000000000000000000000000000011dc6e98e5b90506000612d84826001600160801b038716611b03565b9050612daa876118078661180d670de0b6b3a7640000612da48c886125e3565b906125e3565b979650505050505050565b6000610bd4670de0b6b3a764000061180d612dd08686611b03565b87906125e3565b60008183612e265760405162461bcd60e51b8152602060048201818152835160248401528351909283926044909101919085019080838360008315611e57578181015183820152602001611e3f565b506000838581612e3257fe5b0495945050505050565b6000813f7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470818114801590610bd4575050151592915050565b6001600160a01b03831660009081526020858152604080832054918890529091204391908115801590612ec6575060001982016000908152602082905260409020546001600160801b038481169116145b15612efb576000198201600090815260208290526040902080546001600160801b03808716600160801b029116179055612791565b6040805180820182526001600160801b038086168252868116602080840191825260008781528682528581209451855493518516600160801b029085166001600160801b031990941693909317909316919091179092556001600160a01b038916815290899052206001830190555050505050505050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282612fa95760008555612fef565b82601f10612fc25782800160ff19823516178555612fef565b82800160010185558215612fef579182015b82811115612fef578235825591602001919060010190612fd4565b50612ffb929150613040565b5090565b604051806060016040528060006001600160a01b0316815260200160008152602001600081525090565b604080518082019091526000808252602082015290565b5b80821115612ffb5760008155600101613041565b80356001600160a01b0381168114610bf657600080fd5b803560028110610bf657600080fd5b60008083601f84011261308c578182fd5b50813567ffffffffffffffff8111156130a3578182fd5b6020830191508360208285010111156130bb57600080fd5b9250929050565b803560ff81168114610bf657600080fd5b6000602082840312156130e4578081fd5b610a4a82613055565b600080604083850312156130ff578081fd5b61310883613055565b915061311660208401613055565b90509250929050565b600080600060608486031215613133578081fd5b61313c84613055565b925061314a60208501613055565b9150604084013590509250925092565b600080600080600080600060e0888a031215613174578283fd5b61317d88613055565b965061318b60208901613055565b955060408801359450606088013593506131a7608089016130c2565b925060a0880135915060c0880135905092959891949750929550565b600080604083850312156131d5578182fd5b6131de83613055565b91506131166020840161306c565b600080600080600080600060e0888a031215613206578283fd5b61320f88613055565b965061318b6020890161306c565b6000806040838503121561322f578182fd5b61323883613055565b946020939093013593505050565b60008060006060848603121561325a578283fd5b61326384613055565b9250602084013591506132786040850161306c565b90509250925092565b60008060008060008060c08789031215613299578182fd5b6132a287613055565b955060208701359450604087013593506132be606088016130c2565b92506080870135915060a087013590509295509295509295565b600080602083850312156132ea578081fd5b823567ffffffffffffffff80821115613301578283fd5b818501915085601f830112613314578283fd5b813581811115613322578384fd5b866020606083028501011115613336578384fd5b60209290920196919550909350505050565b60008060008060006060868803121561335f578283fd5b853567ffffffffffffffff80821115613376578485fd5b61338289838a0161307b565b9097509550602088013591508082111561339a578485fd5b506133a78882890161307b565b90945092506133ba9050604087016130c2565b90509295509295909350565b6000602082840312156133d7578081fd5b81356001600160801b0381168114610a4a578182fd5b6000602082840312156133fe578081fd5b5035919050565b6000806000806080858703121561341a578182fd5b843593506020850135925061343160408601613055565b9396929550929360600135925050565b60008151808452815b818110156134665760208185018101518683018201520161344a565b818111156134775782602083870101525b50601f01601f19169290920160200192915050565b61190160f01b81526002810192909252602282015260420190565b6001600160a01b0391909116815260200190565b6001600160a01b039384168152919092166020820152604081019190915260600190565b6001600160a01b03929092168252602082015260400190565b901515815260200190565b90815260200190565b9586526001600160a01b0394851660208701529290931660408501526060840152608083019190915260a082015260c00190565b9384526001600160a01b039290921660208401526040830152606082015260800190565b9485526001600160a01b0393909316602085015260408401919091526060830152608082015260a00190565b9485526020850193909352604084019190915260608301526001600160a01b0316608082015260a00190565b93845260ff9290921660208401526040830152606082015260800190565b600060208252610a4a6020830184613441565b6020808252601390820152721253959053125117d6915493d7d05353d55395606a1b604082015260600190565b602080825260119082015270494e56414c49445f5349474e415455524560781b604082015260600190565b60208082526015908201527424a729aaa32324a1a4a2a72a2fa1a7a7a62227aba760591b604082015260600190565b60208082526012908201527124a72b20a624a22fa2ac2824a920aa24a7a760711b604082015260600190565b6020808252600d908201526c494e56414c49445f4e4f4e434560981b604082015260600190565b60208082526015908201527427a7262cafa2a6a4a9a9a4a7a72fa6a0a720a3a2a960591b604082015260600190565b6020808252600d908201526c24a72b20a624a22fa7aba722a960991b604082015260600190565b60208082526017908201527f554e5354414b455f57494e444f575f46494e4953484544000000000000000000604082015260600190565b6020808252601b908201527f494e56414c49445f42414c414e43455f4f4e5f434f4f4c444f574e0000000000604082015260600190565b6001600160801b0391909116815260200190565b6001600160801b0392831681529116602082015260400190565b6001600160801b039384168152919092166020820152604081019190915260600190565b60ff9190911681526020019056fe45524332303a207472616e7366657220746f20746865207a65726f206164647265737345524332303a206275726e20616d6f756e7420657863656564732062616c616e636545524332303a20617070726f766520746f20746865207a65726f206164647265737345524332303a207472616e7366657220616d6f756e7420657863656564732062616c616e6365536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f7745524332303a207472616e7366657220616d6f756e74206578636565647320616c6c6f77616e6365436f6e747261637420696e7374616e63652068617320616c7265616479206265656e20696e697469616c697a656445524332303a206275726e2066726f6d20746865207a65726f206164647265737345524332303a207472616e736665722066726f6d20746865207a65726f206164647265737345524332303a20617070726f76652066726f6d20746865207a65726f20616464726573735361666545524332303a204552433230206f7065726174696f6e20646964206e6f74207375636365656445524332303a2064656372656173656420616c6c6f77616e63652062656c6f77207a65726fa26469706673582212200c4f0888820d9e117b76e7a1a597eb3afa35dc8ba043094cd2cad6dfa1967c3f64736f6c63430007050033

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

00000000000000000000000001ba67aac7f75f647d94220cc98fb30fcc5105bf00000000000000000000000001ba67aac7f75f647d94220cc98fb30fcc5105bf00000000000000000000000000000000000000000000000000000000000d2f00000000000000000000000000000000000000000000000000000000000002a3000000000000000000000000008ef8eeeb39b21ecdde451e6c539017df24d14a190000000000000000000000008ef8eeeb39b21ecdde451e6c539017df24d14a1900000000000000000000000000000000000000000000000000000000bbf81e00

-----Decoded View---------------
Arg [0] : stakedToken (address): 0x01BA67AAC7f75f647D94220Cc98FB30FCc5105Bf
Arg [1] : rewardToken (address): 0x01BA67AAC7f75f647D94220Cc98FB30FCc5105Bf
Arg [2] : cooldownSeconds (uint256): 864000
Arg [3] : unstakeWindow (uint256): 172800
Arg [4] : rewardsVault (address): 0x8ef8eEEB39b21ECdDE451e6C539017DF24D14a19
Arg [5] : emissionManager (address): 0x8ef8eEEB39b21ECdDE451e6C539017DF24D14a19
Arg [6] : distributionDuration (uint128): 3153600000

-----Encoded View---------------
7 Constructor Arguments found :
Arg [0] : 00000000000000000000000001ba67aac7f75f647d94220cc98fb30fcc5105bf
Arg [1] : 00000000000000000000000001ba67aac7f75f647d94220cc98fb30fcc5105bf
Arg [2] : 00000000000000000000000000000000000000000000000000000000000d2f00
Arg [3] : 000000000000000000000000000000000000000000000000000000000002a300
Arg [4] : 0000000000000000000000008ef8eeeb39b21ecdde451e6c539017df24d14a19
Arg [5] : 0000000000000000000000008ef8eeeb39b21ecdde451e6c539017df24d14a19
Arg [6] : 00000000000000000000000000000000000000000000000000000000bbf81e00


Loading...
Loading
Loading...
Loading
[ Download: CSV Export  ]

A token is a representation of an on-chain or off-chain asset. The token page shows information such as price, total supply, holders, transfers and social links. Learn more about this page in our Knowledge Base.