Address Details
contract

0xAb758532D05d2A2fc7b2853ebe54Ac17fE17Ea7f

Contract Name
ComplexRewarder
Creator
0x71ee4b–f7430e at 0xee3884–99e18a
Balance
0 CELO ( )
Locked CELO Balance
0.00 CELO
Voting CELO Balance
0.00 CELO
Pending Unlocked Gold
0.00 CELO
Tokens
Fetching tokens...
Transactions
1 Transactions
Transfers
841 Transfers
Gas Used
50,342
Last Balance Update
24944801
This contract has been verified via Sourcify. View contract in Sourcify repository
Contract name:
ComplexRewarder




Optimization enabled
true
Compiler version
v0.6.12+commit.27d51765




Optimization runs
999
EVM Version
istanbul




Verified at
2022-06-13T21:49:21.246332Z

contracts/ComplexRewarder.sol

// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;
pragma experimental ABIEncoderV2;


import "./libs/IERC20.sol";
import "./libs/BoringERC20.sol";
import "./libs/IRewarder.sol";
import "./libs/BoringMath.sol";
import "./libs/BaseBoringBatchable.sol";
import "./libs/BoringOwnable.sol";
import "./libs/SignedSafeMath.sol";
import "./SymmChef.sol";


contract ComplexRewarder is IRewarder,  BoringOwnable{
    using BoringMath for uint256;
    using BoringMath128 for uint128;
    using BoringERC20 for IERC20;

    IERC20 public immutable rewardToken;

    /// @notice Info of each chef user.
    /// `amount` LP token amount the user has provided.
    /// `rewardDebt` The amount of SYMM entitled to the user.
    struct UserInfo {
        uint256 amount;
        uint256 rewardDebt;
        uint256 unpaidRewards;
    }

    /// @notice Info of each chef pool.
    /// `allocPoint` The amount of allocation points assigned to the pool.
    /// Also known as the amount of SYMM to distribute per block.
    struct PoolInfo {
        uint128 accSymmPerShare;
        uint64 lastRewardTime;
        uint64 allocPoint;
    }

    /// @notice Info of each pool.
    mapping (uint256 => PoolInfo) public poolInfo;

    uint256[] public poolIds;

    /// @notice Info of each user that stakes LP tokens.
    mapping (uint256 => mapping (address => UserInfo)) public userInfo;
    /// @dev Total allocation points. Must be the sum of all allocation points in all pools.
    uint256 totalAllocPoint;

    uint256 public rewardPerSecond;
    uint256 private constant ACC_TOKEN_PRECISION = 1e12;

    address private immutable chef;

    uint256 internal unlocked;
    modifier lock() {
        require(unlocked == 1, "LOCKED");
        unlocked = 2;
        _;
        unlocked = 1;
    }

    event LogOnReward(address indexed user, uint256 indexed pid, uint256 amount, address indexed to);
    event LogPoolAddition(uint256 indexed pid, uint256 allocPoint);
    event LogSetPool(uint256 indexed pid, uint256 allocPoint);
    event LogUpdatePool(uint256 indexed pid, uint64 lastRewardTime, uint256 lpSupply, uint256 accSymmPerShare);
    event LogRewardPerSecond(uint256 rewardPerSecond);
    event LogInit();

    constructor (IERC20 _rewardToken, uint256 _rewardPerSecond, address _chef) public {
        rewardToken = _rewardToken;
        rewardPerSecond = _rewardPerSecond;
        chef = _chef;
        unlocked = 1;
    }


    function onSymmReward (uint256 pid, address _user, address to, uint256, uint256 lpToken) onlyChef lock override external {
        PoolInfo memory pool = updatePool(pid);
        UserInfo storage user = userInfo[pid][_user];
        uint256 pending;
        if (user.amount > 0) {
            pending =
                (user.amount.mul(pool.accSymmPerShare) / ACC_TOKEN_PRECISION).sub(
                    user.rewardDebt
                ).add(user.unpaidRewards);
            uint256 balance = rewardToken.balanceOf(address(this));
            if (pending > balance) {
                rewardToken.safeTransfer(to, balance);
                user.unpaidRewards = pending - balance;
            } else {
                rewardToken.safeTransfer(to, pending);
                user.unpaidRewards = 0;
            }
        }
        user.amount = lpToken;
        user.rewardDebt = lpToken.mul(pool.accSymmPerShare) / ACC_TOKEN_PRECISION;
        emit LogOnReward(_user, pid, pending - user.unpaidRewards, to);
    }

    function pendingTokens(uint256 pid, address user, uint256) override external view returns (IERC20[] memory rewardTokens, uint256[] memory rewardAmounts) {
        IERC20[] memory _rewardTokens = new IERC20[](1);
        _rewardTokens[0] = (rewardToken);
        uint256[] memory _rewardAmounts = new uint256[](1);
        _rewardAmounts[0] = pendingToken(pid, user);
        return (_rewardTokens, _rewardAmounts);
    }

    /// @notice Sets the symm per second to be distributed. Can only be called by the owner.
    /// @param _rewardPerSecond The amount of Symm to be distributed per second.
    function setRewardPerSecond(uint256 _rewardPerSecond) public onlyOwner {
        rewardPerSecond = _rewardPerSecond;
        emit LogRewardPerSecond(_rewardPerSecond);
    }

    modifier onlyChef {
        require(
            msg.sender == chef,
            "Only chef can call this function."
        );
        _;
    }

    /// @notice Returns the number of chef pools.
    function poolLength() public view returns (uint256 pools) {
        pools = poolIds.length;
    }

    /// @notice Add a new LP to the pool. Can only be called by the owner.
    /// DO NOT add the same LP token more than once. Rewards will be messed up if you do.
    /// @param allocPoint AP of the new pool.
    /// @param _pid Pid on chef
    function add(uint256 allocPoint, uint256 _pid) public onlyOwner {
        require(poolInfo[_pid].lastRewardTime == 0, "Pool already exists");
        uint256 lastRewardTime = block.timestamp;
        totalAllocPoint = totalAllocPoint.add(allocPoint);

        poolInfo[_pid] = PoolInfo({
            allocPoint: allocPoint.to64(),
            lastRewardTime: lastRewardTime.to64(),
            accSymmPerShare: 0
        });
        poolIds.push(_pid);
        emit LogPoolAddition(_pid, allocPoint);
    }

    /// @notice Update the given pool's SYMM allocation point and `IRewarder` contract. Can only be called by the owner.
    /// @param _pid The index of the pool. See `poolInfo`.
    /// @param _allocPoint New AP of the pool.
    function set(uint256 _pid, uint256 _allocPoint) public onlyOwner {
        totalAllocPoint = totalAllocPoint.sub(poolInfo[_pid].allocPoint).add(_allocPoint);
        poolInfo[_pid].allocPoint = _allocPoint.to64();
        emit LogSetPool(_pid, _allocPoint);
    }

    /// @notice Allows owner to reclaim/withdraw any tokens (including reward tokens) held by this contract
    /// @param token Token to reclaim, use 0x00 for Ethereum
    /// @param amount Amount of tokens to reclaim
    /// @param to Receiver of the tokens, first of his name, rightful heir to the lost tokens,
    /// reightful owner of the extra tokens, and ether, protector of mistaken transfers, mother of token reclaimers,
    /// the Khaleesi of the Great Token Sea, the Unburnt, the Breaker of blockchains.
    function reclaimTokens(address token, uint256 amount, address payable to) public onlyOwner {
        if (token == address(0)) {
            to.transfer(amount);
        } else {
            IERC20(token).safeTransfer(to, amount);
        }
    }

    /// @notice View function to see pending Token
    /// @param _pid The index of the pool. See `poolInfo`.
    /// @param _user Address of user.
    /// @return pending SYMM reward for a given user.
    function pendingToken(uint256 _pid, address _user) public view returns (uint256 pending) {
        PoolInfo memory pool = poolInfo[_pid];
        UserInfo storage user = userInfo[_pid][_user];
        uint256 accSymmPerShare = pool.accSymmPerShare;
        uint256 lpSupply = SymmChef(chef).lpToken(_pid).balanceOf(chef);
        if (block.timestamp > pool.lastRewardTime && lpSupply != 0) {
            uint256 time = block.timestamp.sub(pool.lastRewardTime);
            uint256 symmReward = time.mul(rewardPerSecond).mul(pool.allocPoint) / totalAllocPoint;
            accSymmPerShare = accSymmPerShare.add(symmReward.mul(ACC_TOKEN_PRECISION) / lpSupply);
        }
        pending = (user.amount.mul(accSymmPerShare) / ACC_TOKEN_PRECISION).sub(user.rewardDebt).add(user.unpaidRewards);
    }

    /// @notice Update reward variables for all pools. Be careful of gas spending!
    /// @param pids Pool IDs of all to be updated. Make sure to update all active pools.
    function massUpdatePools(uint256[] calldata pids) external {
        uint256 len = pids.length;
        for (uint256 i = 0; i < len; ++i) {
            updatePool(pids[i]);
        }
    }

    /// @notice Update reward variables of the given pool.
    /// @param pid The index of the pool. See `poolInfo`.
    /// @return pool Returns the pool that was updated.
    function updatePool(uint256 pid) public returns (PoolInfo memory pool) {
        pool = poolInfo[pid];
        if (block.timestamp > pool.lastRewardTime) {
            uint256 lpSupply = SymmChef(chef).lpToken(pid).balanceOf(chef);

            if (lpSupply > 0) {
                uint256 time = block.timestamp.sub(pool.lastRewardTime);
                uint256 symmReward = time.mul(rewardPerSecond).mul(pool.allocPoint) / totalAllocPoint;
                pool.accSymmPerShare = pool.accSymmPerShare.add((symmReward.mul(ACC_TOKEN_PRECISION) / lpSupply).to128());
            }
            pool.lastRewardTime = block.timestamp.to64();
            poolInfo[pid] = pool;
            emit LogUpdatePool(pid, pool.lastRewardTime, lpSupply, pool.accSymmPerShare);
        }
    }

}
        

