ETH Price: $3,422.22 (-1.72%)
Gas: 6 Gwei

Contract

0x672ab08eaCa5506e473798fBE982dcf50405D1be
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Token Holdings

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Unstake203075522024-07-14 21:52:593 days ago1720993979IN
0x672ab08e...50405D1be
0 ETH0.000453042.3232107
Process Rewards203039682024-07-14 9:52:353 days ago1720950755IN
0x672ab08e...50405D1be
0 ETH0.000483543.07467098
Unstake203005592024-07-13 22:26:474 days ago1720909607IN
0x672ab08e...50405D1be
0 ETH0.000517392.66571062
Unstake202962652024-07-13 8:02:594 days ago1720857779IN
0x672ab08e...50405D1be
0 ETH0.000422081.98984134
Unstake202817482024-07-11 7:24:116 days ago1720682651IN
0x672ab08e...50405D1be
0 ETH0.000789884.05050254
Unstake202817372024-07-11 7:21:596 days ago1720682519IN
0x672ab08e...50405D1be
0 ETH0.000825894.2349026
Unstake202817302024-07-11 7:20:356 days ago1720682435IN
0x672ab08e...50405D1be
0 ETH0.000812954.16882828
Unstake202779542024-07-10 18:40:597 days ago1720636859IN
0x672ab08e...50405D1be
0 ETH0.001534027.86598184
Unstake202779482024-07-10 18:39:477 days ago1720636787IN
0x672ab08e...50405D1be
0 ETH0.001536177.27419902
Process Rewards202779352024-07-10 18:37:117 days ago1720636631IN
0x672ab08e...50405D1be
0 ETH0.001115587.09359243
Unstake202597072024-07-08 5:28:359 days ago1720416515IN
0x672ab08e...50405D1be
0 ETH0.000376781.77617833
Unstake202591792024-07-08 3:42:359 days ago1720410155IN
0x672ab08e...50405D1be
0 ETH0.00047022.41105829
Unstake202591742024-07-08 3:41:359 days ago1720410095IN
0x672ab08e...50405D1be
0 ETH0.000340751.74725802
Unstake202591672024-07-08 3:40:119 days ago1720410011IN
0x672ab08e...50405D1be
0 ETH0.000203181.92781675
Unstake202591672024-07-08 3:40:119 days ago1720410011IN
0x672ab08e...50405D1be
0 ETH0.000375961.92781675
Unstake202591492024-07-08 3:36:359 days ago1720409795IN
0x672ab08e...50405D1be
0 ETH0.000387381.98636828
Unstake202591422024-07-08 3:35:119 days ago1720409711IN
0x672ab08e...50405D1be
0 ETH0.000292522.06501336
Unstake202591382024-07-08 3:34:239 days ago1720409663IN
0x672ab08e...50405D1be
0 ETH0.000386711.98296133
Unstake202591302024-07-08 3:32:479 days ago1720409567IN
0x672ab08e...50405D1be
0 ETH0.000344741.76774384
Unstake202591252024-07-08 3:31:479 days ago1720409507IN
0x672ab08e...50405D1be
0 ETH0.000078172.00946653
Unstake202591212024-07-08 3:30:599 days ago1720409459IN
0x672ab08e...50405D1be
0 ETH0.000332821.7066068
Unstake202591112024-07-08 3:28:599 days ago1720409339IN
0x672ab08e...50405D1be
0 ETH0.000357331.83228998
Unstake202591072024-07-08 3:27:599 days ago1720409279IN
0x672ab08e...50405D1be
0 ETH0.000068271.75499759
Unstake202591042024-07-08 3:27:239 days ago1720409243IN
0x672ab08e...50405D1be
0 ETH0.000332321.70405913
Unstake202590992024-07-08 3:26:239 days ago1720409183IN
0x672ab08e...50405D1be
0 ETH0.000338011.73325696
View all transactions

Latest 1 internal transaction

Advanced mode:
Parent Transaction Hash Block From To
141730712022-02-09 16:53:24889 days ago1644425604  Contract Creation0 ETH
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
SyndicateCorePool

Compiler Version
v0.8.1+commit.df193b15

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion
File 1 of 20 : SyndicateCorePool.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.1;

import "./SyndicatePoolBase.sol";

/**
 * @title Syndicate Core Pool
 *        Original title: Illuvium Core Pool
 *
 * @notice Core pools represent permanent pools like SYNR or SYNR/ETH Pair pool,
 *      core pools allow staking for arbitrary periods of time up to 1 year
 *
 * @dev See SyndicatePoolBase for more details
 *
 * @author Pedro Bergamini, reviewed by Basil Gorin
 * Adapted for Syn City by Superpower Labs
 */
contract SyndicateCorePool is SyndicatePoolBase {
  /// @dev Flag indicating pool type, false means "core pool"
  // solhint-disable-next-line
  bool public constant override isFlashPool = false;

  /// @dev Control parameter of how much reward in sSYNR user will get immediately upon staking.
  /// 0 when feature is disabled.  In base points.
  uint256 public quickRewardRate;

  uint256 public totalQuickReward;

  uint256 public maxQuickReward;

  /// @dev Link to deployed SyndicateVault instance
  address public vault;

  /// @dev Used to calculate vault rewards
  /// @dev This value is different from "reward per token" used in locked pool
  /// @dev Note: stakes are different in duration and "weight" reflects that
  uint256 public vaultRewardsPerWeight;

  /// @dev Pool tokens value available in the pool;
  ///      pool token examples are SYNR (SYNR core pool) or SYNR/ETH pair (LP core pool)
  /// @dev For LP core pool this value doesnt' count for SYNR tokens received as Vault rewards
  ///      while for SYNR core pool it does count for such tokens as well
  uint256 public poolTokenReserve;

  /**
   * @dev Fired in receiveVaultRewards()
   *
   * @param _by an address that sent the rewards, always a vault
   * @param amount amount of tokens received
   */
  event VaultRewardsReceived(address indexed _by, uint256 amount);

  /**
   * @dev Fired in _processVaultRewards() and dependent functions, like processRewards()
   *
   * @param _by an address which executed the function
   * @param _to an address which received a reward
   * @param amount amount of reward received
   */
  event VaultRewardsClaimed(address indexed _by, address indexed _to, uint256 amount);

  /**
   * @dev Fired in setVault()
   *
   * @param _by an address which executed the function, always a factory owner
   */
  event VaultUpdated(address indexed _by, address _fromVal, address _toVal);

  /**
   * @dev Creates/deploys an instance of the core pool
   *
   * @param _synr SYNR ERC20 Token SyndicateERC20 address
   * @param _ssynr sSYNR ERC20 Token  SyntheticSyndicateERC20 address
   * @param _factory Pool factory SyndicatePoolFactory instance/address
   * @param _poolToken token the pool operates on, for example SYNR or SYNR/ETH pair
   * @param _initBlock initial block used to calculate the rewards
   * @param _weight number representing a weight of the pool, actual weight fraction
   *      is calculated as that number divided by the total pools weight and doesn't exceed one
   */
  constructor(
    address _synr,
    address _ssynr,
    SyndicatePoolFactory _factory,
    address _poolToken,
    uint64 _initBlock,
    uint32 _weight
  ) SyndicatePoolBase(_synr, _ssynr, _factory, _poolToken, _initBlock, _weight) {}

  /**
   * @notice Calculates current vault rewards value available for address specified
   *
   * @dev Performs calculations based on current smart contract state only,
   *      not taking into account any additional time/blocks which might have passed
   *
   * @param _staker an address to calculate vault rewards value for
   * @return pending calculated vault reward value for the given address
   */
  function pendingVaultRewards(address _staker) public view returns (uint256 pending) {
    User storage user = users[_staker];

    return weightToReward(user.totalWeight, vaultRewardsPerWeight) - user.subVaultRewards;
  }

  /** @notice set the reward weight for sSYNR distributed upon staking
   *
   *  @dev  divide quick reward by 10000 to get the actual value and 100000 (10x) is used as a general limit
   *
   *  @param _quickRewardRate the reward rate in base point
   */
  function setQuickRewardRate(uint256 _quickRewardRate) external onlyFactoryOwner {
    // the is a general limit, should not exceed 10x
    require(_quickRewardRate < 100000, "parameter out of range");
    quickRewardRate = _quickRewardRate;
  }

  function setMaxQuickReward(uint256 _maxQuickReward) external onlyFactoryOwner {
    maxQuickReward = _maxQuickReward;
  }

  /**
   * @dev Executed only by the factory owner to Set the vault
   *
   * @param _vault an address of deployed SyndicateVault instance
   */
  function setVault(address _vault) external {
    // verify function is executed by the factory owner
    require(factory.owner() == msg.sender, "access denied");

    // verify input is set
    require(_vault != address(0), "zero input");

    // emit an event
    emit VaultUpdated(msg.sender, vault, _vault);

    // update vault address
    vault = _vault;
  }

  /**
   * @dev Executed by the vault to transfer vault rewards SYNR from the vault
   *      into the pool
   *
   * @dev This function is executed only for SYNR core pools
   *
   * @param _rewardsAmount amount of SYNR rewards to transfer into the pool
   */
  function receiveVaultRewards(uint256 _rewardsAmount) external {
    require(msg.sender == vault, "access denied");
    // return silently if there is no reward to receive
    if (_rewardsAmount == 0) {
      return;
    }
    require(usersLockingWeight > 0, "zero locking weight");

    _transferSynFrom(msg.sender, address(this), _rewardsAmount);

    vaultRewardsPerWeight += rewardToWeight(_rewardsAmount, usersLockingWeight);

    // update `poolTokenReserve` only if this is a SYNR Core Pool
    if (poolToken == synr) {
      poolTokenReserve += _rewardsAmount;
    }

    emit VaultRewardsReceived(msg.sender, _rewardsAmount);
  }

  /**
   * @notice Service function to calculate and pay pending vault and yield rewards to the sender
   *
   * @dev Internally executes similar function `_processRewards` from the parent smart contract
   *      to calculate and pay yield rewards; adds vault rewards processing
   *
   * @dev Can be executed by anyone at any time, but has an effect only when
   *      executed by deposit holder and when at least one block passes from the
   *      previous reward processing
   * @dev Executed internally when "staking as a pool" (`stakeAsPool`)
   * @dev When timing conditions are not met (executed too frequently, or after factory
   *      end block), function doesn't throw and exits silently
   *
   * @dev _useSSYN flag has a context of yield rewards only
   *
   * @param _useSSYN flag indicating whether to mint sSYNR token as a reward or not;
   *      when set to true - sSYNR reward is minted immediately and sent to sender,
   *      when set to false - new SYNR reward deposit gets created if pool is an SYNR pool
   *      (poolToken is SYNR token), or new pool deposit gets created together with sSYNR minted
   *      when pool is not an SYNR pool (poolToken is not an SYNR token)
   */
  function processRewards(bool _useSSYN) external override poolAlive {
    _processRewards(msg.sender, _useSSYN, true);
  }

  /**
   * @dev Executed internally by the pool itself (from the parent `SyndicatePoolBase` smart contract)
   *      as part of yield rewards processing logic (`SyndicatePoolBase._processRewards` function)
   * @dev Executed when _useSSYN is false and pool is not an SYNR pool - see `SyndicatePoolBase._processRewards`
   *
   * @param _staker an address which stakes (the yield reward)
   * @param _amount amount to be staked (yield reward amount)
   */
  function stakeAsPool(address _staker, uint256 _amount) external {
    require(factory.poolExists(msg.sender), "access denied");
    _sync();
    User storage user = users[_staker];
    if (user.tokenAmount > 0) {
      _processRewards(_staker, false, false);
    }
    uint256 depositWeight = _amount * YEAR_STAKE_WEIGHT_MULTIPLIER;
    Deposit memory newDeposit = Deposit({
      tokenAmount: _amount,
      lockedFrom: uint64(now256()),
      lockedUntil: uint64(now256() + 365 days),
      weight: depositWeight,
      isYield: true
    });
    user.tokenAmount += _amount;
    user.totalWeight += depositWeight;
    user.deposits.push(newDeposit);

    usersLockingWeight += depositWeight;

    user.subYieldRewards = weightToReward(user.totalWeight, yieldRewardsPerWeight);
    user.subVaultRewards = weightToReward(user.totalWeight, vaultRewardsPerWeight);

    // update `poolTokenReserve` only if this is a LP Core Pool (stakeAsPool can be executed only for LP pool)
    poolTokenReserve += _amount;
  }

  /**
   * @inheritdoc SyndicatePoolBase
   *
   * @dev Extends base functionality and updates vault rewards checkpoint
   */
  function _updateStakeLock(
    address _staker,
    uint256 _depositId,
    uint64 _lockedUntil,
    bool _useSSYN
  ) internal override poolAlive {
    super._updateStakeLock(_staker, _depositId, _lockedUntil, _useSSYN);
    User storage user = users[_staker];
    // updates sub vault rewards value
    user.subVaultRewards = weightToReward(user.totalWeight, vaultRewardsPerWeight);
  }

  /**
   * @inheritdoc SyndicatePoolBase
   *
   * @dev Additionally to the parent smart contract, updates vault rewards of the holder,
   *      and updates (increases) pool token reserve (pool tokens value available in the pool)
   */
  function _stake(
    address _staker,
    uint256 _amount,
    uint64 _lockedUntil,
    bool _useSSYN,
    bool _isYield
  ) internal override {
    super._stake(_staker, _amount, _lockedUntil, _useSSYN, _isYield);
    User storage user = users[_staker];
    user.subVaultRewards = weightToReward(user.totalWeight, vaultRewardsPerWeight);

    poolTokenReserve += _amount;

    // distribute quick reward, additional checks on _lockedUntil is done in super._stake
    if (quickRewardRate > 0 && _lockedUntil > 0) {
      uint256 reward = ((_lockedUntil - now256()) * _amount * quickRewardRate) / (10000 * 365 days);
      if (totalQuickReward < maxQuickReward) {
        if (totalQuickReward + reward > maxQuickReward) {
          reward = maxQuickReward - totalQuickReward;
        }
        mintSSyn(_staker, reward);
        totalQuickReward += reward;
      }
    }
  }

  /**
   * @inheritdoc SyndicatePoolBase
   *
   * @dev Additionally to the parent smart contract, updates vault rewards of the holder,
   *      and updates (decreases) pool token reserve (pool tokens value available in the pool)
   */
  function _unstake(
    address _staker,
    uint256 _depositId,
    uint256 _amount,
    bool _useSSYN
  ) internal override {
    User storage user = users[_staker];
    Deposit memory stakeDeposit = user.deposits[_depositId];
    require(stakeDeposit.lockedFrom == 0 || now256() > stakeDeposit.lockedUntil, "deposit not yet unlocked");
    poolTokenReserve -= _amount;
    super._unstake(_staker, _depositId, _amount, _useSSYN);
    user.subVaultRewards = weightToReward(user.totalWeight, vaultRewardsPerWeight);
  }

  /**
   * @inheritdoc SyndicatePoolBase
   *
   * @dev Additionally to the parent smart contract, processes vault rewards of the holder,
   *      and for SYNR pool updates (increases) pool token reserve (pool tokens value available in the pool)
   */
  function _processRewards(
    address _staker,
    bool _useSSYN,
    bool _withUpdate
  ) internal override returns (uint256 pendingYield) {
    _processVaultRewards(_staker, _withUpdate);
    pendingYield = super._processRewards(_staker, _useSSYN, _withUpdate);

    // update `poolTokenReserve` only if this is a SYNR Core Pool
    if (poolToken == synr && !_useSSYN) {
      poolTokenReserve += pendingYield;
    }
  }

  /**
   * @dev Used internally to process vault rewards for the staker
   *
   * @param _staker address of the user (staker) to process rewards for
   */
  function _processVaultRewards(address _staker, bool _withUpdate) private {
    User storage user = users[_staker];
    uint256 pendingVaultClaim = pendingVaultRewards(_staker);
    if (pendingVaultClaim == 0) return;
    // read SYNR token balance of the pool via standard ERC20 interface
    uint256 synBalance = IERC20(synr).balanceOf(address(this));
    require(synBalance >= pendingVaultClaim, "contract SYNR balance too low");

    // update `poolTokenReserve` only if this is a SYNR Core Pool
    if (poolToken == synr) {
      // protects against rounding errors
      poolTokenReserve -= pendingVaultClaim > poolTokenReserve ? poolTokenReserve : pendingVaultClaim;
    }

    if (_withUpdate) {
      user.subVaultRewards = weightToReward(user.totalWeight, vaultRewardsPerWeight);
    }

    // transfer fails if pool SYNR balance is not enough - which is a desired behavior
    _transferSyn(_staker, pendingVaultClaim);

    emit VaultRewardsClaimed(msg.sender, _staker, pendingVaultClaim);
  }

  function delegate(address receiver) external onlyFactoryOwner {
    require(poolToken == synr, "CorePool: only synr pool can delegate");
    SyndicateERC20(synr).delegate(receiver);
  }
}

File 2 of 20 : SyndicatePoolBase.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.1;

import "../interfaces/IPool.sol";
import "../interfaces/ICorePool.sol";
import "./ReentrancyGuard.sol";
import "./SyndicatePoolFactory.sol";
import "../utils/SafeERC20.sol";
import "../token/SyntheticSyndicateERC20.sol";
import "../interfaces/IMigrator.sol";

/**
 * @title Syndicate Pool Base
 *        Original title: Illuvium Pool Base
 *
 * @notice An abstract contract containing common logic for any pool,
 *      be it a flash pool (temporary pool like SNX) or a core pool (permanent pool like SYNR/ETH or SYNR pool)
 *
 * @dev Deployment and initialization.
 *      Any pool deployed must be bound to the deployed pool factory (SyndicatePoolFactory)
 *      Additionally, 3 token instance addresses must be defined on deployment:
 *          - SYNR token address
 *          - sSYNR token address, used to mint sSYNR rewards
 *          - pool token address, it can be SYNR token address, SYNR/ETH pair address, and others
 *
 * @dev Pool weight defines the fraction of the yield current pool receives among the other pools,
 *      pool factory is responsible for the weight synchronization between the pools.
 * @dev The weight is logically 10% for SYNR pool and 90% for SYNR/ETH pool.
 *      Since Solidity doesn't support fractions the weight is defined by the division of
 *      pool weight by total pools weight (sum of all registered pools within the factory)
 *
 * @author Pedro Bergamini, reviewed by Basil Gorin
 * Adapted for Syn City by Superpower Labs
 */
