Contract 0xC9A848AC73e378516B16E4EeBBa5ef6aFbC0BBc2

Txn Hash Method
Block
From
To
Value [Txn Fee]
0x74c9c15cba159cbdb021bfdd0b3994f1d3cf13e294ce740b747a01411f5b0afdDeposit750887372022-09-28 11:30:115 hrs 41 mins ago0x5da68351bd082abda73e42ac981db51d9364fe69 IN  0xc9a848ac73e378516b16e4eebba5ef6afbc0bbc20 ETH0.0000134037
0x4e3c138ed3c1ca3d22aaf578d83a6e07dc50a922cb3d2196b79863376390c13fDeposit750705852022-09-28 5:34:2011 hrs 37 mins ago0x2e177a1176b81b2fb49a1dcc1a0945adcafd0881 IN  0xc9a848ac73e378516b16e4eebba5ef6afbc0bbc20 ETH0.0000104251
0x34f8cb4efb250c0d06c5104208ca5514107bbbafe33e08b4f32c574987935a13Deposit750355032022-09-27 18:06:1623 hrs 5 mins ago0xc450b1401d9eacead337398b43568b04da233405 IN  0xc9a848ac73e378516b16e4eebba5ef6afbc0bbc20 ETH0.0000104251
0xfbf3d7230a8adfdf4d5ab5415d25888ba407c3eed3c65d27727e3b9c291930b3Deposit750051532022-09-27 7:56:271 day 9 hrs ago0x67dbc5f16ea68385be21bcc7de6e50be27a56d60 IN  0xc9a848ac73e378516b16e4eebba5ef6afbc0bbc20 ETH0
0x10006fa23bd7c6d4f6fdeaa2975afa2ccde4d48141d6cb602d9d9dfde3a8596fDeposit749625182022-09-26 17:42:051 day 23 hrs ago0xc450b1401d9eacead337398b43568b04da233405 IN  0xc9a848ac73e378516b16e4eebba5ef6afbc0bbc20 ETH0.00001042426
0xf77bc8c66a94b9a68fa430154e7747159e683853f0d7a0ca4ff070de643c4f8bDeposit749545582022-09-26 15:01:302 days 2 hrs ago0x37756db30020cd4ae6fbc5d6312fc88ed2d9b014 IN  0xc9a848ac73e378516b16e4eebba5ef6afbc0bbc20 ETH0
0xf77eab9ec16f4f6bc3e5a6992c0edca5e2aec47c13bc09a29a489b6248e65805Withdraw749544892022-09-26 15:00:082 days 2 hrs ago0x37756db30020cd4ae6fbc5d6312fc88ed2d9b014 IN  0xc9a848ac73e378516b16e4eebba5ef6afbc0bbc20 ETH0
0x5055316d95762d199313ecac44d607a3107cc7127e3b84c6778f15b39fb82f60Deposit749339162022-09-26 8:02:052 days 9 hrs ago0xc0b93a4d523e7a29d6e87755a43de5ecc1f2b66e IN  0xc9a848ac73e378516b16e4eebba5ef6afbc0bbc20 ETH0.0000104251
0x2116d0f74d8005d2b1843626dcf77790ce6a47db9a98b5ae3eee2c1b45dc7dedWithdraw749314952022-09-26 7:13:322 days 9 hrs ago0x5baf597914e62182e5ccafbcc69c966919d5cba8 IN  0xc9a848ac73e378516b16e4eebba5ef6afbc0bbc20 ETH0.00001180718
0x30d9ae0514e84129c988e55b562ce92a54d790aeea6294b1f94774e3e7a947baDeposit748817732022-09-25 14:30:313 days 2 hrs ago0x7248b21b991fa6c99af3d67b015fa403e470c5ca IN  0xc9a848ac73e378516b16e4eebba5ef6afbc0bbc20 ETH0
0x70e7c032ad35fb6950dae193b2341b959ae59ff73ef83f7a57816b6ca79acb20Deposit748797252022-09-25 13:48:163 days 3 hrs ago0x39b4a9268fc9e5366005c319d0103a4c0899e1b5 IN  0xc9a848ac73e378516b16e4eebba5ef6afbc0bbc20 ETH0.00001033564
0xd83cad218509a7a25827e8860e635a54fe1225b990254b632085114c8189cfcfDeposit748442062022-09-25 1:56:293 days 15 hrs ago0xc450b1401d9eacead337398b43568b04da233405 IN  0xc9a848ac73e378516b16e4eebba5ef6afbc0bbc20 ETH0.0000104251
0xbfb46e630930c4fb10ba33bf2f47d189b18d5303ef4271eaf6b360538170fa13Deposit747836342022-09-24 5:18:464 days 11 hrs ago0xc6bb43e4b3fc5d8ecbec809047d529f054e65c38 IN  0xc9a848ac73e378516b16e4eebba5ef6afbc0bbc20 ETH0
0x891cb0206d992ac3315aae5586d22808d5fdfbd7333503a9f63366b985a628fcDeposit747418402022-09-23 14:52:435 days 2 hrs ago0xc8728ae130381eb77fc9a8b715564b00e83e19df IN  0xc9a848ac73e378516b16e4eebba5ef6afbc0bbc20 ETH0
0x2e47e51b239ae0f46a6d744d5f3d88b4dd8fbb3d7de68bd540aab2d22390e6a9Deposit747287012022-09-23 10:22:275 days 6 hrs ago0x368955efb1bf001683cb5f7ab223380caf0b86e5 IN  0xc9a848ac73e378516b16e4eebba5ef6afbc0bbc20 ETH0.0000104251
0x179b0d2b04e0a5c86597a4df72cb83037d792dc7909e1c10b71f734ae6f2d27dDeposit747200972022-09-23 7:20:015 days 9 hrs ago0xf5fdd938b6be0a0e6800262b3f623fe26c098410 IN  0xc9a848ac73e378516b16e4eebba5ef6afbc0bbc20 ETH0.00000956242
0xec291399c36899040a8940084658b646c3f1e3f93c63e996d8c624a1803d4fa8Deposit747200502022-09-23 7:19:035 days 9 hrs ago0xf5fdd938b6be0a0e6800262b3f623fe26c098410 IN  0xc9a848ac73e378516b16e4eebba5ef6afbc0bbc20 ETH0.00000956242
0x40a0cde4307f3ff169b88c6573a9f11d948023430b3435b1eadd4fd0506b15dfDeposit746924752022-09-22 21:42:315 days 19 hrs ago0xc450b1401d9eacead337398b43568b04da233405 IN  0xc9a848ac73e378516b16e4eebba5ef6afbc0bbc20 ETH0.0000104251
0x7d780127d5ecb59fd2e5da9a4df0f72e223eec7db7b22f5d0239d38535b3cc51Deposit746837702022-09-22 18:42:375 days 22 hrs ago0x3a247fd0fb5935394ab7ca75f33bf76b3fd52a45 IN  0xc9a848ac73e378516b16e4eebba5ef6afbc0bbc20 ETH0
0x7ef9cf5716dde15b1c27a8bce748d3e6e6497cb71774286e80d2f6c27a05b510Deposit746796292022-09-22 17:13:385 days 23 hrs ago0x4deb56a42fc63650b2f0bfb9abe1850e3a091371 IN  0xc9a848ac73e378516b16e4eebba5ef6afbc0bbc20 ETH0.00000903294
0x5361ef3a0abccec75c0c46b369b9534cdaad9650104c3eb05dd9ac1ce9438d60Deposit746614502022-09-22 10:45:076 days 6 hrs ago0x9db6e6c2161047155263b8ffbe52733dc6aed973 IN  0xc9a848ac73e378516b16e4eebba5ef6afbc0bbc20 ETH0
0xa09109be4cd509d21e06ee76a796aa0ccb43655fb91157f042bbfb1a5d1779efDeposit746079292022-09-21 15:48:427 days 1 hr ago0x0bec8034d26545c7d81cdd9c9b8a32a5adf38458 IN  0xc9a848ac73e378516b16e4eebba5ef6afbc0bbc20 ETH0.0000090321
0x1987085f5d44385d9f1d383ae158a752d7080ad16efbcaa06a9fe69b28704250Deposit746077602022-09-21 15:45:097 days 1 hr ago0xc6bb43e4b3fc5d8ecbec809047d529f054e65c38 IN  0xc9a848ac73e378516b16e4eebba5ef6afbc0bbc20 ETH0
0x8ccfb1663d12ae9824f2ba4170babff94c0d0cfff69002229bda28fd8e28a51cDeposit746049792022-09-21 14:45:287 days 2 hrs ago0x0aa735dc94630ffceea27ae8459e9212efea0d8d IN  0xc9a848ac73e378516b16e4eebba5ef6afbc0bbc20 ETH0.0000104251
0x531d3c2c11a9523fe7aa59d5ace999ffcbdd59ae3b1afb16390fcab03453b555Deposit745843442022-09-21 7:20:487 days 9 hrs ago0xc8728ae130381eb77fc9a8b715564b00e83e19df IN  0xc9a848ac73e378516b16e4eebba5ef6afbc0bbc20 ETH0.0000092281
[ Download CSV Export 
Parent Txn Hash Block From To Value
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
AuriFairLaunch

Compiler Version
v0.8.11+commit.d7f03943

Optimization Enabled:
Yes with 3000 runs

Other Settings:
default evmVersion

Contract Source Code (Solidity Standard Json-Input format)

File 1 of 11 : AuriFairLaunch.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.11;

import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "./interfaces/PULPInterface.sol";
import {BoringOwnable} from "./BoringOwnable.sol";
import "./AuriMathLib.sol";
import "./interfaces/AuriFairLaunchInterface.sol";

interface ComptrollerInterface {
  function isAllowedToClaimReward(address user, address claimer) external view returns (bool);
}

/// @author Kyber Network
/// Forked from KyberFairLaunch
/// Allow stakers to stake LP tokens and receive a reward token
/// Allow extend or renew a pool to continue/restart the LM program
/// When harvesting, rewards will be transferred according to schedule dictated by TokenLock
contract AuriFairLaunch is ReentrancyGuard, BoringOwnable, AuriFairLaunchInterface {
  using SafeCast for uint256;
  using SafeERC20 for IERC20;

  uint256 internal constant PRECISION = 1e12;

  struct UserRewardData {
    uint256 unclaimedReward;
    uint256 lastRewardPerShare;
  }
  // Info of each user.
  struct UserInfo {
    uint256 amount; // How many Staking tokens the user has provided.
    mapping(uint256 => UserRewardData) userRewardData;
    //
    // Basically, any point in time, the amount of reward token
    // entitled to a user but is pending to be distributed is:
    //
    //   pending reward = user.unclaimAmount + (user.amount * (pool.accRewardPerShare - user.lastRewardPerShare)
    //
    // Whenever a user deposits or withdraws Staking tokens to a pool. Here's what happens:
    //   1. The pool's `accRewardPerShare` (and `lastRewardTimestamp`) gets updated.
    //   2. User receives the pending reward sent to his/her address.
    //   3. User's `lastRewardPerShare` gets updated.
    //   4. User's `amount` gets updated.
  }

  struct PoolRewardData {
    uint256 rewardPerSecond;
    uint256 accRewardPerShare;
  }
  // Info of each pool
  // poolRewardData: reward data for each reward token
  //      rewardPerSecond: amount of reward token per second
  //      accRewardPerShare: accumulated reward per share of token
  // totalStake: total amount of stakeToken has been staked
  // stakeToken: token to stake, should be an ERC20 token
  // startTime: the timestamp that the reward starts
  // endTime: the timestamp that the reward ends
  // lastRewardTimestamp: last timestamp that rewards distribution occurs
  struct PoolInfo {
    uint256 totalStake;
    address stakeToken;
    uint32 startTime;
    uint32 endTime;
    uint32 lastRewardTimestamp;
    mapping(uint256 => PoolRewardData) poolRewardData;
  }

  // check if a pool exists for a stakeToken
  mapping(address => bool) public poolExists;
  // contract for locking reward
  PULPInterface public immutable pulp;
  IERC20 public immutable ply;
  address[] public rewardTokens;

  // comptroller to determine who can harvest
  ComptrollerInterface public immutable unitroller;

  // Info of each pool.
  uint256 public poolLength;
  mapping(uint256 => PoolInfo) internal poolInfo;
  // Info of each user that stakes Staking tokens.
  mapping(uint256 => mapping(address => UserInfo)) internal userInfo;

  constructor(
    PULPInterface _pulp,
    ComptrollerInterface _unitroller,
    address[] memory _rewardTokens
  ) BoringOwnable() {
    rewardTokens = _rewardTokens;
    pulp = _pulp;
    ply = IERC20(pulp.PLY());
    unitroller = _unitroller;

    ply.approve(address(pulp), type(uint256).max);
  }

  /**
   * @dev Allow owner to withdraw only reward token
   */
  function ownerWithdraw(uint256 rewardTokenIndex, uint256 amount) external onlyOwner {
    require(rewardTokenIndex < rewardTokens.length, "invalid index");
    address rewardToken = rewardTokens[rewardTokenIndex];
    IERC20(rewardToken).safeTransfer(msg.sender, amount);
    emit RewardTokenWithdrawn(msg.sender, rewardToken, amount);
  }

  /**
   * @dev Add a new lp to the pool. Can only be called by the owner.
   * @param _stakeToken: token to be staked to the pool
   * @param _startTime: timestamp where the reward starts
   * @param _endTime: timestamp where the reward ends
   * @param _rewardPerSeconds: amount of reward token per second for the pool for each reward token
   */
  function addPool(
    address _stakeToken,
    uint32 _startTime,
    uint32 _endTime,
    uint256[] calldata _rewardPerSeconds
  ) external nonReentrant onlyOwner {
    require(!poolExists[_stakeToken], "add: duplicated pool");
    require(_stakeToken != address(0), "add: invalid stake token");
    require(rewardTokens.length == _rewardPerSeconds.length, "add: invalid length");

    require(_startTime > block.timestamp && _endTime > _startTime, "add: invalid times");

    poolInfo[poolLength].stakeToken = _stakeToken;
    poolInfo[poolLength].startTime = _startTime;
    poolInfo[poolLength].endTime = _endTime;
    poolInfo[poolLength].lastRewardTimestamp = _startTime;

    for (uint256 i = 0; i < _rewardPerSeconds.length; i++) {
      poolInfo[poolLength].poolRewardData[i] = PoolRewardData({
        rewardPerSecond: _rewardPerSeconds[i],
        accRewardPerShare: 0
      });
    }

    poolLength++;

    poolExists[_stakeToken] = true;

    emit AddNewPool(_stakeToken, _startTime, _endTime, _rewardPerSeconds);
  }

  /**
   * @dev Renew a pool to start another liquidity mining program
   * @param _pid: id of the pool to renew, must be pool that has not started or already ended
   * @param _startTime: timestamp where the reward starts
   * @param _endTime: timestamp where the reward ends
   * @param _rewardPerSeconds: amount of reward token per second for the pool
   *   0 if we want to stop the pool from accumulating rewards
   */
  function renewPool(
    uint256 _pid,
    uint32 _startTime,
    uint32 _endTime,
    uint256[] calldata _rewardPerSeconds
  ) external nonReentrant onlyOwner {
    _updatePoolRewards(_pid);

    PoolInfo storage pool = poolInfo[_pid];
    // check if pool has not started or already ended
    require(
      pool.startTime > block.timestamp || pool.endTime < block.timestamp,
      "renew: invalid pool state to renew"
    );
    // checking data of new pool
    require(rewardTokens.length == _rewardPerSeconds.length, "renew: invalid length");
    require(_startTime > block.timestamp && _endTime > _startTime, "renew: invalid times");

    pool.startTime = _startTime;
    pool.endTime = _endTime;
    pool.lastRewardTimestamp = _startTime;

    for (uint256 i = 0; i < _rewardPerSeconds.length; i++) {
      pool.poolRewardData[i].rewardPerSecond = _rewardPerSeconds[i];
    }

    emit RenewPool(_pid, _startTime, _endTime, _rewardPerSeconds);
  }

  /**
   * @dev Update a pool, allow to change end timestamp, reward per second
   * @param _pid: pool id to be renew
   * @param _endTime: timestamp where the reward ends
   * @param _rewardPerSeconds: amount of reward token per second for the pool,
   *   0 if we want to stop the pool from accumulating rewards
   */
  function updatePool(
    uint256 _pid,
    uint32 _endTime,
    uint256[] calldata _rewardPerSeconds
  ) external nonReentrant onlyOwner {
    _updatePoolRewards(_pid);

    PoolInfo storage pool = poolInfo[_pid];

    // should call renew pool if the pool has ended
    require(pool.endTime > block.timestamp, "update: pool already ended");
    require(rewardTokens.length == _rewardPerSeconds.length, "update: invalid length");
    require(_endTime > block.timestamp && _endTime > pool.startTime, "update: invalid end time");

    pool.endTime = _endTime;
    for (uint256 i = 0; i < _rewardPerSeconds.length; i++) {
      pool.poolRewardData[i].rewardPerSecond = _rewardPerSeconds[i];
    }

    emit UpdatePool(_pid, _endTime, _rewardPerSeconds);
  }

  /**
   * @dev Deposit tokens to accumulate rewards without harvesting
   * @param _pid: id of the pool
   * @param _amount: amount of stakeToken to be deposited
   */
  function deposit(uint256 _pid, uint256 _amount) external nonReentrant {
    // update pool rewards, user's rewards
    _updatePoolRewards(_pid);
    _updateUserReward(msg.sender, _pid, false, 0);

    PoolInfo storage pool = poolInfo[_pid];
    UserInfo storage user = userInfo[_pid][msg.sender];

    // collect stakeToken
    IERC20(pool.stakeToken).safeTransferFrom(msg.sender, address(this), _amount);

    // update user staked amount, and total staked amount for the pool
    user.amount = user.amount + _amount;
    pool.totalStake = pool.totalStake + _amount;

    emit Deposit(msg.sender, _pid, block.timestamp, _amount);
  }

  /**
   * @dev Withdraw token (of the sender) from pool without harvesting
   * @param _pid: id of the pool
   * @param _amount: amount of stakeToken to withdraw
   */
  function withdraw(uint256 _pid, uint256 _amount) external nonReentrant {
    _withdraw(_pid, _amount);
  }

  /**
   * @dev Withdraw all tokens (of the sender) from pool without harvesting
   * @param _pid: id of the pool
   */
  function withdrawAll(uint256 _pid) external nonReentrant {
    _withdraw(_pid, userInfo[_pid][msg.sender].amount);
  }

  /**
   * @notice EMERGENCY USAGE ONLY, USER'S REWARDS WILL BE RESET
   * @dev Emergency withdrawal function to allow withdraw all deposited tokens (of the sender)
   *   and reset all rewards
   * @param _pid: id of the pool
   */
  function emergencyWithdraw(uint256 _pid) external nonReentrant {
    PoolInfo storage pool = poolInfo[_pid];
    UserInfo storage user = userInfo[_pid][msg.sender];
    uint256 amount = user.amount;

    user.amount = 0;
    uint256 rTokensLength = rewardTokens.length;
    for (uint256 i = 0; i < rTokensLength; i++) {
      UserRewardData storage rewardData = user.userRewardData[i];
      rewardData.lastRewardPerShare = 0;
      rewardData.unclaimedReward = 0;
    }

    pool.totalStake = pool.totalStake - amount;

    if (amount > 0) {
      IERC20(pool.stakeToken).safeTransfer(msg.sender, amount);
    }

    emit EmergencyWithdraw(msg.sender, _pid, block.timestamp, amount);
  }

  /**
   * @dev update rewards from multiple pools for the account
   */
  function updateMultiplePools(address account, uint256[] calldata _pids) external nonReentrant {
    uint256 pid;
    for (uint256 i = 0; i < _pids.length; i++) {
      pid = _pids[i];
      _updatePoolRewards(pid);
      // update user reward without harvesting
      _updateUserReward(account, pid, false, 0);
    }
  }

  /**
   * @dev Get pending rewards of a user from a pool, mostly for front-end
   * @param _pid: id of the pool
   * @param _user: user to check for pending rewards
   */
  function pendingRewards(uint256 _pid, address _user)
    external
    view
    returns (uint256[] memory rewards)
  {
    uint256 rTokensLength = rewardTokens.length;
    rewards = new uint256[](rTokensLength);
    PoolInfo storage pool = poolInfo[_pid];
    UserInfo storage user = userInfo[_pid][_user];
    uint256 _totalStake = pool.totalStake;
    uint256 _poolLastRewardTimestamp = pool.lastRewardTimestamp;
    uint32 lastAccountedTime = _lastAccountedRewardTime(_pid);

    for (uint256 i = 0; i < rTokensLength; i++) {
      uint256 _accRewardPerShare = pool.poolRewardData[i].accRewardPerShare;
      if (lastAccountedTime > _poolLastRewardTimestamp && _totalStake != 0) {
        uint256 reward = (lastAccountedTime - _poolLastRewardTimestamp) *
          pool.poolRewardData[i].rewardPerSecond;
        _accRewardPerShare = _accRewardPerShare + ((reward * PRECISION) / _totalStake);
      }
      rewards[i] =
        (user.amount * (_accRewardPerShare - user.userRewardData[i].lastRewardPerShare)) /
        PRECISION;
      rewards[i] += user.userRewardData[i].unclaimedReward;
    }
  }

  /**
   * @dev Return list reward tokens
   */
  function getRewardTokens() external view returns (address[] memory) {
    return rewardTokens;
  }

  /**
   * @dev Return full details of a pool
   */
  function getPoolInfo(uint256 _pid)
    external
    view
    returns (
      uint256 totalStake,
      address stakeToken,
      uint32 startTime,
      uint32 endTime,
      uint32 lastRewardTimestamp,
      uint256[] memory rewardPerSeconds,
      uint256[] memory accRewardPerShares
    )
  {
    PoolInfo storage pool = poolInfo[_pid];
    (totalStake, stakeToken, startTime, endTime, lastRewardTimestamp) = (
      pool.totalStake,
      pool.stakeToken,
      pool.startTime,
      pool.endTime,
      pool.lastRewardTimestamp
    );
    uint256 rTokensLength = rewardTokens.length;
    rewardPerSeconds = new uint256[](rTokensLength);
    accRewardPerShares = new uint256[](rTokensLength);
    for (uint256 i = 0; i < rTokensLength; i++) {
      rewardPerSeconds[i] = pool.poolRewardData[i].rewardPerSecond;
      accRewardPerShares[i] = pool.poolRewardData[i].accRewardPerShare;
    }
  }

  /**
   * @dev Return user's info including deposited amount and reward data
   */
  function getUserInfo(uint256 _pid, address _account)
    public
    view
    returns (
      uint256 amount,
      uint256[] memory unclaimedRewards,
      uint256[] memory lastRewardPerShares
    )
  {
    UserInfo storage user = userInfo[_pid][_account];
    amount = user.amount;
    uint256 rTokensLength = rewardTokens.length;
    unclaimedRewards = new uint256[](rTokensLength);
    lastRewardPerShares = new uint256[](rTokensLength);
    for (uint256 i = 0; i < rTokensLength; i++) {
      unclaimedRewards[i] = user.userRewardData[i].unclaimedReward;
      lastRewardPerShares[i] = user.userRewardData[i].lastRewardPerShare;
    }
  }

  /**
   * @dev Return user's info including deposited amount and reward data
   */
  function updateAndGetUserInfo(uint256 _pid, address _account)
    external
    nonReentrant
    returns (
      uint256 amount,
      uint256[] memory unclaimedRewards,
      uint256[] memory lastRewardPerShares
    )
  {
    _updatePoolRewards(_pid);
    _updateUserReward(_account, _pid, false, 0);
    (amount, unclaimedRewards, lastRewardPerShares) = getUserInfo(_pid, _account);
  }

  /**
   * @dev Harvest rewards from a pool for an account. For all rewards that are not PLY, the entire amount will be
   transferred out. For PLY, it will follow the weekly locking schedule specified in PULP
   * @param _pid: id of the pool
   * @param _maxAmountOfPlyToHarvest: the maximum amount of Ply to harvest from the pool
   * @dev Note that only approved claimer can harvest & lock the rewards (check _doTransferOutRewards function)
   */
  function harvest(
    address account,
    uint256 _pid,
    uint256 _maxAmountOfPlyToHarvest
  ) external nonReentrant {
    _updatePoolRewards(_pid);
    _updateUserReward(account, _pid, true, _maxAmountOfPlyToHarvest);
  }

  /**
   * @dev Update rewards for one pool
   */
  function updatePoolRewards(uint256 _pid) external nonReentrant {
    _updatePoolRewards(_pid);
  }

  /**
   * @dev Update rewards for one pool
   */
  function _updatePoolRewards(uint256 _pid) internal {
    require(_pid < poolLength, "invalid pool id");
    PoolInfo storage pool = poolInfo[_pid];
    uint32 lastAccountedTime = _lastAccountedRewardTime(_pid);
    if (lastAccountedTime <= pool.lastRewardTimestamp) return;
    uint256 _totalStake = pool.totalStake;
    if (_totalStake == 0) {
      pool.lastRewardTimestamp = lastAccountedTime;
      return;
    }

    uint256 secondsElapsed = lastAccountedTime - pool.lastRewardTimestamp;
    uint256 rTokensLength = rewardTokens.length;
    for (uint256 i = 0; i < rTokensLength; i++) {
      PoolRewardData storage rewardData = pool.poolRewardData[i];
      uint256 reward = secondsElapsed * rewardData.rewardPerSecond;
      rewardData.accRewardPerShare =
        rewardData.accRewardPerShare +
        (reward * PRECISION) /
        _totalStake;
    }
    pool.lastRewardTimestamp = lastAccountedTime;
  }

  /**
   * @dev Withdraw _amount of stakeToken from pool _pid
   */
  function _withdraw(uint256 _pid, uint256 _amount) internal {
    PoolInfo storage pool = poolInfo[_pid];
    UserInfo storage user = userInfo[_pid][msg.sender];
    require(user.amount >= _amount, "withdraw: insufficient amount");

    // update pool reward and harvest
    _updatePoolRewards(_pid);
    _updateUserReward(msg.sender, _pid, false, 0);

    user.amount = user.amount - _amount;
    pool.totalStake = pool.totalStake - _amount;

    IERC20(pool.stakeToken).safeTransfer(msg.sender, _amount);

    emit Withdraw(msg.sender, _pid, block.timestamp, _amount);
  }

  /**
   * @dev Update reward of _to address from pool _pid, harvest if needed
   */
  function _updateUserReward(
    address _to,
    uint256 _pid,
    bool doHarvest,
    uint256 _maxAmountOfPlyToHarvest
  ) internal {
    uint256 userAmount = userInfo[_pid][_to].amount;
    uint256 rTokensLength = rewardTokens.length;

    if (userAmount == 0) {
      // update user last reward per share to the latest pool reward per share
      // by right if user.amount is 0, user.unclaimedReward should be 0 as well,
      // except when user uses emergencyWithdraw function
      for (uint256 i = 0; i < rTokensLength; i++) {
        userInfo[_pid][_to].userRewardData[i].lastRewardPerShare = poolInfo[_pid]
          .poolRewardData[i]
          .accRewardPerShare;
      }
      return;
    }

    for (uint256 i = 0; i < rTokensLength; i++) {
      uint256 lastAccRewardPerShare = poolInfo[_pid].poolRewardData[i].accRewardPerShare;
      UserRewardData storage rewardData = userInfo[_pid][_to].userRewardData[i];
      // user's unclaim reward + user's amount * (pool's accRewardPerShare - user's lastRewardPerShare) / precision
      uint256 _pending = (userAmount * (lastAccRewardPerShare - rewardData.lastRewardPerShare)) /
        PRECISION;
      _pending = _pending + rewardData.unclaimedReward;

      rewardData.unclaimedReward = _pending;
      // update user last reward per share to the latest pool reward per share
      rewardData.lastRewardPerShare = lastAccRewardPerShare;

      if (doHarvest && _pending > 0) {
        uint256 harvestedAmount = _doTransferOutRewards(
          rewardData,
          IERC20(rewardTokens[i]),
          _to,
          _maxAmountOfPlyToHarvest
        );
        emit Harvest(_to, _pid, rewardTokens[i], harvestedAmount, block.timestamp);
      }
    }
  }

  /**
   * @dev Returns last accounted reward time, either the current timestamp or the endTime of the pool
   */
  function _lastAccountedRewardTime(uint256 _pid) internal view returns (uint32 _value) {
    _value = poolInfo[_pid].endTime;
    if (_value > block.timestamp) _value = block.timestamp.toUint32();
  }

  function _doTransferOutRewards(
    UserRewardData storage rewardData,
    IERC20 rewardToken,
    address _account,
    uint256 _maxAmountOfPlyToHarvest
  ) internal returns (uint256 harvestedAmount) {
    require(unitroller.isAllowedToClaimReward(_account, msg.sender), "not allowed to harvest");

    if (rewardToken == ply) {
      if (_maxAmountOfPlyToHarvest > 0) {
        harvestedAmount = Math.min(_maxAmountOfPlyToHarvest, rewardData.unclaimedReward);
        rewardData.unclaimedReward -= harvestedAmount;
        _lockPly(_account, harvestedAmount);
      }
    } else {
      harvestedAmount = rewardData.unclaimedReward;
      rewardData.unclaimedReward -= harvestedAmount;
      IERC20(rewardToken).safeTransfer(_account, harvestedAmount);
    }
  }

  function _lockPly(address _account, uint256 _amount) internal {
    (uint256 lockAmount, uint256 claimAmount) = pulp.calcLockAmount(_account, _amount);
    if (lockAmount != 0) {
      pulp.lockPly(_account, lockAmount);
    }
    if (claimAmount != 0) {
      ply.safeTransfer(_account, claimAmount);
    }
  }
}

File 2 of 11 : ReentrancyGuard.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (security/ReentrancyGuard.sol)

pragma solidity ^0.8.0;

/**
 * @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 making 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 3 of 11 : SafeERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.0;

import "../IERC20.sol";
import "../../../utils/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'
        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 {
        unchecked {
            uint256 oldAllowance = token.allowance(address(this), spender);
            require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
            uint256 newAllowance = oldAllowance - 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
            require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
        }
    }
}

File 4 of 11 : SafeCast.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/math/SafeCast.sol)

pragma solidity ^0.8.0;

/**
 * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow
 * checks.
 *
 * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
 * easily result in undesired exploitation or bugs, since developers usually
 * assume that overflows raise errors. `SafeCast` restores this intuition by
 * reverting the transaction when such an operation overflows.
 *
 * Using this library instead of the unchecked operations eliminates an entire
 * class of bugs, so it's recommended to use it always.
 *
 * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing
 * all math on `uint256` and `int256` and then downcasting.
 */
library SafeCast {
    /**
     * @dev Returns the downcasted uint224 from uint256, reverting on
     * overflow (when the input is greater than largest uint224).
     *
     * Counterpart to Solidity's `uint224` operator.
     *
     * Requirements:
     *
     * - input must fit into 224 bits
     */
    function toUint224(uint256 value) internal pure returns (uint224) {
        require(value <= type(uint224).max, "SafeCast: value doesn't fit in 224 bits");
        return uint224(value);
    }

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

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

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

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

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

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

    /**
     * @dev Converts a signed int256 into an unsigned uint256.
     *
     * Requirements:
     *
     * - input must be greater than or equal to 0.
     */
    function toUint256(int256 value) internal pure returns (uint256) {
        require(value >= 0, "SafeCast: value must be positive");
        return uint256(value);
    }

    /**
     * @dev Returns the downcasted int128 from int256, reverting on
     * overflow (when the input is less than smallest int128 or
     * greater than largest int128).
     *
     * Counterpart to Solidity's `int128` operator.
     *
     * Requirements:
     *
     * - input must fit into 128 bits
     *
     * _Available since v3.1._
     */
    function toInt128(int256 value) internal pure returns (int128) {
        require(value >= type(int128).min && value <= type(int128).max, "SafeCast: value doesn't fit in 128 bits");
        return int128(value);
    }

    /**
     * @dev Returns the downcasted int64 from int256, reverting on
     * overflow (when the input is less than smallest int64 or
     * greater than largest int64).
     *
     * Counterpart to Solidity's `int64` operator.
     *
     * Requirements:
     *
     * - input must fit into 64 bits
     *
     * _Available since v3.1._
     */
    function toInt64(int256 value) internal pure returns (int64) {
        require(value >= type(int64).min && value <= type(int64).max, "SafeCast: value doesn't fit in 64 bits");
        return int64(value);
    }

    /**
     * @dev Returns the downcasted int32 from int256, reverting on
     * overflow (when the input is less than smallest int32 or
     * greater than largest int32).
     *
     * Counterpart to Solidity's `int32` operator.
     *
     * Requirements:
     *
     * - input must fit into 32 bits
     *
     * _Available since v3.1._
     */
    function toInt32(int256 value) internal pure returns (int32) {
        require(value >= type(int32).min && value <= type(int32).max, "SafeCast: value doesn't fit in 32 bits");
        return int32(value);
    }

    /**
     * @dev Returns the downcasted int16 from int256, reverting on
     * overflow (when the input is less than smallest int16 or
     * greater than largest int16).
     *
     * Counterpart to Solidity's `int16` operator.
     *
     * Requirements:
     *
     * - input must fit into 16 bits
     *
     * _Available since v3.1._
     */
    function toInt16(int256 value) internal pure returns (int16) {
        require(value >= type(int16).min && value <= type(int16).max, "SafeCast: value doesn't fit in 16 bits");
        return int16(value);
    }

    /**
     * @dev Returns the downcasted int8 from int256, reverting on
     * overflow (when the input is less than smallest int8 or
     * greater than largest int8).
     *
     * Counterpart to Solidity's `int8` operator.
     *
     * Requirements:
     *
     * - input must fit into 8 bits.
     *
     * _Available since v3.1._
     */
    function toInt8(int256 value) internal pure returns (int8) {
        require(value >= type(int8).min && value <= type(int8).max, "SafeCast: value doesn't fit in 8 bits");
        return int8(value);
    }

    /**
     * @dev Converts an unsigned uint256 into a signed int256.
     *
     * Requirements:
     *
     * - input must be less than or equal to maxInt256.
     */
    function toInt256(uint256 value) internal pure returns (int256) {
        // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
        require(value <= uint256(type(int256).max), "SafeCast: value doesn't fit in an int256");
        return int256(value);
    }
}

File 5 of 11 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

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

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

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

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

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

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

    /**
     * @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 6 of 11 : PULPInterface.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.11;
import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";

interface PULPInterface is IERC20Metadata {
  function globalLock(uint256 _week) external view returns (uint256);

  function userLock(address _user, uint256 _week) external view returns (bool, uint224);

  function PLY() external view returns (address);

  function lockPly(address recipient, uint256 amount) external;

  function calcLockAmount(address account, uint256 amount)
    external
    view
    returns (uint256 lockAmount, uint256 claimAmount);

  function redeem(address recipient, uint256 amountToRedeem)
    external
    returns (uint256 amountRedeemed);

  function getMaxAmountRedeemable(address account) external view returns (uint256 res);

  event PlyLocked(address indexed locker, address indexed recipient, uint256 amount);

  event GlobalLockSet(uint256 indexed weekNumber, uint256 newPercentLock);

  event UserLockSet(address indexed account, uint256 indexed weekNumber, uint256 newPercentLock);

  event PulpRedeemed(address indexed redeemer, address indexed recipient, uint256 amount);

  event EarlyRedeemAdd(address indexed account, uint256 amountAdd);

  event EarlyRedeemReset(address indexed account, uint256 newAmount);
}

File 7 of 11 : BoringOwnable.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.11;

// Audit on 5-Jan-2021 by Keno and BoringCrypto
// Source: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/access/Ownable.sol + Claimable.sol
// Edited by BoringCrypto

contract BoringOwnableData {
    address public owner;
    address public pendingOwner;
}

contract BoringOwnable is BoringOwnableData {
    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    constructor(){
        owner = msg.sender;
    }

    /// @notice Transfers ownership to `newOwner`. Either directly or claimable by the new pending owner.
    /// Can only be invoked by the current `owner`.
    /// @param newOwner Address of the new owner.
    /// @param direct True if `newOwner` should be set immediately. False if `newOwner` needs to use `claimOwnership`.
    /// @param renounce Allows the `newOwner` to be `address(0)` if `direct` and `renounce` is True. Has no effect otherwise.
    function transferOwnership(
        address newOwner,
        bool direct,
        bool renounce
    ) public onlyOwner {
        if (direct) {
            // Checks
            require(newOwner != address(0) || renounce, "Ownable: zero address");

            // Effects
            emit OwnershipTransferred(owner, newOwner);
            owner = newOwner;
            pendingOwner = address(0);
        } else {
            // Effects
            pendingOwner = newOwner;
        }
    }

    /// @notice Needs to be called by `pendingOwner` to claim ownership.
    function claimOwnership() public {
        address _pendingOwner = pendingOwner;

        // Checks
        require(msg.sender == _pendingOwner, "Ownable: caller != pending owner");

        // Effects
        emit OwnershipTransferred(owner, _pendingOwner);
        owner = _pendingOwner;
        pendingOwner = address(0);
    }

    /// @notice Only allows the `owner` to execute the function.
    modifier onlyOwner() {
        require(msg.sender == owner, "Ownable: caller is not the owner");
        _;
    }
}

File 8 of 11 : AuriMathLib.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.11;

library Math {
  /**
   * @dev Returns the largest of two numbers.
   */
  function max(uint256 a, uint256 b) internal pure returns (uint256) {
    return a >= b ? a : b;
  }

  function max(
    uint256 a,
    uint256 b,
    uint256 c
  ) internal pure returns (uint256) {
    return max(a, max(b, c));
  }

  /**
   * @dev Returns the smallest of two numbers.
   */
  function min(uint256 a, uint256 b) internal pure returns (uint256) {
    return a < b ? a : b;
  }

  function min(
    uint256 a,
    uint256 b,
    uint256 c
  ) internal pure returns (uint256) {
    return min(min(a, b), c);
  }

  /**
   * @dev Returns the average of two numbers. The result is rounded towards
   * zero.
   */
  function average(uint256 a, uint256 b) internal pure returns (uint256) {
    // (a + b) / 2 can overflow.
    return (a & b) + (a ^ b) / 2;
  }

  /**
   * @dev Returns the ceiling of the division of two numbers.
   *
   * This differs from standard division with `/` in that it rounds up instead
   * of rounding down.
   */
  function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
    // (a + b - 1) / b can overflow on addition, so we distribute.
    return a / b + (a % b == 0 ? 0 : 1);
  }

  /**
   * @dev Returns the abs of the difference of two numbers.
   */
  function absDiff(uint256 a, uint256 b) internal pure returns (uint256) {
    return (a > b) ? (a - b) : (b - a);
  }

  function safe216(uint256 n) internal pure returns (uint216) {
    require(n <= type(uint216).max, "safe216");
    return uint216(n);
  }

  function safe224(uint256 n) internal pure returns (uint224) {
    require(n <= type(uint224).max, "safe224");
    return uint224(n);
  }

  function safe32(uint256 n) internal pure returns (uint32) {
    require(n <= type(uint32).max, "safe32");
    return uint32(n);
  }
}