/contracts/SymmChef.sol

// SPDX-License-Identifier: MIT

pragma solidity 0.6.12;
pragma experimental ABIEncoderV2;

import "./libs/BoringMath.sol";
import "./libs/BaseBoringBatchable.sol";
import "./libs/BoringOwnable.sol";
import "./libs/SignedSafeMath.sol";
import "./libs/IRewarder.sol";


contract SymmChef is BoringOwnable, BoringBatchable {
    using BoringMath for uint256;
    using BoringMath128 for uint128;
    using BoringERC20 for IERC20;
    using SignedSafeMath for int256;

    /// @notice Info of each Chef user.
    /// `amount` LP token amount the user has provided.
    /// `rewardDebt` The amount of SYMM entitled to the user.
    struct UserInfo {
        uint256 amount;
        int256 rewardDebt;
    }

    /// @notice Info of each Chef pool.
    /// `allocPoint` The amount of allocation points assigned to the pool.
    /// Also known as the amount of SYMM to distribute per block.
    struct PoolInfo {
        uint128 accSymmPerShare;
        uint64 lastRewardTime;
        uint64 allocPoint;
    }

    /// @notice Address of SYMM contract.
    IERC20 public immutable SYMM;


    /// @notice Info of each Chef pool.
    PoolInfo[] public poolInfo;
    /// @notice Address of the LP token for each Chef pool.
    IERC20[] public lpToken;
    /// @notice Address of each `IRewarder` contract in Chef.
    IRewarder[] public rewarder;

    /// @notice Info of each user that stakes LP tokens.
    mapping (uint256 => mapping (address => UserInfo)) public userInfo;
    /// @dev Total allocation points. Must be the sum of all allocation points in all pools.
    uint256 public totalAllocPoint;

    uint256 public symmPerSecond;
    uint256 private constant ACC_SYMM_PRECISION = 1e12;

    event Deposit(address indexed user, uint256 indexed pid, uint256 amount, address indexed to);
    event Withdraw(address indexed user, uint256 indexed pid, uint256 amount, address indexed to);
    event EmergencyWithdraw(address indexed user, uint256 indexed pid, uint256 amount, address indexed to);
    event Harvest(address indexed user, uint256 indexed pid, uint256 amount);
    event LogPoolAddition(uint256 indexed pid, uint256 allocPoint, IERC20 indexed lpToken, IRewarder indexed rewarder);
    event LogSetPool(uint256 indexed pid, uint256 allocPoint, IRewarder indexed rewarder, bool overwrite);
    event LogUpdatePool(uint256 indexed pid, uint64 lastRewardTime, uint256 lpSupply, uint256 accSymmPerShare);
    event LogSymmPerSecond(uint256 symmPerSecond);

    /// @param _symm The SYMM token contract address.
    constructor(IERC20 _symm) public {
        SYMM = _symm;
    }

    /// @notice Returns the number of Chef pools.
    function poolLength() public view returns (uint256 pools) {
        pools = poolInfo.length;
    }

    /// @notice Add a new LP to the pool. Can only be called by the owner.
    /// DO NOT add the same LP token more than once. Rewards will be messed up if you do.
    /// @param allocPoint AP of the new pool.
    /// @param _lpToken Address of the LP ERC-20 token.
    /// @param _rewarder Address of the rewarder delegate.
    function add(uint256 allocPoint, IERC20 _lpToken, IRewarder _rewarder) public onlyOwner {
        totalAllocPoint = totalAllocPoint.add(allocPoint);
        lpToken.push(_lpToken);
        rewarder.push(_rewarder);

        poolInfo.push(PoolInfo({
            allocPoint: allocPoint.to64(),
            lastRewardTime: block.timestamp.to64(),
            accSymmPerShare: 0
        }));
        emit LogPoolAddition(lpToken.length.sub(1), allocPoint, _lpToken, _rewarder);
    }

    /// @notice Update the given pool's SYMM allocation point and `IRewarder` contract. Can only be called by the owner.
    /// @param _pid The index of the pool. See `poolInfo`.
    /// @param _allocPoint New AP of the pool.
    /// @param _rewarder Address of the rewarder delegate.
    /// @param overwrite True if _rewarder should be `set`. Otherwise `_rewarder` is ignored.
    function set(uint256 _pid, uint256 _allocPoint, IRewarder _rewarder, bool overwrite) public onlyOwner {
        totalAllocPoint = totalAllocPoint.sub(poolInfo[_pid].allocPoint).add(_allocPoint);
        poolInfo[_pid].allocPoint = _allocPoint.to64();
        if (overwrite) { rewarder[_pid] = _rewarder; }
        emit LogSetPool(_pid, _allocPoint, overwrite ? _rewarder : rewarder[_pid], overwrite);
    }

    /// @notice Sets the symm per second to be distributed. Can only be called by the owner.
    /// @param _symmPerSecond The amount of Symm to be distributed per second.
    function setSymmPerSecond(uint256 _symmPerSecond) public onlyOwner {
        symmPerSecond = _symmPerSecond;
        emit LogSymmPerSecond(_symmPerSecond);
    }


    /// @notice View function to see pending SYMM on frontend.
    /// @param _pid The index of the pool. See `poolInfo`.
    /// @param _user Address of user.
    /// @return pending SYMM reward for a given user.
    function pendingSymm(uint256 _pid, address _user) external view returns (uint256 pending) {
        PoolInfo memory pool = poolInfo[_pid];
        UserInfo storage user = userInfo[_pid][_user];
        uint256 accSymmPerShare = pool.accSymmPerShare;
        uint256 lpSupply = lpToken[_pid].balanceOf(address(this));
        if (block.timestamp > pool.lastRewardTime && lpSupply != 0) {
            uint256 time = block.timestamp.sub(pool.lastRewardTime);
            uint256 symmReward = time.mul(symmPerSecond).mul(pool.allocPoint) / totalAllocPoint;
            accSymmPerShare = accSymmPerShare.add(symmReward.mul(ACC_SYMM_PRECISION) / lpSupply);
        }
        pending = int256(user.amount.mul(accSymmPerShare) / ACC_SYMM_PRECISION).sub(user.rewardDebt).toUInt256();
    }

    /// @notice Update reward variables for all pools. Be careful of gas spending!
    /// @param pids Pool IDs of all to be updated. Make sure to update all active pools.
    function massUpdatePools(uint256[] calldata pids) external {
        uint256 len = pids.length;
        for (uint256 i = 0; i < len; ++i) {
            updatePool(pids[i]);
        }
    }

    /// @notice Update reward variables of the given pool.
    /// @param pid The index of the pool. See `poolInfo`.
    /// @return pool Returns the pool that was updated.
    function updatePool(uint256 pid) public returns (PoolInfo memory pool) {
        pool = poolInfo[pid];
        if (block.timestamp > pool.lastRewardTime) {
            uint256 lpSupply = lpToken[pid].balanceOf(address(this));
            if (lpSupply > 0) {
                uint256 time = block.timestamp.sub(pool.lastRewardTime);
                uint256 symmReward = time.mul(symmPerSecond).mul(pool.allocPoint) / totalAllocPoint;
                pool.accSymmPerShare = pool.accSymmPerShare.add((symmReward.mul(ACC_SYMM_PRECISION) / lpSupply).to128());
            }
            pool.lastRewardTime = block.timestamp.to64();
            poolInfo[pid] = pool;
            emit LogUpdatePool(pid, pool.lastRewardTime, lpSupply, pool.accSymmPerShare);
        }
    }

    /// @notice Deposit LP tokens to Chef for SYMM allocation.
    /// @param pid The index of the pool. See `poolInfo`.
    /// @param amount LP token amount to deposit.
    /// @param to The receiver of `amount` deposit benefit.
    function deposit(uint256 pid, uint256 amount, address to) public {
        PoolInfo memory pool = updatePool(pid);
        UserInfo storage user = userInfo[pid][to];

        // Effects
        user.amount = user.amount.add(amount);
        user.rewardDebt = user.rewardDebt.add(int256(amount.mul(pool.accSymmPerShare) / ACC_SYMM_PRECISION));

        // Interactions
        IRewarder _rewarder = rewarder[pid];
        if (address(_rewarder) != address(0)) {
            _rewarder.onSymmReward(pid, to, to, 0, user.amount);
        }

        lpToken[pid].safeTransferFrom(msg.sender, address(this), amount);

        emit Deposit(msg.sender, pid, amount, to);
    }

    /// @notice Withdraw LP tokens from Chef.
    /// @param pid The index of the pool. See `poolInfo`.
    /// @param amount LP token amount to withdraw.
    /// @param to Receiver of the LP tokens.
    function withdraw(uint256 pid, uint256 amount, address to) public {
        PoolInfo memory pool = updatePool(pid);
        UserInfo storage user = userInfo[pid][msg.sender];

        // Effects
        user.rewardDebt = user.rewardDebt.sub(int256(amount.mul(pool.accSymmPerShare) / ACC_SYMM_PRECISION));
        user.amount = user.amount.sub(amount);

        // Interactions
        IRewarder _rewarder = rewarder[pid];
        if (address(_rewarder) != address(0)) {
            _rewarder.onSymmReward(pid, msg.sender, to, 0, user.amount);
        }

        lpToken[pid].safeTransfer(to, amount);

        emit Withdraw(msg.sender, pid, amount, to);
    }

    /// @notice Harvest proceeds for transaction sender to `to`.
    /// @param pid The index of the pool. See `poolInfo`.
    /// @param to Receiver of SYMM rewards.
    function harvest(uint256 pid, address to) public {
        PoolInfo memory pool = updatePool(pid);
        UserInfo storage user = userInfo[pid][msg.sender];
        int256 accumulatedSymm = int256(user.amount.mul(pool.accSymmPerShare) / ACC_SYMM_PRECISION);
        uint256 _pendingSymm = accumulatedSymm.sub(user.rewardDebt).toUInt256();

        // Effects
        user.rewardDebt = accumulatedSymm;

        // Interactions
        if (_pendingSymm != 0) {
            SYMM.safeTransfer(to, _pendingSymm);
        }

        IRewarder _rewarder = rewarder[pid];
        if (address(_rewarder) != address(0)) {
            _rewarder.onSymmReward( pid, msg.sender, to, _pendingSymm, user.amount);
        }

        emit Harvest(msg.sender, pid, _pendingSymm);
    }

    /// @notice Withdraw LP tokens from Chef and harvest proceeds for transaction sender to `to`.
    /// @param pid The index of the pool. See `poolInfo`.
    /// @param amount LP token amount to withdraw.
    /// @param to Receiver of the LP tokens and SYMM rewards.
    function withdrawAndHarvest(uint256 pid, uint256 amount, address to) public {
        PoolInfo memory pool = updatePool(pid);
        UserInfo storage user = userInfo[pid][msg.sender];
        int256 accumulatedSymm = int256(user.amount.mul(pool.accSymmPerShare) / ACC_SYMM_PRECISION);
        uint256 _pendingSymm = accumulatedSymm.sub(user.rewardDebt).toUInt256();

        // Effects
        user.rewardDebt = accumulatedSymm.sub(int256(amount.mul(pool.accSymmPerShare) / ACC_SYMM_PRECISION));
        user.amount = user.amount.sub(amount);

        // Interactions
        SYMM.safeTransfer(to, _pendingSymm);

        IRewarder _rewarder = rewarder[pid];
        if (address(_rewarder) != address(0)) {
            _rewarder.onSymmReward(pid, msg.sender, to, _pendingSymm, user.amount);
        }

        lpToken[pid].safeTransfer(to, amount);

        emit Withdraw(msg.sender, pid, amount, to);
        emit Harvest(msg.sender, pid, _pendingSymm);
    }

    /// @notice Withdraw without caring about rewards. EMERGENCY ONLY.
    /// @param pid The index of the pool. See `poolInfo`.
    /// @param to Receiver of the LP tokens.
    function emergencyWithdraw(uint256 pid, address to) public {
        UserInfo storage user = userInfo[pid][msg.sender];
        uint256 amount = user.amount;
        user.amount = 0;
        user.rewardDebt = 0;

        IRewarder _rewarder = rewarder[pid];
        if (address(_rewarder) != address(0)) {
            _rewarder.onSymmReward(pid, msg.sender, to, 0, 0);
        }

        // Note: transfer can fail or succeed if `amount` is zero.
        lpToken[pid].safeTransfer(to, amount);
        emit EmergencyWithdraw(msg.sender, pid, amount, to);
    }
}
          