abstract contract SyndicatePoolBase is IPool, SyndicateAware, ReentrancyGuard {
  uint256 public minLockTime = 16 weeks;

  IMigrator public migrator;

  /// @dev Token holder storage, maps token holder address to their data record
  mapping(address => User) public users;

  /// @dev Link to sSYNR ERC20 Token  SyntheticSyndicateERC20 instance
  address public immutable override ssynr;

  /// @dev Link to the pool factory SyndicatePoolFactory instance
  SyndicatePoolFactory public immutable factory;

  /// @dev Link to the pool token instance, for example SYNR or SYNR/ETH pair
  address public immutable override poolToken;

  /// @dev Pool weight, 100 for SYNR pool or 900 for SYNR/ETH
  uint32 public override weight;

  /// @dev Block number of the last yield distribution event
  uint64 public override lastYieldDistribution;

  /// @dev Used to calculate yield rewards
  /// @dev This value is different from "reward per token" used in locked pool
  /// @dev Note: stakes are different in duration and "weight" reflects that
  uint256 public override yieldRewardsPerWeight;

  /// @dev Used to calculate yield rewards, keeps track of the tokens weight locked in staking
  uint256 public override usersLockingWeight;

  uint256 public totalYieldReward;

  /**
   * @dev Stake weight is proportional to deposit amount and time locked, precisely
   *      "deposit amount wei multiplied by (fraction of the year locked plus one)"
   * @dev To avoid significant precision loss due to multiplication by "fraction of the year" [0, 1],
   *      weight is stored multiplied by 1e6 constant, as an integer
   * @dev Corner case 1: if time locked is zero, weight is deposit amount multiplied by 1e6
   * @dev Corner case 2: if time locked is one year, fraction of the year locked is one, and
   *      weight is a deposit amount multiplied by 2 * 1e6
   */
  // solhint-disable-next-line
  uint256 internal constant WEIGHT_MULTIPLIER = 1e6;

  /**
   * @dev When we know beforehand that staking is done for a year, and fraction of the year locked is one,
   *      we use simplified calculation and use the following constant instead previos one
   */
  // solhint-disable-next-line
  uint256 internal constant YEAR_STAKE_WEIGHT_MULTIPLIER = 2 * WEIGHT_MULTIPLIER;

  /**
   * @dev Rewards per weight are stored multiplied by 1e20, as integers.
   */
  // solhint-disable-next-line
  uint256 internal constant REWARD_PER_WEIGHT_MULTIPLIER = 1e20;

  /**
   * @dev Fired in _stake() and stake()
   *
   * @param _by an address which performed an operation, usually token holder
   * @param _from token holder address, the tokens will be returned to that address
   * @param amount amount of tokens staked
   */
  event Staked(address indexed _by, address indexed _from, uint256 amount);

  /**
   * @dev Fired in _updateStakeLock() and updateStakeLock()
   *
   * @param _by an address which performed an operation
   * @param depositId updated deposit ID
   * @param lockedFrom deposit locked from value
   * @param lockedUntil updated deposit locked until value
   */
  event StakeLockUpdated(address indexed _by, uint256 depositId, uint64 lockedFrom, uint64 lockedUntil);

  /**
   * @dev Fired in _unstake() and unstake()
   *
   * @param _by an address which performed an operation, usually token holder
   * @param _to an address which received the unstaked tokens, usually token holder
   * @param amount amount of tokens unstaked
   */
  event Unstaked(address indexed _by, address indexed _to, uint256 amount);

  /**
   * @dev Fired in _sync(), sync() and dependent functions (stake, unstake, etc.)
   *
   * @param _by an address which performed an operation
   * @param yieldRewardsPerWeight updated yield rewards per weight value
   * @param lastYieldDistribution usually, current block number
   */
  event Synchronized(address indexed _by, uint256 yieldRewardsPerWeight, uint64 lastYieldDistribution);

  /**
   * @dev Fired in _processRewards(), processRewards() and dependent functions (stake, unstake, etc.)
   *
   * @param _by an address which performed an operation
   * @param _to an address which claimed the yield reward
   * @param sSyn flag indicating if reward was paid (minted) in sSYNR
   * @param amount amount of yield paid
   */
  event YieldClaimed(address indexed _by, address indexed _to, bool sSyn, uint256 amount);

  /**
   * @dev Fired in setWeight()
   *
   * @param _by an address which performed an operation, always a factory
   * @param _fromVal old pool weight value
   * @param _toVal new pool weight value
   */
  event PoolWeightUpdated(address indexed _by, uint32 _fromVal, uint32 _toVal);

  modifier onlyFactoryOwner() {
    // verify function is executed by the factory owner
    require(factory.owner() == msg.sender, "access denied");
    _;
  }

  modifier poolAlive() {
    require(weight > 0, "pool disabled");
    _;
  }

  /**
   * @dev Overridden in sub-contracts to construct the pool
   *
   * @param _synr SYNR ERC20 Token SyndicateERC20 address
   * @param _ssynr sSYNR ERC20 Token  SyntheticSyndicateERC20 address
   * @param _factory Pool factory SyndicatePoolFactory instance/address
   * @param _poolToken token the pool operates on, for example SYNR or SYNR/ETH pair
   * @param _initBlock initial block used to calculate the rewards
   *      note: _initBlock can be set to the future effectively meaning _sync() calls will do nothing
   * @param _weight number representing a weight of the pool, actual weight fraction
   *      is calculated as that number divided by the total pools weight and doesn't exceed one
   */
  constructor(
    address _synr,
    address _ssynr,
    SyndicatePoolFactory _factory,
    address _poolToken,
    uint64 _initBlock,
    uint32 _weight
  ) SyndicateAware(_synr) {
    // verify the inputs are set
    require(_ssynr != address(0), "sSYNR address not set");
    require(address(_factory) != address(0), "SYNR Pool fct address not set");
    require(_poolToken != address(0), "pool token address not set");
    require(_initBlock > 0, "init block not set");
    require(_weight > 0, "pool weight not set");

    // verify sSYNR instance supplied
    require(
      SyntheticSyndicateERC20(_ssynr).TOKEN_UID() == 0xac3051b8d4f50966afb632468a4f61483ae6a953b74e387a01ef94316d6b7d62,
      "unexpected sSYNR TOKEN_UID"
    );
    // verify SyndicatePoolFactory instance supplied
    require(
      _factory.FACTORY_UID() == 0xc5cfd88c6e4d7e5c8a03c0f0f03af23c0918d8e82cac196f57466af3fd4a5ec7,
      "unexpected FACTORY_UID"
    );

    // save the inputs into internal state variables
    ssynr = _ssynr;
    factory = _factory;
    poolToken = _poolToken;
    weight = _weight;

    // init the dependent internal state variables
    lastYieldDistribution = _initBlock;
  }

  function setMigrator(IMigrator _migrator) external onlyFactoryOwner {
    require(address(_migrator) != address(0), "migrator cannot be 0x0");
    migrator = _migrator;
  }

  function migrate() external {
    require(weight == 0, "disable pool first");
    require(address(migrator) != address(0), "migrator not set");
    User storage user = users[msg.sender];
    require(user.tokenAmount != 0, "no tokens to migrate");
    migrator.receiveDeposits(msg.sender, user);
    uint256 tokenToMigrate;
    for (uint256 i = user.deposits.length; i > 0; i--) {
      if (!user.deposits[i - 1].isYield) {
        tokenToMigrate += user.deposits[i - 1].tokenAmount;
      }
      user.deposits.pop();
    }
    SyndicateERC20(synr).transfer(address(migrator), tokenToMigrate);
    delete users[msg.sender];
  }

  /**
   * @notice Calculates current yield rewards value available for address specified
   *
   * @param _staker an address to calculate yield rewards value for
   * @return calculated yield reward value for the given address
   */
  function pendingYieldRewards(address _staker) external view override returns (uint256) {
    // `newYieldRewardsPerWeight` will store stored or recalculated value for `yieldRewardsPerWeight`
    uint256 newYieldRewardsPerWeight;

    // if smart contract state was not updated recently, `yieldRewardsPerWeight` value
    // is outdated and we need to recalculate it in order to calculate pending rewards correctly
    if (blockNumber() > lastYieldDistribution && usersLockingWeight != 0) {
      uint256 endBlock = factory.endBlock();
      uint256 multiplier = blockNumber() > endBlock ? endBlock - lastYieldDistribution : blockNumber() - lastYieldDistribution;
      uint256 synRewards = (multiplier * weight * factory.synrPerBlock()) / factory.totalWeight();

      // recalculated value for `yieldRewardsPerWeight`
      newYieldRewardsPerWeight = rewardToWeight(synRewards, usersLockingWeight) + yieldRewardsPerWeight;
    } else {
      // if smart contract state is up to date, we don't recalculate
      newYieldRewardsPerWeight = yieldRewardsPerWeight;
    }

    // based on the rewards per weight value, calculate pending rewards;
    User storage user = users[_staker];
    uint256 pending = weightToReward(user.totalWeight, newYieldRewardsPerWeight) - user.subYieldRewards;
    return pending;
  }

  /**
   * @notice Returns total staked token balance for the given address
   *
   * @param _user an address to query balance for
   * @return total staked token balance
   */
  function balanceOf(address _user) external view override returns (uint256) {
    // read specified user token amount and return
    return users[_user].tokenAmount;
  }

  /**
   * @notice Returns information on the given deposit for the given address
   *
   * @dev See getDepositsLength
   *
   * @param _user an address to query deposit for
   * @param _depositId zero-indexed deposit ID for the address specified
   * @return deposit info as Deposit structure
   */
  function getDeposit(address _user, uint256 _depositId) external view override returns (Deposit memory) {
    // read deposit at specified index and return
    return users[_user].deposits[_depositId];
  }

  /**
   * @notice Returns number of deposits for the given address. Allows iteration over deposits.
   *
   * @dev See getDeposit
   *
   * @param _user an address to query deposit length for
   * @return number of deposits for the given address
   */
  function getDepositsLength(address _user) external view override returns (uint256) {
    // read deposits array length and return
    return users[_user].deposits.length;
  }

  /**
   * @notice Stakes specified amount of tokens for the specified amount of time,
   *      and pays pending yield rewards if any
   *
   * @dev Requires amount to stake to be greater than zero
   *
   * @param _amount amount of tokens to stake
   * @param _lockUntil stake period as unix timestamp; zero means no locking
   * @param _useSSYN a flag indicating if previous reward to be paid as sSYNR
   */
  function stake(
    uint256 _amount,
    uint64 _lockUntil,
    bool _useSSYN
  ) external override poolAlive {
    // delegate call to an internal function
    _stake(msg.sender, _amount, _lockUntil, _useSSYN, false);
  }

  /**
   * @notice Unstakes specified amount of tokens, and pays pending yield rewards if any
   *
   * @dev Requires amount to unstake to be greater than zero
   *
   * @param _depositId deposit ID to unstake from, zero-indexed
   * @param _amount amount of tokens to unstake
   * @param _useSSYN a flag indicating if reward to be paid as sSYNR
   */
  function unstake(
    uint256 _depositId,
    uint256 _amount,
    bool _useSSYN
  ) external override poolAlive {
    // delegate call to an internal function
    _unstake(msg.sender, _depositId, _amount, _useSSYN);
  }

  /**
   * @notice Extends locking period for a given deposit
   *
   * @dev Requires new lockedUntil value to be:
   *      higher than the current one, and
   *      in the future, but
   *      no more than 1 year in the future
   *
   * @param depositId updated deposit ID
   * @param lockedUntil updated deposit locked until value
   * @param useSSYN used for _processRewards check if it should use SYNR or sSYNR
   */
  function updateStakeLock(
    uint256 depositId,
    uint64 lockedUntil,
    bool useSSYN
  ) external poolAlive {
    // delegate call to an internal function
    _updateStakeLock(msg.sender, depositId, lockedUntil, useSSYN);
  }

  /**
   * @notice Service function to synchronize pool state with current time
   *
   * @dev Can be executed by anyone at any time, but has an effect only when
   *      at least one block passes between synchronizations
   * @dev Executed internally when staking, unstaking, processing rewards in order
   *      for calculations to be correct and to reflect state progress of the contract
   * @dev When timing conditions are not met (executed too frequently, or after factory
   *      end block), function doesn't throw and exits silently
   */
  function sync() external override poolAlive {
    // delegate call to an internal function
    _sync();
  }

  /**
   * @notice Service function to calculate and pay pending yield rewards to the sender
   *
   * @dev Can be executed by anyone at any time, but has an effect only when
   *      executed by deposit holder and when at least one block passes from the
   *      previous reward processing
   * @dev Executed internally when staking and unstaking, executes sync() under the hood
   *      before making further calculations and payouts
   * @dev When timing conditions are not met (executed too frequently, or after factory
   *      end block), function doesn't throw and exits silently
   *
   * @param _useSSYN flag indicating whether to mint sSYNR token as a reward or not;
   *      when set to true - sSYNR reward is minted immediately and sent to sender,
   *      when set to false - new SYNR reward deposit gets created if pool is an SYNR pool
   *      (poolToken is SYNR token), or new pool deposit gets created together with sSYNR minted
   *      when pool is not an SYNR pool (poolToken is not an SYNR token)
   */
  function processRewards(bool _useSSYN) external virtual override poolAlive {
    // delegate call to an internal function
    _processRewards(msg.sender, _useSSYN, true);
  }

  /**
   * @dev Executed by the factory to modify pool weight; the factory is expected
   *      to keep track of the total pools weight when updating
   *
   * @dev Set weight to zero to disable the pool
   *
   * @param _weight new weight to set for the pool
   */
  function setWeight(uint32 _weight) external override {
    // verify function is executed by the factory
    require(msg.sender == address(factory), "access denied");

    // emit an event logging old and new weight values
    emit PoolWeightUpdated(msg.sender, weight, _weight);

    // set the new weight value
    weight = _weight;
  }

  /**
   * @dev Similar to public pendingYieldRewards, but performs calculations based on
   *      current smart contract state only, not taking into account any additional
   *      time/blocks which might have passed
   *
   * @param _staker an address to calculate yield rewards value for
   * @return pending calculated yield reward value for the given address
   */
  function _pendingYieldRewards(address _staker) internal view returns (uint256 pending) {
    // read user data structure into memory
    User storage user = users[_staker];

    // and perform the calculation using the values read
    return weightToReward(user.totalWeight, yieldRewardsPerWeight) - user.subYieldRewards;
  }

  function setMinLockTime(uint256 _minLockTime) external {
    require(_minLockTime < 365 days, "invalid minLockTime");
    minLockTime = _minLockTime;
  }

  /**
   * @dev Used internally, mostly by children implementations, see stake()
   *
   * @param _staker an address which stakes tokens and which will receive them back
   * @param _amount amount of tokens to stake
   * @param _lockUntil stake period as unix timestamp; zero means no locking
   * @param _useSSYN a flag indicating if previous reward to be paid as sSYNR
   * @param _isYield a flag indicating if that stake is created to store yield reward
   *      from the previously unstaked stake
   */
  function _stake(
    address _staker,
    uint256 _amount,
    uint64 _lockUntil,
    bool _useSSYN,
    bool _isYield
  ) internal virtual {
    // validate the inputs
    require(_amount > 0, "SyndicatePoolBase: zero amount");
    // we need to the limit of max locking time to limit the yield bonus
    require(
      _lockUntil >= now256() + minLockTime && _lockUntil - now256() <= 365 days,
      "SyndicatePoolBase: invalid lock interval"
    );
    // update smart contract state
    _sync();

    // get a link to user data struct, we will write to it later
    User storage user = users[_staker];
    // process current pending rewards if any
    if (user.tokenAmount > 0) {
      _processRewards(_staker, _useSSYN, false);
    }

    // in most of the cases added amount `addedAmount` is simply `_amount`
    // however for deflationary tokens this can be different
    // read the current balance
    uint256 previousBalance = IERC20(poolToken).balanceOf(address(this));
    // transfer `_amount`; note: some tokens may get burnt here
    transferPoolTokenFrom(address(msg.sender), address(this), _amount);
    // read new balance, usually this is just the difference `previousBalance - _amount`
    uint256 newBalance = IERC20(poolToken).balanceOf(address(this));
    // calculate real amount taking into account deflation
    uint256 addedAmount = newBalance - previousBalance;

    // set the `lockFrom` and `lockUntil` taking into account that
    // zero value for `_lockUntil` means "no locking" and leads to zero values
    // for both `lockFrom` and `lockUntil`
    uint64 lockFrom = _lockUntil > 0 ? uint64(now256()) : 0;
    uint64 lockUntil = _lockUntil;

    // stake weight formula rewards for locking
    uint256 stakeWeight = getStakeWeight(lockUntil - lockFrom, addedAmount);

    // makes sure stakeWeight is valid
    assert(stakeWeight > 0);

    // create and save the deposit (append it to deposits array)
    Deposit memory deposit = Deposit({
      tokenAmount: addedAmount,
      weight: stakeWeight,
      lockedFrom: lockFrom,
      lockedUntil: lockUntil,
      isYield: _isYield
    });
    // deposit ID is an index of the deposit in `deposits` array
    user.deposits.push(deposit);

    // update user record
    user.tokenAmount += addedAmount;
    user.totalWeight += stakeWeight;
    user.subYieldRewards = weightToReward(user.totalWeight, yieldRewardsPerWeight);

    // update global variable
    usersLockingWeight += stakeWeight;

    // emit an event
    emit Staked(msg.sender, _staker, _amount);
  }

  function getStakeWeight(uint256 lockedTime, uint256 addedAmount) public pure returns (uint256) {
    return ((lockedTime * WEIGHT_MULTIPLIER) / 365 days + WEIGHT_MULTIPLIER) * addedAmount;
  }

  /**
   * @dev Used internally, mostly by children implementations, see unstake()
   *
   * @param _staker an address which unstakes tokens (which previously staked them)
   * @param _depositId deposit ID to unstake from, zero-indexed
   * @param _amount amount of tokens to unstake
   * @param _useSSYN a flag indicating if reward to be paid as sSYNR
   */
  function _unstake(
    address _staker,
    uint256 _depositId,
    uint256 _amount,
    bool _useSSYN
  ) internal virtual {
    // verify an amount is set
    require(_amount > 0, "zero amount");

    // get a link to user data struct, we will write to it later
    User storage user = users[_staker];
    // get a link to the corresponding deposit, we may write to it later
    Deposit storage stakeDeposit = user.deposits[_depositId];
    // deposit structure may get deleted, so we save isYield flag to be able to use it
    bool isYield = stakeDeposit.isYield;

    // verify available balance
    // if staker address ot deposit doesn't exist this check will fail as well
    require(stakeDeposit.tokenAmount >= _amount, "amount exceeds stake");

    // update smart contract state
    _sync();
    // and process current pending rewards if any
    _processRewards(_staker, _useSSYN, false);

    // recalculate deposit weight
    uint256 previousWeight = stakeDeposit.weight;
    uint256 newWeight = (((stakeDeposit.lockedUntil - stakeDeposit.lockedFrom) * WEIGHT_MULTIPLIER) /
      365 days +
      WEIGHT_MULTIPLIER) * (stakeDeposit.tokenAmount - _amount);

    // update the deposit, or delete it if its depleted
    if (stakeDeposit.tokenAmount - _amount == 0) {
      delete user.deposits[_depositId];
    } else {
      stakeDeposit.tokenAmount -= _amount;
      stakeDeposit.weight = newWeight;
    }

    // update user record
    user.tokenAmount -= _amount;
    user.totalWeight = user.totalWeight - previousWeight + newWeight;
    user.subYieldRewards = weightToReward(user.totalWeight, yieldRewardsPerWeight);

    // update global variable
    usersLockingWeight = usersLockingWeight - previousWeight + newWeight;

    // if the deposit was created by the pool itself as a yield reward
    if (isYield) {
      // mint the yield via the factory
      factory.mintYieldTo(msg.sender, _amount);
    } else {
      // otherwise just return tokens back to holder
      transferPoolToken(msg.sender, _amount);
    }

    // emit an event
    emit Unstaked(msg.sender, _staker, _amount);
  }

  /**
   * @dev Used internally, mostly by children implementations, see sync()
   *
   * @dev Updates smart contract state (`yieldRewardsPerWeight`, `lastYieldDistribution`),
   *      updates factory state via `updateSYNPerBlock`
   */
  function _sync() internal virtual {
    // update SYNR per block value in factory if required
    if (factory.shouldUpdateRatio()) {
      factory.updateSYNPerBlock();
    }
    // check bound conditions and if these are not met -
    // exit silently, without emitting an event
    uint256 endBlock = factory.endBlock();
    if (lastYieldDistribution >= endBlock) {
      return;
    }
    if (blockNumber() <= lastYieldDistribution) {
      return;
    }
    // if locking weight is zero - update only `lastYieldDistribution` and exit
    if (usersLockingWeight == 0) {
      lastYieldDistribution = uint64(blockNumber());
      return;
    }
    // to calculate the reward we need to know how many blocks passed, and reward per block
    uint256 currentBlock = blockNumber() > endBlock ? endBlock : blockNumber();
    uint256 blocksPassed = currentBlock - lastYieldDistribution;
    uint256 synrPerBlock = factory.synrPerBlock();

    // calculate the reward
    uint256 synReward = (blocksPassed * synrPerBlock * weight) / factory.totalWeight();

    totalYieldReward += synReward;

    // update rewards per weight and `lastYieldDistribution`
    yieldRewardsPerWeight += rewardToWeight(synReward, usersLockingWeight);
    lastYieldDistribution = uint64(currentBlock);

    // emit an event
    emit Synchronized(msg.sender, yieldRewardsPerWeight, lastYieldDistribution);
  }

  /**
   * @dev Used internally, mostly by children implementations, see processRewards()
   *
   * @param _staker an address which receives the reward (which has staked some tokens earlier)
   * @param _useSSYN flag indicating whether to mint sSYNR token as a reward or not, see processRewards()
   * @param _withUpdate flag allowing to disable synchronization (see sync()) if set to false
   * @return pendingYield the rewards calculated and optionally re-staked
   */
  function _processRewards(
    address _staker,
    bool _useSSYN,
    bool _withUpdate
  ) internal virtual returns (uint256 pendingYield) {
    // update smart contract state if required
    if (_withUpdate) {
      _sync();
    }

    // calculate pending yield rewards, this value will be returned
    pendingYield = _pendingYieldRewards(_staker);

    // if pending yield is zero - just return silently
    if (pendingYield == 0) return 0;

    // get link to a user data structure, we will write into it later
    User storage user = users[_staker];

    // if sSYNR is requested
    if (_useSSYN) {
      // - mint sSYNR
      mintSSyn(_staker, pendingYield);
    } else if (poolToken == synr) {
      // calculate pending yield weight,
      // 2e6 is the bonus weight when staking for 1 year
      uint256 depositWeight = pendingYield * YEAR_STAKE_WEIGHT_MULTIPLIER;

      // if the pool is SYNR Pool - create new SYNR deposit
      // and save it - push it into deposits array
      Deposit memory newDeposit = Deposit({
        tokenAmount: pendingYield,
        lockedFrom: uint64(now256()),
        lockedUntil: uint64(now256() + 365 days), // staking yield for 1 year
        weight: depositWeight,
        isYield: true
      });
      user.deposits.push(newDeposit);

      // update user record
      user.tokenAmount += pendingYield;
      user.totalWeight += depositWeight;

      // update global variable
      usersLockingWeight += depositWeight;
    } else {
      // for other pools - stake as pool
      address synPool = factory.getPoolAddress(synr);
      ICorePool(synPool).stakeAsPool(_staker, pendingYield);
    }

    // update users's record for `subYieldRewards` if requested
    if (_withUpdate) {
      user.subYieldRewards = weightToReward(user.totalWeight, yieldRewardsPerWeight);
    }

    // emit an event
    emit YieldClaimed(msg.sender, _staker, _useSSYN, pendingYield);
  }

  /**
   * @dev See updateStakeLock()
   *
   * @param _staker an address to update stake lock
   * @param _depositId updated deposit ID
   * @param _lockedUntil updated deposit locked until value
   * @param _useSSYN used for _processRewards check if it should use SYNR or sSYNR
   */
  function _updateStakeLock(
    address _staker,
    uint256 _depositId,
    uint64 _lockedUntil,
    bool _useSSYN
  ) internal virtual {
    // synchronizes pool state
    _sync();
    // validate the input time
    require(_lockedUntil > now256(), "lock should be in the future");
    // get a link to user data struct, we will write to it later
    User storage user = users[_staker];
    if (user.tokenAmount > 0) {
      _processRewards(_staker, _useSSYN, false);
    }
    // get a link to the corresponding deposit, we may write to it later
    Deposit storage stakeDeposit = user.deposits[_depositId];

    // validate the input against deposit structure
    require(_lockedUntil > stakeDeposit.lockedUntil, "invalid new lock");

    // verify locked from and locked until values
    if (stakeDeposit.lockedFrom == 0) {
      require(_lockedUntil - now256() <= 365 days, "max lock period is 365 days");
      stakeDeposit.lockedFrom = uint64(now256());
    } else {
      require(_lockedUntil - stakeDeposit.lockedFrom <= 365 days, "max lock period is 365 days");
    }

    // update locked until value, calculate new weight
    stakeDeposit.lockedUntil = _lockedUntil;
    uint256 newWeight = (((stakeDeposit.lockedUntil - stakeDeposit.lockedFrom) * WEIGHT_MULTIPLIER) /
      365 days +
      WEIGHT_MULTIPLIER) * stakeDeposit.tokenAmount;

    // save previous weight
    uint256 previousWeight = stakeDeposit.weight;
    // update weight
    stakeDeposit.weight = newWeight;

    // update user total weight, sub yield rewards and global locking weight
    user.totalWeight = user.totalWeight - previousWeight + newWeight;
    user.subYieldRewards = weightToReward(user.totalWeight, yieldRewardsPerWeight);
    usersLockingWeight = usersLockingWeight - previousWeight + newWeight;

    // emit an event
    emit StakeLockUpdated(_staker, _depositId, stakeDeposit.lockedFrom, _lockedUntil);
  }

  /**
   * @dev Converts stake weight (not to be mixed with the pool weight) to
   *      SYNR reward value, applying the 10^12 division on weight
   *
   * @param _weight stake weight
   * @param rewardPerWeight SYNR reward per weight
   * @return reward value normalized to 10^12
   */
  function weightToReward(uint256 _weight, uint256 rewardPerWeight) public pure returns (uint256) {
    // apply the formula and return
    return (_weight * rewardPerWeight) / REWARD_PER_WEIGHT_MULTIPLIER;
  }

  /**
   * @dev Converts reward SYNR value to stake weight (not to be mixed with the pool weight),
   *      applying the 10^12 multiplication on the reward
   *      - OR -
   * @dev Converts reward SYNR value to reward/weight if stake weight is supplied as second
   *      function parameter instead of reward/weight
   *
   * @param reward yield reward
   * @param rewardPerWeight reward/weight (or stake weight)
   * @return stake weight (or reward/weight)
   */
  function rewardToWeight(uint256 reward, uint256 rewardPerWeight) public pure returns (uint256) {
    // apply the reverse formula and return
    return (reward * REWARD_PER_WEIGHT_MULTIPLIER) / rewardPerWeight;
  }

  /**
   * @dev Testing time-dependent functionality is difficult and the best way of
   *      doing it is to override block number in helper test smart contracts
   *
   * @return `block.number` in mainnet, custom values in testnets (if overridden)
   */
  function blockNumber() public view virtual returns (uint256) {
    // return current block number
    return block.number;
  }

  /**
   * @dev Testing time-dependent functionality is difficult and the best way of
   *      doing it is to override time in helper test smart contracts
   *
   * @return `block.timestamp` in mainnet, custom values in testnets (if overridden)
   */
  function now256() public view virtual returns (uint256) {
    // return current block timestamp
    return block.timestamp;
  }

  /**
   * @dev Executes  SyntheticSyndicateERC20.mint(_to, _values)
   *      on the bound  SyntheticSyndicateERC20 instance
   *
   * @dev Reentrancy safe due to the  SyntheticSyndicateERC20 design
   */
  // solhint-disable-next-line
  function mintSSyn(address _to, uint256 _value) internal {
    // just delegate call to the target
    SyntheticSyndicateERC20(ssynr).mint(_to, _value);
  }

  /**
   * @dev Executes SafeERC20.safeTransfer on a pool token
   *
   * @dev Reentrancy safety enforced via `ReentrancyGuard.nonReentrant`
   */
  // solhint-disable-next-line
  function transferPoolToken(address _to, uint256 _value) internal nonReentrant {
    // just delegate call to the target
    SafeERC20.safeTransfer(IERC20(poolToken), _to, _value);
  }

  /**
   * @dev Executes SafeERC20.safeTransferFrom on a pool token
   *
   * @dev Reentrancy safety enforced via `ReentrancyGuard.nonReentrant`
   */
  // solhint-disable-next-line
  function transferPoolTokenFrom(
    address _from,
    address _to,
    uint256 _value
  ) internal nonReentrant {
    // just delegate call to the target
    SafeERC20.safeTransferFrom(IERC20(poolToken), _from, _to, _value);
  }
}

File 3 of 20 : IPool.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.1;

import "./ILinkedToSYN.sol";

/**
 * @title Syndicate Pool
 *        Original title: Illuvium Pool
 *
 * @notice An abstraction representing a pool, see SyndicatePoolBase for details
 *
 * @author Pedro Bergamini, reviewed by Basil Gorin
 * Adapted for Syn City by Superpower Labs
 */
interface IPool is ILinkedToSYN {
  /**
   * @dev Deposit is a key data structure used in staking,
   *      it represents a unit of stake with its amount, weight and term (time interval)
   */
  struct Deposit {
    // @dev token amount staked
    uint256 tokenAmount;
    // @dev stake weight
    uint256 weight;
    // @dev locking period - from
    uint64 lockedFrom;
    // @dev locking period - until
    uint64 lockedUntil;
    // @dev indicates if the stake was created as a yield reward
    bool isYield;
  }

  /// @dev Data structure representing token holder using a pool
  struct User {
    // @dev Total staked amount
    uint256 tokenAmount;
    // @dev Total weight
    uint256 totalWeight;
    // @dev Auxiliary variable for yield calculation
    uint256 subYieldRewards;
    // @dev Auxiliary variable for vault rewards calculation
    uint256 subVaultRewards;
    // @dev An array of holder's deposits
    Deposit[] deposits;
  }

  // for the rest of the functions see Soldoc in SyndicatePoolBase

  function ssynr() external view returns (address);

  function poolToken() external view returns (address);

  function isFlashPool() external view returns (bool);

  function weight() external view returns (uint32);

  function lastYieldDistribution() external view returns (uint64);

  function yieldRewardsPerWeight() external view returns (uint256);

  function usersLockingWeight() external view returns (uint256);

  function pendingYieldRewards(address _user) external view returns (uint256);

  function balanceOf(address _user) external view returns (uint256);

  function getDeposit(address _user, uint256 _depositId) external view returns (Deposit memory);

  function getDepositsLength(address _user) external view returns (uint256);

  function stake(
    uint256 _amount,
    uint64 _lockedUntil,
    bool useSSYN
  ) external;

  function unstake(
    uint256 _depositId,
    uint256 _amount,
    bool useSSYN
  ) external;

  function sync() external;

  function processRewards(bool useSSYN) external;

  function setWeight(uint32 _weight) external;
}

File 4 of 20 : ICorePool.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.1;

import "./IPool.sol";

interface ICorePool is IPool {
  function vaultRewardsPerToken() external view returns (uint256);

  function poolTokenReserve() external view returns (uint256);

  function stakeAsPool(address _staker, uint256 _amount) external;

  function receiveVaultRewards(uint256 _amount) external;
}

File 5 of 20 : ReentrancyGuard.sol
// https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-contracts/master/contracts/security/ReentrancyGuard.sol
// #24a0bc23cfe3fbc76f8f2510b78af1e948ae6651

// SPDX-License-Identifier: MIT

pragma solidity 0.8.1;

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 */
abstract contract ReentrancyGuard {
  // Booleans are more expensive than uint256 or any type that takes up a full
  // word because each write operation emits an extra SLOAD to first read the
  // slot's contents, replace the bits taken up by the boolean, and then write
  // back. This is the compiler's defense against contract upgrades and
  // pointer aliasing, and it cannot be disabled.

  // The values being non-zero value makes deployment a bit more expensive,
  // but in exchange the refund on every call to nonReentrant will be lower in
  // amount. Since refunds are capped to a percentage of the total
  // transaction's gas, it is best to keep them low in cases like this one, to
  // increase the likelihood of the full refund coming into effect.
  uint256 private constant _NOT_ENTERED = 1;
  uint256 private constant _ENTERED = 2;

  uint256 private _status;

  constructor() {
    _status = _NOT_ENTERED;
  }

  /**
   * @dev Prevents a contract from calling itself, directly or indirectly.
   * Calling a `nonReentrant` function from another `nonReentrant`
   * function is not supported. It is possible to prevent this from happening
   * by making the `nonReentrant` function external, and make it call a
   * `private` function that does the actual work.
   */
  modifier nonReentrant() {
    // On the first call to nonReentrant, _notEntered will be true
    require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

    // Any calls to nonReentrant after this point will fail
    _status = _ENTERED;

    _;

    // By storing the original value once again, a refund is triggered (see
    // https://eips.ethereum.org/EIPS/eip-2200)
    _status = _NOT_ENTERED;
  }
}

File 6 of 20 : SyndicatePoolFactory.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.1;

import "../interfaces/IPool.sol";
import "./SyndicateAware.sol";
import "./SyndicateCorePool.sol";
import "../token/SyntheticSyndicateERC20.sol";
import "../utils/Ownable.sol";

import "hardhat/console.sol";

/**
 * @title Syndicate Pool Factory
 *        Original title: Illuvium Pool Factory
 *
 * @notice SYNR Pool Factory manages Syndicate Yield farming pools, provides a single
 *      public interface to access the pools, provides an interface for the pools
 *      to mint yield rewards, access pool-related info, update weights, etc.
 *
 * @notice The factory is authorized (via its owner) to register new pools, change weights
 *      of the existing pools, removing the pools (by changing their weights to zero)
 *
 * @dev The factory requires ROLE_TOKEN_CREATOR permission on the SYNR token to mint yield
 *      (see `mintYieldTo` function)
 *
 * @author Pedro Bergamini, reviewed by Basil Gori
 * Adapted for Syn City by Superpower Labs
 */