File 9 of 11 : AuriFairLaunchInterface.sol
pragma solidity 0.8.11;

interface AuriFairLaunchInterface {
  event AddNewPool(
    address indexed stakeToken,
    uint32 indexed startTime,
    uint32 indexed endTime,
    uint256[] rewardPerSeconds
  );
  event RenewPool(
    uint256 indexed pid,
    uint32 indexed startTime,
    uint32 indexed endTime,
    uint256[] rewardPerSeconds
  );
  event UpdatePool(uint256 indexed pid, uint32 indexed endTime, uint256[] rewardPerSecond);
  event Deposit(
    address indexed user,
    uint256 indexed pid,
    uint256 indexed timestamp,
    uint256 amount
  );
  event Withdraw(
    address indexed user,
    uint256 indexed pid,
    uint256 indexed timestamp,
    uint256 amount
  );
  event Harvest(
    address indexed user,
    uint256 indexed pid,
    address indexed rewardToken,
    uint256 lockedAmount,
    uint256 timestamp
  );
  event EmergencyWithdraw(
    address indexed user,
    uint256 indexed pid,
    uint256 indexed timestamp,
    uint256 amount
  );
  event RewardTokenWithdrawn(address indexed owner, address indexed rewardToken, uint256 amount);

  function harvest(
    address account,
    uint256 _pid,
    uint256 _maxAmountToHarvest
  ) external;