/contracts/libs/BaseBoringBatchable.sol

// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;

import "./IERC20.sol";


pragma experimental ABIEncoderV2;
// solhint-disable avoid-low-level-calls
// T1 - T4: OK
contract BaseBoringBatchable {
    function _getRevertMsg(bytes memory _returnData) internal pure returns (string memory) {
        // If the _res length is less than 68, then the transaction failed silently (without a revert message)
        if (_returnData.length < 68) return "Transaction reverted silently";
        assembly {
            // Slice the sighash.
            _returnData := add(_returnData, 0x04)
        }
        return abi.decode(_returnData, (string)); // All that remains is the revert string
    }    
    
    // F3 - F9: OK
    // F1: External is ok here because this is the batch function, adding it to a batch makes no sense
    // F2: Calls in the batch may be payable, delegatecall operates in the same context, so each call in the batch has access to msg.value
    // C1 - C21: OK
    // C3: The length of the loop is fully under user control, so can't be exploited
    // C7: Delegatecall is only used on the same contract, so it's safe
    function batch(bytes[] calldata calls, bool revertOnFail) external payable returns(bool[] memory successes, bytes[] memory results) {
        // Interactions
        successes = new bool[](calls.length);
        results = new bytes[](calls.length);
        for (uint256 i = 0; i < calls.length; i++) {
            (bool success, bytes memory result) = address(this).delegatecall(calls[i]);
            require(success || !revertOnFail, _getRevertMsg(result));
            successes[i] = success;
            results[i] = result;
        }
    }
}
contract BoringBatchable is BaseBoringBatchable {
    // F1 - F9: OK
    // F6: Parameters can be used front-run the permit and the user's permit will fail (due to nonce or other revert)
    //     if part of a batch this could be used to grief once as the second call would not need the permit
    // C1 - C21: OK
    function permitToken(IERC20 token, address from, address to, uint256 amount, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
        // Interactions
        // X1 - X5
        token.permit(from, to, amount, deadline, v, r, s);
    }
}
          

/contracts/libs/BoringERC20.sol

// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;

import "./IERC20.sol";