contract SyndicatePoolFactory is Ownable, SyndicateAware {
  /**
   * @dev Smart contract unique identifier, a random number
   * @dev Should be regenerated each time smart contact source code is changed
   *      and changes smart contract itself is to be redeployed
   * @dev Generated using https://www.random.org/bytes/
   */
  uint256 public constant FACTORY_UID = 0xc5cfd88c6e4d7e5c8a03c0f0f03af23c0918d8e82cac196f57466af3fd4a5ec7;

  // the pool contract's parameters cannot be changed once it's final
  bool public isFinal = false;

  /// @dev Auxiliary data structure used only in getPoolData() view function
  struct PoolData {
    // @dev pool token address (like SYNR)
    address poolToken;
    // @dev pool address (like deployed core pool instance)
    address poolAddress;
    // @dev pool weight (200 for SYNR pools, 800 for SYNR/ETH pools - set during deployment)
    uint32 weight;
    // @dev flash pool flag
    bool isFlashPool;
  }

  /**
   * @dev SYNR/block determines yield farming reward base
   *      used by the yield pools controlled by the factory
   */
  uint192 public synrPerBlock;

  /**
   * @dev The yield is distributed proportionally to pool weights;
   *      total weight is here to help in determining the proportion
   */
  uint32 public totalWeight;

  /**
   * @dev SYNR/block decreases by 3% every blocks/update (set to 91252 blocks during deployment);
   *      an update is triggered by executing `updateSYNPerBlock` public function
   */
  uint32 public blocksPerUpdate;

  /**
   * @dev End block is the last block when SYNR/block can be decreased;
   *      it is implied that yield farming stops after that block
   */
  uint32 public endBlock;

  uint32 public decayFactor = 97;

  /**
   * @dev Each time the SYNR/block ratio gets updated, the block number
   *      when the operation has occurred gets recorded into `lastRatioUpdate`
   * @dev This block number is then used to check if blocks/update `blocksPerUpdate`
   *      has passed when decreasing yield reward by 3%
   */
  uint32 public lastRatioUpdate;

  /// @dev sSYNR token address is used to create SYNR core pool(s)
  address public immutable ssynr;

  /// @dev Maps pool token address (like SYNR) -> pool address (like core pool instance)
  mapping(address => address) public pools;

  /// @dev Keeps track of registered pool addresses, maps pool address -> exists flag
  mapping(address => bool) public poolExists;

  modifier notFinal() {
    require(! isFinal, "pool is final");
    _;
  }

  /**
   * @dev Fired in createPool() and registerPool()
   *
   * @param _by an address which executed an action
   * @param poolToken pool token address (like SYNR)
   * @param poolAddress deployed pool instance address
   * @param weight pool weight
   * @param isFlashPool flag indicating if pool is a flash pool
   */
  event PoolRegistered(
    address indexed _by,
    address indexed poolToken,
    address indexed poolAddress,
    uint32 weight,
    bool isFlashPool
  );

  /**
   * @dev Fired in changePoolWeight()
   *
   * @param _by an address which executed an action
   * @param poolAddress deployed pool instance address
   * @param weight new pool weight
   */
  event WeightUpdated(address indexed _by, address indexed poolAddress, uint32 weight);

  /**
   * @dev Fired in updateSYNPerBlock()
   *
   * @param _by an address which executed an action
   * @param newSynPerBlock new SYNR/block value
   */
  event SynRatioUpdated(address indexed _by, uint256 newSynPerBlock);

  /**
   * @dev Creates/deploys a factory instance
   *
   * @param _synr SYNR ERC20 token address
   * @param _ssynr sSYNR ERC20 token address
   * @param _synrPerBlock initial SYNR/block value for rewards
   * @param _blocksPerUpdate how frequently the rewards gets updated (decreased by 3%), blocks
   * @param _initBlock block number to measure _blocksPerUpdate from
   * @param _endBlock block number when farming stops and rewards cannot be updated anymore
   */
  constructor(
    address _synr,
    address _ssynr,
    uint192 _synrPerBlock,
    uint32 _blocksPerUpdate,
    uint32 _initBlock,
    uint32 _endBlock
  ) SyndicateAware(_synr) {
    // verify the inputs are set
    require(_ssynr != address(0), "sSYNR address not set");
    require(_synrPerBlock > 0, "SYNR/block not set");
    require(_blocksPerUpdate > 0, "blocks/update not set");
    require(_initBlock > 0, "init block not set");
    require(_endBlock > _initBlock, "invalid end block: must be greater than init block");

    // verify sSYNR instance supplied
    require(
      SyntheticSyndicateERC20(_ssynr).TOKEN_UID() == 0xac3051b8d4f50966afb632468a4f61483ae6a953b74e387a01ef94316d6b7d62,
      "unexpected sSYNR TOKEN_UID"
    );

    // save the inputs into internal state variables
    ssynr = _ssynr;
    synrPerBlock = _synrPerBlock;
    blocksPerUpdate = _blocksPerUpdate;
    lastRatioUpdate = _initBlock;
    endBlock = _endBlock;
  }

  /**
   * @notice Given a pool token retrieves corresponding pool address
   *
   * @dev A shortcut for `pools` mapping
   *
   * @param poolToken pool token address (like SYNR) to query pool address for
   * @return pool address for the token specified
   */
  function getPoolAddress(address poolToken) external view returns (address) {
    // read the mapping and return
    return pools[poolToken];
  }

  /**
   * @notice Reads pool information for the pool defined by its pool token address,
   *      designed to simplify integration with the front ends
   *
   * @param _poolToken pool token address to query pool information for
   * @return pool information packed in a PoolData struct
   */
  function getPoolData(address _poolToken) public view returns (PoolData memory) {
    // get the pool address from the mapping
    address poolAddr = pools[_poolToken];

    // throw if there is no pool registered for the token specified
    require(poolAddr != address(0), "pool not found");

    // read pool information from the pool smart contract
    // via the pool interface (IPool)
    address poolToken = IPool(poolAddr).poolToken();
    bool isFlashPool = IPool(poolAddr).isFlashPool();
    uint32 weight = IPool(poolAddr).weight();

    // create the in-memory structure and return it
    return PoolData({poolToken: poolToken, poolAddress: poolAddr, weight: weight, isFlashPool: isFlashPool});
  }

  /**
   * @dev Verifies if `blocksPerUpdate` has passed since last SYNR/block
   *      ratio update and if SYNR/block reward can be decreased by 3%
   *
   * @return true if enough time has passed and `updateSYNPerBlock` can be executed
   */
  function shouldUpdateRatio() public view returns (bool) {
    // if yield farming period has ended
    if (blockNumber() > endBlock) {
      // SYNR/block reward cannot be updated anymore
      return false;
    }

    // check if blocks/update (91252 blocks) have passed since last update
    return blockNumber() >= lastRatioUpdate + blocksPerUpdate;
  }

  /**
   * @dev Creates a core pool (SyndicateCorePool) and registers it within the factory
   *
   * @dev Can be executed by the pool factory owner only
   *
   * @param poolToken pool token address (like SYNR, or SYNR/ETH pair)
   * @param initBlock init block to be used for the pool created
   * @param weight weight of the pool to be created
   */
  function createPool(
    address poolToken,
    uint64 initBlock,
    uint32 weight
  ) external virtual onlyOwner {
    // create/deploy new core pool instance
    IPool pool = new SyndicateCorePool(synr, ssynr, this, poolToken, initBlock, weight);

    // register it within a factory
    registerPool(address(pool));
  }

  /**
   * @dev Registers an already deployed pool instance within the factory
   *
   * @dev Can be executed by the pool factory owner only
   *
   * @param poolAddr address of the already deployed pool instance
   */
  function registerPool(address poolAddr) public onlyOwner {
    // read pool information from the pool smart contract
    // via the pool interface (IPool)
    address poolToken = IPool(poolAddr).poolToken();
    bool isFlashPool = IPool(poolAddr).isFlashPool();
    uint32 weight = IPool(poolAddr).weight();

    // ensure that the pool is not already registered within the factory
    require(pools[poolToken] == address(0), "this pool is already registered");

    // create pool structure, register it within the factory
    pools[poolToken] = poolAddr;
    poolExists[poolAddr] = true;
    // update total pool weight of the factory
    totalWeight += weight;

    // emit an event
    emit PoolRegistered(msg.sender, poolToken, poolAddr, weight, isFlashPool);
  }

  /**
   * @notice Decreases SYNR/block reward by 3%, can be executed
   *      no more than once per `blocksPerUpdate` blocks
   */
  function updateSYNPerBlock() external {
    // checks if ratio can be updated i.e. if blocks/update (91252 blocks) have passed
    require(shouldUpdateRatio(), "too frequent");

    // decreases SYNR/block reward by 3%
    synrPerBlock = (synrPerBlock * decayFactor) / 100;

    // set current block as the last ratio update block
    lastRatioUpdate = uint32(blockNumber());

    // emit an event
    emit SynRatioUpdated(msg.sender, synrPerBlock);
  }

  function overrideSYNPerBlock(uint192 _synrPerBlock) external onlyOwner notFinal {
    synrPerBlock = _synrPerBlock;
    // emit an event
    emit SynRatioUpdated(msg.sender, synrPerBlock);
  }

  function overrideBlockesPerUpdate(uint32 _blocksPerUpdate) external onlyOwner notFinal {
    blocksPerUpdate = _blocksPerUpdate;
  }

  function overrideEndblock(uint32 _endBlock) external onlyOwner notFinal {
    endBlock = _endBlock;
  }

  function overrideDecayFactor(uint32 _decayFactor) external onlyOwner notFinal {
    decayFactor = _decayFactor;
  }

  function finalizePool() external onlyOwner {
    isFinal = true;
  }

  /**
   * @dev Mints SYNR tokens; executed by SYNR Pool only
   *
   * @dev Requires factory to have ROLE_TOKEN_CREATOR permission
   *      on the SYNR ERC20 token instance
   *
   * @param _to an address to mint tokens to
   * @param _amount amount of SYNR tokens to mint
   */
  function mintYieldTo(address _to, uint256 _amount) external {
    // verify that sender is a pool registered withing the factory
    require(poolExists[msg.sender], "access denied");

    // mint SYNR tokens as required
    _mintSyn(_to, _amount);
  }

  /**
   * @dev Changes the weight of the pool;
   *      executed by the pool itself or by the factory owner
   *
   * @param poolAddr address of the pool to change weight for
   * @param weight new weight value to set to
   */
  function changePoolWeight(address poolAddr, uint32 weight) external {
    // verify function is executed either by factory owner or by the pool itself
    require(msg.sender == owner() || poolExists[msg.sender], "access denied");

    // recalculate total weight
    totalWeight = totalWeight + weight - IPool(poolAddr).weight();

    // set the new pool weight
    IPool(poolAddr).setWeight(weight);

    // emit an event
    emit WeightUpdated(msg.sender, poolAddr, weight);
  }

  /**
   * @dev Testing time-dependent functionality is difficult and the best way of
   *      doing it is to override block number in helper test smart contracts
   *
   * @return `block.number` in mainnet, custom values in testnets (if overridden)
   */
  function blockNumber() public view virtual returns (uint256) {
    // return current block number
    return block.number;
  }
}

File 7 of 20 : SafeERC20.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.1;

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

/**
 * @title SafeERC20
 * @dev 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 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));
  }

  /**
   * @dev Deprecated. This function has issues similar to the ones found in
   * {IERC20-approve}, and its usage is discouraged.
   *
   * Whenever possible, use {safeIncreaseAllowance} and
   * {safeDecreaseAllowance} instead.
   */
  function safeApprove(
    IERC20 token,
    address spender,
    uint256 value
  ) internal {
    // safeApprove should only be called when setting an initial allowance,
    // or when resetting it to zero. To increase and decrease it, use
    // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
    // solhint-disable-next-line max-line-length
    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 safeIncreaseAllowance(
    IERC20 token,
    address spender,
    uint256 value
  ) internal {
    uint256 newAllowance = token.allowance(address(this), spender) + value;
    _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
  }

  function safeDecreaseAllowance(
    IERC20 token,
    address spender,
    uint256 value
  ) internal {
    uint256 newAllowance = token.allowance(address(this), spender) - value;
    _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
  }

  /**
   * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
   * on the return value: the return value is optional (but if data is returned, it must not be false).
   * @param token The token targeted by the call.
   * @param data The call data (encoded using abi.encode or one of its variants).
   */
  function _callOptionalReturn(IERC20 token, bytes memory data) private {
    // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
    // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
    // the target address contains contract code and also asserts for success in the low-level call.
    bytes memory returndata = address(token).functionCall(data, "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 8 of 20 : SyntheticSyndicateERC20.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.1;

import "../utils/ERC20.sol";
import "../utils/AccessControl.sol";

contract SyntheticSyndicateERC20 is ERC20("Synthetic Syndicate Token", "sSYNR"), AccessControl {
  /**
   * @dev Smart contract unique identifier, a random number
   * @dev Should be regenerated each time smart contact source code is changed
   *      and changes smart contract itself is to be redeployed
   * @dev Generated using https://www.random.org/bytes/
   */
  uint256 public constant TOKEN_UID = 0xac3051b8d4f50966afb632468a4f61483ae6a953b74e387a01ef94316d6b7d62;

  uint32 public constant ROLE_TOKEN_DESTROYER = 0x0002_0000;

  uint32 public constant ROLE_WHITE_LISTED_RECEIVER = 0x0004_0000;

  constructor(address _superAdmin) AccessControl(_superAdmin) {}

  /**
   * @notice Must be called by ROLE_TOKEN_CREATOR addresses.
   *
   * @param recipient address to receive the tokens.
   * @param amount number of tokens to be minted.
   */
  function mint(address recipient, uint256 amount) external {
    require(isSenderInRole(ROLE_TOKEN_CREATOR), "sSYNR: insufficient privileges (ROLE_TOKEN_CREATOR required)");
    _mint(recipient, amount);
  }

  /**
   * @param amount number of tokens to be burned.
   */
  function burn(uint256 amount) external {
    _burn(msg.sender, amount);
  }

  /**
   * @notice Must be called by ROLE_TOKEN_DESTROYER addresses (SynrSwapper)
   *         Can burn only tokens owned by ROLE_WHITE_LISTED_RECEIVER address
   * @param recipient address to burn the tokens.
   * @param amount number of tokens to be burned
   */
  function burn(address recipient, uint256 amount) external {
    require(isSenderInRole(ROLE_TOKEN_DESTROYER), "sSYNR: insufficient privileges (ROLE_TOKEN_DESTROYER required)");
    require(isOperatorInRole(recipient, ROLE_WHITE_LISTED_RECEIVER), "sSYNR: Non Allowed Receiver");
    _burn(recipient, amount);
  }

  function _transfer(
    address sender,
    address recipient,
    uint256 amount
  ) internal virtual override {
    require(isOperatorInRole(recipient, ROLE_WHITE_LISTED_RECEIVER), "sSYNR: Non Allowed Receiver");
    super._transfer(sender, recipient, amount);
  }
}

File 9 of 20 : IMigrator.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.1;
import "./IPool.sol";

interface IMigrator {
  function receiveDeposits(address _staker, IPool.User memory _user) external;
}

File 10 of 20 : ILinkedToSYN.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.1;

/**
 * @title Linked to SYNR Marker Interface
 *        Original title: Linked to ILV Marker Interface
 *
 * @notice Marks smart contracts which are linked to SyndicateERC20 token instance upon construction,
 *      all these smart contracts share a common synr() address getter
 *
 * @notice Implementing smart contracts MUST verify that they get linked to real SyndicateERC20 instance
 *      and that synr() getter returns this very same instance address
 * @author Basil Gorin
 * Adapted for Syn City by Superpower Labs
 */
interface ILinkedToSYN {
  /**
   * @notice Getter for a verified SyndicateERC20 instance address
   *
   * @return SyndicateERC20 token instance address smart contract is linked to
   */
  function synr() external view returns (address);
}

File 11 of 20 : SyndicateAware.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.1;

import "../token/SyndicateERC20.sol";
import "../interfaces/ILinkedToSYN.sol";

/**
 * @title Syndicate Aware
 *        Original title: Illuvium Aware
 *
 * @notice Helper smart contract to be inherited by other smart contracts requiring to
 *      be linked to verified SyndicateERC20 instance and performing some basic tasks on it
 *
 * @author Basil Gorin
 * Adapted for Syn City by Superpower Labs
 */
abstract contract SyndicateAware is ILinkedToSYN {
  /// @dev Link to SYNR ERC20 Token SyndicateERC20 instance
  address public immutable override synr;

  /**
   * @dev Creates SyndicateAware instance, requiring to supply deployed SyndicateERC20 instance address
   *
   * @param _synr deployed SyndicateERC20 instance address
   */
  constructor(address _synr) {
    // verify SYNR address is set and is correct
    require(_synr != address(0), "SYNR address not set");
    require(
      SyndicateERC20(_synr).TOKEN_UID() == 0x83ecb176af7c4f35a45ff0018282e3a05a1018065da866182df12285866f5a2c,
      "unexpected TOKEN_UID"
    );

    // write SYNR address
    synr = _synr;
  }

  /**
   * @dev Executes SyndicateERC20.safeTransferFrom(address(this), _to, _value, "")
   *      on the bound SyndicateERC20 instance
   *
   * @dev Reentrancy safe due to the SyndicateERC20 design
   */
  function _transferSyn(address _to, uint256 _value) internal {
    // just delegate call to the target
    _transferSynFrom(address(this), _to, _value);
  }

  /**
   * @dev Executes SyndicateERC20.transferFrom(_from, _to, _value)
   *      on the bound SyndicateERC20 instance
   *
   * @dev Reentrancy safe due to the SyndicateERC20 design
   */
  function _transferSynFrom(
    address _from,
    address _to,
    uint256 _value
  ) internal {
    // just delegate call to the target
    SyndicateERC20(synr).transferFrom(_from, _to, _value);
  }

  /**
   * @dev Executes SyndicateERC20.mint(_to, _values)
   *      on the bound SyndicateERC20 instance
   *
   * @dev Reentrancy safe due to the SyndicateERC20 design
   */
  function _mintSyn(address _to, uint256 _value) internal {
    // just delegate call to the target
    SyndicateERC20(synr).mint(_to, _value);
  }
}

File 12 of 20 : Ownable.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.1;

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

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

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

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

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

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

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

File 13 of 20 : console.sol
// SPDX-License-Identifier: MIT
pragma solidity >= 0.4.22 <0.9.0;

library console {
	address constant CONSOLE_ADDRESS = address(0x000000000000000000636F6e736F6c652e6c6f67);

	function _sendLogPayload(bytes memory payload) private view {
		uint256 payloadLength = payload.length;
		address consoleAddress = CONSOLE_ADDRESS;
		assembly {
			let payloadStart := add(payload, 32)
			let r := staticcall(gas(), consoleAddress, payloadStart, payloadLength, 0, 0)
		}
	}

	function log() internal view {
		_sendLogPayload(abi.encodeWithSignature("log()"));
	}

	function logInt(int p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(int)", p0));
	}

	function logUint(uint p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint)", p0));
	}

	function logString(string memory p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string)", p0));
	}

	function logBool(bool p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool)", p0));
	}

	function logAddress(address p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address)", p0));
	}

	function logBytes(bytes memory p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes)", p0));
	}

	function logBytes1(bytes1 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes1)", p0));
	}

	function logBytes2(bytes2 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes2)", p0));
	}

	function logBytes3(bytes3 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes3)", p0));
	}

	function logBytes4(bytes4 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes4)", p0));
	}

	function logBytes5(bytes5 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes5)", p0));
	}

	function logBytes6(bytes6 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes6)", p0));
	}

	function logBytes7(bytes7 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes7)", p0));
	}

	function logBytes8(bytes8 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes8)", p0));
	}

	function logBytes9(bytes9 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes9)", p0));
	}

	function logBytes10(bytes10 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes10)", p0));
	}

	function logBytes11(bytes11 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes11)", p0));
	}

	function logBytes12(bytes12 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes12)", p0));
	}

	function logBytes13(bytes13 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes13)", p0));
	}

	function logBytes14(bytes14 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes14)", p0));
	}

	function logBytes15(bytes15 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes15)", p0));
	}

	function logBytes16(bytes16 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes16)", p0));
	}

	function logBytes17(bytes17 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes17)", p0));
	}

	function logBytes18(bytes18 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes18)", p0));
	}

	function logBytes19(bytes19 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes19)", p0));
	}

	function logBytes20(bytes20 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes20)", p0));
	}

	function logBytes21(bytes21 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes21)", p0));
	}

	function logBytes22(bytes22 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes22)", p0));
	}

	function logBytes23(bytes23 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes23)", p0));
	}

	function logBytes24(bytes24 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes24)", p0));
	}

	function logBytes25(bytes25 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes25)", p0));
	}

	function logBytes26(bytes26 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes26)", p0));
	}

	function logBytes27(bytes27 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes27)", p0));
	}

	function logBytes28(bytes28 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes28)", p0));
	}

	function logBytes29(bytes29 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes29)", p0));
	}

	function logBytes30(bytes30 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes30)", p0));
	}

	function logBytes31(bytes31 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes31)", p0));
	}

	function logBytes32(bytes32 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes32)", p0));
	}

	function log(uint p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint)", p0));
	}

	function log(string memory p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string)", p0));
	}

	function log(bool p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool)", p0));
	}

	function log(address p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address)", p0));
	}

	function log(uint p0, uint p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint)", p0, p1));
	}

	function log(uint p0, string memory p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string)", p0, p1));
	}

	function log(uint p0, bool p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool)", p0, p1));
	}

	function log(uint p0, address p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address)", p0, p1));
	}

	function log(string memory p0, uint p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint)", p0, p1));
	}

	function log(string memory p0, string memory p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string)", p0, p1));
	}

	function log(string memory p0, bool p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool)", p0, p1));
	}

	function log(string memory p0, address p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address)", p0, p1));
	}

	function log(bool p0, uint p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint)", p0, p1));
	}

	function log(bool p0, string memory p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string)", p0, p1));
	}

	function log(bool p0, bool p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool)", p0, p1));
	}

	function log(bool p0, address p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address)", p0, p1));
	}

	function log(address p0, uint p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint)", p0, p1));
	}

	function log(address p0, string memory p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string)", p0, p1));
	}

	function log(address p0, bool p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool)", p0, p1));
	}

	function log(address p0, address p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address)", p0, p1));
	}

	function log(uint p0, uint p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint)", p0, p1, p2));
	}

	function log(uint p0, uint p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,string)", p0, p1, p2));
	}

	function log(uint p0, uint p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool)", p0, p1, p2));
	}

	function log(uint p0, uint p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,address)", p0, p1, p2));
	}

	function log(uint p0, string memory p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,uint)", p0, p1, p2));
	}

	function log(uint p0, string memory p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,string)", p0, p1, p2));
	}

	function log(uint p0, string memory p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,bool)", p0, p1, p2));
	}

	function log(uint p0, string memory p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,address)", p0, p1, p2));
	}

	function log(uint p0, bool p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint)", p0, p1, p2));
	}

	function log(uint p0, bool p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,string)", p0, p1, p2));
	}

	function log(uint p0, bool p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool)", p0, p1, p2));
	}

	function log(uint p0, bool p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,address)", p0, p1, p2));
	}

	function log(uint p0, address p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,uint)", p0, p1, p2));
	}

	function log(uint p0, address p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,string)", p0, p1, p2));
	}

	function log(uint p0, address p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,bool)", p0, p1, p2));
	}

	function log(uint p0, address p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,address)", p0, p1, p2));
	}

	function log(string memory p0, uint p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,uint)", p0, p1, p2));
	}

	function log(string memory p0, uint p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,string)", p0, p1, p2));
	}

	function log(string memory p0, uint p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,bool)", p0, p1, p2));
	}

	function log(string memory p0, uint p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,address)", p0, p1, p2));
	}

	function log(string memory p0, string memory p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,uint)", p0, p1, p2));
	}

	function log(string memory p0, string memory p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,string)", p0, p1, p2));
	}

	function log(string memory p0, string memory p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,bool)", p0, p1, p2));
	}

	function log(string memory p0, string memory p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,address)", p0, p1, p2));
	}

	function log(string memory p0, bool p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,uint)", p0, p1, p2));
	}

	function log(string memory p0, bool p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,string)", p0, p1, p2));
	}

	function log(string memory p0, bool p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,bool)", p0, p1, p2));
	}

	function log(string memory p0, bool p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,address)", p0, p1, p2));
	}

	function log(string memory p0, address p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,uint)", p0, p1, p2));
	}

	function log(string memory p0, address p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,string)", p0, p1, p2));
	}

	function log(string memory p0, address p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,bool)", p0, p1, p2));
	}

	function log(string memory p0, address p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,address)", p0, p1, p2));
	}

	function log(bool p0, uint p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint)", p0, p1, p2));
	}

	function log(bool p0, uint p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,string)", p0, p1, p2));
	}

	function log(bool p0, uint p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool)", p0, p1, p2));
	}

	function log(bool p0, uint p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,address)", p0, p1, p2));
	}

	function log(bool p0, string memory p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,uint)", p0, p1, p2));
	}

	function log(bool p0, string memory p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,string)", p0, p1, p2));
	}

	function log(bool p0, string memory p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,bool)", p0, p1, p2));
	}

	function log(bool p0, string memory p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,address)", p0, p1, p2));
	}

	function log(bool p0, bool p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint)", p0, p1, p2));
	}

	function log(bool p0, bool p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,string)", p0, p1, p2));
	}

	function log(bool p0, bool p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool)", p0, p1, p2));
	}

	function log(bool p0, bool p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,address)", p0, p1, p2));
	}

	function log(bool p0, address p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,uint)", p0, p1, p2));
	}

	function log(bool p0, address p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,string)", p0, p1, p2));
	}

	function log(bool p0, address p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,bool)", p0, p1, p2));
	}

	function log(bool p0, address p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,address)", p0, p1, p2));
	}

	function log(address p0, uint p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,uint)", p0, p1, p2));
	}

	function log(address p0, uint p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,string)", p0, p1, p2));
	}

	function log(address p0, uint p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,bool)", p0, p1, p2));
	}

	function log(address p0, uint p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,address)", p0, p1, p2));
	}

	function log(address p0, string memory p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,uint)", p0, p1, p2));
	}

	function log(address p0, string memory p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,string)", p0, p1, p2));
	}

	function log(address p0, string memory p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,bool)", p0, p1, p2));
	}

	function log(address p0, string memory p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,address)", p0, p1, p2));
	}

	function log(address p0, bool p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,uint)", p0, p1, p2));
	}

	function log(address p0, bool p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,string)", p0, p1, p2));
	}

	function log(address p0, bool p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,bool)", p0, p1, p2));
	}

	function log(address p0, bool p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,address)", p0, p1, p2));
	}

	function log(address p0, address p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,uint)", p0, p1, p2));
	}

	function log(address p0, address p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,string)", p0, p1, p2));
	}

	function log(address p0, address p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,bool)", p0, p1, p2));
	}

	function log(address p0, address p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,address)", p0, p1, p2));
	}

	function log(uint p0, uint p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, uint p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint,string)", p0, p1, p2, p3));
	}

	function log(uint p0, uint p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, uint p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint,address)", p0, p1, p2, p3));
	}

	function log(uint p0, uint p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,string,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, uint p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,string,string)", p0, p1, p2, p3));
	}

	function log(uint p0, uint p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,string,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, uint p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,string,address)", p0, p1, p2, p3));
	}

	function log(uint p0, uint p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, uint p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool,string)", p0, p1, p2, p3));
	}

	function log(uint p0, uint p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, uint p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool,address)", p0, p1, p2, p3));
	}

	function log(uint p0, uint p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,address,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, uint p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,address,string)", p0, p1, p2, p3));
	}

	function log(uint p0, uint p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,address,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, uint p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,address,address)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,uint,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,uint,string)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,uint,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,uint,address)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,string,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,string,string)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,string,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,string,address)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,bool,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,bool,string)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,bool,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,bool,address)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,address,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,address,string)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,address,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,address,address)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint,string)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint,address)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,string,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,string,string)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,string,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,string,address)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool,string)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool,address)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,address,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,address,string)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,address,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,address,address)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,uint,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,uint,string)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,uint,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,uint,address)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,string,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,string,string)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,string,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,string,address)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,bool,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,bool,string)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,bool,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,bool,address)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,address,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,address,string)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,address,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,address,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,string,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,string,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,string,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,string,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,bool,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,bool,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,bool,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,bool,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,address,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,address,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,address,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,address,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,uint,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,uint,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,uint,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,uint,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,string,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,string,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,string,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,string,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,bool,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,bool,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,bool,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,bool,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,address,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,address,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,address,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,address,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,uint,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,uint,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,uint,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,uint,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,string,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,string,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,string,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,string,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,address,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,address,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,address,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,address,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,uint,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,uint,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,uint,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,uint,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,string,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,string,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,string,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,string,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,bool,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,bool,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,bool,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,bool,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,address,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,address,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,address,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,address,address)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint,string)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint,address)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,string,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,string,string)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,string,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,string,address)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool,string)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool,address)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,address,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,address,string)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,address,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,address,address)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,uint,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,uint,string)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,uint,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,uint,address)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,string,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,string,string)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,string,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,string,address)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,string)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,address)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,address,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,address,string)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,address,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,address,address)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint,string)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint,address)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,string)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,address)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,string)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,address)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,string)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,address)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,uint,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,uint,string)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,uint,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,uint,address)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,string,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,string,string)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,string,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,string,address)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,string)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,address)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,address,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,address,string)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,address,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,address,address)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,uint,uint)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,uint,string)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,uint,bool)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,uint,address)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,string,uint)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,string,string)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,string,bool)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,string,address)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,bool,uint)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,bool,string)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,bool,bool)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,bool,address)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,address,uint)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,address,string)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,address,bool)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,address,address)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,uint,uint)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,uint,string)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,uint,bool)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,uint,address)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,string,uint)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,string,string)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,string,bool)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,string,address)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,bool,uint)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,bool,string)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,bool,bool)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,bool,address)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,address,uint)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,address,string)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,address,bool)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,address,address)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,uint,uint)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,uint,string)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,uint,bool)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,uint,address)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,string,uint)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,string,string)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,string,bool)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,string,address)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,uint)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,string)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,bool)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,address)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,address,uint)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,address,string)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,address,bool)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,address,address)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,uint,uint)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,uint,string)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,uint,bool)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,uint,address)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,string,uint)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,string,string)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,string,bool)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,string,address)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,bool,uint)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,bool,string)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,bool,bool)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,bool,address)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,address,uint)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,address,string)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,address,bool)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,address,address)", p0, p1, p2, p3));
	}

}