  function getUserInfo(uint256 _pid, address _account)
    external
    view
    returns (
      uint256 amount,
      uint256[] memory unclaimedRewards,
      uint256[] memory lastRewardPerShares
    );

  function updateAndGetUserInfo(uint256 _pid, address _account)
    external
    returns (
      uint256 amount,
      uint256[] memory unclaimedRewards,
      uint256[] memory lastRewardPerShares
    );

  function updateMultiplePools(address account, uint256[] calldata _pids) external;
}

File 10 of 11 : Address.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol)

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
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize/address.code.length, which returns 0
        // for contracts in construction, since the code is only stored at the end
        // of the constructor execution.

        return account.code.length > 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");

        (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");

        (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");

        (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.4._
     */
    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.4._
     */
    function functionDelegateCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(isContract(target), "Address: delegate call to non-contract");

        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal 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

                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}

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

pragma solidity ^0.8.0;

import "../IERC20.sol";

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

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

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

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

Contract ABI

[{"inputs":[{"internalType":"contract PULPInterface","name":"_pulp","type":"address"},{"internalType":"contract ComptrollerInterface","name":"_unitroller","type":"address"},{"internalType":"address[]","name":"_rewardTokens","type":"address[]"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"stakeToken","type":"address"},{"indexed":true,"internalType":"uint32","name":"startTime","type":"uint32"},{"indexed":true,"internalType":"uint32","name":"endTime","type":"uint32"},{"indexed":false,"internalType":"uint256[]","name":"rewardPerSeconds","type":"uint256[]"}],"name":"AddNewPool","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"uint256","name":"pid","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"timestamp","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"uint256","name":"pid","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"timestamp","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"EmergencyWithdraw","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"uint256","name":"pid","type":"uint256"},{"indexed":true,"internalType":"address","name":"rewardToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"lockedAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"Harvest","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"pid","type":"uint256"},{"indexed":true,"internalType":"uint32","name":"startTime","type":"uint32"},{"indexed":true,"internalType":"uint32","name":"endTime","type":"uint32"},{"indexed":false,"internalType":"uint256[]","name":"rewardPerSeconds","type":"uint256[]"}],"name":"RenewPool","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"rewardToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"RewardTokenWithdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"pid","type":"uint256"},{"indexed":true,"internalType":"uint32","name":"endTime","type":"uint32"},{"indexed":false,"internalType":"uint256[]","name":"rewardPerSecond","type":"uint256[]"}],"name":"UpdatePool","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"uint256","name":"pid","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"timestamp","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Withdraw","type":"event"},{"inputs":[{"internalType":"address","name":"_stakeToken","type":"address"},{"internalType":"uint32","name":"_startTime","type":"uint32"},{"internalType":"uint32","name":"_endTime","type":"uint32"},{"internalType":"uint256[]","name":"_rewardPerSeconds","type":"uint256[]"}],"name":"addPool","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"claimOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_pid","type":"uint256"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"deposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_pid","type":"uint256"}],"name":"emergencyWithdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_pid","type":"uint256"}],"name":"getPoolInfo","outputs":[{"internalType":"uint256","name":"totalStake","type":"uint256"},{"internalType":"address","name":"stakeToken","type":"address"},{"internalType":"uint32","name":"startTime","type":"uint32"},{"internalType":"uint32","name":"endTime","type":"uint32"},{"internalType":"uint32","name":"lastRewardTimestamp","type":"uint32"},{"internalType":"uint256[]","name":"rewardPerSeconds","type":"uint256[]"},{"internalType":"uint256[]","name":"accRewardPerShares","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getRewardTokens","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_pid","type":"uint256"},{"internalType":"address","name":"_account","type":"address"}],"name":"getUserInfo","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256[]","name":"unclaimedRewards","type":"uint256[]"},{"internalType":"uint256[]","name":"lastRewardPerShares","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"_pid","type":"uint256"},{"internalType":"uint256","name":"_maxAmountOfPlyToHarvest","type":"uint256"}],"name":"harvest","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"rewardTokenIndex","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"ownerWithdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"pendingOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_pid","type":"uint256"},{"internalType":"address","name":"_user","type":"address"}],"name":"pendingRewards","outputs":[{"internalType":"uint256[]","name":"rewards","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ply","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"poolExists","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"poolLength","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pulp","outputs":[{"internalType":"contract PULPInterface","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_pid","type":"uint256"},{"internalType":"uint32","name":"_startTime","type":"uint32"},{"internalType":"uint32","name":"_endTime","type":"uint32"},{"internalType":"uint256[]","name":"_rewardPerSeconds","type":"uint256[]"}],"name":"renewPool","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"rewardTokens","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"},{"internalType":"bool","name":"direct","type":"bool"},{"internalType":"bool","name":"renounce","type":"bool"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unitroller","outputs":[{"internalType":"contract ComptrollerInterface","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_pid","type":"uint256"},{"internalType":"address","name":"_account","type":"address"}],"name":"updateAndGetUserInfo","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256[]","name":"unclaimedRewards","type":"uint256[]"},{"internalType":"uint256[]","name":"lastRewardPerShares","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256[]","name":"_pids","type":"uint256[]"}],"name":"updateMultiplePools","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_pid","type":"uint256"},{"internalType":"uint32","name":"_endTime","type":"uint32"},{"internalType":"uint256[]","name":"_rewardPerSeconds","type":"uint256[]"}],"name":"updatePool","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_pid","type":"uint256"}],"name":"updatePoolRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_pid","type":"uint256"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_pid","type":"uint256"}],"name":"withdrawAll","outputs":[],"stateMutability":"nonpayable","type":"function"}]