library BoringERC20 {
    function safeSymbol(IERC20 token) internal view returns(string memory) {
        (bool success, bytes memory data) = address(token).staticcall(abi.encodeWithSelector(0x95d89b41));
        return success && data.length > 0 ? abi.decode(data, (string)) : "???";
    }
    function safeName(IERC20 token) internal view returns(string memory) {
        (bool success, bytes memory data) = address(token).staticcall(abi.encodeWithSelector(0x06fdde03));
        return success && data.length > 0 ? abi.decode(data, (string)) : "???";
    }
    function safeDecimals(IERC20 token) internal view returns (uint8) {
        (bool success, bytes memory data) = address(token).staticcall(abi.encodeWithSelector(0x313ce567));
        return success && data.length == 32 ? abi.decode(data, (uint8)) : 18;
    }
    function safeTransfer(IERC20 token, address to, uint256 amount) internal {
        (bool success, bytes memory data) = address(token).call(abi.encodeWithSelector(0xa9059cbb, to, amount));
        require(success && (data.length == 0 || abi.decode(data, (bool))), "BoringERC20: Transfer failed");
    }
    function safeTransferFrom(IERC20 token, address from, address to, uint256 amount) internal {
        (bool success, bytes memory data) = address(token).call(abi.encodeWithSelector(0x23b872dd, from, to, amount));
        require(success && (data.length == 0 || abi.decode(data, (bool))), "BoringERC20: TransferFrom failed");
    }
}
          

/contracts/libs/BoringMath.sol

// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;
// a library for performing overflow-safe math, updated with awesomeness from of DappHub (https://github.com/dapphub/ds-math)
library BoringMath {
    function add(uint256 a, uint256 b) internal pure returns (uint256 c) {require((c = a + b) >= b, "BoringMath: Add Overflow");}
    function sub(uint256 a, uint256 b) internal pure returns (uint256 c) {require((c = a - b) <= a, "BoringMath: Underflow");}
    function mul(uint256 a, uint256 b) internal pure returns (uint256 c) {require(b == 0 || (c = a * b)/b == a, "BoringMath: Mul Overflow");}
    function to128(uint256 a) internal pure returns (uint128 c) {
        require(a <= uint128(-1), "BoringMath: uint128 Overflow");
        c = uint128(a);
    }
    function to64(uint256 a) internal pure returns (uint64 c) {
        require(a <= uint64(-1), "BoringMath: uint64 Overflow");
        c = uint64(a);
    }
    function to32(uint256 a) internal pure returns (uint32 c) {
        require(a <= uint32(-1), "BoringMath: uint32 Overflow");
        c = uint32(a);
    }
}
library BoringMath128 {
    function add(uint128 a, uint128 b) internal pure returns (uint128 c) {require((c = a + b) >= b, "BoringMath: Add Overflow");}
    function sub(uint128 a, uint128 b) internal pure returns (uint128 c) {require((c = a - b) <= a, "BoringMath: Underflow");}
}
library BoringMath64 {
    function add(uint64 a, uint64 b) internal pure returns (uint64 c) {require((c = a + b) >= b, "BoringMath: Add Overflow");}
    function sub(uint64 a, uint64 b) internal pure returns (uint64 c) {require((c = a - b) <= a, "BoringMath: Underflow");}
}
library BoringMath32 {
    function add(uint32 a, uint32 b) internal pure returns (uint32 c) {require((c = a + b) >= b, "BoringMath: Add Overflow");}
    function sub(uint32 a, uint32 b) internal pure returns (uint32 c) {require((c = a - b) <= a, "BoringMath: Underflow");}
}
          

/contracts/libs/BoringOwnable.sol

// SPDX-License-Identifier: MIT

// P1 - P3: OK
pragma solidity 0.6.12;
// Source: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/access/Ownable.sol + Claimable.sol
// Edited by BoringCrypto
// T1 - T4: OK
contract BoringOwnableData {
    // V1 - V5: OK
    address public owner;
    // V1 - V5: OK
    address public pendingOwner;
}
// T1 - T4: OK
contract BoringOwnable is BoringOwnableData {
    // E1: OK
    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
    constructor () public {
        owner = msg.sender;
        emit OwnershipTransferred(address(0), msg.sender);
    }
    // F1 - F9: OK
    // C1 - C21: OK
    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;
        }
    }
    // F1 - F9: OK
    // C1 - C21: OK
    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);
    }
    // M1 - M5: OK
    // C1 - C21: OK
    modifier onlyOwner() {
        require(msg.sender == owner, "Ownable: caller is not the owner");
        _;
    }
}
          

/contracts/libs/IERC20.sol

// SPDX-License-Identifier: MIT

pragma solidity 0.6.12;


interface IERC20 {
    function totalSupply() external view returns (uint256);
    function balanceOf(address account) external view returns (uint256);
    function allowance(address owner, address spender) external view returns (uint256);
    function approve(address spender, uint256 amount) external returns (bool);
    event Transfer(address indexed from, address indexed to, uint256 value);
    event Approval(address indexed owner, address indexed spender, uint256 value);
    // EIP 2612
    function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external;
}
          

/contracts/libs/IRewarder.sol

// SPDX-License-Identifier: MIT

pragma solidity 0.6.12;

import "./BoringERC20.sol";
import "./IERC20.sol";


interface IRewarder {
    using BoringERC20 for IERC20;
    function onSymmReward(uint256 pid, address user, address recipient, uint256 symmAmount, uint256 newLpAmount) external;
    function pendingTokens(uint256 pid, address user, uint256 symmAmount) external view returns (IERC20[] memory, uint256[] memory);
}
          

/contracts/libs/SignedSafeMath.sol

// SPDX-License-Identifier: MIT

pragma solidity 0.6.12;
library SignedSafeMath {
    int256 constant private _INT256_MIN = -2**255;
    /**
     * @dev Returns the multiplication of two signed integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `*` operator.
     *
     * Requirements:
     *
     * - Multiplication cannot overflow.
     */
    function mul(int256 a, int256 b) internal pure returns (int256) {
        // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
        // benefit is lost if 'b' is also tested.
        // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
        if (a == 0) {
            return 0;
        }
        require(!(a == -1 && b == _INT256_MIN), "SignedSafeMath: multiplication overflow");
        int256 c = a * b;
        require(c / a == b, "SignedSafeMath: multiplication overflow");
        return c;
    }
    /**
     * @dev Returns the integer division of two signed integers. Reverts on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(int256 a, int256 b) internal pure returns (int256) {
        require(b != 0, "SignedSafeMath: division by zero");
        require(!(b == -1 && a == _INT256_MIN), "SignedSafeMath: division overflow");
        int256 c = a / b;
        return c;
    }
    /**
     * @dev Returns the subtraction of two signed integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(int256 a, int256 b) internal pure returns (int256) {
        int256 c = a - b;
        require((b >= 0 && c <= a) || (b < 0 && c > a), "SignedSafeMath: subtraction overflow");
        return c;
    }
    /**
     * @dev Returns the addition of two signed integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `+` operator.
     *
     * Requirements:
     *
     * - Addition cannot overflow.
     */
    function add(int256 a, int256 b) internal pure returns (int256) {
        int256 c = a + b;
        require((b >= 0 && c >= a) || (b < 0 && c < a), "SignedSafeMath: addition overflow");
        return c;
    }
    function toUInt256(int256 a) internal pure returns (uint256) {
        require(a >= 0, "Integer < 0");
        return uint256(a);
    }
}
          

Contract ABI