File 14 of 20 : SyndicateERC20.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.1;

import "../utils/AddressUtils.sol";
import "../utils/AccessControl.sol";
import "./ERC20Receiver.sol";

/**
 * @title Syndicate (SYNR) ERC20 token
 *        Original title: Illuvium (ILV) ERC20 token
 *
 * @notice Syndicate is a core ERC20 token powering the game.
 *      It serves as an in-game currency, is tradable on exchanges,
 *      it powers up the governance protocol (Syndicate DAO) and participates in Yield Farming.
 *
 * @dev Token Summary:
 *      - Symbol: SYNR
 *      - Name: Syndicate
 *      - Decimals: 18
 *      - Initial token supply: 9,000,000,000 SYNR
 *      - Maximum final token supply: 10,000,000,000 SYNR
 *          - Up to 1,000,000,000 SYNR may get minted in 3 years period via yield farming
 *      - Mintable: total supply may increase
 *      - Burnable: total supply may decrease
 *
 * @dev ?? Token balances and total supply are effectively 192 bits long, meaning that maximum
 *      possible total supply smart contract is able to track is 2^192 (close to 10^40 tokens)
 *
 * @dev Smart contract doesn't use safe math. All arithmetic operations are overflow/underflow safe.
 *      Additionally, Solidity 0.8.1 enforces overflow/underflow safety.
 *
 * @dev ERC20: reviewed according to https://eips.ethereum.org/EIPS/eip-20
 *
 * @dev ERC20: contract has passed OpenZeppelin ERC20 tests,
 *      see https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/test/token/ERC20/ERC20.behavior.js
 *      see https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/test/token/ERC20/ERC20.test.js
 *      see adopted copies of these tests in the `test` folder
 *
 * @dev ERC223/ERC777: not supported;
 *      send tokens via `safeTransferFrom` and implement `ERC20Receiver.onERC20Received` on the receiver instead
 *
 * @dev Multiple Withdrawal Attack on ERC20 Tokens (ISBN:978-1-7281-3027-9) - resolved
 *      Related events and functions are marked with "ISBN:978-1-7281-3027-9" tag:
 *        - event Transferred(address indexed _by, address indexed _from, address indexed _to, uint256 _value)
 *        - event Approved(address indexed _owner, address indexed _spender, uint256 _oldValue, uint256 _value)
 *        - function increaseAllowance(address _spender, uint256 _value) public returns (bool)
 *        - function decreaseAllowance(address _spender, uint256 _value) public returns (bool)
 *      See: https://ieeexplore.ieee.org/document/8802438
 *      See: https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
 *
 * @author Basil Gorin
 * Adapted for Syn City by Superpower Labs
 */
contract SyndicateERC20 is AccessControl {
  /**
   * @dev Smart contract unique identifier, a random number
   * @dev Should be regenerated each time smart contact source code is changed
   *      and changes smart contract itself is to be redeployed
   * @dev Generated using https://www.random.org/bytes/
   */
  uint256 public constant TOKEN_UID = 0x83ecb176af7c4f35a45ff0018282e3a05a1018065da866182df12285866f5a2c;

  /**
   * @notice Name of the token: Syndicate
   *
   * @notice ERC20 name of the token (long name)
   *
   * @dev ERC20 `function name() public view returns (string)`
   *
   * @dev Field is declared public: getter name() is created when compiled,
   *      it returns the name of the token.
   */
  // solhint-disable-next-line
  string public constant name = "Syndicate Token";

  /**
   * @notice Symbol of the token: SYNR
   *
   * @notice ERC20 symbol of that token (short name)
   *
   * @dev ERC20 `function symbol() public view returns (string)`
   *
   * @dev Field is declared public: getter symbol() is created when compiled,
   *      it returns the symbol of the token
   */
  // solhint-disable-next-line
  string public symbol = "SYNR";

  event SymbolUpdated(string _symbol);

  /**
   * @notice Decimals of the token: 18
   *
   * @dev ERC20 `function decimals() public view returns (uint8)`
   *
   * @dev Field is declared public: getter decimals() is created when compiled,
   *      it returns the number of decimals used to get its user representation.
   *      For example, if `decimals` equals `6`, a balance of `1,500,000` tokens should
   *      be displayed to a user as `1,5` (`1,500,000 / 10 ** 6`).
   *
   * @dev NOTE: This information is only used for _display_ purposes: it in
   *      no way affects any of the arithmetic of the contract, including balanceOf() and transfer().
   */
  // solhint-disable-next-line
  uint8 public constant decimals = 18;

  /**
   * @notice Max total supply of the token: initially 70% of that max,
   *      with the potential to grow up to the max during yield farming period (3 years)
   *
   * @dev ERC20 `function totalSupply() public view returns (uint256)`
   *
   * @dev Field is declared public: getter totalSupply() is created when compiled,
   *      it returns the amount of tokens in existence.
   */
  uint256 public maxTotalSupply; // is set up in the constructor

  /**
   * @notice Total supply of the token: initially 70% of maxTotalSupply
   *      with the potential to grow up maxTotalSupply during yield farming period (3 years)
   *
   * @dev ERC20 `function totalSupply() public view returns (uint256)`
   *
   * @dev Field is declared public: getter totalSupply() is created when compiled,
   *      it returns the amount of tokens in existence.
   */
  uint256 public totalSupply; // is set to 700 million * 10^18 in the constructor

  /**
   * @dev A record of all the token balances
   * @dev This mapping keeps record of all token owners:
   *      owner => balance
   */
  mapping(address => uint256) public tokenBalances;

  /**
   * @notice A record of each account's voting delegate
   *
   * @dev Auxiliary data structure used to sum up an account's voting power
   *
   * @dev This mapping keeps record of all voting power delegations:
   *      voting delegator (token owner) => voting delegate
   */
  mapping(address => address) public votingDelegates;

  /**
   * @notice A voting power record binds voting power of a delegate to a particular
   *      block when the voting power delegation change happened
   */
  struct VotingPowerRecord {
    /*
     * @dev block.number when delegation has changed; starting from
     *      that block voting power value is in effect
     */
    uint64 blockNumber;
    /*
     * @dev cumulative voting power a delegate has obtained starting
     *      from the block stored in blockNumber
     */
    uint192 votingPower;
  }

  /**
   * @notice A record of each account's voting power
   *
   * @dev Primarily data structure to store voting power for each account.
   *      Voting power sums up from the account's token balance and delegated
   *      balances.
   *
   * @dev Stores current value and entire history of its changes.
   *      The changes are stored as an array of checkpoints.
   *      Checkpoint is an auxiliary data structure containing voting
   *      power (number of votes) and block number when the checkpoint is saved
   *
   * @dev Maps voting delegate => voting power record
   */
  mapping(address => VotingPowerRecord[]) public votingPowerHistory;

  /**
   * @dev A record of nonces for signing/validating signatures in `delegateWithSig`
   *      for every delegate, increases after successful validation
   *
   * @dev Maps delegate address => delegate nonce
   */
  mapping(address => uint256) public nonces;

  /**
   * @notice A record of all the allowances to spend tokens on behalf
   * @dev Maps token owner address to an address approved to spend
   *      some tokens on behalf, maps approved address to that amount
   * @dev owner => spender => value
   */
  mapping(address => mapping(address => uint256)) public transferAllowances;

  /**
   * @notice Enables ERC20 transfers of the tokens
   *      (transfer by the token owner himself)
   * @dev Feature FEATURE_TRANSFERS must be enabled in order for
   *      `transfer()` function to succeed
   */
  uint32 public constant FEATURE_TRANSFERS = 0x0000_0001;

  /**
   * @notice Enables ERC20 transfers on behalf
   *      (transfer by someone else on behalf of token owner)
   * @dev Feature FEATURE_TRANSFERS_ON_BEHALF must be enabled in order for
   *      `transferFrom()` function to succeed
   * @dev Token owner must call `approve()` first to authorize
   *      the transfer on behalf
   */
  uint32 public constant FEATURE_TRANSFERS_ON_BEHALF = 0x0000_0002;

  /**
   * @dev Defines if the default behavior of `transfer` and `transferFrom`
   *      checks if the receiver smart contract supports ERC20 tokens
   * @dev When feature FEATURE_UNSAFE_TRANSFERS is enabled the transfers do not
   *      check if the receiver smart contract supports ERC20 tokens,
   *      i.e. `transfer` and `transferFrom` behave like `unsafeTransferFrom`
   * @dev When feature FEATURE_UNSAFE_TRANSFERS is disabled (default) the transfers
   *      check if the receiver smart contract supports ERC20 tokens,
   *      i.e. `transfer` and `transferFrom` behave like `safeTransferFrom`
   */
  uint32 public constant FEATURE_UNSAFE_TRANSFERS = 0x0000_0004;

  /**
   * @notice Enables token owners to burn their own tokens,
   *      including locked tokens which are burnt first
   * @dev Feature FEATURE_OWN_BURNS must be enabled in order for
   *      `burn()` function to succeed when called by token owner
   */
  uint32 public constant FEATURE_OWN_BURNS = 0x0000_0008;

  /**
   * @notice Enables approved operators to burn tokens on behalf of their owners,
   *      including locked tokens which are burnt first
   * @dev Feature FEATURE_OWN_BURNS must be enabled in order for
   *      `burn()` function to succeed when called by approved operator
   */
  uint32 public constant FEATURE_BURNS_ON_BEHALF = 0x0000_0010;

  /**
   * @notice Enables delegators to elect delegates
   * @dev Feature FEATURE_DELEGATIONS must be enabled in order for
   *      `delegate()` function to succeed
   */
  uint32 public constant FEATURE_DELEGATIONS = 0x0000_0020;

  /**
   * @notice Enables delegators to elect delegates on behalf
   *      (via an EIP712 signature)
   * @dev Feature FEATURE_DELEGATIONS must be enabled in order for
   *      `delegateWithSig()` function to succeed
   */
  uint32 public constant FEATURE_DELEGATIONS_ON_BEHALF = 0x0000_0040;

  /**
   * @notice Token creator is responsible for creating (minting)
   *      tokens to an arbitrary address
   * @dev Role ROLE_TOKEN_CREATOR allows minting tokens
   *      (calling `mint` function)
   */
  uint32 public constant ROLE_TOKEN_CREATOR = 0x0001_0000;

  /**
   * @notice Token destroyer is responsible for destroying (burning)
   *      tokens owned by an arbitrary address
   * @dev Role ROLE_TOKEN_DESTROYER allows burning tokens
   *      (calling `burn` function)
   */
  uint32 public constant ROLE_TOKEN_DESTROYER = 0x0002_0000;

  /**
   * @notice ERC20 receivers are allowed to receive tokens without ERC20 safety checks,
   *      which may be useful to simplify tokens transfers into "legacy" smart contracts
   * @dev When `FEATURE_UNSAFE_TRANSFERS` is not enabled addresses having
   *      `ROLE_ERC20_RECEIVER` permission are allowed to receive tokens
   *      via `transfer` and `transferFrom` functions in the same way they
   *      would via `unsafeTransferFrom` function
   * @dev When `FEATURE_UNSAFE_TRANSFERS` is enabled `ROLE_ERC20_RECEIVER` permission
   *      doesn't affect the transfer behaviour since
   *      `transfer` and `transferFrom` behave like `unsafeTransferFrom` for any receiver
   * @dev ROLE_ERC20_RECEIVER is a shortening for ROLE_UNSAFE_ERC20_RECEIVER
   */
  uint32 public constant ROLE_ERC20_RECEIVER = 0x0004_0000;

  /**
   * @notice ERC20 senders are allowed to send tokens without ERC20 safety checks,
   *      which may be useful to simplify tokens transfers into "legacy" smart contracts
   * @dev When `FEATURE_UNSAFE_TRANSFERS` is not enabled senders having
   *      `ROLE_ERC20_SENDER` permission are allowed to send tokens
   *      via `transfer` and `transferFrom` functions in the same way they
   *      would via `unsafeTransferFrom` function
   * @dev When `FEATURE_UNSAFE_TRANSFERS` is enabled `ROLE_ERC20_SENDER` permission
   *      doesn't affect the transfer behaviour since
   *      `transfer` and `transferFrom` behave like `unsafeTransferFrom` for any receiver
   * @dev ROLE_ERC20_SENDER is a shortening for ROLE_UNSAFE_ERC20_SENDER
   */
  uint32 public constant ROLE_ERC20_SENDER = 0x0008_0000;

  uint32 public constant ROLE_WHITE_LISTED_SPENDER = 0x0010_0000;

  /**
   * @notice White listed treasury can swap sSYNR to receive SYNR
   * @dev Role ROLE_TREASURY can get SYNR in exchange for sSYNR
   */
  uint32 public constant ROLE_TREASURY = 0x0020_0000;

  /**
   * @dev Magic value to be returned by ERC20Receiver upon successful reception of token(s)
   * @dev Equal to `bytes4(keccak256("onERC20Received(address,address,uint256,bytes)"))`,
   *      which can be also obtained as `ERC20Receiver(address(0)).onERC20Received.selector`
   */
  // solhint-disable-next-line
  bytes4 private constant ERC20_RECEIVED = 0x4fc35859;

  /**
   * @notice EIP-712 contract's domain typeHash, see https://eips.ethereum.org/EIPS/eip-712#rationale-for-typehash
   */
  bytes32 public constant DOMAIN_TYPEHASH = keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)");

  /**
   * @notice EIP-712 delegation struct typeHash, see https://eips.ethereum.org/EIPS/eip-712#rationale-for-typehash
   */
  bytes32 public constant DELEGATION_TYPEHASH = keccak256("Delegation(address delegate,uint256 nonce,uint256 expiry)");

  /**
   * @dev Fired in transfer(), transferFrom() and some other (non-ERC20) functions
   *
   * @dev ERC20 `event Transfer(address indexed _from, address indexed _to, uint256 _value)`
   *
   * @param _from an address tokens were consumed from
   * @param _to an address tokens were sent to
   * @param _value number of tokens transferred
   */
  event Transfer(address indexed _from, address indexed _to, uint256 _value);

  /**
   * @dev Fired in approve() and approveAtomic() functions
   *
   * @dev ERC20 `event Approval(address indexed _owner, address indexed _spender, uint256 _value)`
   *
   * @param _owner an address which granted a permission to transfer
   *      tokens on its behalf
   * @param _spender an address which received a permission to transfer
   *      tokens on behalf of the owner `_owner`
   * @param _value amount of tokens granted to transfer on behalf
   */
  event Approval(address indexed _owner, address indexed _spender, uint256 _value);

  /**
   * @dev Fired in mint() function
   *
   * @param _by an address which minted some tokens (transaction sender)
   * @param _to an address the tokens were minted to
   * @param _value an amount of tokens minted
   */
  event Minted(address indexed _by, address indexed _to, uint256 _value);

  /**
   * @dev Fired in burn() function
   *
   * @param _by an address which burned some tokens (transaction sender)
   * @param _from an address the tokens were burnt from
   * @param _value an amount of tokens burnt
   */
  event Burnt(address indexed _by, address indexed _from, uint256 _value);

  /**
   * @dev Resolution for the Multiple Withdrawal Attack on ERC20 Tokens (ISBN:978-1-7281-3027-9)
   *
   * @dev Similar to ERC20 Transfer event, but also logs an address which executed transfer
   *
   * @dev Fired in transfer(), transferFrom() and some other (non-ERC20) functions
   *
   * @param _by an address which performed the transfer
   * @param _from an address tokens were consumed from
   * @param _to an address tokens were sent to
   * @param _value number of tokens transferred
   */
  event Transferred(address indexed _by, address indexed _from, address indexed _to, uint256 _value);

  /**
   * @dev Resolution for the Multiple Withdrawal Attack on ERC20 Tokens (ISBN:978-1-7281-3027-9)
   *
   * @dev Similar to ERC20 Approve event, but also logs old approval value
   *
   * @dev Fired in approve() and approveAtomic() functions
   *
   * @param _owner an address which granted a permission to transfer
   *      tokens on its behalf
   * @param _spender an address which received a permission to transfer
   *      tokens on behalf of the owner `_owner`
   * @param _oldValue previously granted amount of tokens to transfer on behalf
   * @param _value new granted amount of tokens to transfer on behalf
   */
  event Approved(address indexed _owner, address indexed _spender, uint256 _oldValue, uint256 _value);

  /**
   * @dev Notifies that a key-value pair in `votingDelegates` mapping has changed,
   *      i.e. a delegator address has changed its delegate address
   *
   * @param _of delegator address, a token owner
   * @param _from old delegate, an address which delegate right is revoked
   * @param _to new delegate, an address which received the voting power
   */
  event DelegateChanged(address indexed _of, address indexed _from, address indexed _to);

  /**
   * @dev Notifies that a key-value pair in `votingPowerHistory` mapping has changed,
   *      i.e. a delegate's voting power has changed.
   *
   * @param _of delegate whose voting power has changed
   * @param _fromVal previous number of votes delegate had
   * @param _toVal new number of votes delegate has
   */
  event VotingPowerChanged(address indexed _of, uint256 _fromVal, uint256 _toVal);

  /**
   * @dev Deploys the token smart contract,
   *      assigns initial token supply to the address specified
   *
   * @param _initialHolder owner of the initial token supply
   * @param _maxTotalSupply max token supply without decimals
   * @param _superAdmin the contract admin (Gnosis Safe 0x8e75AEe46961019A66A88670ec18f022B8ef815E)
   */
  constructor(
    address _initialHolder,
    uint256 _maxTotalSupply,
    address _superAdmin
  ) AccessControl(_superAdmin) {
    // verify initial holder address non-zero (is set)
    require(_initialHolder != address(0), "_initialHolder not set (zero address)");
    require(_maxTotalSupply >= 10e9, "_maxTotalSupply less than minimum accepted amount");

    maxTotalSupply = _maxTotalSupply * 10**18;
    // mint initial supply
    mint(_initialHolder, (maxTotalSupply * 9) / 10);
  }

  // ===== Start: ERC20/ERC223/ERC777 functions =====

  /**
   * @notice Gets the balance of a particular address
   *
   * @dev ERC20 `function balanceOf(address _owner) public view returns (uint256 balance)`
   *
   * @param _owner the address to query the the balance for
   * @return balance an amount of tokens owned by the address specified
   */
  function balanceOf(address _owner) public view returns (uint256 balance) {
    // read the balance and return
    return tokenBalances[_owner];
  }

  /**
   * @notice Transfers some tokens to an external address or a smart contract
   *
   * @dev ERC20 `function transfer(address _to, uint256 _value) public returns (bool success)`
   *
   * @dev Called by token owner (an address which has a
   *      positive token balance tracked by this smart contract)
   * @dev Throws on any error like
   *      * insufficient token balance or
   *      * incorrect `_to` address:
   *          * zero address or
   *          * self address or
   *          * smart contract which doesn't support ERC20
   *
   * @param _to an address to transfer tokens to,
   *      must be either an external address or a smart contract,
   *      compliant with the ERC20 standard
   * @param _value amount of tokens to be transferred, must
   *      be greater than zero
   * @return success true on success, throws otherwise
   */
  function transfer(address _to, uint256 _value) public returns (bool success) {
    // just delegate call to `transferFrom`,
    // `FEATURE_TRANSFERS` is verified inside it
    return transferFrom(msg.sender, _to, _value);
  }

  function updateSymbol(string memory _symbol) external {
    // caller must have a permission to update user roles
    require(isSenderInRole(ROLE_ACCESS_MANAGER), "insufficient privileges (ROLE_ACCESS_MANAGER required)");
    symbol = _symbol;
    // fire an event
    emit SymbolUpdated(_symbol);
  }

  /**
   * @notice Transfers some tokens on behalf of address `_from' (token owner)
   *      to some other address `_to`
   *
   * @dev ERC20 `function transferFrom(address _from, address _to, uint256 _value) public returns (bool success)`
   *
   * @dev Called by token owner on his own or approved address,
   *      an address approved earlier by token owner to
   *      transfer some amount of tokens on its behalf
   * @dev Throws on any error like
   *      * insufficient token balance or
   *      * incorrect `_to` address:
   *          * zero address or
   *          * same as `_from` address (self transfer)
   *          * smart contract which doesn't support ERC20
   *
   * @param _from token owner which approved caller (transaction sender)
   *      to transfer `_value` of tokens on its behalf
   * @param _to an address to transfer tokens to,
   *      must be either an external address or a smart contract,
   *      compliant with the ERC20 standard
   * @param _value amount of tokens to be transferred, must
   *      be greater than zero
   * @return success true on success, throws otherwise
   */
  function transferFrom(
    address _from,
    address _to,
    uint256 _value
  ) public returns (bool success) {
    // depending on `FEATURE_UNSAFE_TRANSFERS` we execute either safe (default)
    // or unsafe transfer
    // if `FEATURE_UNSAFE_TRANSFERS` is enabled
    // or receiver has `ROLE_ERC20_RECEIVER` permission
    // or sender has `ROLE_ERC20_SENDER` permission
    if (
      isFeatureEnabled(FEATURE_UNSAFE_TRANSFERS) ||
      isOperatorInRole(_to, ROLE_ERC20_RECEIVER) ||
      isSenderInRole(ROLE_ERC20_SENDER)
    ) {
      // we execute unsafe transfer - delegate call to `unsafeTransferFrom`,
      // `FEATURE_TRANSFERS` is verified inside it
      unsafeTransferFrom(_from, _to, _value);
    }
    // otherwise - if `FEATURE_UNSAFE_TRANSFERS` is disabled
    // and receiver doesn't have `ROLE_ERC20_RECEIVER` permission
    else {
      // we execute safe transfer - delegate call to `safeTransferFrom`, passing empty `_data`,
      // `FEATURE_TRANSFERS` is verified inside it
      safeTransferFrom(_from, _to, _value, "");
    }

    // both `unsafeTransferFrom` and `safeTransferFrom` throw on any error, so
    // if we're here - it means operation successful,
    // just return true
    return true;
  }

  /**
   * @notice Transfers some tokens on behalf of address `_from' (token owner)
   *      to some other address `_to`
   *
   * @dev Inspired by ERC721 safeTransferFrom, this function allows to
   *      send arbitrary data to the receiver on successful token transfer
   * @dev Called by token owner on his own or approved address,
   *      an address approved earlier by token owner to
   *      transfer some amount of tokens on its behalf
   * @dev Throws on any error like
   *      * insufficient token balance or
   *      * incorrect `_to` address:
   *          * zero address or
   *          * same as `_from` address (self transfer)
   *          * smart contract which doesn't support ERC20Receiver interface
   * @dev Returns silently on success, throws otherwise
   *
   * @param _from token owner which approved caller (transaction sender)
   *      to transfer `_value` of tokens on its behalf
   * @param _to an address to transfer tokens to,
   *      must be either an external address or a smart contract,
   *      compliant with the ERC20 standard
   * @param _value amount of tokens to be transferred, must
   *      be greater than zero
   * @param _data [optional] additional data with no specified format,
   *      sent in onERC20Received call to `_to` in case if its a smart contract
   */
  function safeTransferFrom(
    address _from,
    address _to,
    uint256 _value,
    bytes memory _data
  ) public {
    // first delegate call to `unsafeTransferFrom`
    // to perform the unsafe token(s) transfer
    unsafeTransferFrom(_from, _to, _value);

    // after the successful transfer - check if receiver supports
    // ERC20Receiver and execute a callback handler `onERC20Received`,
    // reverting whole transaction on any error:
    // check if receiver `_to` supports ERC20Receiver interface
    if (AddressUtils.isContract(_to)) {
      // if `_to` is a contract - execute onERC20Received
      bytes4 response = ERC20Receiver(_to).onERC20Received(msg.sender, _from, _value, _data);

      // expected response is ERC20_RECEIVED
      require(response == ERC20_RECEIVED, "invalid onERC20Received response");
    }
  }

  /**
   * @notice Transfers some tokens on behalf of address `_from' (token owner)
   *      to some other address `_to`
   *
   * @dev In contrast to `safeTransferFrom` doesn't check recipient
   *      smart contract to support ERC20 tokens (ERC20Receiver)
   * @dev Designed to be used by developers when the receiver is known
   *      to support ERC20 tokens but doesn't implement ERC20Receiver interface
   * @dev Called by token owner on his own or approved address,
   *      an address approved earlier by token owner to
   *      transfer some amount of tokens on its behalf
   * @dev Throws on any error like
   *      * insufficient token balance or
   *      * incorrect `_to` address:
   *          * zero address or
   *          * same as `_from` address (self transfer)
   * @dev Returns silently on success, throws otherwise
   *
   * @param _from token owner which approved caller (transaction sender)
   *      to transfer `_value` of tokens on its behalf
   * @param _to an address to transfer tokens to,
   *      must be either an external address or a smart contract,
   *      compliant with the ERC20 standard
   * @param _value amount of tokens to be transferred, must
   *      be greater than zero
   */
  function unsafeTransferFrom(
    address _from,
    address _to,
    uint256 _value
  ) public {
    // if `_from` is equal to sender, require transfers feature to be enabled
    // otherwise require transfers on behalf feature to be enabled
    require(
      (_from == msg.sender && isFeatureEnabled(FEATURE_TRANSFERS)) ||
        (_from != msg.sender && (isFeatureEnabled(FEATURE_TRANSFERS_ON_BEHALF) || isSenderInRole(ROLE_WHITE_LISTED_SPENDER))),
      _from == msg.sender ? "transfers are disabled" : "transfers on behalf are disabled"
    );
    // non-zero source address check - Zeppelin
    // obviously, zero source address is a client mistake
    // it's not part of ERC20 standard but it's reasonable to fail fast
    // since for zero value transfer transaction succeeds otherwise
    require(_from != address(0), "SYNR: transfer from the zero address"); // Zeppelin msg

    // non-zero recipient address check
    require(_to != address(0), "SYNR: transfer to the zero address"); // Zeppelin msg

    // sender and recipient cannot be the same
    require(_from != _to, "sender and recipient are the same (_from = _to)");

    // sending tokens to the token smart contract itself is a client mistake
    require(_to != address(this), "invalid recipient (transfer to the token smart contract itself)");

    // according to ERC-20 Token Standard, https://eips.ethereum.org/EIPS/eip-20
    // "Transfers of 0 values MUST be treated as normal transfers and fire the Transfer event."
    if (_value == 0) {
      // emit an ERC20 transfer event
      emit Transfer(_from, _to, _value);

      // don't forget to return - we're done
      return;
    }

    // no need to make arithmetic overflow check on the _value - by design of mint()

    // in case of transfer on behalf
    if (_from != msg.sender) {
      // read allowance value - the amount of tokens allowed to transfer - into the stack
      uint256 _allowance = transferAllowances[_from][msg.sender];

      // verify sender has an allowance to transfer amount of tokens requested
      require(_allowance >= _value, "SYNR: transfer amount exceeds allowance"); // Zeppelin msg

      // update allowance value on the stack
      _allowance -= _value;

      // update the allowance value in storage
      transferAllowances[_from][msg.sender] = _allowance;

      // emit an improved atomic approve event
      emit Approved(_from, msg.sender, _allowance + _value, _allowance);

      // emit an ERC20 approval event to reflect the decrease
      emit Approval(_from, msg.sender, _allowance);
    }

    // verify sender has enough tokens to transfer on behalf
    require(tokenBalances[_from] >= _value, "SYNR: transfer amount exceeds balance"); // Zeppelin msg

    // perform the transfer:
    // decrease token owner (sender) balance
    tokenBalances[_from] -= _value;

    // increase `_to` address (receiver) balance
    tokenBalances[_to] += _value;

    // move voting power associated with the tokens transferred
    __moveVotingPower(votingDelegates[_from], votingDelegates[_to], _value);

    // emit an improved transfer event
    emit Transferred(msg.sender, _from, _to, _value);

    // emit an ERC20 transfer event
    emit Transfer(_from, _to, _value);
  }

  /**
   * @notice Approves address called `_spender` to transfer some amount
   *      of tokens on behalf of the owner
   *
   * @dev ERC20 `function approve(address _spender, uint256 _value) public returns (bool success)`
   *
   * @dev Caller must not necessarily own any tokens to grant the permission
   *
   * @param _spender an address approved by the caller (token owner)
   *      to spend some tokens on its behalf
   * @param _value an amount of tokens spender `_spender` is allowed to
   *      transfer on behalf of the token owner
   * @return success true on success, throws otherwise
   */
  function approve(address _spender, uint256 _value) public returns (bool success) {
    // non-zero spender address check - Zeppelin
    // obviously, zero spender address is a client mistake
    // it's not part of ERC20 standard but it's reasonable to fail fast
    require(_spender != address(0), "SYNR: approve to the zero address"); // Zeppelin msg

    // if transfer on behave is not allowed, then approve is also not allowed, unless it's white listed
    require(
      isFeatureEnabled(FEATURE_TRANSFERS_ON_BEHALF) || isOperatorInRole(_spender, ROLE_WHITE_LISTED_SPENDER),
      "SYNR: spender not allowed"
    );

    // read old approval value to emmit an improved event (ISBN:978-1-7281-3027-9)
    uint256 _oldValue = transferAllowances[msg.sender][_spender];

    // perform an operation: write value requested into the storage
    transferAllowances[msg.sender][_spender] = _value;

    // emit an improved atomic approve event (ISBN:978-1-7281-3027-9)
    emit Approved(msg.sender, _spender, _oldValue, _value);

    // emit an ERC20 approval event
    emit Approval(msg.sender, _spender, _value);

    // operation successful, return true
    return true;
  }

  /**
   * @notice Returns the amount which _spender is still allowed to withdraw from _owner.
   *
   * @dev ERC20 `function allowance(address _owner, address _spender) public view returns (uint256 remaining)`
   *
   * @dev A function to check an amount of tokens owner approved
   *      to transfer on its behalf by some other address called "spender"
   *
   * @param _owner an address which approves transferring some tokens on its behalf
   * @param _spender an address approved to transfer some tokens on behalf
   * @return remaining an amount of tokens approved address `_spender` can transfer on behalf
   *      of token owner `_owner`
   */
  function allowance(address _owner, address _spender) public view returns (uint256 remaining) {
    // read the value from storage and return
    return transferAllowances[_owner][_spender];
  }

  // ===== End: ERC20/ERC223/ERC777 functions =====

  // ===== Start: Resolution for the Multiple Withdrawal Attack on ERC20 Tokens (ISBN:978-1-7281-3027-9) =====

  /**
   * @notice Increases the allowance granted to `spender` by the transaction sender
   *
   * @dev Resolution for the Multiple Withdrawal Attack on ERC20 Tokens (ISBN:978-1-7281-3027-9)
   *
   * @dev Throws if value to increase by is zero or too big and causes arithmetic overflow
   *
   * @param _spender an address approved by the caller (token owner)
   *      to spend some tokens on its behalf
   * @param _value an amount of tokens to increase by
   * @return success true on success, throws otherwise
   */
  function increaseAllowance(address _spender, uint256 _value) public virtual returns (bool) {
    // read current allowance value
    uint256 currentVal = transferAllowances[msg.sender][_spender];

    // non-zero _value and arithmetic overflow check on the allowance
    require(currentVal + _value > currentVal, "zero value approval increase or arithmetic overflow");

    // delegate call to `approve` with the new value
    return approve(_spender, currentVal + _value);
  }

  /**
   * @notice Decreases the allowance granted to `spender` by the caller.
   *
   * @dev Resolution for the Multiple Withdrawal Attack on ERC20 Tokens (ISBN:978-1-7281-3027-9)
   *
   * @dev Throws if value to decrease by is zero or is bigger than currently allowed value
   *
   * @param _spender an address approved by the caller (token owner)
   *      to spend some tokens on its behalf
   * @param _value an amount of tokens to decrease by
   * @return success true on success, throws otherwise
   */
  function decreaseAllowance(address _spender, uint256 _value) public virtual returns (bool) {
    // read current allowance value
    uint256 currentVal = transferAllowances[msg.sender][_spender];

    // non-zero _value check on the allowance
    require(_value > 0, "zero value approval decrease");

    // verify allowance decrease doesn't underflow
    require(currentVal >= _value, "SYNR: decreased allowance below zero");

    // delegate call to `approve` with the new value
    return approve(_spender, currentVal - _value);
  }

  // ===== End: Resolution for the Multiple Withdrawal Attack on ERC20 Tokens (ISBN:978-1-7281-3027-9) =====

  // ===== Start: Minting/burning extension =====

  /**
   * @dev Mints (creates) some tokens to address specified
   * @dev The value specified is treated as is without taking
   *      into account what `decimals` value is
   * @dev Behaves effectively as `mintTo` function, allowing
   *      to specify an address to mint tokens to
   * @dev Requires sender to have `ROLE_TOKEN_CREATOR` permission
   *
   * @dev Throws on overflow, if totalSupply + _value doesn't fit into uint192
   *
   * @param _to an address to mint tokens to
   * @param _value an amount of tokens to mint (create)
   */
  function mint(address _to, uint256 _value) public {
    // check if caller has sufficient permissions to mint tokens
    require(isSenderInRole(ROLE_TOKEN_CREATOR), "insufficient privileges (ROLE_TOKEN_CREATOR required)");

    // non-zero recipient address check
    require(_to != address(0), "SYNR: mint to the zero address"); // Zeppelin msg

    // non-zero _value and arithmetic overflow check on the total supply
    // this check automatically secures arithmetic overflow on the individual balance
    require(totalSupply + _value > totalSupply, "zero value mint or arithmetic overflow");

    // uint192 overflow check (required by voting delegation)
    require(totalSupply + _value <= type(uint192).max, "total supply overflow (uint192)");

    require(totalSupply + _value <= maxTotalSupply, "reached total max supply");

    // perform mint:
    // increase total amount of tokens value
    totalSupply += _value;

    // increase `_to` address balance
    tokenBalances[_to] += _value;

    // create voting power associated with the tokens minted
    __moveVotingPower(address(0), votingDelegates[_to], _value);

    // fire a minted event
    emit Minted(msg.sender, _to, _value);

    // emit an improved transfer event
    emit Transferred(msg.sender, address(0), _to, _value);

    // fire ERC20 compliant transfer event
    emit Transfer(address(0), _to, _value);
  }

  /**
   * @dev Burns (destroys) some tokens from the address specified
   * @dev The value specified is treated as is without taking
   *      into account what `decimals` value is
   * @dev Behaves effectively as `burnFrom` function, allowing
   *      to specify an address to burn tokens from
   * @dev Requires sender to have `ROLE_TOKEN_DESTROYER` permission
   *
   * @param _from an address to burn some tokens from
   * @param _value an amount of tokens to burn (destroy)
   */
  function burn(address _from, uint256 _value) public {
    // check if caller has sufficient permissions to burn tokens
    // and if not - check for possibility to burn own tokens or to burn on behalf
    if (!isSenderInRole(ROLE_TOKEN_DESTROYER)) {
      // if `_from` is equal to sender, require own burns feature to be enabled
      // otherwise require burns on behalf feature to be enabled
      require(
        (_from == msg.sender && isFeatureEnabled(FEATURE_OWN_BURNS)) ||
          (_from != msg.sender && isFeatureEnabled(FEATURE_BURNS_ON_BEHALF)),
        _from == msg.sender ? "burns are disabled" : "burns on behalf are disabled"
      );

      // in case of burn on behalf
      if (_from != msg.sender) {
        // read allowance value - the amount of tokens allowed to be burnt - into the stack
        uint256 _allowance = transferAllowances[_from][msg.sender];

        // verify sender has an allowance to burn amount of tokens requested
        require(_allowance >= _value, "SYNR: burn amount exceeds allowance"); // Zeppelin msg

        // update allowance value on the stack
        _allowance -= _value;

        // update the allowance value in storage
        transferAllowances[_from][msg.sender] = _allowance;

        // emit an improved atomic approve event
        emit Approved(msg.sender, _from, _allowance + _value, _allowance);

        // emit an ERC20 approval event to reflect the decrease
        emit Approval(_from, msg.sender, _allowance);
      }
    }

    // at this point we know that either sender is ROLE_TOKEN_DESTROYER or
    // we burn own tokens or on behalf (in latest case we already checked and updated allowances)
    // we have left to execute balance checks and burning logic itself

    // non-zero burn value check
    require(_value != 0, "zero value burn");

    // non-zero source address check - Zeppelin
    require(_from != address(0), "SYNR: burn from the zero address"); // Zeppelin msg

    // verify `_from` address has enough tokens to destroy
    // (basically this is a arithmetic overflow check)
    require(tokenBalances[_from] >= _value, "SYNR: burn amount exceeds balance"); // Zeppelin msg

    // perform burn:
    // decrease `_from` address balance
    tokenBalances[_from] -= _value;

    // decrease total amount of tokens value
    totalSupply -= _value;

    // destroy voting power associated with the tokens burnt
    __moveVotingPower(votingDelegates[_from], address(0), _value);

    // fire a burnt event
    emit Burnt(msg.sender, _from, _value);

    // emit an improved transfer event
    emit Transferred(msg.sender, _from, address(0), _value);

    // fire ERC20 compliant transfer event
    emit Transfer(_from, address(0), _value);
  }

  // ===== End: Minting/burning extension =====

  // ===== Start: DAO Support (Compound-like voting delegation) =====

  /**
   * @notice Gets current voting power of the account `_of`
   * @param _of the address of account to get voting power of
   * @return current cumulative voting power of the account,
   *      sum of token balances of all its voting delegators
   */
  function getVotingPower(address _of) public view returns (uint256) {
    // get a link to an array of voting power history records for an address specified
    VotingPowerRecord[] storage history = votingPowerHistory[_of];

    // lookup the history and return latest element
    return history.length == 0 ? 0 : history[history.length - 1].votingPower;
  }

  /**
   * @notice Gets past voting power of the account `_of` at some block `_blockNum`
   * @dev Throws if `_blockNum` is not in the past (not the finalized block)
   * @param _of the address of account to get voting power of
   * @param _blockNum block number to get the voting power at
   * @return past cumulative voting power of the account,
   *      sum of token balances of all its voting delegators at block number `_blockNum`
   */
  function getVotingPowerAt(address _of, uint256 _blockNum) public view returns (uint256) {
    // make sure block number is not in the past (not the finalized block)
    require(_blockNum < block.number, "not yet determined"); // Compound msg

    // get a link to an array of voting power history records for an address specified
    VotingPowerRecord[] storage history = votingPowerHistory[_of];

    // if voting power history for the account provided is empty
    if (history.length == 0) {
      // than voting power is zero - return the result
      return 0;
    }

    // check latest voting power history record block number:
    // if history was not updated after the block of interest
    if (history[history.length - 1].blockNumber <= _blockNum) {
      // we're done - return last voting power record
      return getVotingPower(_of);
    }

    // check first voting power history record block number:
    // if history was never updated before the block of interest
    if (history[0].blockNumber > _blockNum) {
      // we're done - voting power at the block num of interest was zero
      return 0;
    }

    // `votingPowerHistory[_of]` is an array ordered by `blockNumber`, ascending;
    // apply binary search on `votingPowerHistory[_of]` to find such an entry number `i`, that
    // `votingPowerHistory[_of][i].blockNumber <= _blockNum`, but in the same time
    // `votingPowerHistory[_of][i + 1].blockNumber > _blockNum`
    // return the result - voting power found at index `i`
    return history[__binaryLookup(_of, _blockNum)].votingPower;
  }

  /**
   * @dev Reads an entire voting power history array for the delegate specified
   *
   * @param _of delegate to query voting power history for
   * @return voting power history array for the delegate of interest
   */
  function getVotingPowerHistory(address _of) public view returns (VotingPowerRecord[] memory) {
    // return an entire array as memory
    return votingPowerHistory[_of];
  }

  /**
   * @dev Returns length of the voting power history array for the delegate specified;
   *      useful since reading an entire array just to get its length is expensive (gas cost)
   *
   * @param _of delegate to query voting power history length for
   * @return voting power history array length for the delegate of interest
   */
  function getVotingPowerHistoryLength(address _of) public view returns (uint256) {
    // read array length and return
    return votingPowerHistory[_of].length;
  }

  /**
   * @notice Delegates voting power of the delegator `msg.sender` to the delegate `_to`
   *
   * @dev Accepts zero value address to delegate voting power to, effectively
   *      removing the delegate in that case
   *
   * @param _to address to delegate voting power to
   */
  function delegate(address _to) public {
    // verify delegations are enabled
    require(isFeatureEnabled(FEATURE_DELEGATIONS), "delegations are disabled");
    // delegate call to `__delegate`
    __delegate(msg.sender, _to);
  }

  /**
   * @notice Delegates voting power of the delegator (represented by its signature) to the delegate `_to`
   *
   * @dev Accepts zero value address to delegate voting power to, effectively
   *      removing the delegate in that case
   *
   * @dev Compliant with EIP-712: Ethereum typed structured data hashing and signing,
   *      see https://eips.ethereum.org/EIPS/eip-712
   *
   * @param _to address to delegate voting power to
   * @param _nonce nonce used to construct the signature, and used to validate it;
   *      nonce is increased by one after successful signature validation and vote delegation
   * @param _exp signature expiration time
   * @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 delegateWithSig(
    address _to,
    uint256 _nonce,
    uint256 _exp,
    uint8 v,
    bytes32 r,
    bytes32 s
  ) public {
    // verify delegations on behalf are enabled
    require(isFeatureEnabled(FEATURE_DELEGATIONS_ON_BEHALF), "delegations on behalf are disabled");

    // build the EIP-712 contract domain separator
    bytes32 domainSeparator = keccak256(abi.encode(DOMAIN_TYPEHASH, keccak256(bytes(name)), block.chainid, address(this)));

    // build the EIP-712 hashStruct of the delegation message
    bytes32 hashStruct = keccak256(abi.encode(DELEGATION_TYPEHASH, _to, _nonce, _exp));

    // calculate the EIP-712 digest "\x19\x01" ‖ domainSeparator ‖ hashStruct(message)
    bytes32 digest = keccak256(abi.encodePacked("\x19\x01", domainSeparator, hashStruct));

    // recover the address who signed the message with v, r, s
    address signer = ecrecover(digest, v, r, s);

    // perform message integrity and security validations
    require(signer != address(0), "invalid signature"); // Compound msg
    require(_nonce == nonces[signer], "invalid nonce"); // Compound msg
    require(block.timestamp < _exp, "signature expired"); // Compound msg

    // update the nonce for that particular signer to avoid replay attack
    nonces[signer]++;

    // delegate call to `__delegate` - execute the logic required
    __delegate(signer, _to);
  }

  /**
   * @dev Auxiliary function to delegate delegator's `_from` voting power to the delegate `_to`
   * @dev Writes to `votingDelegates` and `votingPowerHistory` mappings
   *
   * @param _from delegator who delegates his voting power
   * @param _to delegate who receives the voting power
   */
  function __delegate(address _from, address _to) private {
    // read current delegate to be replaced by a new one
    address _fromDelegate = votingDelegates[_from];

    // read current voting power (it is equal to token balance)
    uint256 _value = tokenBalances[_from];

    // reassign voting delegate to `_to`
    votingDelegates[_from] = _to;

    // update voting power for `_fromDelegate` and `_to`
    __moveVotingPower(_fromDelegate, _to, _value);

    // emit an event
    emit DelegateChanged(_from, _fromDelegate, _to);
  }

  /**
   * @dev Auxiliary function to move voting power `_value`
   *      from delegate `_from` to the delegate `_to`
   *
   * @dev Doesn't have any effect if `_from == _to`, or if `_value == 0`
   *
   * @param _from delegate to move voting power from
   * @param _to delegate to move voting power to
   * @param _value voting power to move from `_from` to `_to`
   */
  function __moveVotingPower(
    address _from,
    address _to,
    uint256 _value
  ) private {
    // if there is no move (`_from == _to`) or there is nothing to move (`_value == 0`)
    if (_from == _to || _value == 0) {
      // return silently with no action
      return;
    }

    // if source address is not zero - decrease its voting power
    if (_from != address(0)) {
      // read current source address voting power
      uint256 _fromVal = getVotingPower(_from);

      // calculate decreased voting power
      // underflow is not possible by design:
      // voting power is limited by token balance which is checked by the callee
      uint256 _toVal = _fromVal - _value;

      // update source voting power from `_fromVal` to `_toVal`
      __updateVotingPower(_from, _fromVal, _toVal);
    }

    // if destination address is not zero - increase its voting power
    if (_to != address(0)) {
      // read current destination address voting power
      uint256 _fromVal = getVotingPower(_to);

      // calculate increased voting power
      // overflow is not possible by design:
      // max token supply limits the cumulative voting power
      uint256 _toVal = _fromVal + _value;

      // update destination voting power from `_fromVal` to `_toVal`
      __updateVotingPower(_to, _fromVal, _toVal);
    }
  }

  /**
   * @dev Auxiliary function to update voting power of the delegate `_of`
   *      from value `_fromVal` to value `_toVal`
   *
   * @param _of delegate to update its voting power
   * @param _fromVal old voting power of the delegate
   * @param _toVal new voting power of the delegate
   */
  function __updateVotingPower(
    address _of,
    uint256 _fromVal,
    uint256 _toVal
  ) private {
    // get a link to an array of voting power history records for an address specified
    VotingPowerRecord[] storage history = votingPowerHistory[_of];

    // if there is an existing voting power value stored for current block
    if (history.length != 0 && history[history.length - 1].blockNumber == block.number) {
      // update voting power which is already stored in the current block
      history[history.length - 1].votingPower = uint192(_toVal);
    }
    // otherwise - if there is no value stored for current block
    else {
      // add new element into array representing the value for current block
      history.push(VotingPowerRecord(uint64(block.number), uint192(_toVal)));
    }

    // emit an event
    emit VotingPowerChanged(_of, _fromVal, _toVal);
  }

  /**
   * @dev Auxiliary function to lookup an element in a sorted (asc) array of elements
   *
   * @dev This function finds the closest element in an array to the value
   *      of interest (not exceeding that value) and returns its index within an array
   *
   * @dev An array to search in is `votingPowerHistory[_to][i].blockNumber`,
   *      it is sorted in ascending order (blockNumber increases)
   *
   * @param _to an address of the delegate to get an array for
   * @param n value of interest to look for
   * @return an index of the closest element in an array to the value
   *      of interest (not exceeding that value)
   */
  function __binaryLookup(address _to, uint256 n) private view returns (uint256) {
    // get a link to an array of voting power history records for an address specified
    VotingPowerRecord[] storage history = votingPowerHistory[_to];

    // left bound of the search interval, originally start of the array
    uint256 i = 0;

    // right bound of the search interval, originally end of the array
    uint256 j = history.length - 1;

    // the iteration process narrows down the bounds by
    // splitting the interval in a half oce per each iteration
    while (j > i) {
      // get an index in the middle of the interval [i, j]
      uint256 k = j - (j - i) / 2;

      // read an element to compare it with the value of interest
      VotingPowerRecord memory cp = history[k];

      // if we've got a strict equal - we're lucky and done
      if (cp.blockNumber == n) {
        // just return the result - index `k`
        return k;
      }
      // if the value of interest is bigger - move left bound to the middle
      else if (cp.blockNumber < n) {
        // move left bound `i` to the middle position `k`
        i = k;
      }
      // otherwise, when the value of interest is smaller - move right bound to the middle
      else {
        // move right bound `j` to the middle position `k - 1`:
        // element at position `k` is bigger and cannot be the result
        j = k - 1;
      }
    }

    // reaching that point means no exact match found
    // since we're interested in the element which is not bigger than the
    // element of interest, we return the lower bound `i`
    return i;
  }
}