60e06040523480156200001157600080fd5b50604051620032aa380380620032aa833981016040819052620000349162000223565b6001600081905580546001600160a01b0319163317905580516200006090600490602084019062000161565b506001600160a01b03831660808190526040805163254e0f4360e01b8152905163254e0f43916004808201926020929091908290030181865afa158015620000ac573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620000d2919062000323565b6001600160a01b0390811660a081905283821660c05260805160405163095ea7b360e01b81529216600483015260001960248301529063095ea7b3906044016020604051808303816000875af115801562000131573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200015791906200034a565b505050506200036e565b828054828255906000526020600020908101928215620001b9579160200282015b82811115620001b957825182546001600160a01b0319166001600160a01b0390911617825560209092019160019091019062000182565b50620001c7929150620001cb565b5090565b5b80821115620001c75760008155600101620001cc565b6001600160a01b0381168114620001f857600080fd5b50565b634e487b7160e01b600052604160045260246000fd5b80516200021e81620001e2565b919050565b6000806000606084860312156200023957600080fd5b83516200024681620001e2565b809350506020808501516200025b81620001e2565b60408601519093506001600160401b03808211156200027957600080fd5b818701915087601f8301126200028e57600080fd5b815181811115620002a357620002a3620001fb565b8060051b604051601f19603f83011681018181108582111715620002cb57620002cb620001fb565b60405291825284820192508381018501918a831115620002ea57600080fd5b938501935b828510156200031357620003038562000211565b84529385019392850192620002ef565b8096505050505050509250925092565b6000602082840312156200033657600080fd5b81516200034381620001e2565b9392505050565b6000602082840312156200035d57600080fd5b815180151581146200034357600080fd5b60805160a05160c051612ee9620003c1600039600081816103680152612323015260008181610411015281816123de015261276e0152600081816103c40152818161264901526126fe0152612ee96000f3fe608060405234801561001057600080fd5b50600436106101ae5760003560e01c8063958e2d31116100ee578063c4f59f9b11610097578063e2bbb15811610071578063e2bbb158146103e6578063e30c3978146103f9578063e502677a1461040c578063f75887011461043357600080fd5b8063c4f59f9b1461038a578063d18df53c1461039f578063e0e1a949146103bf57600080fd5b8063b43c6270116100c8578063b43c62701461033d578063b93b222414610350578063bad1d3da1461036357600080fd5b8063958e2d3114610304578063adb82b3114610317578063affb9fc81461032a57600080fd5b80632fb40ce11161015b5780635312ea8e116101355780635312ea8e146102a0578063722f6649146102b35780637bb7bed1146102c65780638da5cb5b146102f157600080fd5b80632fb40ce114610272578063441a3e70146102855780634e71e0c81461029857600080fd5b80631e1c6a071161018c5780631e1c6a071461020657806322a82b1d146102395780632f380b351461024c57600080fd5b8063078dfbe7146101b3578063081e3eda146101c85780631069f3b5146101e4575b600080fd5b6101c66101c13660046128d1565b610446565b005b6101d160055481565b6040519081526020015b60405180910390f35b6101f76101f236600461291a565b6105c2565b6040516101db93929190612981565b6102296102143660046129b6565b60036020526000908152604090205460ff1681565b60405190151581526020016101db565b6101f761024736600461291a565b610709565b61025f61025a3660046129d1565b610799565b6040516101db97969594939291906129ea565b6101c6610280366004612ab0565b610915565b6101c6610293366004612b26565b610d74565b6101c6610ddf565b6101c66102ae3660046129d1565b610eb5565b6101c66102c1366004612b48565b610fda565b6102d96102d43660046129d1565b61108d565b6040516001600160a01b0390911681526020016101db565b6001546102d9906001600160a01b031681565b6101c66103123660046129d1565b6110b7565b6101c66103253660046129d1565b61113c565b6101c6610338366004612b9b565b61119d565b6101c661034b366004612bf5565b611471565b6101c661035e366004612b26565b6117f7565b6102d97f000000000000000000000000000000000000000000000000000000000000000081565b61039261191e565b6040516101db9190612c1d565b6103b26103ad36600461291a565b611980565b6040516101db9190612c6a565b6102d97f000000000000000000000000000000000000000000000000000000000000000081565b6101c66103f4366004612b26565b611b63565b6002546102d9906001600160a01b031681565b6102d97f000000000000000000000000000000000000000000000000000000000000000081565b6101c6610441366004612c7d565b611c6f565b6001546001600160a01b031633146104a55760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064015b60405180910390fd5b8115610589576001600160a01b0383161515806104bf5750805b61050b5760405162461bcd60e51b815260206004820152601560248201527f4f776e61626c653a207a65726f20616464726573730000000000000000000000604482015260640161049c565b6001546040516001600160a01b038086169216907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3600180546001600160a01b0385167fffffffffffffffffffffffff000000000000000000000000000000000000000091821617909155600280549091169055505050565b600280547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0385161790555b505050565b60008281526007602090815260408083206001600160a01b03851684529091529020805460045490916060918291908067ffffffffffffffff81111561060a5761060a612cb0565b604051908082528060200260200182016040528015610633578160200160208202803683370190505b5093508067ffffffffffffffff81111561064f5761064f612cb0565b604051908082528060200260200182016040528015610678578160200160208202803683370190505b50925060005b818110156106ff57600081815260018401602052604090205485518690839081106106ab576106ab612cc6565b602002602001018181525050826001016000828152602001908152602001600020600101548482815181106106e2576106e2612cc6565b6020908102919091010152806106f781612cf2565b91505061067e565b5050509250925092565b6000606080600260005414156107615760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015260640161049c565b600260005561076f85611ce7565b61077c8486600080611e9a565b61078685856105c2565b6001600055919790965090945092505050565b60008181526006602052604090208054600182015460045491926001600160a01b0382169263ffffffff740100000000000000000000000000000000000000008404811693600160c01b8104821693600160e01b9091049091169160609182918067ffffffffffffffff81111561081257610812612cb0565b60405190808252806020026020018201604052801561083b578160200160208202803683370190505b5093508067ffffffffffffffff81111561085757610857612cb0565b604051908082528060200260200182016040528015610880578160200160208202803683370190505b50925060005b8181101561090757600081815260028401602052604090205485518690839081106108b3576108b3612cc6565b602002602001018181525050826002016000828152602001908152602001600020600101548482815181106108ea576108ea612cc6565b6020908102919091010152806108ff81612cf2565b915050610886565b505050919395979092949650565b600260005414156109685760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015260640161049c565b60026000556001546001600160a01b031633146109c75760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161049c565b6001600160a01b03851660009081526003602052604090205460ff1615610a305760405162461bcd60e51b815260206004820152601460248201527f6164643a206475706c69636174656420706f6f6c000000000000000000000000604482015260640161049c565b6001600160a01b038516610a865760405162461bcd60e51b815260206004820152601860248201527f6164643a20696e76616c6964207374616b6520746f6b656e0000000000000000604482015260640161049c565b6004548114610ad75760405162461bcd60e51b815260206004820152601360248201527f6164643a20696e76616c6964206c656e67746800000000000000000000000000604482015260640161049c565b428463ffffffff16118015610af757508363ffffffff168363ffffffff16115b610b435760405162461bcd60e51b815260206004820152601260248201527f6164643a20696e76616c69642074696d65730000000000000000000000000000604482015260640161049c565b60058054600090815260066020526040808220600190810180547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b038b1617905583548352818320810180547fffffffffffffffff00000000ffffffffffffffffffffffffffffffffffffffff167401000000000000000000000000000000000000000063ffffffff8b81169182029290921790925585548552838520830180547fffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffffff16600160c01b928b1692909202919091179055935483529082200180547bffffffffffffffffffffffffffffffffffffffffffffffffffffffff16600160e01b909302929092179091555b81811015610cca576040518060400160405280848484818110610c7d57610c7d612cc6565b602090810292909201358352506000918101829052600554825260068152604080832085845260020182529091208251815591015160019091015580610cc281612cf2565b915050610c58565b5060058054906000610cdb83612cf2565b90915550506001600160a01b0385166000818152600360205260409081902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790555163ffffffff80861692908716917feb19e76b03b99eff4d0329a19e44569c131f242c9590234f19b76f2c56a817a190610d609087908790612d0d565b60405180910390a450506001600055505050565b60026000541415610dc75760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015260640161049c565b6002600055610dd682826120a6565b50506001600055565b6002546001600160a01b0316338114610e3a5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c657220213d2070656e64696e67206f776e6572604482015260640161049c565b6001546040516001600160a01b038084169216907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3600180546001600160a01b039092167fffffffffffffffffffffffff0000000000000000000000000000000000000000928316179055600280549091169055565b60026000541415610f085760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015260640161049c565b6002600090815581815260066020908152604080832060078352818420338552909252822080548382556004549293919290915b81811015610f6e5760008181526001808601602052604082209081018290555580610f6681612cf2565b915050610f3c565b508354610f7c908390612d62565b84558115610f9d576001840154610f9d906001600160a01b031633846121b0565b4285336001600160a01b03167f2369db1bafee945aee5630782f4a170682e3f8188d8dc247a4c73eb8c9e692d285604051610d6091815260200190565b6002600054141561102d5760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015260640161049c565b60026000908155805b828110156110815783838281811061105057611050612cc6565b90506020020135915061106282611ce7565b61106f8583600080611e9a565b8061107981612cf2565b915050611036565b50506001600055505050565b6004818154811061109d57600080fd5b6000918252602090912001546001600160a01b0316905081565b6002600054141561110a5760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015260640161049c565b600260009081558181526007602090815260408083203384529091529020546111349082906120a6565b506001600055565b6002600054141561118f5760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015260640161049c565b600260005561113481611ce7565b600260005414156111f05760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015260640161049c565b60026000556001546001600160a01b0316331461124f5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161049c565b61125884611ce7565b6000848152600660205260409020600181015442600160c01b90910463ffffffff16116112c75760405162461bcd60e51b815260206004820152601a60248201527f7570646174653a20706f6f6c20616c726561647920656e646564000000000000604482015260640161049c565b60045482146113185760405162461bcd60e51b815260206004820152601660248201527f7570646174653a20696e76616c6964206c656e67746800000000000000000000604482015260640161049c565b428463ffffffff161180156113525750600181015463ffffffff740100000000000000000000000000000000000000009091048116908516115b61139e5760405162461bcd60e51b815260206004820152601860248201527f7570646174653a20696e76616c696420656e642074696d650000000000000000604482015260640161049c565b6001810180547fffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffffff16600160c01b63ffffffff87160217905560005b82811015611423578383828181106113f3576113f3612cc6565b6000848152600286016020908152604090912091029290920135909155508061141b81612cf2565b9150506113d9565b508363ffffffff16857fba2d8506275d684e80223910f1065a195ea279f37e27eb97ac24f4107eba6cf9858560405161145d929190612d0d565b60405180910390a350506001600055505050565b600260005414156114c45760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015260640161049c565b60026000556001546001600160a01b031633146115235760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161049c565b61152c85611ce7565b60008581526006602052604090206001810154427401000000000000000000000000000000000000000090910463ffffffff16118061157c5750600181015442600160c01b90910463ffffffff16105b6115ee5760405162461bcd60e51b815260206004820152602260248201527f72656e65773a20696e76616c696420706f6f6c20737461746520746f2072656e60448201527f6577000000000000000000000000000000000000000000000000000000000000606482015260840161049c565b600454821461163f5760405162461bcd60e51b815260206004820152601560248201527f72656e65773a20696e76616c6964206c656e6774680000000000000000000000604482015260640161049c565b428563ffffffff1611801561165f57508463ffffffff168463ffffffff16115b6116ab5760405162461bcd60e51b815260206004820152601460248201527f72656e65773a20696e76616c69642074696d6573000000000000000000000000604482015260640161049c565b6001810180547fffffffff0000000000000000ffffffffffffffffffffffffffffffffffffffff167401000000000000000000000000000000000000000063ffffffff8881169182027fffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffffff1692909217600160c01b92881692909202919091177bffffffffffffffffffffffffffffffffffffffffffffffffffffffff16600160e01b9190910217905560005b828110156117a15783838281811061177157611771612cc6565b6000848152600286016020908152604090912091029290920135909155508061179981612cf2565b915050611757565b508363ffffffff168563ffffffff16877f4f863dc93468acf71ff8f5770599fbead4d0ff8ea6bd0c3251576f68fdad090486866040516117e2929190612d0d565b60405180910390a45050600160005550505050565b6001546001600160a01b031633146118515760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161049c565b60045482106118a25760405162461bcd60e51b815260206004820152600d60248201527f696e76616c696420696e64657800000000000000000000000000000000000000604482015260640161049c565b6000600483815481106118b7576118b7612cc6565b6000918252602090912001546001600160a01b031690506118d98133846121b0565b6040518281526001600160a01b0382169033907fee2b58b94e40200b25d4090dfdb75a8a57b85332e158dacd6b7e5f8c1e2efbf09060200160405180910390a3505050565b6060600480548060200260200160405190810160405280929190818152602001828054801561197657602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311611958575b5050505050905090565b6004546060908067ffffffffffffffff81111561199f5761199f612cb0565b6040519080825280602002602001820160405280156119c8578160200160208202803683370190505b506000858152600660209081526040808320600783528184206001600160a01b038916855290925282208154600183015494965091939092600160e01b90910463ffffffff1690611a1889612259565b905060005b86811015611b5657600081815260028701602052604090206001015463ffffffff831684108015611a4d57508415155b15611aa7576000828152600288016020526040812054611a738663ffffffff8716612d62565b611a7d9190612d79565b905085611a8f64e8d4a5100083612d79565b611a999190612d98565b611aa39083612dba565b9150505b600082815260018088016020526040909120015464e8d4a5100090611acc9083612d62565b8754611ad89190612d79565b611ae29190612d98565b898381518110611af457611af4612cc6565b60200260200101818152505085600101600083815260200190815260200160002060000154898381518110611b2b57611b2b612cc6565b60200260200101818151611b3f9190612dba565b905250819050611b4e81612cf2565b915050611a1d565b5050505050505092915050565b60026000541415611bb65760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015260640161049c565b6002600055611bc482611ce7565b611bd13383600080611e9a565b600082815260066020908152604080832060078352818420338086529352922060018301549091611c0d916001600160a01b0316903086612291565b8054611c1a908490612dba565b81558154611c29908490612dba565b82556040518381524290859033907f36af321ec8d3c75236829c5317affd40ddb308863a1236d2d277a4025cccee1e9060200160405180910390a4505060016000555050565b60026000541415611cc25760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015260640161049c565b6002600055611cd082611ce7565b611cdd8383600184611e9a565b5050600160005550565b6005548110611d385760405162461bcd60e51b815260206004820152600f60248201527f696e76616c696420706f6f6c2069640000000000000000000000000000000000604482015260640161049c565b600081815260066020526040812090611d5083612259565b600183015490915063ffffffff600160e01b909104811690821611611d7457505050565b815480611dba57506001909101805463ffffffff909216600160e01b027bffffffffffffffffffffffffffffffffffffffffffffffffffffffff90921691909117905550565b6001830154600090611dd990600160e01b900463ffffffff1684612dd2565b60045463ffffffff91909116915060005b81811015611e5857600081815260028701602052604081208054909190611e119086612d79565b905085611e2364e8d4a5100083612d79565b611e2d9190612d98565b8260010154611e3c9190612dba565b6001909201919091555080611e5081612cf2565b915050611dea565b5050506001909201805463ffffffff909216600160e01b027bffffffffffffffffffffffffffffffffffffffffffffffffffffffff9092169190911790555050565b60008381526007602090815260408083206001600160a01b038816845290915290205460045481611f355760005b81811015611f2d57600086815260066020908152604080832084845260020182528083206001908101548a8552600784528285206001600160a01b038d1686528452828520868652820190935292209091015580611f2581612cf2565b915050611ec8565b5050506120a0565b60005b8181101561209c57600086815260066020908152604080832084845260020182528083206001908101548a8552600784528285206001600160a01b038d16865284528285208686528201909352908320908101549192909164e8d4a5100090611fa19085612d62565b611fab9088612d79565b611fb59190612d98565b8254909150611fc49082612dba565b808355600183018490559050878015611fdd5750600081115b156120865760006120178360048781548110611ffb57611ffb612cc6565b6000918252602090912001546001600160a01b03168d8b6122e2565b90506004858154811061202c5761202c612cc6565b600091825260209182902001546040805184815242938101939093526001600160a01b03918216928d928f16917ff75f3d433c6628e04e01a0be1a2c6692a6011ce4b00389824cfa8b8a12f3c1c4910160405180910390a4505b505050808061209490612cf2565b915050611f38565b5050505b50505050565b60008281526006602090815260408083206007835281842033855290925290912080548311156121185760405162461bcd60e51b815260206004820152601d60248201527f77697468647261773a20696e73756666696369656e7420616d6f756e74000000604482015260640161049c565b61212184611ce7565b61212e3385600080611e9a565b805461213b908490612d62565b8155815461214a908490612d62565b82556001820154612165906001600160a01b031633856121b0565b4284336001600160a01b03167f02f25270a4d87bea75db541cdfe559334a275b4a233520ed6c0a2429667cca94866040516121a291815260200190565b60405180910390a450505050565b6040516001600160a01b0383166024820152604481018290526105bd9084907fa9059cbb00000000000000000000000000000000000000000000000000000000906064015b60408051601f198184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090931692909217909152612488565b600081815260066020526040902060010154600160c01b900463ffffffff164281111561228c576122894261256d565b90505b919050565b6040516001600160a01b03808516602483015283166044820152606481018290526120a09085907f23b872dd00000000000000000000000000000000000000000000000000000000906084016121f5565b6040517f349fb2b20000000000000000000000000000000000000000000000000000000081526001600160a01b0383811660048301523360248301526000917f00000000000000000000000000000000000000000000000000000000000000009091169063349fb2b290604401602060405180830381865afa15801561236c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123909190612df7565b6123dc5760405162461bcd60e51b815260206004820152601660248201527f6e6f7420616c6c6f77656420746f206861727665737400000000000000000000604482015260640161049c565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316846001600160a01b031614156124555781156124505761242a8286600001546125ed565b9050808560000160008282546124409190612d62565b9091555061245090508382612605565b612480565b508354808560006124668380612d62565b9091555061248090506001600160a01b03851684836121b0565b949350505050565b60006124dd826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166127959092919063ffffffff16565b8051909150156105bd57808060200190518101906124fb9190612df7565b6105bd5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f74207375636365656400000000000000000000000000000000000000000000606482015260840161049c565b600063ffffffff8211156125e95760405162461bcd60e51b815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203360448201527f3220626974730000000000000000000000000000000000000000000000000000606482015260840161049c565b5090565b60008183106125fc57816125fe565b825b9392505050565b6040517f0a45a8dc0000000000000000000000000000000000000000000000000000000081526001600160a01b0383811660048301526024820183905260009182917f00000000000000000000000000000000000000000000000000000000000000001690630a45a8dc906044016040805180830381865afa15801561268f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126b39190612e14565b915091508160001461275b576040517fd9cb19b60000000000000000000000000000000000000000000000000000000081526001600160a01b038581166004830152602482018490527f0000000000000000000000000000000000000000000000000000000000000000169063d9cb19b690604401600060405180830381600087803b15801561274257600080fd5b505af1158015612756573d6000803e3d6000fd5b505050505b80156120a0576120a06001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001685836121b0565b60606124808484600085856001600160a01b0385163b6127f75760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015260640161049c565b600080866001600160a01b031685876040516128139190612e64565b60006040518083038185875af1925050503d8060008114612850576040519150601f19603f3d011682016040523d82523d6000602084013e612855565b606091505b5091509150612865828286612870565b979650505050505050565b6060831561287f5750816125fe565b82511561288f5782518084602001fd5b8160405162461bcd60e51b815260040161049c9190612e80565b80356001600160a01b038116811461228c57600080fd5b80151581146128ce57600080fd5b50565b6000806000606084860312156128e657600080fd5b6128ef846128a9565b925060208401356128ff816128c0565b9150604084013561290f816128c0565b809150509250925092565b6000806040838503121561292d57600080fd5b8235915061293d602084016128a9565b90509250929050565b600081518084526020808501945080840160005b838110156129765781518752958201959082019060010161295a565b509495945050505050565b83815260606020820152600061299a6060830185612946565b82810360408401526129ac8185612946565b9695505050505050565b6000602082840312156129c857600080fd5b6125fe826128a9565b6000602082840312156129e357600080fd5b5035919050565b8781526001600160a01b0387166020820152600063ffffffff8088166040840152808716606084015280861660808401525060e060a0830152612a3060e0830185612946565b82810360c0840152612a428185612946565b9a9950505050505050505050565b803563ffffffff8116811461228c57600080fd5b60008083601f840112612a7657600080fd5b50813567ffffffffffffffff811115612a8e57600080fd5b6020830191508360208260051b8501011115612aa957600080fd5b9250929050565b600080600080600060808688031215612ac857600080fd5b612ad1866128a9565b9450612adf60208701612a50565b9350612aed60408701612a50565b9250606086013567ffffffffffffffff811115612b0957600080fd5b612b1588828901612a64565b969995985093965092949392505050565b60008060408385031215612b3957600080fd5b50508035926020909101359150565b600080600060408486031215612b5d57600080fd5b612b66846128a9565b9250602084013567ffffffffffffffff811115612b8257600080fd5b612b8e86828701612a64565b9497909650939450505050565b60008060008060608587031215612bb157600080fd5b84359350612bc160208601612a50565b9250604085013567ffffffffffffffff811115612bdd57600080fd5b612be987828801612a64565b95989497509550505050565b600080600080600060808688031215612c0d57600080fd5b85359450612adf60208701612a50565b6020808252825182820181905260009190848201906040850190845b81811015612c5e5783516001600160a01b031683529284019291840191600101612c39565b50909695505050505050565b6020815260006125fe6020830184612946565b600080600060608486031215612c9257600080fd5b612c9b846128a9565b95602085013595506040909401359392505050565b634e487b7160e01b600052604160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b6000600019821415612d0657612d06612cdc565b5060010190565b6020815281602082015260007f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff831115612d4657600080fd5b8260051b80856040850137600092016040019182525092915050565b600082821015612d7457612d74612cdc565b500390565b6000816000190483118215151615612d9357612d93612cdc565b500290565b600082612db557634e487b7160e01b600052601260045260246000fd5b500490565b60008219821115612dcd57612dcd612cdc565b500190565b600063ffffffff83811690831681811015612def57612def612cdc565b039392505050565b600060208284031215612e0957600080fd5b81516125fe816128c0565b60008060408385031215612e2757600080fd5b505080516020909101519092909150565b60005b83811015612e53578181015183820152602001612e3b565b838111156120a05750506000910152565b60008251612e76818460208701612e38565b9190910192915050565b6020815260008251806020840152612e9f816040850160208701612e38565b601f01601f1916919091016040019291505056fea26469706673582212203b7c2728b63eb5fda13f1597c10ab3fd5fdd5a97e95f37aa252a7e53929b686764736f6c634300080b003300000000000000000000000004ac48711bcdc45b4d223fb021e09da73c71095e000000000000000000000000817af6cfaf35bdc1a634d6cc94ee9e4c68369aeb0000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000600000000000000000000000009c9d464b58d96837f8d8b6f4d9fe4ad408d3a4f00000000000000000000000004ac48711bcdc45b4d223fb021e09da73c71095e000000000000000000000000c42c30ac6cc15fac9bd938618bcaa1a1fae8501d00000000000000000000000007f9f7f963c5cd2bbffd30ccfb964be114332e300000000000000000000000008bec47865ade3b172a928df8f990bc7f2a3b9f79000000000000000000000000fa94348467f64d5a457f75f8bc40495d33c65abb

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