[{"type":"constructor","stateMutability":"nonpayable","inputs":[{"type":"address","name":"_rewardToken","internalType":"contract IERC20"},{"type":"uint256","name":"_rewardPerSecond","internalType":"uint256"},{"type":"address","name":"_chef","internalType":"address"}]},{"type":"event","name":"LogInit","inputs":[],"anonymous":false},{"type":"event","name":"LogOnReward","inputs":[{"type":"address","name":"user","internalType":"address","indexed":true},{"type":"uint256","name":"pid","internalType":"uint256","indexed":true},{"type":"uint256","name":"amount","internalType":"uint256","indexed":false},{"type":"address","name":"to","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"LogPoolAddition","inputs":[{"type":"uint256","name":"pid","internalType":"uint256","indexed":true},{"type":"uint256","name":"allocPoint","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"LogRewardPerSecond","inputs":[{"type":"uint256","name":"rewardPerSecond","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"LogSetPool","inputs":[{"type":"uint256","name":"pid","internalType":"uint256","indexed":true},{"type":"uint256","name":"allocPoint","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"LogUpdatePool","inputs":[{"type":"uint256","name":"pid","internalType":"uint256","indexed":true},{"type":"uint64","name":"lastRewardTime","internalType":"uint64","indexed":false},{"type":"uint256","name":"lpSupply","internalType":"uint256","indexed":false},{"type":"uint256","name":"accSymmPerShare","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"OwnershipTransferred","inputs":[{"type":"address","name":"previousOwner","internalType":"address","indexed":true},{"type":"address","name":"newOwner","internalType":"address","indexed":true}],"anonymous":false},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"add","inputs":[{"type":"uint256","name":"allocPoint","internalType":"uint256"},{"type":"uint256","name":"_pid","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"claimOwnership","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"massUpdatePools","inputs":[{"type":"uint256[]","name":"pids","internalType":"uint256[]"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"onSymmReward","inputs":[{"type":"uint256","name":"pid","internalType":"uint256"},{"type":"address","name":"_user","internalType":"address"},{"type":"address","name":"to","internalType":"address"},{"type":"uint256","name":"","internalType":"uint256"},{"type":"uint256","name":"lpToken","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"owner","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"pendingOwner","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"pending","internalType":"uint256"}],"name":"pendingToken","inputs":[{"type":"uint256","name":"_pid","internalType":"uint256"},{"type":"address","name":"_user","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address[]","name":"rewardTokens","internalType":"contract IERC20[]"},{"type":"uint256[]","name":"rewardAmounts","internalType":"uint256[]"}],"name":"pendingTokens","inputs":[{"type":"uint256","name":"pid","internalType":"uint256"},{"type":"address","name":"user","internalType":"address"},{"type":"uint256","name":"","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"poolIds","inputs":[{"type":"uint256","name":"","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint128","name":"accSymmPerShare","internalType":"uint128"},{"type":"uint64","name":"lastRewardTime","internalType":"uint64"},{"type":"uint64","name":"allocPoint","internalType":"uint64"}],"name":"poolInfo","inputs":[{"type":"uint256","name":"","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"pools","internalType":"uint256"}],"name":"poolLength","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"reclaimTokens","inputs":[{"type":"address","name":"token","internalType":"address"},{"type":"uint256","name":"amount","internalType":"uint256"},{"type":"address","name":"to","internalType":"address payable"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"rewardPerSecond","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IERC20"}],"name":"rewardToken","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"set","inputs":[{"type":"uint256","name":"_pid","internalType":"uint256"},{"type":"uint256","name":"_allocPoint","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setRewardPerSecond","inputs":[{"type":"uint256","name":"_rewardPerSecond","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"transferOwnership","inputs":[{"type":"address","name":"newOwner","internalType":"address"},{"type":"bool","name":"direct","internalType":"bool"},{"type":"bool","name":"renounce","internalType":"bool"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"tuple","name":"pool","internalType":"struct ComplexRewarder.PoolInfo","components":[{"type":"uint128","name":"accSymmPerShare","internalType":"uint128"},{"type":"uint64","name":"lastRewardTime","internalType":"uint64"},{"type":"uint64","name":"allocPoint","internalType":"uint64"}]}],"name":"updatePool","inputs":[{"type":"uint256","name":"pid","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"amount","internalType":"uint256"},{"type":"uint256","name":"rewardDebt","internalType":"uint256"},{"type":"uint256","name":"unpaidRewards","internalType":"uint256"}],"name":"userInfo","inputs":[{"type":"uint256","name":"","internalType":"uint256"},{"type":"address","name":"","internalType":"address"}]}]
              

Contract Creation Code

0x60c06040523480156200001157600080fd5b5060405162001cd238038062001cd283398101604081905262000034916200009e565b600080546001600160a01b0319163390811782556040519091907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a36001600160601b0319606093841b811660805260069290925590911b1660a0526001600755620000fe565b600080600060608486031215620000b3578283fd5b8351620000c081620000e5565b602085015160408601519194509250620000da81620000e5565b809150509250925092565b6001600160a01b0381168114620000fb57600080fd5b50565b60805160601c60a05160601c611b8262000150600039806105a4528061063952806108ca528061095f5280610e51525080610f335280610fe452806110255280611197528061123f5250611b826000f3fe608060405234801561001057600080fd5b50600436106101515760003560e01c806369883b4e116100cd578063ac9e6d9211610081578063d63b3c4911610066578063d63b3c49146102aa578063e30c3978146102cb578063f7c618c1146102d357610151565b8063ac9e6d9214610284578063c1ea38681461029757610151565b80638da5cb5b116100b25780638da5cb5b146102455780638f10369a1461025a57806393f1a40b1461026257610151565b806369883b4e1461021f578063771602f71461023257610151565b806348e43af41161012457806351eb05a61161010957806351eb05a6146101d957806357a5b58c146101f957806366da58151461020c57610151565b806348e43af4146101be5780634e71e0c8146101d157610151565b8063078dfbe714610156578063081e3eda1461016b5780631526fe27146101895780631ab06ee5146101ab575b600080fd5b61016961016436600461148d565b6102db565b005b6101736103e4565b6040516101809190611adc565b60405180910390f35b61019c6101973660046115bb565b6103ea565b60405161018093929190611ab1565b6101696101b93660046116a2565b610422565b6101736101cc3660046115eb565b610514565b6101696107b6565b6101ec6101e73660046115bb565b610850565b6040516101809190611a77565b61016961020736600461150d565b610b80565b61016961021a3660046115bb565b610bb6565b61017361022d3660046115bb565b610c20565b6101696102403660046116a2565b610c3e565b61024d610e05565b6040516101809190611704565b610173610e14565b6102756102703660046115eb565b610e1a565b60405161018093929190611ae5565b61016961029236600461161a565b610e46565b6101696102a53660046114d7565b6110e7565b6102bd6102b836600461166b565b61116f565b604051610180929190611731565b61024d61122e565b61024d61123d565b6000546001600160a01b0316331461030e5760405162461bcd60e51b81526004016103059061190b565b60405180910390fd5b81156103b6576001600160a01b0383161515806103285750805b6103445760405162461bcd60e51b815260040161030590611866565b600080546040516001600160a01b03808716939216917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3600080546001600160a01b03851673ffffffffffffffffffffffffffffffffffffffff19918216179091556001805490911690556103df565b6001805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0385161790555b505050565b60035490565b6002602052600090815260409020546001600160801b0381169067ffffffffffffffff600160801b8204811691600160c01b90041683565b6000546001600160a01b0316331461044c5760405162461bcd60e51b81526004016103059061190b565b60008281526002602052604090205460055461048491839161047e91600160c01b900467ffffffffffffffff16611261565b9061128a565b600555610490816112ad565b60008381526002602052604090819020805467ffffffffffffffff93909316600160c01b0277ffffffffffffffffffffffffffffffffffffffffffffffff909316929092179091555182907f942cc7e17a17c164bd977f32ab8c54265d5b9d481e4e352bf874f1e568874e7c90610508908490611adc565b60405180910390a25050565b600061051e61146d565b506000838152600260209081526040808320815160608101835290546001600160801b03808216835267ffffffffffffffff600160801b8304811684870152600160c01b9092049091168284015287855260048085528386206001600160a01b03808a1688529552838620835194516378ed5d1f60e01b815293969095949092169391927f0000000000000000000000000000000000000000000000000000000000000000909216916378ed5d1f916105d9918b9101611adc565b60206040518083038186803b1580156105f157600080fd5b505afa158015610605573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610629919061159f565b6001600160a01b03166370a082317f00000000000000000000000000000000000000000000000000000000000000006040518263ffffffff1660e01b81526004016106749190611704565b60206040518083038186803b15801561068c57600080fd5b505afa1580156106a0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106c491906115d3565b9050836020015167ffffffffffffffff16421180156106e257508015155b1561077057600061070a856020015167ffffffffffffffff164261126190919063ffffffff16565b9050600060055461073e876040015167ffffffffffffffff16610738600654866112db90919063ffffffff16565b906112db565b8161074557fe5b04905061076b8361075b8364e8d4a510006112db565b8161076257fe5b8691900461128a565b935050505b6107ab836002015461047e856001015464e8d4a5100061079d8789600001546112db90919063ffffffff16565b816107a457fe5b0490611261565b979650505050505050565b6001546001600160a01b03163381146107e15760405162461bcd60e51b81526004016103059061199d565b600080546040516001600160a01b03808516939216917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3600080546001600160a01b0390921673ffffffffffffffffffffffffffffffffffffffff19928316179055600180549091169055565b61085861146d565b50600081815260026020908152604091829020825160608101845290546001600160801b038116825267ffffffffffffffff600160801b82048116938301849052600160c01b9091041692810192909252421115610b7b576040516378ed5d1f60e01b81526000906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906378ed5d1f906108ff908690600401611adc565b60206040518083038186803b15801561091757600080fd5b505afa15801561092b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061094f919061159f565b6001600160a01b03166370a082317f00000000000000000000000000000000000000000000000000000000000000006040518263ffffffff1660e01b815260040161099a9190611704565b60206040518083038186803b1580156109b257600080fd5b505afa1580156109c6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109ea91906115d3565b90508015610a8f576000610a15836020015167ffffffffffffffff164261126190919063ffffffff16565b90506000600554610a43856040015167ffffffffffffffff16610738600654866112db90919063ffffffff16565b81610a4a57fe5b049050610a81610a7084610a638464e8d4a510006112db565b81610a6a57fe5b04611312565b85516001600160801b03169061133b565b6001600160801b0316845250505b610a98426112ad565b67ffffffffffffffff908116602084810191825260008681526002909152604090819020855181549351838801516fffffffffffffffffffffffffffffffff199095166001600160801b038316177fffffffffffffffff0000000000000000ffffffffffffffffffffffffffffffff16600160801b828816021777ffffffffffffffffffffffffffffffffffffffffffffffff16600160c01b95909616949094029490941790555185927f0fc9545022a542541ad085d091fb09a2ab36fee366a4576ab63714ea907ad35392610b719290918691611afb565b60405180910390a2505b919050565b8060005b81811015610bb057610ba7848483818110610b9b57fe5b90506020020135610850565b50600101610b84565b50505050565b6000546001600160a01b03163314610be05760405162461bcd60e51b81526004016103059061190b565b60068190556040517fde89cb17ac7f58f94792b3e91e086ed85403819c24ceea882491f960ccb1a27890610c15908390611adc565b60405180910390a150565b60038181548110610c2d57fe5b600091825260209091200154905081565b6000546001600160a01b03163314610c685760405162461bcd60e51b81526004016103059061190b565b600081815260026020526040902054600160801b900467ffffffffffffffff1615610ca55760405162461bcd60e51b8152600401610305906117f8565b6005544290610cb4908461128a565b60055560408051606081019091526000815260208101610cd3836112ad565b67ffffffffffffffff168152602001610ceb856112ad565b67ffffffffffffffff90811690915260008481526002602090815260408083208551815493870151968301518616600160c01b0277ffffffffffffffffffffffffffffffffffffffffffffffff97909616600160801b027fffffffffffffffff0000000000000000ffffffffffffffffffffffffffffffff6001600160801b039092166fffffffffffffffffffffffffffffffff1990951694909417169290921794909416929092179091556003805460018101825591527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b018390555182907f38410508059921573ab9ebdca2a5034be738d236366b8f32de4434ea95ed3c8190610df8908690611adc565b60405180910390a2505050565b6000546001600160a01b031681565b60065481565b600460209081526000928352604080842090915290825290208054600182015460029092015490919083565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614610e8e5760405162461bcd60e51b815260040161030590611940565b600754600114610eb05760405162461bcd60e51b815260040161030590611a09565b6002600755610ebd61146d565b610ec686610850565b60008781526004602090815260408083206001600160a01b038a16845290915281208054929350911561105657610f2d826002015461047e846001015464e8d4a5100061079d88600001516001600160801b031688600001546112db90919063ffffffff16565b905060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166370a08231306040518263ffffffff1660e01b8152600401610f7d9190611704565b60206040518083038186803b158015610f9557600080fd5b505afa158015610fa9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fcd91906115d3565b9050808211156110185761100b6001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016888361136a565b8082036002840155611054565b61104c6001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016888461136a565b600060028401555b505b838255825164e8d4a51000906110769086906001600160801b03166112db565b8161107d57fe5b048260010181905550856001600160a01b031688886001600160a01b03167f2ece88ca2bc08dd018db50e1d25a20bf1241e5fab1c396caa51f01a54bd2f75b856002015485036040516110d09190611adc565b60405180910390a450506001600755505050505050565b6000546001600160a01b031633146111115760405162461bcd60e51b81526004016103059061190b565b6001600160a01b03831661115b576040516001600160a01b0382169083156108fc029084906000818181858888f19350505050158015611155573d6000803e3d6000fd5b506103df565b6103df6001600160a01b038416828461136a565b60408051600180825281830190925260609182918291602080830190803683370190505090507f0000000000000000000000000000000000000000000000000000000000000000816000815181106111c357fe5b6001600160a01b03929092166020928302919091019091015260408051600180825281830190925260609181602001602082028036833701905050905061120a8787610514565b8160008151811061121757fe5b602090810291909101015290969095509350505050565b6001546001600160a01b031681565b7f000000000000000000000000000000000000000000000000000000000000000081565b808203828111156112845760405162461bcd60e51b8152600401610305906117c1565b92915050565b818101818110156112845760405162461bcd60e51b8152600401610305906118d4565b600067ffffffffffffffff8211156112d75760405162461bcd60e51b8152600401610305906119d2565b5090565b60008115806112f6575050808202828282816112f357fe5b04145b6112845760405162461bcd60e51b815260040161030590611a40565b60006001600160801b038211156112d75760405162461bcd60e51b81526004016103059061189d565b8181016001600160801b0380831690821610156112845760405162461bcd60e51b8152600401610305906118d4565b60006060846001600160a01b031663a9059cbb8585604051602401611390929190611718565b6040516020818303038152906040529060e01b6020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506040516113de91906116cb565b6000604051808303816000865af19150503d806000811461141b576040519150601f19603f3d011682016040523d82523d6000602084013e611420565b606091505b509150915081801561144a57508051158061144a57508080602001905181019061144a919061157c565b6114665760405162461bcd60e51b81526004016103059061182f565b5050505050565b604080516060810182526000808252602082018190529181019190915290565b6000806000606084860312156114a1578283fd5b83356114ac81611b26565b925060208401356114bc81611b3e565b915060408401356114cc81611b3e565b809150509250925092565b6000806000606084860312156114eb578283fd5b83356114f681611b26565b92506020840135915060408401356114cc81611b26565b6000806020838503121561151f578182fd5b823567ffffffffffffffff80821115611536578384fd5b818501915085601f830112611549578384fd5b813581811115611557578485fd5b866020808302850101111561156a578485fd5b60209290920196919550909350505050565b60006020828403121561158d578081fd5b815161159881611b3e565b9392505050565b6000602082840312156115b0578081fd5b815161159881611b26565b6000602082840312156115cc578081fd5b5035919050565b6000602082840312156115e4578081fd5b5051919050565b600080604083850312156115fd578182fd5b82359150602083013561160f81611b26565b809150509250929050565b600080600080600060a08688031215611631578081fd5b85359450602086013561164381611b26565b9350604086013561165381611b26565b94979396509394606081013594506080013592915050565b60008060006060848603121561167f578283fd5b83359250602084013561169181611b26565b929592945050506040919091013590565b600080604083850312156116b4578182fd5b50508035926020909101359150565b815260200190565b60008251815b818110156116eb57602081860181015185830152016116d1565b818111156116f95782828501525b509190910192915050565b6001600160a01b0391909116815260200190565b6001600160a01b03929092168252602082015260400190565b604080825283519082018190526000906020906060840190828701845b828110156117735781516001600160a01b03168452928401929084019060010161174e565b5050508381038285015280855161178a8184611adc565b91508387019250845b818110156117b4576117a68385516116c3565b938501939250600101611793565b5090979650505050505050565b60208082526015908201527f426f72696e674d6174683a20556e646572666c6f770000000000000000000000604082015260600190565b60208082526013908201527f506f6f6c20616c72656164792065786973747300000000000000000000000000604082015260600190565b6020808252601c908201527f426f72696e6745524332303a205472616e73666572206661696c656400000000604082015260600190565b60208082526015908201527f4f776e61626c653a207a65726f20616464726573730000000000000000000000604082015260600190565b6020808252601c908201527f426f72696e674d6174683a2075696e74313238204f766572666c6f7700000000604082015260600190565b60208082526018908201527f426f72696e674d6174683a20416464204f766572666c6f770000000000000000604082015260600190565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b60208082526021908201527f4f6e6c7920636865662063616e2063616c6c20746869732066756e6374696f6e60408201527f2e00000000000000000000000000000000000000000000000000000000000000606082015260800190565b6020808252818101527f4f776e61626c653a2063616c6c657220213d2070656e64696e67206f776e6572604082015260600190565b6020808252601b908201527f426f72696e674d6174683a2075696e743634204f766572666c6f770000000000604082015260600190565b60208082526006908201527f4c4f434b45440000000000000000000000000000000000000000000000000000604082015260600190565b60208082526018908201527f426f72696e674d6174683a204d756c204f766572666c6f770000000000000000604082015260600190565b81516001600160801b0316815260208083015167ffffffffffffffff90811691830191909152604092830151169181019190915260600190565b6001600160801b0393909316835267ffffffffffffffff918216602084015216604082015260600190565b90815260200190565b9283526020830191909152604082015260600190565b67ffffffffffffffff93909316835260208301919091526001600160801b0316604082015260600190565b6001600160a01b0381168114611b3b57600080fd5b50565b8015158114611b3b57600080fdfea2646970667358221220ed78e27cbda8d6a9db26686b92bb9b87dc103bd57e13c5c938a002a2aa84ea7964736f6c634300060c003300000000000000000000000020677d4f3d0f08e735ab512393524a3cfceb250c000000000000000000000000000000000000000000000000003abdf105fc10cc000000000000000000000000359a3060a68488f0ea43d5cd8f6f53fe81a15f59

Deployed ByteCode

0x608060405234801561001057600080fd5b50600436106101515760003560e01c806369883b4e116100cd578063ac9e6d9211610081578063d63b3c4911610066578063d63b3c49146102aa578063e30c3978146102cb578063f7c618c1146102d357610151565b8063ac9e6d9214610284578063c1ea38681461029757610151565b80638da5cb5b116100b25780638da5cb5b146102455780638f10369a1461025a57806393f1a40b1461026257610151565b806369883b4e1461021f578063771602f71461023257610151565b806348e43af41161012457806351eb05a61161010957806351eb05a6146101d957806357a5b58c146101f957806366da58151461020c57610151565b806348e43af4146101be5780634e71e0c8146101d157610151565b8063078dfbe714610156578063081e3eda1461016b5780631526fe27146101895780631ab06ee5146101ab575b600080fd5b61016961016436600461148d565b6102db565b005b6101736103e4565b6040516101809190611adc565b60405180910390f35b61019c6101973660046115bb565b6103ea565b60405161018093929190611ab1565b6101696101b93660046116a2565b610422565b6101736101cc3660046115eb565b610514565b6101696107b6565b6101ec6101e73660046115bb565b610850565b6040516101809190611a77565b61016961020736600461150d565b610b80565b61016961021a3660046115bb565b610bb6565b61017361022d3660046115bb565b610c20565b6101696102403660046116a2565b610c3e565b61024d610e05565b6040516101809190611704565b610173610e14565b6102756102703660046115eb565b610e1a565b60405161018093929190611ae5565b61016961029236600461161a565b610e46565b6101696102a53660046114d7565b6110e7565b6102bd6102b836600461166b565b61116f565b604051610180929190611731565b61024d61122e565b61024d61123d565b6000546001600160a01b0316331461030e5760405162461bcd60e51b81526004016103059061190b565b60405180910390fd5b81156103b6576001600160a01b0383161515806103285750805b6103445760405162461bcd60e51b815260040161030590611866565b600080546040516001600160a01b03808716939216917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3600080546001600160a01b03851673ffffffffffffffffffffffffffffffffffffffff19918216179091556001805490911690556103df565b6001805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0385161790555b505050565b60035490565b6002602052600090815260409020546001600160801b0381169067ffffffffffffffff600160801b8204811691600160c01b90041683565b6000546001600160a01b0316331461044c5760405162461bcd60e51b81526004016103059061190b565b60008281526002602052604090205460055461048491839161047e91600160c01b900467ffffffffffffffff16611261565b9061128a565b600555610490816112ad565b60008381526002602052604090819020805467ffffffffffffffff93909316600160c01b0277ffffffffffffffffffffffffffffffffffffffffffffffff909316929092179091555182907f942cc7e17a17c164bd977f32ab8c54265d5b9d481e4e352bf874f1e568874e7c90610508908490611adc565b60405180910390a25050565b600061051e61146d565b506000838152600260209081526040808320815160608101835290546001600160801b03808216835267ffffffffffffffff600160801b8304811684870152600160c01b9092049091168284015287855260048085528386206001600160a01b03808a1688529552838620835194516378ed5d1f60e01b815293969095949092169391927f000000000000000000000000359a3060a68488f0ea43d5cd8f6f53fe81a15f59909216916378ed5d1f916105d9918b9101611adc565b60206040518083038186803b1580156105f157600080fd5b505afa158015610605573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610629919061159f565b6001600160a01b03166370a082317f000000000000000000000000359a3060a68488f0ea43d5cd8f6f53fe81a15f596040518263ffffffff1660e01b81526004016106749190611704565b60206040518083038186803b15801561068c57600080fd5b505afa1580156106a0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106c491906115d3565b9050836020015167ffffffffffffffff16421180156106e257508015155b1561077057600061070a856020015167ffffffffffffffff164261126190919063ffffffff16565b9050600060055461073e876040015167ffffffffffffffff16610738600654866112db90919063ffffffff16565b906112db565b8161074557fe5b04905061076b8361075b8364e8d4a510006112db565b8161076257fe5b8691900461128a565b935050505b6107ab836002015461047e856001015464e8d4a5100061079d8789600001546112db90919063ffffffff16565b816107a457fe5b0490611261565b979650505050505050565b6001546001600160a01b03163381146107e15760405162461bcd60e51b81526004016103059061199d565b600080546040516001600160a01b03808516939216917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3600080546001600160a01b0390921673ffffffffffffffffffffffffffffffffffffffff19928316179055600180549091169055565b61085861146d565b50600081815260026020908152604091829020825160608101845290546001600160801b038116825267ffffffffffffffff600160801b82048116938301849052600160c01b9091041692810192909252421115610b7b576040516378ed5d1f60e01b81526000906001600160a01b037f000000000000000000000000359a3060a68488f0ea43d5cd8f6f53fe81a15f5916906378ed5d1f906108ff908690600401611adc565b60206040518083038186803b15801561091757600080fd5b505afa15801561092b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061094f919061159f565b6001600160a01b03166370a082317f000000000000000000000000359a3060a68488f0ea43d5cd8f6f53fe81a15f596040518263ffffffff1660e01b815260040161099a9190611704565b60206040518083038186803b1580156109b257600080fd5b505afa1580156109c6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109ea91906115d3565b90508015610a8f576000610a15836020015167ffffffffffffffff164261126190919063ffffffff16565b90506000600554610a43856040015167ffffffffffffffff16610738600654866112db90919063ffffffff16565b81610a4a57fe5b049050610a81610a7084610a638464e8d4a510006112db565b81610a6a57fe5b04611312565b85516001600160801b03169061133b565b6001600160801b0316845250505b610a98426112ad565b67ffffffffffffffff908116602084810191825260008681526002909152604090819020855181549351838801516fffffffffffffffffffffffffffffffff199095166001600160801b038316177fffffffffffffffff0000000000000000ffffffffffffffffffffffffffffffff16600160801b828816021777ffffffffffffffffffffffffffffffffffffffffffffffff16600160c01b95909616949094029490941790555185927f0fc9545022a542541ad085d091fb09a2ab36fee366a4576ab63714ea907ad35392610b719290918691611afb565b60405180910390a2505b919050565b8060005b81811015610bb057610ba7848483818110610b9b57fe5b90506020020135610850565b50600101610b84565b50505050565b6000546001600160a01b03163314610be05760405162461bcd60e51b81526004016103059061190b565b60068190556040517fde89cb17ac7f58f94792b3e91e086ed85403819c24ceea882491f960ccb1a27890610c15908390611adc565b60405180910390a150565b60038181548110610c2d57fe5b600091825260209091200154905081565b6000546001600160a01b03163314610c685760405162461bcd60e51b81526004016103059061190b565b600081815260026020526040902054600160801b900467ffffffffffffffff1615610ca55760405162461bcd60e51b8152600401610305906117f8565b6005544290610cb4908461128a565b60055560408051606081019091526000815260208101610cd3836112ad565b67ffffffffffffffff168152602001610ceb856112ad565b67ffffffffffffffff90811690915260008481526002602090815260408083208551815493870151968301518616600160c01b0277ffffffffffffffffffffffffffffffffffffffffffffffff97909616600160801b027fffffffffffffffff0000000000000000ffffffffffffffffffffffffffffffff6001600160801b039092166fffffffffffffffffffffffffffffffff1990951694909417169290921794909416929092179091556003805460018101825591527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b018390555182907f38410508059921573ab9ebdca2a5034be738d236366b8f32de4434ea95ed3c8190610df8908690611adc565b60405180910390a2505050565b6000546001600160a01b031681565b60065481565b600460209081526000928352604080842090915290825290208054600182015460029092015490919083565b336001600160a01b037f000000000000000000000000359a3060a68488f0ea43d5cd8f6f53fe81a15f591614610e8e5760405162461bcd60e51b815260040161030590611940565b600754600114610eb05760405162461bcd60e51b815260040161030590611a09565b6002600755610ebd61146d565b610ec686610850565b60008781526004602090815260408083206001600160a01b038a16845290915281208054929350911561105657610f2d826002015461047e846001015464e8d4a5100061079d88600001516001600160801b031688600001546112db90919063ffffffff16565b905060007f00000000000000000000000020677d4f3d0f08e735ab512393524a3cfceb250c6001600160a01b03166370a08231306040518263ffffffff1660e01b8152600401610f7d9190611704565b60206040518083038186803b158015610f9557600080fd5b505afa158015610fa9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fcd91906115d3565b9050808211156110185761100b6001600160a01b037f00000000000000000000000020677d4f3d0f08e735ab512393524a3cfceb250c16888361136a565b8082036002840155611054565b61104c6001600160a01b037f00000000000000000000000020677d4f3d0f08e735ab512393524a3cfceb250c16888461136a565b600060028401555b505b838255825164e8d4a51000906110769086906001600160801b03166112db565b8161107d57fe5b048260010181905550856001600160a01b031688886001600160a01b03167f2ece88ca2bc08dd018db50e1d25a20bf1241e5fab1c396caa51f01a54bd2f75b856002015485036040516110d09190611adc565b60405180910390a450506001600755505050505050565b6000546001600160a01b031633146111115760405162461bcd60e51b81526004016103059061190b565b6001600160a01b03831661115b576040516001600160a01b0382169083156108fc029084906000818181858888f19350505050158015611155573d6000803e3d6000fd5b506103df565b6103df6001600160a01b038416828461136a565b60408051600180825281830190925260609182918291602080830190803683370190505090507f00000000000000000000000020677d4f3d0f08e735ab512393524a3cfceb250c816000815181106111c357fe5b6001600160a01b03929092166020928302919091019091015260408051600180825281830190925260609181602001602082028036833701905050905061120a8787610514565b8160008151811061121757fe5b602090810291909101015290969095509350505050565b6001546001600160a01b031681565b7f00000000000000000000000020677d4f3d0f08e735ab512393524a3cfceb250c81565b808203828111156112845760405162461bcd60e51b8152600401610305906117c1565b92915050565b818101818110156112845760405162461bcd60e51b8152600401610305906118d4565b600067ffffffffffffffff8211156112d75760405162461bcd60e51b8152600401610305906119d2565b5090565b60008115806112f6575050808202828282816112f357fe5b04145b6112845760405162461bcd60e51b815260040161030590611a40565b60006001600160801b038211156112d75760405162461bcd60e51b81526004016103059061189d565b8181016001600160801b0380831690821610156112845760405162461bcd60e51b8152600401610305906118d4565b60006060846001600160a01b031663a9059cbb8585604051602401611390929190611718565b6040516020818303038152906040529060e01b6020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506040516113de91906116cb565b6000604051808303816000865af19150503d806000811461141b576040519150601f19603f3d011682016040523d82523d6000602084013e611420565b606091505b509150915081801561144a57508051158061144a57508080602001905181019061144a919061157c565b6114665760405162461bcd60e51b81526004016103059061182f565b5050505050565b604080516060810182526000808252602082018190529181019190915290565b6000806000606084860312156114a1578283fd5b83356114ac81611b26565b925060208401356114bc81611b3e565b915060408401356114cc81611b3e565b809150509250925092565b6000806000606084860312156114eb578283fd5b83356114f681611b26565b92506020840135915060408401356114cc81611b26565b6000806020838503121561151f578182fd5b823567ffffffffffffffff80821115611536578384fd5b818501915085601f830112611549578384fd5b813581811115611557578485fd5b866020808302850101111561156a578485fd5b60209290920196919550909350505050565b60006020828403121561158d578081fd5b815161159881611b3e565b9392505050565b6000602082840312156115b0578081fd5b815161159881611b26565b6000602082840312156115cc578081fd5b5035919050565b6000602082840312156115e4578081fd5b5051919050565b600080604083850312156115fd578182fd5b82359150602083013561160f81611b26565b809150509250929050565b600080600080600060a08688031215611631578081fd5b85359450602086013561164381611b26565b9350604086013561165381611b26565b94979396509394606081013594506080013592915050565b60008060006060848603121561167f578283fd5b83359250602084013561169181611b26565b929592945050506040919091013590565b600080604083850312156116b4578182fd5b50508035926020909101359150565b815260200190565b60008251815b818110156116eb57602081860181015185830152016116d1565b818111156116f95782828501525b509190910192915050565b6001600160a01b0391909116815260200190565b6001600160a01b03929092168252602082015260400190565b604080825283519082018190526000906020906060840190828701845b828110156117735781516001600160a01b03168452928401929084019060010161174e565b5050508381038285015280855161178a8184611adc565b91508387019250845b818110156117b4576117a68385516116c3565b938501939250600101611793565b5090979650505050505050565b60208082526015908201527f426f72696e674d6174683a20556e646572666c6f770000000000000000000000604082015260600190565b60208082526013908201527f506f6f6c20616c72656164792065786973747300000000000000000000000000604082015260600190565b6020808252601c908201527f426f72696e6745524332303a205472616e73666572206661696c656400000000604082015260600190565b60208082526015908201527f4f776e61626c653a207a65726f20616464726573730000000000000000000000604082015260600190565b6020808252601c908201527f426f72696e674d6174683a2075696e74313238204f766572666c6f7700000000604082015260600190565b60208082526018908201527f426f72696e674d6174683a20416464204f766572666c6f770000000000000000604082015260600190565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b60208082526021908201527f4f6e6c7920636865662063616e2063616c6c20746869732066756e6374696f6e60408201527f2e00000000000000000000000000000000000000000000000000000000000000606082015260800190565b6020808252818101527f4f776e61626c653a2063616c6c657220213d2070656e64696e67206f776e6572604082015260600190565b6020808252601b908201527f426f72696e674d6174683a2075696e743634204f766572666c6f770000000000604082015260600190565b60208082526006908201527f4c4f434b45440000000000000000000000000000000000000000000000000000604082015260600190565b60208082526018908201527f426f72696e674d6174683a204d756c204f766572666c6f770000000000000000604082015260600190565b81516001600160801b0316815260208083015167ffffffffffffffff90811691830191909152604092830151169181019190915260600190565b6001600160801b0393909316835267ffffffffffffffff918216602084015216604082015260600190565b90815260200190565b9283526020830191909152604082015260600190565b67ffffffffffffffff93909316835260208301919091526001600160801b0316604082015260600190565b6001600160a01b0381168114611b3b57600080fd5b50565b8015158114611b3b57600080fdfea2646970667358221220ed78e27cbda8d6a9db26686b92bb9b87dc103bd57e13c5c938a002a2aa84ea7964736f6c634300060c0033