// ===== End: DAO Support (Compound-like voting delegation) =====

File 15 of 20 : AddressUtils.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.1;

/**
 * @title Address Utils
 *
 * @dev Utility library of inline functions on addresses
 *
 * @author Basil Gorin
 */
library AddressUtils {
  /**
   * @notice Checks if the target address is a contract
   * @dev This function will return false if invoked during the constructor of a contract,
   *      as the code is not actually created until after the constructor finishes.
   * @param addr address to check
   * @return whether the target address is a contract
   */
  function isContract(address addr) internal view returns (bool) {
    // a variable to load `extcodesize` to
    uint256 size = 0;

    // XXX Currently there is no better way to check if there is a contract in an address
    // than to check the size of the code at that address.
    // See https://ethereum.stackexchange.com/a/14016/36603 for more details about how this works.
    // TODO: Check this again before the Serenity release, because all addresses will be contracts.
    // solium-disable-next-line security/no-inline-assembly
    assembly {
      // retrieve the size of the code at address `addr`
      size := extcodesize(addr)
    }

    // positive size indicates a smart contract address
    return size > 0;
  }
}

File 16 of 20 : AccessControl.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.1;

// import "hardhat/console.sol";

/**
 * @title Access Control List
 *
 * @notice Access control smart contract provides an API to check
 *      if specific operation is permitted globally and/or
 *      if particular user has a permission to execute it.
 *
 * @notice It deals with two main entities: features and roles.
 *
 * @notice Features are designed to be used to enable/disable specific
 *      functions (public functions) of the smart contract for everyone.
 * @notice User roles are designed to restrict access to specific
 *      functions (restricted functions) of the smart contract to some users.
 *
 * @notice Terms "role", "permissions" and "set of permissions" have equal meaning
 *      in the documentation text and may be used interchangeably.
 * @notice Terms "permission", "single permission" implies only one permission bit set.
 *
 * @dev This smart contract is designed to be inherited by other
 *      smart contracts which require access control management capabilities.
 *
 * @author Basil Gorin
 */
contract AccessControl {
  /**
   * @notice Access manager is responsible for assigning the roles to users,
   *      enabling/disabling global features of the smart contract
   * @notice Access manager can add, remove and update user roles,
   *      remove and update global features
   *
   * @dev Role ROLE_ACCESS_MANAGER allows modifying user roles and global features
   * @dev Role ROLE_ACCESS_MANAGER has single bit at position 255 enabled
   */
  uint256 public constant ROLE_ACCESS_MANAGER = 0x8000000000000000000000000000000000000000000000000000000000000000;

  /**
   * @dev Bitmask representing all the possible permissions (super admin role)
   * @dev Has all the bits are enabled (2^256 - 1 value)
   */
  // solhint-disable-next-line
  uint256 private constant FULL_PRIVILEGES_MASK = type(uint256).max; // before 0.8.0: uint256(-1) overflows to 0xFFFF...

  /**
   * @notice Privileged addresses with defined roles/permissions
   * @notice In the context of ERC20/ERC721 tokens these can be permissions to
   *      allow minting or burning tokens, transferring on behalf and so on
   *
   * @dev Maps user address to the permissions bitmask (role), where each bit
   *      represents a permission
   * @dev Bitmask 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
   *      represents all possible permissions
   * @dev Zero address mapping represents global features of the smart contract
   */
  mapping(address => uint256) public userRoles;

  /**
   * @dev Fired in updateRole() and updateFeatures()
   *
   * @param _by operator which called the function
   * @param _to address which was granted/revoked permissions
   * @param _requested permissions requested
   * @param _actual permissions effectively set
   */
  event RoleUpdated(address indexed _by, address indexed _to, uint256 _requested, uint256 _actual);

  /**
   * @notice Creates an access control instance,
   *      setting contract creator to have full privileges
   */
  constructor(address _superAdmin) {
    userRoles[_superAdmin] = FULL_PRIVILEGES_MASK;
    userRoles[msg.sender] = FULL_PRIVILEGES_MASK;
  }

  /**
   * @notice Retrieves globally set of features enabled
   *
   * @dev Auxiliary getter function to maintain compatibility with previous
   *      versions of the Access Control List smart contract, where
   *      features was a separate uint256 public field
   *
   * @return 256-bit bitmask of the features enabled
   */
  function features() public view returns (uint256) {
    // according to new design features are stored in zero address
    // mapping of `userRoles` structure
    return userRoles[address(0)];
  }

  /**
   * @notice Updates set of the globally enabled features (`features`),
   *      taking into account sender's permissions
   *
   * @dev Requires transaction sender to have `ROLE_ACCESS_MANAGER` permission
   * @dev Function is left for backward compatibility with older versions
   *
   * @param _mask bitmask representing a set of features to enable/disable
   */
  function updateFeatures(uint256 _mask) public {
    // delegate call to `updateRole`
    updateRole(address(0), _mask);
  }

  /**
   * @notice Updates set of permissions (role) for a given user,
   *      taking into account sender's permissions.
   *
   * @dev Setting role to zero is equivalent to removing an all permissions
   * @dev Setting role to `FULL_PRIVILEGES_MASK` is equivalent to
   *      copying senders' permissions (role) to the user
   * @dev Requires transaction sender to have `ROLE_ACCESS_MANAGER` permission
   *
   * @param operator address of a user to alter permissions for or zero
   *      to alter global features of the smart contract
   * @param role bitmask representing a set of permissions to
   *      enable/disable for a user specified
   */
  function updateRole(address operator, uint256 role) public {
    // caller must have a permission to update user roles
    require(isSenderInRole(ROLE_ACCESS_MANAGER), "insufficient privileges (ROLE_ACCESS_MANAGER required)");

    // evaluate the role and reassign it
    userRoles[operator] = evaluateBy(msg.sender, userRoles[operator], role);

    // fire an event
    emit RoleUpdated(msg.sender, operator, role, userRoles[operator]);
  }

  /**
   * @notice Determines the permission bitmask an operator can set on the
   *      target permission set
   * @notice Used to calculate the permission bitmask to be set when requested
   *     in `updateRole` and `updateFeatures` functions
   *
   * @dev Calculated based on:
   *      1) operator's own permission set read from userRoles[operator]
   *      2) target permission set - what is already set on the target
   *      3) desired permission set - what do we want set target to
   *
   * @dev Corner cases:
   *      1) Operator is super admin and its permission set is `FULL_PRIVILEGES_MASK`:
   *        `desired` bitset is returned regardless of the `target` permission set value
   *        (what operator sets is what they get)
   *      2) Operator with no permissions (zero bitset):
   *        `target` bitset is returned regardless of the `desired` value
   *        (operator has no authority and cannot modify anything)
   *
   * @dev Example:
   *      Consider an operator with the permissions bitmask     00001111
   *      is about to modify the target permission set          01010101
   *      Operator wants to set that permission set to          00110011
   *      Based on their role, an operator has the permissions
   *      to update only lowest 4 bits on the target, meaning that
   *      high 4 bits of the target set in this example is left
   *      unchanged and low 4 bits get changed as desired:      01010011
   *
   * @param operator address of the contract operator which is about to set the permissions
   * @param target input set of permissions to operator is going to modify
   * @param desired desired set of permissions operator would like to set
   * @return resulting set of permissions given operator will set
   */
  function evaluateBy(
    address operator,
    uint256 target,
    uint256 desired
  ) public view returns (uint256) {
    // read operator's permissions
    uint256 p = userRoles[operator];

    // taking into account operator's permissions,
    // 1) enable the permissions desired on the `target`
    target |= p & desired;
    // 2) disable the permissions desired on the `target`
    target &= FULL_PRIVILEGES_MASK ^ (p & (FULL_PRIVILEGES_MASK ^ desired));

    // return calculated result
    return target;
  }

  /**
   * @notice Checks if requested set of features is enabled globally on the contract
   *
   * @param required set of features to check against
   * @return true if all the features requested are enabled, false otherwise
   */
  function isFeatureEnabled(uint256 required) public view returns (bool) {
    // delegate call to `__hasRole`, passing `features` property
    return __hasRole(features(), required);
  }

  /**
   * @notice Checks if transaction sender `msg.sender` has all the permissions required
   *
   * @param required set of permissions (role) to check against
   * @return true if all the permissions requested are enabled, false otherwise
   */
  function isSenderInRole(uint256 required) public view returns (bool) {
    // delegate call to `isOperatorInRole`, passing transaction sender
    return isOperatorInRole(msg.sender, required);
  }

  /**
   * @notice Checks if operator has all the permissions (role) required
   *
   * @param operator address of the user to check role for
   * @param required set of permissions (role) to check
   * @return true if all the permissions requested are enabled, false otherwise
   */
  function isOperatorInRole(address operator, uint256 required) public view returns (bool) {
    // delegate call to `__hasRole`, passing operator's permissions (role)
    return __hasRole(userRoles[operator], required);
  }

  /**
   * @dev Checks if role `actual` contains all the permissions required `required`
   *
   * @param actual existent role
   * @param required required role
   * @return true if actual has required role (all permissions), false otherwise
   */
  function __hasRole(uint256 actual, uint256 required) internal pure returns (bool) {
    // check the bitmask for the role required and return the result
    return actual & required == required;
  }
}