00000000000000000000000004ac48711bcdc45b4d223fb021e09da73c71095e000000000000000000000000817af6cfaf35bdc1a634d6cc94ee9e4c68369aeb0000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000600000000000000000000000009c9d464b58d96837f8d8b6f4d9fe4ad408d3a4f00000000000000000000000004ac48711bcdc45b4d223fb021e09da73c71095e000000000000000000000000c42c30ac6cc15fac9bd938618bcaa1a1fae8501d00000000000000000000000007f9f7f963c5cd2bbffd30ccfb964be114332e300000000000000000000000008bec47865ade3b172a928df8f990bc7f2a3b9f79000000000000000000000000fa94348467f64d5a457f75f8bc40495d33c65abb

-----Decoded View---------------
Arg [0] : _pulp (address): 0x04ac48711bcdc45b4d223fb021e09da73c71095e
Arg [1] : _unitroller (address): 0x817af6cfaf35bdc1a634d6cc94ee9e4c68369aeb
Arg [2] : _rewardTokens (address[]): 0x09c9d464b58d96837f8d8b6f4d9fe4ad408d3a4f,0x04ac48711bcdc45b4d223fb021e09da73c71095e,0xc42c30ac6cc15fac9bd938618bcaa1a1fae8501d,0x07f9f7f963c5cd2bbffd30ccfb964be114332e30,0x8bec47865ade3b172a928df8f990bc7f2a3b9f79,0xfa94348467f64d5a457f75f8bc40495d33c65abb