File 17 of 20 : ERC20Receiver.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.1;

/**
 * @title ERC20 token receiver interface
 *
 * @dev Interface for any contract that wants to support safe transfers
 *      from ERC20 token smart contracts.
 * @dev Inspired by ERC721 and ERC223 token standards
 *
 * @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
 * @dev See https://github.com/ethereum/EIPs/issues/223
 * @author Basil Gorin
 * Adapted for Syn City by Superpower Labs
 */
interface ERC20Receiver {
  /**
   * @notice Handle the receipt of a ERC20 token(s)
   * @dev The ERC20 smart contract calls this function on the recipient
   *      after a successful transfer (`safeTransferFrom`).
   *      This function MAY throw to revert and reject the transfer.
   *      Return of other than the magic value MUST result in the transaction being reverted.
   * @notice The contract address is always the message sender.
   *      A wallet/broker/auction application MUST implement the wallet interface
   *      if it will accept safe transfers.
   * @param _operator The address which called `safeTransferFrom` function
   * @param _from The address which previously owned the token
   * @param _value amount of tokens which is being transferred
   * @param _data additional data with no specified format
   * @return `bytes4(keccak256("onERC20Received(address,address,uint256,bytes)"))` unless throwing
   */
  function onERC20Received(
    address _operator,
    address _from,
    uint256 _value,
    bytes calldata _data
  ) external returns (bytes4);
}

File 18 of 20 : ERC20.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.1;

import "../interfaces/IERC20.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}.
 */

// Copied from Open Zeppelin

contract ERC20 is IERC20 {
  mapping(address => uint256) private _balances;

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

  uint256 private _totalSupply;

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

  /**
   * @notice Token creator is responsible for creating (minting)
   *      tokens to an arbitrary address
   * @dev Role ROLE_TOKEN_CREATOR allows minting tokens
   *      (calling `mint` function)
   */
  uint32 public constant ROLE_TOKEN_CREATOR = 0x0001_0000;

  /**
   * @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_) {
    _name = name_;
    _symbol = symbol_;
    _decimals = 18;
  }

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

  /**
   * @dev Returns the symbol of the token, usually a shorter version of the
   * name.
   */
  function symbol() public view virtual 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 virtual returns (uint8) {
    return _decimals;
  }

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

  /**
   * @dev See {IERC20-balanceOf}.
   */
  function balanceOf(address account) public view virtual 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(msg.sender, 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(msg.sender, 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, msg.sender, _allowances[sender][msg.sender] - amount);
    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(msg.sender, spender, _allowances[msg.sender][spender] + 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(msg.sender, spender, _allowances[msg.sender][spender] - subtractedValue);
    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] - amount;
    _balances[recipient] = _balances[recipient] + 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 + amount;
    _balances[account] = _balances[account] + 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] - amount;
    _totalSupply = _totalSupply - amount;
    emit Transfer(account, address(0), amount);
  }

  /**
   * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
   *
   * This 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 virtual {
    _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 19 of 20 : IERC20.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.1;

/**
 * @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 20 of 20 : Address.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.1;

/**
 * @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) {
    // This method relies on extcodesize, which returns 0 for contracts in
    // construction, since the code is only stored at the end of the
    // constructor execution.

    uint256 size;
    // solhint-disable-next-line no-inline-assembly
    assembly {
      size := extcodesize(account)
    }
    return size > 0;
  }

  /**
   * @dev 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");
  }

  /**
   * @dev Performs a Solidity function call using a low level `call`. A
   * plain`call` is an unsafe replacement for a function call: use this
   * function instead.
   *
   * If `target` reverts with a revert reason, it is bubbled up by this
   * function (like regular Solidity function calls).
   *
   * Returns the raw returned data. To convert to the expected return value,
   * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
   *
   * Requirements:
   *
   * - `target` must be a contract.
   * - calling `target` with `data` must not revert.
   *
   * _Available since v3.1._
   */
  function functionCall(address target, bytes memory data) internal returns (bytes memory) {
    return functionCall(target, data, "Address: low-level call failed");
  }

  /**
   * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
   * `errorMessage` as a fallback revert reason when `target` reverts.
   *
   * _Available since v3.1._
   */
  function functionCall(
    address target,
    bytes memory data,
    string memory errorMessage
  ) internal returns (bytes memory) {
    return functionCallWithValue(target, data, 0, errorMessage);
  }

  /**
   * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
   * but also transferring `value` wei to `target`.
   *
   * Requirements:
   *
   * - the calling contract must have an ETH balance of at least `value`.
   * - the called Solidity function must be `payable`.
   *
   * _Available since v3.1._
   */
  function functionCallWithValue(
    address target,
    bytes memory data,
    uint256 value
  ) internal returns (bytes memory) {
    return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
  }

  /**
   * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
   * with `errorMessage` as a fallback revert reason when `target` reverts.
   *
   * _Available since v3.1._
   */
  function functionCallWithValue(
    address target,
    bytes memory data,
    uint256 value,
    string memory errorMessage
  ) internal returns (bytes memory) {
    require(address(this).balance >= value, "Address: insufficient balance for call");
    require(isContract(target), "Address: call to non-contract");

    // solhint-disable-next-line avoid-low-level-calls
    (bool success, bytes memory returndata) = target.call{value: value}(data);
    return _verifyCallResult(success, returndata, errorMessage);
  }

  /**
   * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
   * but performing a static call.
   *
   * _Available since v3.3._
   */
  function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
    return functionStaticCall(target, data, "Address: low-level static call failed");
  }

  /**
   * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
   * but performing a static call.
   *
   * _Available since v3.3._
   */
  function functionStaticCall(
    address target,
    bytes memory data,
    string memory errorMessage
  ) internal view returns (bytes memory) {
    require(isContract(target), "Address: static call to non-contract");

    // solhint-disable-next-line avoid-low-level-calls
    (bool success, bytes memory returndata) = target.staticcall(data);
    return _verifyCallResult(success, returndata, errorMessage);
  }

  /**
   * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
   * but performing a delegate call.
   *
   * _Available since v3.3._
   */
  function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
    return functionDelegateCall(target, data, "Address: low-level delegate call failed");
  }

  /**
   * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
   * but performing a delegate call.
   *
   * _Available since v3.3._
   */
  function functionDelegateCall(
    address target,
    bytes memory data,
    string memory errorMessage
  ) internal returns (bytes memory) {
    require(isContract(target), "Address: delegate call to non-contract");

    // solhint-disable-next-line avoid-low-level-calls
    (bool success, bytes memory returndata) = target.delegatecall(data);
    return _verifyCallResult(success, returndata, errorMessage);
  }

  function _verifyCallResult(
    bool success,
    bytes memory returndata,
    string memory errorMessage
  ) private pure returns (bytes memory) {
    if (success) {
      return returndata;
    } else {
      // Look for revert reason and bubble it up if present
      if (returndata.length > 0) {
        // The easiest way to bubble the revert reason is using memory via assembly

        // solhint-disable-next-line no-inline-assembly
        assembly {
          let returndata_size := mload(returndata)
          revert(add(32, returndata), returndata_size)
        }
      } else {
        revert(errorMessage);
      }
    }
  }
}

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

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"_synr","type":"address"},{"internalType":"address","name":"_ssynr","type":"address"},{"internalType":"contract SyndicatePoolFactory","name":"_factory","type":"address"},{"internalType":"address","name":"_poolToken","type":"address"},{"internalType":"uint64","name":"_initBlock","type":"uint64"},{"internalType":"uint32","name":"_weight","type":"uint32"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_by","type":"address"},{"indexed":false,"internalType":"uint32","name":"_fromVal","type":"uint32"},{"indexed":false,"internalType":"uint32","name":"_toVal","type":"uint32"}],"name":"PoolWeightUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_by","type":"address"},{"indexed":false,"internalType":"uint256","name":"depositId","type":"uint256"},{"indexed":false,"internalType":"uint64","name":"lockedFrom","type":"uint64"},{"indexed":false,"internalType":"uint64","name":"lockedUntil","type":"uint64"}],"name":"StakeLockUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_by","type":"address"},{"indexed":true,"internalType":"address","name":"_from","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Staked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_by","type":"address"},{"indexed":false,"internalType":"uint256","name":"yieldRewardsPerWeight","type":"uint256"},{"indexed":false,"internalType":"uint64","name":"lastYieldDistribution","type":"uint64"}],"name":"Synchronized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_by","type":"address"},{"indexed":true,"internalType":"address","name":"_to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Unstaked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_by","type":"address"},{"indexed":true,"internalType":"address","name":"_to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"VaultRewardsClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_by","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"VaultRewardsReceived","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_by","type":"address"},{"indexed":false,"internalType":"address","name":"_fromVal","type":"address"},{"indexed":false,"internalType":"address","name":"_toVal","type":"address"}],"name":"VaultUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_by","type":"address"},{"indexed":true,"internalType":"address","name":"_to","type":"address"},{"indexed":false,"internalType":"bool","name":"sSyn","type":"bool"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"YieldClaimed","type":"event"},{"inputs":[{"internalType":"address","name":"_user","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"blockNumber","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"}],"name":"delegate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"factory","outputs":[{"internalType":"contract SyndicatePoolFactory","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"},{"internalType":"uint256","name":"_depositId","type":"uint256"}],"name":"getDeposit","outputs":[{"components":[{"internalType":"uint256","name":"tokenAmount","type":"uint256"},{"internalType":"uint256","name":"weight","type":"uint256"},{"internalType":"uint64","name":"lockedFrom","type":"uint64"},{"internalType":"uint64","name":"lockedUntil","type":"uint64"},{"internalType":"bool","name":"isYield","type":"bool"}],"internalType":"struct IPool.Deposit","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"}],"name":"getDepositsLength","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"lockedTime","type":"uint256"},{"internalType":"uint256","name":"addedAmount","type":"uint256"}],"name":"getStakeWeight","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"isFlashPool","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastYieldDistribution","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxQuickReward","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"migrate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"migrator","outputs":[{"internalType":"contract IMigrator","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minLockTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"now256","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_staker","type":"address"}],"name":"pendingVaultRewards","outputs":[{"internalType":"uint256","name":"pending","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_staker","type":"address"}],"name":"pendingYieldRewards","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"poolToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"poolTokenReserve","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"_useSSYN","type":"bool"}],"name":"processRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"quickRewardRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_rewardsAmount","type":"uint256"}],"name":"receiveVaultRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"reward","type":"uint256"},{"internalType":"uint256","name":"rewardPerWeight","type":"uint256"}],"name":"rewardToWeight","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"_maxQuickReward","type":"uint256"}],"name":"setMaxQuickReward","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IMigrator","name":"_migrator","type":"address"}],"name":"setMigrator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_minLockTime","type":"uint256"}],"name":"setMinLockTime","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_quickRewardRate","type":"uint256"}],"name":"setQuickRewardRate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_vault","type":"address"}],"name":"setVault","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"_weight","type":"uint32"}],"name":"setWeight","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"ssynr","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"uint64","name":"_lockUntil","type":"uint64"},{"internalType":"bool","name":"_useSSYN","type":"bool"}],"name":"stake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_staker","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"stakeAsPool","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"sync","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"synr","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalQuickReward","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalYieldReward","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_depositId","type":"uint256"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"bool","name":"_useSSYN","type":"bool"}],"name":"unstake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"depositId","type":"uint256"},{"internalType":"uint64","name":"lockedUntil","type":"uint64"},{"internalType":"bool","name":"useSSYN","type":"bool"}],"name":"updateStakeLock","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"users","outputs":[{"internalType":"uint256","name":"tokenAmount","type":"uint256"},{"internalType":"uint256","name":"totalWeight","type":"uint256"},{"internalType":"uint256","name":"subYieldRewards","type":"uint256"},{"internalType":"uint256","name":"subVaultRewards","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"usersLockingWeight","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"vault","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"vaultRewardsPerWeight","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"weight","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_weight","type":"uint256"},{"internalType":"uint256","name":"rewardPerWeight","type":"uint256"}],"name":"weightToReward","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"yieldRewardsPerWeight","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]

6101006040526293a8006001553480156200001957600080fd5b5060405162004762380380620047628339810160408190526200003c91620003ef565b858585858585856001600160a01b038116620000755760405162461bcd60e51b81526004016200006c9062000622565b60405180910390fd5b806001600160a01b0316638a114e136040518163ffffffff1660e01b815260040160206040518083038186803b158015620000af57600080fd5b505afa158015620000c4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620000ea919062000493565b7f83ecb176af7c4f35a45ff0018282e3a05a1018065da866182df12285866f5a2c146200012b5760405162461bcd60e51b81526004016200006c906200057d565b60601b6001600160601b03191660805260016000556001600160a01b038516620001695760405162461bcd60e51b81526004016200006c90620005b4565b6001600160a01b038416620001925760405162461bcd60e51b81526004016200006c90620005eb565b6001600160a01b038316620001bb5760405162461bcd60e51b81526004016200006c90620004e3565b6000826001600160401b031611620001e75760405162461bcd60e51b81526004016200006c9062000551565b60008163ffffffff1611620002105760405162461bcd60e51b81526004016200006c9062000659565b846001600160a01b0316638a114e136040518163ffffffff1660e01b815260040160206040518083038186803b1580156200024a57600080fd5b505afa1580156200025f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000285919062000493565b7fac3051b8d4f50966afb632468a4f61483ae6a953b74e387a01ef94316d6b7d6214620002c65760405162461bcd60e51b81526004016200006c906200051a565b836001600160a01b031663f70b7fce6040518163ffffffff1660e01b815260040160206040518083038186803b1580156200030057600080fd5b505afa15801562000315573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200033b919062000493565b7fc5cfd88c6e4d7e5c8a03c0f0f03af23c0918d8e82cac196f57466af3fd4a5ec7146200037c5760405162461bcd60e51b81526004016200006c90620004ac565b606094851b6001600160601b031990811660a05293851b841660c0529190931b90911660e0526004805463ffffffff191663ffffffff90921691909117600160201b600160601b0319166401000000006001600160401b039093169290920291909117905550620006a995505050505050565b60008060008060008060c0878903121562000408578182fd5b8651620004158162000690565b6020880151909650620004288162000690565b60408801519095506200043b8162000690565b60608801519094506200044e8162000690565b60808801519093506001600160401b03811681146200046b578283fd5b60a088015190925063ffffffff8116811462000485578182fd5b809150509295509295509295565b600060208284031215620004a5578081fd5b5051919050565b60208082526016908201527f756e657870656374656420464143544f52595f55494400000000000000000000604082015260600190565b6020808252601a908201527f706f6f6c20746f6b656e2061646472657373206e6f7420736574000000000000604082015260600190565b6020808252601a908201527f756e6578706563746564207353594e5220544f4b454e5f554944000000000000604082015260600190565b6020808252601290820152711a5b9a5d08189b1bd8dac81b9bdd081cd95d60721b604082015260600190565b60208082526014908201527f756e657870656374656420544f4b454e5f554944000000000000000000000000604082015260600190565b60208082526015908201527f7353594e522061646472657373206e6f74207365740000000000000000000000604082015260600190565b6020808252601d908201527f53594e5220506f6f6c206663742061646472657373206e6f7420736574000000604082015260600190565b60208082526014908201527f53594e522061646472657373206e6f7420736574000000000000000000000000604082015260600190565b60208082526013908201527f706f6f6c20776569676874206e6f742073657400000000000000000000000000604082015260600190565b6001600160a01b0381168114620006a657600080fd5b50565b60805160601c60a05160601c60c05160601c60e05160601c613f70620007f260003960008181610c190152818161122d0152818161183f01528181611ef401528181612590015281816126e101528181612ad301528181612b7f015281816131df015261323d015260008181610581015281816106d3015281816107c70152818161085e0152818161099e01528181610cd901528181610d8d015281816110860152818161114c015281816112ff0152818161181b01528181611a3201528181611ac801528181611b3e01528181611ca601528181611d440152818161288201526130aa015260008181610ca60152612e08015260008181610bef0152818161120301528181611289015281816116f801528181611887015281816119a501528181611eca015281816124bd01528181612566015281816126b701526128af0152613f706000f3fe608060405234801561001057600080fd5b50600436106102745760003560e01c80636817031b11610151578063a87430ba116100c3578063e8d3cad511610087578063e8d3cad5146104ee578063e9d7f17514610501578063fa213bd614610509578063fbfa77cf14610511578063fc27076f14610519578063fff6cae91461052c57610274565b8063a87430ba146104ab578063beb0ed6c146104ce578063c45a0155146104d6578063cbdf382c146104de578063ce111541146104e657610274565b806382e5d1bc1161011557806382e5d1bc1461044d5780638e169d47146104605780638fd3ab8014610473578063a156dc281461047b578063a1aab33f1461048e578063a60ff766146104a357610274565b80636817031b1461040457806370a08231146104175780637221024f1461042a57806379b91d69146104325780637cd07e471461044557610274565b806331c520da116101ea5780634ce0f9a6116101ae5780634ce0f9a6146103a857806351c547f8146103b057806352044ec9146103c357806357e871e7146103d657806358b5c544146103de5780635c19a95c146103f157610274565b806331c520da1461035d578063379560b6146103725780634087aeb71461037a57806344cc892d1461038d57806348eb1ff2146103a057610274565b80631da10d911161023c5780631da10d91146102e557806323cf3118146102fa578063250531a71461030d5780632726b5061461031557806329eb5f2c146103355780633021a5601461034a57610274565b8063022bb3db1461027957806305472358146102a257806311769bc0146102aa57806315e4b1ae146102bf5780631984db99146102d2575b600080fd5b61028c6102873660046135f5565b610534565b6040516102999190613d80565b60405180910390f35b61028c61056f565b6102bd6102b83660046135c5565b610575565b005b6102bd6102cd36600461364e565b610666565b61028c6102e0366004613503565b61069c565b6102ed61098d565b604051610299919061381c565b6102bd610308366004613503565b610992565b61028c610a9b565b61032861032336600461353b565b610aa1565b6040516102999190613d35565b61033d610b52565b6040516102999190613e02565b6102bd6103583660046135c5565b610b68565b610365610ca4565b60405161029991906136e3565b61028c610cc8565b6102bd61038836600461368f565b610cce565b6102bd61039b36600461353b565b610d76565b61028c611004565b61028c61100a565b6102bd6103be366004613566565b61100e565b6102bd6103d136600461364e565b611043565b61028c611076565b6102bd6103ec3660046135c5565b61107a565b6102bd6103ff366004613503565b611140565b6102bd610412366004613503565b6112f3565b61028c610425366004613503565b611443565b61028c61145e565b6102bd610440366004613616565b611464565b610365611495565b6102bd61045b3660046135c5565b6114a4565b61028c61046e3660046135f5565b6114cc565b6102bd6114ec565b61028c6104893660046135f5565b6117c2565b6104966117d8565b6040516102999190613dda565b61028c6117e4565b6104be6104b9366004613503565b6117ea565b6040516102999493929190613d89565b61028c611813565b610365611819565b61036561183d565b61028c611861565b61028c6104fc366004613503565b611867565b610365611885565b61028c6118a9565b6103656118af565b61028c610527366004613503565b6118be565b6102bd6118f8565b600081620f42406301e1338061054a8287613e4e565b6105549190613e2e565b61055e9190613e16565b6105689190613e4e565b9392505050565b600c5481565b336001600160a01b03167f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b1580156105d857600080fd5b505afa1580156105ec573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610610919061351f565b6001600160a01b03161461063f5760405162461bcd60e51b815260040161063690613c04565b60405180910390fd5b620186a081106106615760405162461bcd60e51b81526004016106369061399f565b600855565b60045463ffffffff1661068b5760405162461bcd60e51b815260040161063690613b7c565b61069733848484611927565b505050565b6004546000908190600160201b90046001600160401b03166106bc611076565b1180156106ca575060065415155b156109465760007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663083c63236040518163ffffffff1660e01b815260040160206040518083038186803b15801561072a57600080fd5b505afa15801561073e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061076291906136ab565b63ffffffff169050600081610775611076565b116107a457600454600160201b90046001600160401b0316610795611076565b61079f9190613e6d565b6107c1565b6004546107c190600160201b90046001600160401b031683613e6d565b905060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166396c82e576040518163ffffffff1660e01b815260040160206040518083038186803b15801561081e57600080fd5b505afa158015610832573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061085691906136ab565b63ffffffff167f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663f74c16126040518163ffffffff1660e01b815260040160206040518083038186803b1580156108b557600080fd5b505afa1580156108c9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108ed919061359e565b6004546001600160c01b03919091169061090d9063ffffffff1685613e4e565b6109179190613e4e565b6109219190613e2e565b9050600554610932826006546117c2565b61093c9190613e16565b935050505061094b565b506005545b6001600160a01b03831660009081526003602052604081206002810154600182015491929161097a90856114cc565b6109849190613e6d565b95945050505050565b600081565b336001600160a01b03167f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b1580156109f557600080fd5b505afa158015610a09573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a2d919061351f565b6001600160a01b031614610a535760405162461bcd60e51b815260040161063690613c04565b6001600160a01b038116610a795760405162461bcd60e51b8152600401610636906139cf565b600280546001600160a01b0319166001600160a01b0392909216919091179055565b60095481565b610aa9613488565b6001600160a01b0383166000908152600360205260409020600401805483908110610ae457634e487b7160e01b600052603260045260246000fd5b60009182526020918290206040805160a081018252600393909302909101805483526001810154938301939093526002909201546001600160401b0380821693830193909352600160401b81049092166060820152600160801b90910460ff16151560808201529392505050565b600454600160201b90046001600160401b031681565b600b546001600160a01b03163314610b925760405162461bcd60e51b815260040161063690613c04565b80610b9c57610ca1565b600060065411610bbe5760405162461bcd60e51b815260040161063690613b22565b610bc933308361198e565b610bd5816006546117c2565b600c6000828254610be69190613e16565b925050819055507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03167f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03161415610c5f5780600d6000828254610c599190613e16565b90915550505b336001600160a01b03167fe1745dfad8f400852fcec0e4b23dabb3b55a98c67df52ee99c5385887277d72f82604051610c989190613d80565b60405180910390a25b50565b7f000000000000000000000000000000000000000000000000000000000000000081565b60075481565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614610d165760405162461bcd60e51b815260040161063690613c04565b60045460405133917f06555fe9dc8cbe328585a0c60ae1b7aafe71c28a706c2769d6cb4ee6e3e44e4691610d529163ffffffff16908590613deb565b60405180910390a26004805463ffffffff191663ffffffff92909216919091179055565b604051631e1c6a0760e01b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690631e1c6a0790610dc29033906004016136e3565b60206040518083038186803b158015610dda57600080fd5b505afa158015610dee573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e129190613582565b610e2e5760405162461bcd60e51b815260040161063690613c04565b610e36611a30565b6001600160a01b0382166000908152600360205260409020805415610e6357610e6183600080611eaf565b505b6000610e73620f42406002613e4e565b610e7d9084613e4e565b905060006040518060a00160405280858152602001838152602001610ea061100a565b6001600160401b03168152602001610eb661100a565b610ec4906301e13380613e16565b6001600160401b0316815260200160011515815250905083836000016000828254610eef9190613e16565b9250508190555081836001016000828254610f0a9190613e16565b90915550506004830180546001818101835560009283526020808420855160039094020192835584015190820155604083015160029091018054606085015160808601511515600160801b0260ff60801b196001600160401b03928316600160401b0267ffffffffffffffff60401b199390961667ffffffffffffffff199094169390931791909116939093171691909117905560068054849290610fb0908490613e16565b92505081905550610fc783600101546005546114cc565b60028401556001830154600c54610fde91906114cc565b836003018190555083600d6000828254610ff89190613e16565b90915550505050505050565b600a5481565b4290565b60045463ffffffff166110335760405162461bcd60e51b815260040161063690613b7c565b61103f33826001611eaf565b5050565b60045463ffffffff166110685760405162461bcd60e51b815260040161063690613b7c565b610697338484846000611f4a565b4390565b336001600160a01b03167f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b1580156110dd57600080fd5b505afa1580156110f1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611115919061351f565b6001600160a01b03161461113b5760405162461bcd60e51b815260040161063690613c04565b600a55565b336001600160a01b03167f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b1580156111a357600080fd5b505afa1580156111b7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111db919061351f565b6001600160a01b0316146112015760405162461bcd60e51b815260040161063690613c04565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03167f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316146112725760405162461bcd60e51b815260040161063690613aa6565b6040516317066a5760e21b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690635c19a95c906112be9084906004016136e3565b600060405180830381600087803b1580156112d857600080fd5b505af11580156112ec573d6000803e3d6000fd5b5050505050565b336001600160a01b03167f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b15801561135657600080fd5b505afa15801561136a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061138e919061351f565b6001600160a01b0316146113b45760405162461bcd60e51b815260040161063690613c04565b6001600160a01b0381166113da5760405162461bcd60e51b815260040161063690613c62565b600b5460405133917f2afec66505e0ceed692012e3833f6609d4933ded34732135bc05f2842374406591611419916001600160a01b03169085906136f7565b60405180910390a2600b80546001600160a01b0319166001600160a01b0392909216919091179055565b6001600160a01b031660009081526003602052604090205490565b60085481565b60045463ffffffff166114895760405162461bcd60e51b815260040161063690613b7c565b61069733848484612068565b6002546001600160a01b031681565b6301e1338081106114c75760405162461bcd60e51b815260040161063690613b4f565b600155565b600068056bc75e2d631000006114e28385613e4e565b6105689190613e2e565b60045463ffffffff16156115125760405162461bcd60e51b81526004016106369061386a565b6002546001600160a01b031661153a5760405162461bcd60e51b815260040161063690613a7c565b33600090815260036020526040902080546115675760405162461bcd60e51b815260040161063690613896565b60025460405163739b11d560e01b81526001600160a01b039091169063739b11d5906115999033908590600401613735565b600060405180830381600087803b1580156115b357600080fd5b505af11580156115c7573d6000803e3d6000fd5b5050506004820154600091505b80156116dd57600483016115e9600183613e6d565b8154811061160757634e487b7160e01b600052603260045260246000fd5b906000526020600020906003020160020160109054906101000a900460ff166116775760048301611639600183613e6d565b8154811061165757634e487b7160e01b600052603260045260246000fd5b906000526020600020906003020160000154826116749190613e16565b91505b8260040180548061169857634e487b7160e01b600052603160045260246000fd5b600082815260208120600360001990930192830201818155600181019190915560020180546001600160881b03191690559055806116d581613ed8565b9150506115d4565b5060025460405163a9059cbb60e01b81526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000081169263a9059cbb926117339291909116908590600401613803565b602060405180830381600087803b15801561174d57600080fd5b505af1158015611761573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117859190613582565b5033600090815260036020819052604082208281556001810183905560028101839055908101829055906117bc60048301826134b6565b50505050565b6000816114e268056bc75e2d6310000085613e4e565b60045463ffffffff1681565b60015481565b600360208190526000918252604090912080546001820154600283015492909301549092919084565b60065481565b7f000000000000000000000000000000000000000000000000000000000000000081565b7f000000000000000000000000000000000000000000000000000000000000000081565b600d5481565b6001600160a01b031660009081526003602052604090206004015490565b7f000000000000000000000000000000000000000000000000000000000000000081565b60055481565b600b546001600160a01b031681565b6001600160a01b03811660009081526003602081905260408220908101546001820154600c546118ee91906114cc565b6105689190613e6d565b60045463ffffffff1661191d5760405162461bcd60e51b815260040161063690613b7c565b611925611a30565b565b60045463ffffffff1661194c5760405162461bcd60e51b815260040161063690613b7c565b6119588484848461218b565b6001600160a01b03841660009081526003602052604090206001810154600c5461198291906114cc565b60039091015550505050565b6040516323b872dd60e01b81526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906323b872dd906119de90869086908690600401613711565b602060405180830381600087803b1580156119f857600080fd5b505af1158015611a0c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117bc9190613582565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316639f1dc9bd6040518163ffffffff1660e01b815260040160206040518083038186803b158015611a8957600080fd5b505afa158015611a9d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ac19190613582565b15611b3a577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663c8a6ac6f6040518163ffffffff1660e01b8152600401600060405180830381600087803b158015611b2157600080fd5b505af1158015611b35573d6000803e3d6000fd5b505050505b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663083c63236040518163ffffffff1660e01b815260040160206040518083038186803b158015611b9557600080fd5b505afa158015611ba9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611bcd91906136ab565b63ffffffff1690508060048054906101000a90046001600160401b03166001600160401b031610611bfe5750611925565b600454600160201b90046001600160401b0316611c19611076565b11611c245750611925565b600654611c5e57611c33611076565b6004806101000a8154816001600160401b0302191690836001600160401b0316021790555050611925565b600081611c69611076565b11611c7b57611c76611076565b611c7d565b815b600454909150600090611ca090600160201b90046001600160401b031683613e6d565b905060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663f74c16126040518163ffffffff1660e01b815260040160206040518083038186803b158015611cfd57600080fd5b505afa158015611d11573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d35919061359e565b6001600160c01b0316905060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166396c82e576040518163ffffffff1660e01b815260040160206040518083038186803b158015611d9b57600080fd5b505afa158015611daf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611dd391906136ab565b60045463ffffffff9182169116611dea8486613e4e565b611df49190613e4e565b611dfe9190613e2e565b90508060076000828254611e129190613e16565b92505081905550611e25816006546117c2565b60056000828254611e369190613e16565b9091555050600480546bffffffffffffffff000000001916600160201b6001600160401b038781168202929092179283905560055460405133947f5ffbf9ce09d035b92503aad17a31b3d37ca5cd887b63701ddc2200be77d9ccc794611ea0949091041690613da4565b60405180910390a25050505050565b6000611ebb8483612473565b611ec6848484612660565b90507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03167f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316148015611f27575082155b156105685780600d6000828254611f3e9190613e16565b90915550509392505050565b611f5785858585856129fd565b6001600160a01b03851660009081526003602052604090206001810154600c54611f8191906114cc565b816003018190555084600d6000828254611f9b9190613e16565b909155505060085415801590611fba57506000846001600160401b0316115b1561206057600064496cebb80060085487611fd361100a565b611fe6906001600160401b038a16613e6d565b611ff09190613e4e565b611ffa9190613e4e565b6120049190613e2e565b9050600a54600954101561205e57600a54816009546120239190613e16565b111561203c57600954600a546120399190613e6d565b90505b6120468782612df1565b80600960008282546120589190613e16565b90915550505b505b505050505050565b6001600160a01b0384166000908152600360205260408120600481018054919291869081106120a757634e487b7160e01b600052603260045260246000fd5b60009182526020918290206040805160a0810182526003909302909101805483526001810154938301939093526002909201546001600160401b03808216938301849052600160401b820416606083015260ff600160801b90910416151560808201529150158061212b575080606001516001600160401b031661212961100a565b115b6121475760405162461bcd60e51b8152600401610636906139ff565b83600d60008282546121599190613e6d565b9091555061216b905086868686612e6d565b61217b8260010154600c546114cc565b8260030181905550505050505050565b612193611a30565b61219b61100a565b826001600160401b0316116121c25760405162461bcd60e51b815260040161063690613aeb565b6001600160a01b03841660009081526003602052604090208054156121ef576121ed85836000611eaf565b505b600081600401858154811061221457634e487b7160e01b600052603260045260246000fd5b906000526020600020906003020190508060020160089054906101000a90046001600160401b03166001600160401b0316846001600160401b03161161226c5760405162461bcd60e51b815260040161063690613ba3565b60028101546001600160401b03166122ec576301e1338061228b61100a565b61229e906001600160401b038716613e6d565b11156122bc5760405162461bcd60e51b8152600401610636906138c4565b6122c461100a565b60028201805467ffffffffffffffff19166001600160401b0392909216919091179055612331565b60028101546301e133809061230a906001600160401b031686613e84565b6001600160401b031611156123315760405162461bcd60e51b8152600401610636906138c4565b60028101805467ffffffffffffffff60401b1916600160401b6001600160401b038781168202929092179283905583546000939092620f4240926301e1338092849261238292828216920416613e84565b6001600160401b03166123959190613e4e565b61239f9190613e2e565b6123a99190613e16565b6123b39190613e4e565b6001808401805490839055908501549192509082906123d3908390613e6d565b6123dd9190613e16565b600185018190556005546123f191906114cc565b60028501556006548290612406908390613e6d565b6124109190613e16565b60065560028301546040516001600160a01b038a16917f85daa0d8a4afa74e5bd57c0f5d2cddf52920ec882a02b8d3f646c972b4cfb6b491612461918b916001600160401b03909116908b90613dbb565b60405180910390a25050505050505050565b6001600160a01b038216600090815260036020526040812090612495846118be565b9050806124a357505061103f565b6040516370a0823160e01b81526000906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a08231906124f29030906004016136e3565b60206040518083038186803b15801561250a57600080fd5b505afa15801561251e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061254291906135dd565b9050818110156125645760405162461bcd60e51b815260040161063690613bcd565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03167f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031614156125e857600d5482116125cd57816125d1565b600d545b600d60008282546125e29190613e6d565b90915550505b8315612604576125fe8360010154600c546114cc565b60038401555b61260e8583613178565b846001600160a01b0316336001600160a01b03167f291d65ce1109b491b8b6540dabe662b3478f4c808f76bb4130c3f7afa2fab59c846040516126519190613d80565b60405180910390a35050505050565b6000811561267057612670611a30565b61267984613183565b90508061268857506000610568565b6001600160a01b038416600090815260036020526040902083156126b5576126b08583612df1565b61298c565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03167f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316141561286857600061271f620f42406002613e4e565b6127299084613e4e565b905060006040518060a0016040528085815260200183815260200161274c61100a565b6001600160401b0316815260200161276261100a565b612770906301e13380613e16565b6001600160401b039081168252600160209283018190526004870180548083018255600091825284822086516003909202019081559385015191840191909155604084015160029093018054606086015160808701511515600160801b0260ff60801b19918616600160401b0267ffffffffffffffff60401b199790961667ffffffffffffffff19909316929092179590951693909317939093169190911790915584549192508591859190612827908490613e16565b92505081905550818360010160008282546128429190613e16565b92505081905550816006600082825461285b9190613e16565b9091555061298c92505050565b60405163091465f760e11b81526000906001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690631228cbee906128d7907f0000000000000000000000000000000000000000000000000000000000000000906004016136e3565b60206040518083038186803b1580156128ef57600080fd5b505afa158015612903573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612927919061351f565b6040516344cc892d60e01b81529091506001600160a01b038216906344cc892d906129589089908790600401613803565b600060405180830381600087803b15801561297257600080fd5b505af1158015612986573d6000803e3d6000fd5b50505050505b82156129a8576129a281600101546005546114cc565b60028201555b846001600160a01b0316336001600160a01b03167f5033fdcf01566fb38fe1493114b856ff2a5d1c7875a6fafdacd1d320a012806a86856040516129ed929190613827565b60405180910390a3509392505050565b60008411612a1d5760405162461bcd60e51b815260040161063690613968565b600154612a2861100a565b612a329190613e16565b836001600160401b031610158015612a6857506301e13380612a5261100a565b612a65906001600160401b038616613e6d565b11155b612a845760405162461bcd60e51b8152600401610636906138fb565b612a8c611a30565b6001600160a01b0385166000908152600360205260409020805415612ab957612ab786846000611eaf565b505b6040516370a0823160e01b81526000906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a0823190612b089030906004016136e3565b60206040518083038186803b158015612b2057600080fd5b505afa158015612b34573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b5891906135dd565b9050612b653330886131b2565b6040516370a0823160e01b81526000906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a0823190612bb49030906004016136e3565b60206040518083038186803b158015612bcc57600080fd5b505afa158015612be0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c0491906135dd565b90506000612c128383613e6d565b9050600080886001600160401b031611612c2d576000612c35565b612c3561100a565b9050876000612c56612c478484613e84565b6001600160401b031685610534565b905060008111612c7657634e487b7160e01b600052600160045260246000fd5b6040805160a08101825285815260208082018481526001600160401b03878116948401948552868116606085019081528d15156080860190815260048e01805460018082018355600092835296822088516003909202019081559451958501959095559551600290930180549151965167ffffffffffffffff199092169383169390931767ffffffffffffffff60401b1916600160401b96909216959095021760ff60801b1916600160801b94151594909402939093179092558854909186918a9190612d44908490613e16565b9250508190555081886001016000828254612d5f9190613e16565b92505081905550612d7688600101546005546114cc565b88600201819055508160066000828254612d909190613e16565b925050819055508c6001600160a01b0316336001600160a01b03167f5dac0c1b1112564a045ba943c9d50270893e8e826c49be8e7073adc713ab7bd78e604051612dda9190613d80565b60405180910390a350505050505050505050505050565b6040516340c10f1960e01b81526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906340c10f1990612e3f9085908590600401613803565b600060405180830381600087803b158015612e5957600080fd5b505af1158015612060573d6000803e3d6000fd5b60008211612e8d5760405162461bcd60e51b815260040161063690613943565b6001600160a01b038416600090815260036020526040812060048101805491929186908110612ecc57634e487b7160e01b600052603260045260246000fd5b6000918252602090912060039091020160028101548154919250600160801b900460ff1690851115612f105760405162461bcd60e51b815260040161063690613d07565b612f18611a30565b612f2487856000611eaf565b5060018201548254600090612f3a908890613e6d565b6002850154620f4240906301e13380908290612f69906001600160401b0380821691600160401b900416613e84565b6001600160401b0316612f7c9190613e4e565b612f869190613e2e565b612f909190613e16565b612f9a9190613e4e565b8454909150612faa908890613e6d565b61300157846004018881548110612fd157634e487b7160e01b600052603260045260246000fd5b600091825260208220600390910201818155600181019190915560020180546001600160881b0319169055613022565b868460000160008282546130159190613e6d565b9091555050600184018190555b868560000160008282546130369190613e6d565b90915550506001850154819061304d908490613e6d565b6130579190613e16565b6001860181905560055461306b91906114cc565b60028601556006548190613080908490613e6d565b61308a9190613e16565b60065582156131185760405163e14bdb7160e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063e14bdb71906130e19033908b90600401613803565b600060405180830381600087803b1580156130fb57600080fd5b505af115801561310f573d6000803e3d6000fd5b50505050613122565b6131223388613210565b886001600160a01b0316336001600160a01b03167fd8654fcc8cf5b36d30b3f5e4688fc78118e6d68de60b9994e09902268b57c3e3896040516131659190613d80565b60405180910390a3505050505050505050565b61103f30838361198e565b6001600160a01b0381166000908152600360205260408120600281015460018201546005546118ee91906114cc565b600260005414156131d55760405162461bcd60e51b815260040161063690613cd0565b60026000556132067f000000000000000000000000000000000000000000000000000000000000000084848461326c565b5050600160005550565b600260005414156132335760405162461bcd60e51b815260040161063690613cd0565b60026000556132637f000000000000000000000000000000000000000000000000000000000000000083836132c4565b50506001600055565b6117bc846323b872dd60e01b85858560405160240161328d93929190613711565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b0319909316929092179091526132e3565b6106978363a9059cbb60e01b848460405160240161328d929190613803565b6000613338826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166133729092919063ffffffff16565b80519091501561069757808060200190518101906133569190613582565b6106975760405162461bcd60e51b815260040161063690613c86565b60606133818484600085613389565b949350505050565b6060824710156133ab5760405162461bcd60e51b815260040161063690613a36565b6133b485613449565b6133d05760405162461bcd60e51b815260040161063690613c2b565b600080866001600160a01b031685876040516133ec91906136c7565b60006040518083038185875af1925050503d8060008114613429576040519150601f19603f3d011682016040523d82523d6000602084013e61342e565b606091505b509150915061343e82828661344f565b979650505050505050565b3b151590565b6060831561345e575081610568565b82511561346e5782518084602001fd5b8160405162461bcd60e51b81526004016106369190613837565b6040805160a08101825260008082526020820181905291810182905260608101829052608081019190915290565b5080546000825560030290600052602060002090810190610ca191905b808211156134ff57600080825560018201556002810180546001600160881b03191690556003016134d3565b5090565b600060208284031215613514578081fd5b813561056881613f05565b600060208284031215613530578081fd5b815161056881613f05565b6000806040838503121561354d578081fd5b823561355881613f05565b946020939093013593505050565b600060208284031215613577578081fd5b813561056881613f1a565b600060208284031215613593578081fd5b815161056881613f1a565b6000602082840312156135af578081fd5b81516001600160c01b0381168114610568578182fd5b6000602082840312156135d6578081fd5b5035919050565b6000602082840312156135ee578081fd5b5051919050565b60008060408385031215613607578182fd5b50508035926020909101359150565b60008060006060848603121561362a578081fd5b8335925060208401359150604084013561364381613f1a565b809150509250925092565b600080600060608486031215613662578283fd5b8335925060208401356001600160401b038116811461367f578283fd5b9150604084013561364381613f1a565b6000602082840312156136a0578081fd5b813561056881613f28565b6000602082840312156136bc578081fd5b815161056881613f28565b600082516136d9818460208701613eac565b9190910192915050565b6001600160a01b0391909116815260200190565b6001600160a01b0392831681529116602082015260400190565b6001600160a01b039384168152919092166020820152604081019190915260600190565b6000604060018060a01b03851683526020818185015260e08401855483860152600180870154606081818901526002915081890154608081818b015260039150818b015460a081818d015260048d0191508060c08d0152878254808a526101008e019150838d528a8d2099508c93505b808410156137f05789548252888a01548b830152878a01546001600160401b038082168e850152818e1c1688840152851c60ff161515858301529885019892880192908201906137a5565b509e9d5050505050505050505050505050565b6001600160a01b03929092168252602082015260400190565b901515815260200190565b9115158252602082015260400190565b6000602082528251806020840152613856816040850160208701613eac565b601f01601f19169190910160400192915050565b602080825260129082015271191a5cd8589b19481c1bdbdb08199a5c9cdd60721b604082015260600190565b6020808252601490820152736e6f20746f6b656e7320746f206d69677261746560601b604082015260600190565b6020808252601b908201527f6d6178206c6f636b20706572696f642069732033363520646179730000000000604082015260600190565b60208082526028908201527f53796e646963617465506f6f6c426173653a20696e76616c6964206c6f636b206040820152671a5b9d195c9d985b60c21b606082015260800190565b6020808252600b908201526a1e995c9bc8185b5bdd5b9d60aa1b604082015260600190565b6020808252601e908201527f53796e646963617465506f6f6c426173653a207a65726f20616d6f756e740000604082015260600190565b602080825260169082015275706172616d65746572206f7574206f662072616e676560501b604082015260600190565b60208082526016908201527506d69677261746f722063616e6e6f74206265203078360541b604082015260600190565b60208082526018908201527f6465706f736974206e6f742079657420756e6c6f636b65640000000000000000604082015260600190565b60208082526026908201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6040820152651c8818d85b1b60d21b606082015260800190565b60208082526010908201526f1b5a59dc985d1bdc881b9bdd081cd95d60821b604082015260600190565b60208082526025908201527f436f7265506f6f6c3a206f6e6c792073796e7220706f6f6c2063616e2064656c604082015264656761746560d81b606082015260800190565b6020808252601c908201527f6c6f636b2073686f756c6420626520696e207468652066757475726500000000604082015260600190565b6020808252601390820152721e995c9bc81b1bd8dada5b99c81dd95a59da1d606a1b604082015260600190565b602080825260139082015272696e76616c6964206d696e4c6f636b54696d6560681b604082015260600190565b6020808252600d908201526c1c1bdbdb08191a5cd8589b1959609a1b604082015260600190565b60208082526010908201526f696e76616c6964206e6577206c6f636b60801b604082015260600190565b6020808252601d908201527f636f6e74726163742053594e522062616c616e636520746f6f206c6f77000000604082015260600190565b6020808252600d908201526c1858d8d95cdcc819195b9a5959609a1b604082015260600190565b6020808252601d908201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604082015260600190565b6020808252600a90820152691e995c9bc81a5b9c1d5d60b21b604082015260600190565b6020808252602a908201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6040820152691bdd081cdd58d8d9595960b21b606082015260800190565b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b602080825260149082015273616d6f756e742065786365656473207374616b6560601b604082015260600190565b600060a082019050825182526020830151602083015260408301516001600160401b038082166040850152806060860151166060850152505060808301511515608083015292915050565b90815260200190565b93845260208401929092526040830152606082015260800190565b9182526001600160401b0316602082015260400190565b9283526001600160401b03918216602084015216604082015260600190565b63ffffffff91909116815260200190565b63ffffffff92831681529116602082015260400190565b6001600160401b0391909116815260200190565b60008219821115613e2957613e29613eef565b500190565b600082613e4957634e487b7160e01b81526012600452602481fd5b500490565b6000816000190483118215151615613e6857613e68613eef565b500290565b600082821015613e7f57613e7f613eef565b500390565b60006001600160401b0383811690831681811015613ea457613ea4613eef565b039392505050565b60005b83811015613ec7578181015183820152602001613eaf565b838111156117bc5750506000910152565b600081613ee757613ee7613eef565b506000190190565b634e487b7160e01b600052601160045260246000fd5b6001600160a01b0381168114610ca157600080fd5b8015158114610ca157600080fd5b63ffffffff81168114610ca157600080fdfea2646970667358221220d046c2d7141ffa89b1ca11ce3a9993c3623008577e570b217c9143889e302cf664736f6c63430008010033000000000000000000000000bc6e06778708177a18210181b073da747c88490a000000000000000000000000598f0fb0eb8863fb5b07c2d7c7dc12308f72c05b0000000000000000000000001344b1caf421202916f7688008aaa78615fa4de9000000000000000000000000bc6e06778708177a18210181b073da747c88490a0000000000000000000000000000000000000000000000000000000000d8438e00000000000000000000000000000000000000000000000000000000000000c8

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106102745760003560e01c80636817031b11610151578063a87430ba116100c3578063e8d3cad511610087578063e8d3cad5146104ee578063e9d7f17514610501578063fa213bd614610509578063fbfa77cf14610511578063fc27076f14610519578063fff6cae91461052c57610274565b8063a87430ba146104ab578063beb0ed6c146104ce578063c45a0155146104d6578063cbdf382c146104de578063ce111541146104e657610274565b806382e5d1bc1161011557806382e5d1bc1461044d5780638e169d47146104605780638fd3ab8014610473578063a156dc281461047b578063a1aab33f1461048e578063a60ff766146104a357610274565b80636817031b1461040457806370a08231146104175780637221024f1461042a57806379b91d69146104325780637cd07e471461044557610274565b806331c520da116101ea5780634ce0f9a6116101ae5780634ce0f9a6146103a857806351c547f8146103b057806352044ec9146103c357806357e871e7146103d657806358b5c544146103de5780635c19a95c146103f157610274565b806331c520da1461035d578063379560b6146103725780634087aeb71461037a57806344cc892d1461038d57806348eb1ff2146103a057610274565b80631da10d911161023c5780631da10d91146102e557806323cf3118146102fa578063250531a71461030d5780632726b5061461031557806329eb5f2c146103355780633021a5601461034a57610274565b8063022bb3db1461027957806305472358146102a257806311769bc0146102aa57806315e4b1ae146102bf5780631984db99146102d2575b600080fd5b61028c6102873660046135f5565b610534565b6040516102999190613d80565b60405180910390f35b61028c61056f565b6102bd6102b83660046135c5565b610575565b005b6102bd6102cd36600461364e565b610666565b61028c6102e0366004613503565b61069c565b6102ed61098d565b604051610299919061381c565b6102bd610308366004613503565b610992565b61028c610a9b565b61032861032336600461353b565b610aa1565b6040516102999190613d35565b61033d610b52565b6040516102999190613e02565b6102bd6103583660046135c5565b610b68565b610365610ca4565b60405161029991906136e3565b61028c610cc8565b6102bd61038836600461368f565b610cce565b6102bd61039b36600461353b565b610d76565b61028c611004565b61028c61100a565b6102bd6103be366004613566565b61100e565b6102bd6103d136600461364e565b611043565b61028c611076565b6102bd6103ec3660046135c5565b61107a565b6102bd6103ff366004613503565b611140565b6102bd610412366004613503565b6112f3565b61028c610425366004613503565b611443565b61028c61145e565b6102bd610440366004613616565b611464565b610365611495565b6102bd61045b3660046135c5565b6114a4565b61028c61046e3660046135f5565b6114cc565b6102bd6114ec565b61028c6104893660046135f5565b6117c2565b6104966117d8565b6040516102999190613dda565b61028c6117e4565b6104be6104b9366004613503565b6117ea565b6040516102999493929190613d89565b61028c611813565b610365611819565b61036561183d565b61028c611861565b61028c6104fc366004613503565b611867565b610365611885565b61028c6118a9565b6103656118af565b61028c610527366004613503565b6118be565b6102bd6118f8565b600081620f42406301e1338061054a8287613e4e565b6105549190613e2e565b61055e9190613e16565b6105689190613e4e565b9392505050565b600c5481565b336001600160a01b03167f0000000000000000000000001344b1caf421202916f7688008aaa78615fa4de96001600160a01b0316638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b1580156105d857600080fd5b505afa1580156105ec573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610610919061351f565b6001600160a01b03161461063f5760405162461bcd60e51b815260040161063690613c04565b60405180910390fd5b620186a081106106615760405162461bcd60e51b81526004016106369061399f565b600855565b60045463ffffffff1661068b5760405162461bcd60e51b815260040161063690613b7c565b61069733848484611927565b505050565b6004546000908190600160201b90046001600160401b03166106bc611076565b1180156106ca575060065415155b156109465760007f0000000000000000000000001344b1caf421202916f7688008aaa78615fa4de96001600160a01b031663083c63236040518163ffffffff1660e01b815260040160206040518083038186803b15801561072a57600080fd5b505afa15801561073e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061076291906136ab565b63ffffffff169050600081610775611076565b116107a457600454600160201b90046001600160401b0316610795611076565b61079f9190613e6d565b6107c1565b6004546107c190600160201b90046001600160401b031683613e6d565b905060007f0000000000000000000000001344b1caf421202916f7688008aaa78615fa4de96001600160a01b03166396c82e576040518163ffffffff1660e01b815260040160206040518083038186803b15801561081e57600080fd5b505afa158015610832573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061085691906136ab565b63ffffffff167f0000000000000000000000001344b1caf421202916f7688008aaa78615fa4de96001600160a01b031663f74c16126040518163ffffffff1660e01b815260040160206040518083038186803b1580156108b557600080fd5b505afa1580156108c9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108ed919061359e565b6004546001600160c01b03919091169061090d9063ffffffff1685613e4e565b6109179190613e4e565b6109219190613e2e565b9050600554610932826006546117c2565b61093c9190613e16565b935050505061094b565b506005545b6001600160a01b03831660009081526003602052604081206002810154600182015491929161097a90856114cc565b6109849190613e6d565b95945050505050565b600081565b336001600160a01b03167f0000000000000000000000001344b1caf421202916f7688008aaa78615fa4de96001600160a01b0316638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b1580156109f557600080fd5b505afa158015610a09573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a2d919061351f565b6001600160a01b031614610a535760405162461bcd60e51b815260040161063690613c04565b6001600160a01b038116610a795760405162461bcd60e51b8152600401610636906139cf565b600280546001600160a01b0319166001600160a01b0392909216919091179055565b60095481565b610aa9613488565b6001600160a01b0383166000908152600360205260409020600401805483908110610ae457634e487b7160e01b600052603260045260246000fd5b60009182526020918290206040805160a081018252600393909302909101805483526001810154938301939093526002909201546001600160401b0380821693830193909352600160401b81049092166060820152600160801b90910460ff16151560808201529392505050565b600454600160201b90046001600160401b031681565b600b546001600160a01b03163314610b925760405162461bcd60e51b815260040161063690613c04565b80610b9c57610ca1565b600060065411610bbe5760405162461bcd60e51b815260040161063690613b22565b610bc933308361198e565b610bd5816006546117c2565b600c6000828254610be69190613e16565b925050819055507f000000000000000000000000bc6e06778708177a18210181b073da747c88490a6001600160a01b03167f000000000000000000000000bc6e06778708177a18210181b073da747c88490a6001600160a01b03161415610c5f5780600d6000828254610c599190613e16565b90915550505b336001600160a01b03167fe1745dfad8f400852fcec0e4b23dabb3b55a98c67df52ee99c5385887277d72f82604051610c989190613d80565b60405180910390a25b50565b7f000000000000000000000000598f0fb0eb8863fb5b07c2d7c7dc12308f72c05b81565b60075481565b336001600160a01b037f0000000000000000000000001344b1caf421202916f7688008aaa78615fa4de91614610d165760405162461bcd60e51b815260040161063690613c04565b60045460405133917f06555fe9dc8cbe328585a0c60ae1b7aafe71c28a706c2769d6cb4ee6e3e44e4691610d529163ffffffff16908590613deb565b60405180910390a26004805463ffffffff191663ffffffff92909216919091179055565b604051631e1c6a0760e01b81526001600160a01b037f0000000000000000000000001344b1caf421202916f7688008aaa78615fa4de91690631e1c6a0790610dc29033906004016136e3565b60206040518083038186803b158015610dda57600080fd5b505afa158015610dee573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e129190613582565b610e2e5760405162461bcd60e51b815260040161063690613c04565b610e36611a30565b6001600160a01b0382166000908152600360205260409020805415610e6357610e6183600080611eaf565b505b6000610e73620f42406002613e4e565b610e7d9084613e4e565b905060006040518060a00160405280858152602001838152602001610ea061100a565b6001600160401b03168152602001610eb661100a565b610ec4906301e13380613e16565b6001600160401b0316815260200160011515815250905083836000016000828254610eef9190613e16565b9250508190555081836001016000828254610f0a9190613e16565b90915550506004830180546001818101835560009283526020808420855160039094020192835584015190820155604083015160029091018054606085015160808601511515600160801b0260ff60801b196001600160401b03928316600160401b0267ffffffffffffffff60401b199390961667ffffffffffffffff199094169390931791909116939093171691909117905560068054849290610fb0908490613e16565b92505081905550610fc783600101546005546114cc565b60028401556001830154600c54610fde91906114cc565b836003018190555083600d6000828254610ff89190613e16565b90915550505050505050565b600a5481565b4290565b60045463ffffffff166110335760405162461bcd60e51b815260040161063690613b7c565b61103f33826001611eaf565b5050565b60045463ffffffff166110685760405162461bcd60e51b815260040161063690613b7c565b610697338484846000611f4a565b4390565b336001600160a01b03167f0000000000000000000000001344b1caf421202916f7688008aaa78615fa4de96001600160a01b0316638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b1580156110dd57600080fd5b505afa1580156110f1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611115919061351f565b6001600160a01b03161461113b5760405162461bcd60e51b815260040161063690613c04565b600a55565b336001600160a01b03167f0000000000000000000000001344b1caf421202916f7688008aaa78615fa4de96001600160a01b0316638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b1580156111a357600080fd5b505afa1580156111b7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111db919061351f565b6001600160a01b0316146112015760405162461bcd60e51b815260040161063690613c04565b7f000000000000000000000000bc6e06778708177a18210181b073da747c88490a6001600160a01b03167f000000000000000000000000bc6e06778708177a18210181b073da747c88490a6001600160a01b0316146112725760405162461bcd60e51b815260040161063690613aa6565b6040516317066a5760e21b81526001600160a01b037f000000000000000000000000bc6e06778708177a18210181b073da747c88490a1690635c19a95c906112be9084906004016136e3565b600060405180830381600087803b1580156112d857600080fd5b505af11580156112ec573d6000803e3d6000fd5b5050505050565b336001600160a01b03167f0000000000000000000000001344b1caf421202916f7688008aaa78615fa4de96001600160a01b0316638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b15801561135657600080fd5b505afa15801561136a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061138e919061351f565b6001600160a01b0316146113b45760405162461bcd60e51b815260040161063690613c04565b6001600160a01b0381166113da5760405162461bcd60e51b815260040161063690613c62565b600b5460405133917f2afec66505e0ceed692012e3833f6609d4933ded34732135bc05f2842374406591611419916001600160a01b03169085906136f7565b60405180910390a2600b80546001600160a01b0319166001600160a01b0392909216919091179055565b6001600160a01b031660009081526003602052604090205490565b60085481565b60045463ffffffff166114895760405162461bcd60e51b815260040161063690613b7c565b61069733848484612068565b6002546001600160a01b031681565b6301e1338081106114c75760405162461bcd60e51b815260040161063690613b4f565b600155565b600068056bc75e2d631000006114e28385613e4e565b6105689190613e2e565b60045463ffffffff16156115125760405162461bcd60e51b81526004016106369061386a565b6002546001600160a01b031661153a5760405162461bcd60e51b815260040161063690613a7c565b33600090815260036020526040902080546115675760405162461bcd60e51b815260040161063690613896565b60025460405163739b11d560e01b81526001600160a01b039091169063739b11d5906115999033908590600401613735565b600060405180830381600087803b1580156115b357600080fd5b505af11580156115c7573d6000803e3d6000fd5b5050506004820154600091505b80156116dd57600483016115e9600183613e6d565b8154811061160757634e487b7160e01b600052603260045260246000fd5b906000526020600020906003020160020160109054906101000a900460ff166116775760048301611639600183613e6d565b8154811061165757634e487b7160e01b600052603260045260246000fd5b906000526020600020906003020160000154826116749190613e16565b91505b8260040180548061169857634e487b7160e01b600052603160045260246000fd5b600082815260208120600360001990930192830201818155600181019190915560020180546001600160881b03191690559055806116d581613ed8565b9150506115d4565b5060025460405163a9059cbb60e01b81526001600160a01b037f000000000000000000000000bc6e06778708177a18210181b073da747c88490a81169263a9059cbb926117339291909116908590600401613803565b602060405180830381600087803b15801561174d57600080fd5b505af1158015611761573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117859190613582565b5033600090815260036020819052604082208281556001810183905560028101839055908101829055906117bc60048301826134b6565b50505050565b6000816114e268056bc75e2d6310000085613e4e565b60045463ffffffff1681565b60015481565b600360208190526000918252604090912080546001820154600283015492909301549092919084565b60065481565b7f0000000000000000000000001344b1caf421202916f7688008aaa78615fa4de981565b7f000000000000000000000000bc6e06778708177a18210181b073da747c88490a81565b600d5481565b6001600160a01b031660009081526003602052604090206004015490565b7f000000000000000000000000bc6e06778708177a18210181b073da747c88490a81565b60055481565b600b546001600160a01b031681565b6001600160a01b03811660009081526003602081905260408220908101546001820154600c546118ee91906114cc565b6105689190613e6d565b60045463ffffffff1661191d5760405162461bcd60e51b815260040161063690613b7c565b611925611a30565b565b60045463ffffffff1661194c5760405162461bcd60e51b815260040161063690613b7c565b6119588484848461218b565b6001600160a01b03841660009081526003602052604090206001810154600c5461198291906114cc565b60039091015550505050565b6040516323b872dd60e01b81526001600160a01b037f000000000000000000000000bc6e06778708177a18210181b073da747c88490a16906323b872dd906119de90869086908690600401613711565b602060405180830381600087803b1580156119f857600080fd5b505af1158015611a0c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117bc9190613582565b7f0000000000000000000000001344b1caf421202916f7688008aaa78615fa4de96001600160a01b0316639f1dc9bd6040518163ffffffff1660e01b815260040160206040518083038186803b158015611a8957600080fd5b505afa158015611a9d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ac19190613582565b15611b3a577f0000000000000000000000001344b1caf421202916f7688008aaa78615fa4de96001600160a01b031663c8a6ac6f6040518163ffffffff1660e01b8152600401600060405180830381600087803b158015611b2157600080fd5b505af1158015611b35573d6000803e3d6000fd5b505050505b60007f0000000000000000000000001344b1caf421202916f7688008aaa78615fa4de96001600160a01b031663083c63236040518163ffffffff1660e01b815260040160206040518083038186803b158015611b9557600080fd5b505afa158015611ba9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611bcd91906136ab565b63ffffffff1690508060048054906101000a90046001600160401b03166001600160401b031610611bfe5750611925565b600454600160201b90046001600160401b0316611c19611076565b11611c245750611925565b600654611c5e57611c33611076565b6004806101000a8154816001600160401b0302191690836001600160401b0316021790555050611925565b600081611c69611076565b11611c7b57611c76611076565b611c7d565b815b600454909150600090611ca090600160201b90046001600160401b031683613e6d565b905060007f0000000000000000000000001344b1caf421202916f7688008aaa78615fa4de96001600160a01b031663f74c16126040518163ffffffff1660e01b815260040160206040518083038186803b158015611cfd57600080fd5b505afa158015611d11573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d35919061359e565b6001600160c01b0316905060007f0000000000000000000000001344b1caf421202916f7688008aaa78615fa4de96001600160a01b03166396c82e576040518163ffffffff1660e01b815260040160206040518083038186803b158015611d9b57600080fd5b505afa158015611daf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611dd391906136ab565b60045463ffffffff9182169116611dea8486613e4e565b611df49190613e4e565b611dfe9190613e2e565b90508060076000828254611e129190613e16565b92505081905550611e25816006546117c2565b60056000828254611e369190613e16565b9091555050600480546bffffffffffffffff000000001916600160201b6001600160401b038781168202929092179283905560055460405133947f5ffbf9ce09d035b92503aad17a31b3d37ca5cd887b63701ddc2200be77d9ccc794611ea0949091041690613da4565b60405180910390a25050505050565b6000611ebb8483612473565b611ec6848484612660565b90507f000000000000000000000000bc6e06778708177a18210181b073da747c88490a6001600160a01b03167f000000000000000000000000bc6e06778708177a18210181b073da747c88490a6001600160a01b0316148015611f27575082155b156105685780600d6000828254611f3e9190613e16565b90915550509392505050565b611f5785858585856129fd565b6001600160a01b03851660009081526003602052604090206001810154600c54611f8191906114cc565b816003018190555084600d6000828254611f9b9190613e16565b909155505060085415801590611fba57506000846001600160401b0316115b1561206057600064496cebb80060085487611fd361100a565b611fe6906001600160401b038a16613e6d565b611ff09190613e4e565b611ffa9190613e4e565b6120049190613e2e565b9050600a54600954101561205e57600a54816009546120239190613e16565b111561203c57600954600a546120399190613e6d565b90505b6120468782612df1565b80600960008282546120589190613e16565b90915550505b505b505050505050565b6001600160a01b0384166000908152600360205260408120600481018054919291869081106120a757634e487b7160e01b600052603260045260246000fd5b60009182526020918290206040805160a0810182526003909302909101805483526001810154938301939093526002909201546001600160401b03808216938301849052600160401b820416606083015260ff600160801b90910416151560808201529150158061212b575080606001516001600160401b031661212961100a565b115b6121475760405162461bcd60e51b8152600401610636906139ff565b83600d60008282546121599190613e6d565b9091555061216b905086868686612e6d565b61217b8260010154600c546114cc565b8260030181905550505050505050565b612193611a30565b61219b61100a565b826001600160401b0316116121c25760405162461bcd60e51b815260040161063690613aeb565b6001600160a01b03841660009081526003602052604090208054156121ef576121ed85836000611eaf565b505b600081600401858154811061221457634e487b7160e01b600052603260045260246000fd5b906000526020600020906003020190508060020160089054906101000a90046001600160401b03166001600160401b0316846001600160401b03161161226c5760405162461bcd60e51b815260040161063690613ba3565b60028101546001600160401b03166122ec576301e1338061228b61100a565b61229e906001600160401b038716613e6d565b11156122bc5760405162461bcd60e51b8152600401610636906138c4565b6122c461100a565b60028201805467ffffffffffffffff19166001600160401b0392909216919091179055612331565b60028101546301e133809061230a906001600160401b031686613e84565b6001600160401b031611156123315760405162461bcd60e51b8152600401610636906138c4565b60028101805467ffffffffffffffff60401b1916600160401b6001600160401b038781168202929092179283905583546000939092620f4240926301e1338092849261238292828216920416613e84565b6001600160401b03166123959190613e4e565b61239f9190613e2e565b6123a99190613e16565b6123b39190613e4e565b6001808401805490839055908501549192509082906123d3908390613e6d565b6123dd9190613e16565b600185018190556005546123f191906114cc565b60028501556006548290612406908390613e6d565b6124109190613e16565b60065560028301546040516001600160a01b038a16917f85daa0d8a4afa74e5bd57c0f5d2cddf52920ec882a02b8d3f646c972b4cfb6b491612461918b916001600160401b03909116908b90613dbb565b60405180910390a25050505050505050565b6001600160a01b038216600090815260036020526040812090612495846118be565b9050806124a357505061103f565b6040516370a0823160e01b81526000906001600160a01b037f000000000000000000000000bc6e06778708177a18210181b073da747c88490a16906370a08231906124f29030906004016136e3565b60206040518083038186803b15801561250a57600080fd5b505afa15801561251e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061254291906135dd565b9050818110156125645760405162461bcd60e51b815260040161063690613bcd565b7f000000000000000000000000bc6e06778708177a18210181b073da747c88490a6001600160a01b03167f000000000000000000000000bc6e06778708177a18210181b073da747c88490a6001600160a01b031614156125e857600d5482116125cd57816125d1565b600d545b600d60008282546125e29190613e6d565b90915550505b8315612604576125fe8360010154600c546114cc565b60038401555b61260e8583613178565b846001600160a01b0316336001600160a01b03167f291d65ce1109b491b8b6540dabe662b3478f4c808f76bb4130c3f7afa2fab59c846040516126519190613d80565b60405180910390a35050505050565b6000811561267057612670611a30565b61267984613183565b90508061268857506000610568565b6001600160a01b038416600090815260036020526040902083156126b5576126b08583612df1565b61298c565b7f000000000000000000000000bc6e06778708177a18210181b073da747c88490a6001600160a01b03167f000000000000000000000000bc6e06778708177a18210181b073da747c88490a6001600160a01b0316141561286857600061271f620f42406002613e4e565b6127299084613e4e565b905060006040518060a0016040528085815260200183815260200161274c61100a565b6001600160401b0316815260200161276261100a565b612770906301e13380613e16565b6001600160401b039081168252600160209283018190526004870180548083018255600091825284822086516003909202019081559385015191840191909155604084015160029093018054606086015160808701511515600160801b0260ff60801b19918616600160401b0267ffffffffffffffff60401b199790961667ffffffffffffffff19909316929092179590951693909317939093169190911790915584549192508591859190612827908490613e16565b92505081905550818360010160008282546128429190613e16565b92505081905550816006600082825461285b9190613e16565b9091555061298c92505050565b60405163091465f760e11b81526000906001600160a01b037f0000000000000000000000001344b1caf421202916f7688008aaa78615fa4de91690631228cbee906128d7907f000000000000000000000000bc6e06778708177a18210181b073da747c88490a906004016136e3565b60206040518083038186803b1580156128ef57600080fd5b505afa158015612903573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612927919061351f565b6040516344cc892d60e01b81529091506001600160a01b038216906344cc892d906129589089908790600401613803565b600060405180830381600087803b15801561297257600080fd5b505af1158015612986573d6000803e3d6000fd5b50505050505b82156129a8576129a281600101546005546114cc565b60028201555b846001600160a01b0316336001600160a01b03167f5033fdcf01566fb38fe1493114b856ff2a5d1c7875a6fafdacd1d320a012806a86856040516129ed929190613827565b60405180910390a3509392505050565b60008411612a1d5760405162461bcd60e51b815260040161063690613968565b600154612a2861100a565b612a329190613e16565b836001600160401b031610158015612a6857506301e13380612a5261100a565b612a65906001600160401b038616613e6d565b11155b612a845760405162461bcd60e51b8152600401610636906138fb565b612a8c611a30565b6001600160a01b0385166000908152600360205260409020805415612ab957612ab786846000611eaf565b505b6040516370a0823160e01b81526000906001600160a01b037f000000000000000000000000bc6e06778708177a18210181b073da747c88490a16906370a0823190612b089030906004016136e3565b60206040518083038186803b158015612b2057600080fd5b505afa158015612b34573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b5891906135dd565b9050612b653330886131b2565b6040516370a0823160e01b81526000906001600160a01b037f000000000000000000000000bc6e06778708177a18210181b073da747c88490a16906370a0823190612bb49030906004016136e3565b60206040518083038186803b158015612bcc57600080fd5b505afa158015612be0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c0491906135dd565b90506000612c128383613e6d565b9050600080886001600160401b031611612c2d576000612c35565b612c3561100a565b9050876000612c56612c478484613e84565b6001600160401b031685610534565b905060008111612c7657634e487b7160e01b600052600160045260246000fd5b6040805160a08101825285815260208082018481526001600160401b03878116948401948552868116606085019081528d15156080860190815260048e01805460018082018355600092835296822088516003909202019081559451958501959095559551600290930180549151965167ffffffffffffffff199092169383169390931767ffffffffffffffff60401b1916600160401b96909216959095021760ff60801b1916600160801b94151594909402939093179092558854909186918a9190612d44908490613e16565b9250508190555081886001016000828254612d5f9190613e16565b92505081905550612d7688600101546005546114cc565b88600201819055508160066000828254612d909190613e16565b925050819055508c6001600160a01b0316336001600160a01b03167f5dac0c1b1112564a045ba943c9d50270893e8e826c49be8e7073adc713ab7bd78e604051612dda9190613d80565b60405180910390a350505050505050505050505050565b6040516340c10f1960e01b81526001600160a01b037f000000000000000000000000598f0fb0eb8863fb5b07c2d7c7dc12308f72c05b16906340c10f1990612e3f9085908590600401613803565b600060405180830381600087803b158015612e5957600080fd5b505af1158015612060573d6000803e3d6000fd5b60008211612e8d5760405162461bcd60e51b815260040161063690613943565b6001600160a01b038416600090815260036020526040812060048101805491929186908110612ecc57634e487b7160e01b600052603260045260246000fd5b6000918252602090912060039091020160028101548154919250600160801b900460ff1690851115612f105760405162461bcd60e51b815260040161063690613d07565b612f18611a30565b612f2487856000611eaf565b5060018201548254600090612f3a908890613e6d565b6002850154620f4240906301e13380908290612f69906001600160401b0380821691600160401b900416613e84565b6001600160401b0316612f7c9190613e4e565b612f869190613e2e565b612f909190613e16565b612f9a9190613e4e565b8454909150612faa908890613e6d565b61300157846004018881548110612fd157634e487b7160e01b600052603260045260246000fd5b600091825260208220600390910201818155600181019190915560020180546001600160881b0319169055613022565b868460000160008282546130159190613e6d565b9091555050600184018190555b868560000160008282546130369190613e6d565b90915550506001850154819061304d908490613e6d565b6130579190613e16565b6001860181905560055461306b91906114cc565b60028601556006548190613080908490613e6d565b61308a9190613e16565b60065582156131185760405163e14bdb7160e01b81526001600160a01b037f0000000000000000000000001344b1caf421202916f7688008aaa78615fa4de9169063e14bdb71906130e19033908b90600401613803565b600060405180830381600087803b1580156130fb57600080fd5b505af115801561310f573d6000803e3d6000fd5b50505050613122565b6131223388613210565b886001600160a01b0316336001600160a01b03167fd8654fcc8cf5b36d30b3f5e4688fc78118e6d68de60b9994e09902268b57c3e3896040516131659190613d80565b60405180910390a3505050505050505050565b61103f30838361198e565b6001600160a01b0381166000908152600360205260408120600281015460018201546005546118ee91906114cc565b600260005414156131d55760405162461bcd60e51b815260040161063690613cd0565b60026000556132067f000000000000000000000000bc6e06778708177a18210181b073da747c88490a84848461326c565b5050600160005550565b600260005414156132335760405162461bcd60e51b815260040161063690613cd0565b60026000556132637f000000000000000000000000bc6e06778708177a18210181b073da747c88490a83836132c4565b50506001600055565b6117bc846323b872dd60e01b85858560405160240161328d93929190613711565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b0319909316929092179091526132e3565b6106978363a9059cbb60e01b848460405160240161328d929190613803565b6000613338826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166133729092919063ffffffff16565b80519091501561069757808060200190518101906133569190613582565b6106975760405162461bcd60e51b815260040161063690613c86565b60606133818484600085613389565b949350505050565b6060824710156133ab5760405162461bcd60e51b815260040161063690613a36565b6133b485613449565b6133d05760405162461bcd60e51b815260040161063690613c2b565b600080866001600160a01b031685876040516133ec91906136c7565b60006040518083038185875af1925050503d8060008114613429576040519150601f19603f3d011682016040523d82523d6000602084013e61342e565b606091505b509150915061343e82828661344f565b979650505050505050565b3b151590565b6060831561345e575081610568565b82511561346e5782518084602001fd5b8160405162461bcd60e51b81526004016106369190613837565b6040805160a08101825260008082526020820181905291810182905260608101829052608081019190915290565b5080546000825560030290600052602060002090810190610ca191905b808211156134ff57600080825560018201556002810180546001600160881b03191690556003016134d3565b5090565b600060208284031215613514578081fd5b813561056881613f05565b600060208284031215613530578081fd5b815161056881613f05565b6000806040838503121561354d578081fd5b823561355881613f05565b946020939093013593505050565b600060208284031215613577578081fd5b813561056881613f1a565b600060208284031215613593578081fd5b815161056881613f1a565b6000602082840312156135af578081fd5b81516001600160c01b0381168114610568578182fd5b6000602082840312156135d6578081fd5b5035919050565b6000602082840312156135ee578081fd5b5051919050565b60008060408385031215613607578182fd5b50508035926020909101359150565b60008060006060848603121561362a578081fd5b8335925060208401359150604084013561364381613f1a565b809150509250925092565b600080600060608486031215613662578283fd5b8335925060208401356001600160401b038116811461367f578283fd5b9150604084013561364381613f1a565b6000602082840312156136a0578081fd5b813561056881613f28565b6000602082840312156136bc578081fd5b815161056881613f28565b600082516136d9818460208701613eac565b9190910192915050565b6001600160a01b0391909116815260200190565b6001600160a01b0392831681529116602082015260400190565b6001600160a01b039384168152919092166020820152604081019190915260600190565b6000604060018060a01b03851683526020818185015260e08401855483860152600180870154606081818901526002915081890154608081818b015260039150818b015460a081818d015260048d0191508060c08d0152878254808a526101008e019150838d528a8d2099508c93505b808410156137f05789548252888a01548b830152878a01546001600160401b038082168e850152818e1c1688840152851c60ff161515858301529885019892880192908201906137a5565b509e9d5050505050505050505050505050565b6001600160a01b03929092168252602082015260400190565b901515815260200190565b9115158252602082015260400190565b6000602082528251806020840152613856816040850160208701613eac565b601f01601f19169190910160400192915050565b602080825260129082015271191a5cd8589b19481c1bdbdb08199a5c9cdd60721b604082015260600190565b6020808252601490820152736e6f20746f6b656e7320746f206d69677261746560601b604082015260600190565b6020808252601b908201527f6d6178206c6f636b20706572696f642069732033363520646179730000000000604082015260600190565b60208082526028908201527f53796e646963617465506f6f6c426173653a20696e76616c6964206c6f636b206040820152671a5b9d195c9d985b60c21b606082015260800190565b6020808252600b908201526a1e995c9bc8185b5bdd5b9d60aa1b604082015260600190565b6020808252601e908201527f53796e646963617465506f6f6c426173653a207a65726f20616d6f756e740000604082015260600190565b602080825260169082015275706172616d65746572206f7574206f662072616e676560501b604082015260600190565b60208082526016908201527506d69677261746f722063616e6e6f74206265203078360541b604082015260600190565b60208082526018908201527f6465706f736974206e6f742079657420756e6c6f636b65640000000000000000604082015260600190565b60208082526026908201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6040820152651c8818d85b1b60d21b606082015260800190565b60208082526010908201526f1b5a59dc985d1bdc881b9bdd081cd95d60821b604082015260600190565b60208082526025908201527f436f7265506f6f6c3a206f6e6c792073796e7220706f6f6c2063616e2064656c604082015264656761746560d81b606082015260800190565b6020808252601c908201527f6c6f636b2073686f756c6420626520696e207468652066757475726500000000604082015260600190565b6020808252601390820152721e995c9bc81b1bd8dada5b99c81dd95a59da1d606a1b604082015260600190565b602080825260139082015272696e76616c6964206d696e4c6f636b54696d6560681b604082015260600190565b6020808252600d908201526c1c1bdbdb08191a5cd8589b1959609a1b604082015260600190565b60208082526010908201526f696e76616c6964206e6577206c6f636b60801b604082015260600190565b6020808252601d908201527f636f6e74726163742053594e522062616c616e636520746f6f206c6f77000000604082015260600190565b6020808252600d908201526c1858d8d95cdcc819195b9a5959609a1b604082015260600190565b6020808252601d908201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604082015260600190565b6020808252600a90820152691e995c9bc81a5b9c1d5d60b21b604082015260600190565b6020808252602a908201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6040820152691bdd081cdd58d8d9595960b21b606082015260800190565b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b602080825260149082015273616d6f756e742065786365656473207374616b6560601b604082015260600190565b600060a082019050825182526020830151602083015260408301516001600160401b038082166040850152806060860151166060850152505060808301511515608083015292915050565b90815260200190565b93845260208401929092526040830152606082015260800190565b9182526001600160401b0316602082015260400190565b9283526001600160401b03918216602084015216604082015260600190565b63ffffffff91909116815260200190565b63ffffffff92831681529116602082015260400190565b6001600160401b0391909116815260200190565b60008219821115613e2957613e29613eef565b500190565b600082613e4957634e487b7160e01b81526012600452602481fd5b500490565b6000816000190483118215151615613e6857613e68613eef565b500290565b600082821015613e7f57613e7f613eef565b500390565b60006001600160401b0383811690831681811015613ea457613ea4613eef565b039392505050565b60005b83811015613ec7578181015183820152602001613eaf565b838111156117bc5750506000910152565b600081613ee757613ee7613eef565b506000190190565b634e487b7160e01b600052601160045260246000fd5b6001600160a01b0381168114610ca157600080fd5b8015158114610ca157600080fd5b63ffffffff81168114610ca157600080fdfea2646970667358221220d046c2d7141ffa89b1ca11ce3a9993c3623008577e570b217c9143889e302cf664736f6c63430008010033

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