-----Encoded View---------------
10 Constructor Arguments found :
Arg [0] : 00000000000000000000000004ac48711bcdc45b4d223fb021e09da73c71095e
Arg [1] : 000000000000000000000000817af6cfaf35bdc1a634d6cc94ee9e4c68369aeb
Arg [2] : 0000000000000000000000000000000000000000000000000000000000000060
Arg [3] : 0000000000000000000000000000000000000000000000000000000000000006
Arg [4] : 00000000000000000000000009c9d464b58d96837f8d8b6f4d9fe4ad408d3a4f
Arg [5] : 00000000000000000000000004ac48711bcdc45b4d223fb021e09da73c71095e
Arg [6] : 000000000000000000000000c42c30ac6cc15fac9bd938618bcaa1a1fae8501d
Arg [7] : 00000000000000000000000007f9f7f963c5cd2bbffd30ccfb964be114332e30
Arg [8] : 0000000000000000000000008bec47865ade3b172a928df8f990bc7f2a3b9f79
Arg [9] : 000000000000000000000000fa94348467f64d5a457f75f8bc40495d33c65abb


Block Transaction Gas Used Reward
Age Block Fee Address BC Fee Address Voting Power Jailed Incoming
Block Uncle Number Difficulty Gas Used Reward
Loading
Make sure to use the "Vote Down" button for any spammy posts, and the "Vote Up" for interesting conversations.