000000000000000000000000bc6e06778708177a18210181b073da747c88490a000000000000000000000000598f0fb0eb8863fb5b07c2d7c7dc12308f72c05b0000000000000000000000001344b1caf421202916f7688008aaa78615fa4de9000000000000000000000000bc6e06778708177a18210181b073da747c88490a0000000000000000000000000000000000000000000000000000000000d8438e00000000000000000000000000000000000000000000000000000000000000c8

-----Decoded View---------------
Arg [0] : _synr (address): 0xbc6E06778708177a18210181b073DA747C88490a
Arg [1] : _ssynr (address): 0x598f0fb0EB8863fB5b07C2D7C7dc12308F72C05B
Arg [2] : _factory (address): 0x1344B1cAf421202916F7688008AAa78615Fa4De9
Arg [3] : _poolToken (address): 0xbc6E06778708177a18210181b073DA747C88490a
Arg [4] : _initBlock (uint64): 14173070
Arg [5] : _weight (uint32): 200

-----Encoded View---------------
6 Constructor Arguments found :
Arg [0] : 000000000000000000000000bc6e06778708177a18210181b073da747c88490a
Arg [1] : 000000000000000000000000598f0fb0eb8863fb5b07c2d7c7dc12308f72c05b
Arg [2] : 0000000000000000000000001344b1caf421202916f7688008aaa78615fa4de9
Arg [3] : 000000000000000000000000bc6e06778708177a18210181b073da747c88490a
Arg [4] : 0000000000000000000000000000000000000000000000000000000000d8438e
Arg [5] : 00000000000000000000000000000000000000000000000000000000000000c8


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

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

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.