false
false

Contract Address Details

0x7f196CF0aD709e2aDFC348cA7E2e166581Ef4673

Contract Name
BribeFactory
Creator
0x0348f8–a27d70 at 0xe3d523–6ed599
Balance
0 KCS
Tokens
Fetching tokens...
Transactions
0 Transactions
Transfers
0 Transfers
Gas Used
Fetching gas used...
Last Balance Update
44726434
Contract is not verified. However, we found a verified contract with the same bytecode in Blockscout DB 0xbe8666fd6873b0e188898180f922a49c3dd14c20.
All metadata displayed below is from that contract. In order to verify current contract, click Verify & Publish button
Verify & Publish
Contract name:
BribeFactory




Optimization enabled
true
Compiler version
v0.8.13+commit.abaa5c0e




Optimization runs
200
Verified at
2023-08-25T19:26:29.411982Z

Contract source code

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

interface IBribeFactory {
    function createExternalBribe(address[] memory) external returns (address);
}

// OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol)

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    enum Rounding {
        Down, // Toward negative infinity
        Up, // Toward infinity
        Zero // Toward zero
    }

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

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

    /**
     * @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 == 0 ? 0 : (a - 1) / b + 1;
    }

    /**
     * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
     * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
     * with further edits by Uniswap Labs also under MIT license.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
        unchecked {
            // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
            // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
            // variables such that product = prod1 * 2^256 + prod0.
            uint256 prod0; // Least significant 256 bits of the product
            uint256 prod1; // Most significant 256 bits of the product
            assembly {
                let mm := mulmod(x, y, not(0))
                prod0 := mul(x, y)
                prod1 := sub(sub(mm, prod0), lt(mm, prod0))
            }

            // Handle non-overflow cases, 256 by 256 division.
            if (prod1 == 0) {
                // Solidity will revert if denominator == 0, unlike the div opcode on its own.
                // The surrounding unchecked block does not change this fact.
                // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
                return prod0 / denominator;
            }

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            require(denominator > prod1, "Math: mulDiv overflow");

            ///////////////////////////////////////////////
            // 512 by 256 division.
            ///////////////////////////////////////////////

            // Make division exact by subtracting the remainder from [prod1 prod0].
            uint256 remainder;
            assembly {
                // Compute remainder using mulmod.
                remainder := mulmod(x, y, denominator)

                // Subtract 256 bit number from 512 bit number.
                prod1 := sub(prod1, gt(remainder, prod0))
                prod0 := sub(prod0, remainder)
            }

            // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
            // See https://cs.stackexchange.com/q/138556/92363.

            // Does not overflow because the denominator cannot be zero at this stage in the function.
            uint256 twos = denominator & (~denominator + 1);
            assembly {
                // Divide denominator by twos.
                denominator := div(denominator, twos)

                // Divide [prod1 prod0] by twos.
                prod0 := div(prod0, twos)

                // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                twos := add(div(sub(0, twos), twos), 1)
            }

            // Shift in bits from prod1 into prod0.
            prod0 |= prod1 * twos;

            // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
            // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
            // four bits. That is, denominator * inv = 1 mod 2^4.
            uint256 inverse = (3 * denominator) ^ 2;

            // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
            // in modular arithmetic, doubling the correct bits in each step.
            inverse *= 2 - denominator * inverse; // inverse mod 2^8
            inverse *= 2 - denominator * inverse; // inverse mod 2^16
            inverse *= 2 - denominator * inverse; // inverse mod 2^32
            inverse *= 2 - denominator * inverse; // inverse mod 2^64
            inverse *= 2 - denominator * inverse; // inverse mod 2^128
            inverse *= 2 - denominator * inverse; // inverse mod 2^256

            // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
            // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
            // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
            // is no longer required.
            result = prod0 * inverse;
            return result;
        }
    }

    /**
     * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
        uint256 result = mulDiv(x, y, denominator);
        if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
            result += 1;
        }
        return result;
    }

    /**
     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
     *
     * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
     */
    function sqrt(uint256 a) internal pure returns (uint256) {
        if (a == 0) {
            return 0;
        }

        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
        //
        // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
        // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
        //
        // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
        // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
        // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
        //
        // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
        uint256 result = 1 << (log2(a) >> 1);

        // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
        // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
        // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
        // into the expected uint128 result.
        unchecked {
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            return min(result, a / result);
        }
    }

    /**
     * @notice Calculates sqrt(a), following the selected rounding direction.
     */
    function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = sqrt(a);
            return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 2, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 128;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 64;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 32;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 16;
            }
            if (value >> 8 > 0) {
                value >>= 8;
                result += 8;
            }
            if (value >> 4 > 0) {
                value >>= 4;
                result += 4;
            }
            if (value >> 2 > 0) {
                value >>= 2;
                result += 2;
            }
            if (value >> 1 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log2(value);
            return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 10, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >= 10 ** 64) {
                value /= 10 ** 64;
                result += 64;
            }
            if (value >= 10 ** 32) {
                value /= 10 ** 32;
                result += 32;
            }
            if (value >= 10 ** 16) {
                value /= 10 ** 16;
                result += 16;
            }
            if (value >= 10 ** 8) {
                value /= 10 ** 8;
                result += 8;
            }
            if (value >= 10 ** 4) {
                value /= 10 ** 4;
                result += 4;
            }
            if (value >= 10 ** 2) {
                value /= 10 ** 2;
                result += 2;
            }
            if (value >= 10 ** 1) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log10(value);
            return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 256, rounded down, of a positive value.
     * Returns 0 if given 0.
     *
     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
     */
    function log256(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 16;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 8;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 4;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 2;
            }
            if (value >> 8 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 256, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log256(value);
            return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);
        }
    }
}

interface IBribe {
    function _deposit(uint amount, uint tokenId) external;
    function _withdraw(uint amount, uint tokenId) external;
    function getRewardForOwner(uint tokenId, address[] memory tokens) external;
    function notifyRewardAmount(address token, uint amount) external;
    function left(address token) external view returns (uint);
}

interface IERC20 {
    function totalSupply() external view returns (uint256);
    function transfer(address recipient, uint amount) external returns (bool);
    function decimals() external view returns (uint8);
    function symbol() external view returns (string memory);
    function balanceOf(address) external view returns (uint);
    function transferFrom(address sender, address recipient, uint amount) external returns (bool);
    function allowance(address owner, address spender) external view returns (uint);
    function approve(address spender, uint value) external returns (bool);

    event Transfer(address indexed from, address indexed to, uint value);
    event Approval(address indexed owner, address indexed spender, uint value);
}

interface IGauge {
    function notifyRewardAmount(address token, uint amount) external;
    function getReward(address account, address[] memory tokens) external;
    function left(address token) external view returns (uint);
    function stake() external view returns (address);
}

interface IVoter {
    function _ve() external view returns (address);
    function governor() external view returns (address);
    function emergencyCouncil() external view returns (address);
    function attachTokenToGauge(uint _tokenId, address account) external;
    function detachTokenFromGauge(uint _tokenId, address account) external;
    function emitDeposit(uint _tokenId, address account, uint amount) external;
    function emitWithdraw(uint _tokenId, address account, uint amount) external;
    function isWhitelisted(address token) external view returns (bool);
    function notifyRewardAmount(uint amount) external;
    function distribute(address _gauge) external;
    function gauges(address) external view returns (address);
}

interface IVotingEscrow {

    struct Point {
        int128 bias;
        int128 slope; // # -dweight / dt
        uint256 ts;
        uint256 blk; // block
    }

    function token() external view returns (address);
    function team() external returns (address);
    function epoch() external view returns (uint);
    function point_history(uint loc) external view returns (Point memory);
    function user_point_history(uint tokenId, uint loc) external view returns (Point memory);
    function user_point_epoch(uint tokenId) external view returns (uint);

    function ownerOf(uint) external view returns (address);
    function isApprovedOrOwner(address, uint) external view returns (bool);
    function transferFrom(address, address, uint) external;

    function voting(uint tokenId) external;
    function abstain(uint tokenId) external;
    function attach(uint tokenId) external;
    function detach(uint tokenId) external;

    function checkpoint() external;
    function deposit_for(uint tokenId, uint value) external;
    function create_lock_for(uint, uint, address) external returns (uint);

    function balanceOfNFT(uint) external view returns (uint);
    function totalSupply() external view returns (uint);
}

// Bribes pay out rewards for a given pool based on the votes that were received from the user (goes hand in hand with Voter.vote())
contract ExternalBribe is IBribe {
    address public immutable voter; // only voter can modify balances (since it only happens on vote())
    address public immutable _ve; // 天使のたまご

    uint internal constant DURATION = 7 days; // rewards are released over the voting period
    uint internal constant MAX_REWARD_TOKENS = 16;

    uint public totalSupply;
    mapping(uint => uint) public balanceOf;
    mapping(address => mapping(uint => uint)) public tokenRewardsPerEpoch;
    mapping(address => uint) public periodFinish;
    mapping(address => mapping(uint => uint)) public lastEarn;

    address[] public rewards;
    mapping(address => bool) public isReward;

    /// @notice A checkpoint for marking balance
    struct Checkpoint {
        uint timestamp;
        uint balanceOf;
    }

    /// @notice A checkpoint for marking supply
    struct SupplyCheckpoint {
        uint timestamp;
        uint supply;
    }

    /// @notice A record of balance checkpoints for each account, by index
    mapping (uint => mapping (uint => Checkpoint)) public checkpoints;
    /// @notice The number of checkpoints for each account
    mapping (uint => uint) public numCheckpoints;
    /// @notice A record of balance checkpoints for each token, by index
    mapping (uint => SupplyCheckpoint) public supplyCheckpoints;
    /// @notice The number of checkpoints
    uint public supplyNumCheckpoints;

    event Deposit(address indexed from, uint tokenId, uint amount);
    event Withdraw(address indexed from, uint tokenId, uint amount);
    event NotifyReward(address indexed from, address indexed reward, uint epoch, uint amount);
    event ClaimRewards(address indexed from, address indexed reward, uint amount);
    event HandleLeftOverRewards(address indexed reward, uint originalEpoch, uint updatedEpoch, uint amount);

    constructor(address _voter, address[] memory _allowedRewardTokens) {
        voter = _voter;
        _ve = IVoter(_voter)._ve();

        for (uint i; i < _allowedRewardTokens.length; i++) {
            if (_allowedRewardTokens[i] != address(0)) {
                isReward[_allowedRewardTokens[i]] = true;
                rewards.push(_allowedRewardTokens[i]);
            }
        }
    }

    // simple re-entrancy check
    uint internal _unlocked = 1;
    modifier lock() {
        require(_unlocked == 1);
        _unlocked = 2;
        _;
        _unlocked = 1;
    }

    function _bribeStart(uint timestamp) internal pure returns (uint) {
        return timestamp - (timestamp % (7 days));
    }

    function getEpochStart(uint timestamp) public pure returns (uint) {
        uint bribeStart = _bribeStart(timestamp);
        uint bribeEnd = bribeStart + DURATION;
        return timestamp < bribeEnd ? bribeStart : bribeStart + 7 days;
    }

    /**
    * @notice Determine the prior balance for an account as of a block number
    * @dev Block number must be a finalized block or else this function will revert to prevent misinformation.
    * @param tokenId The token of the NFT to check
    * @param timestamp The timestamp to get the balance at
    * @return The balance the account had as of the given block
    */
    function getPriorBalanceIndex(uint tokenId, uint timestamp) public view returns (uint) {
        uint nCheckpoints = numCheckpoints[tokenId];
        if (nCheckpoints == 0) {
            return 0;
        }
        // First check most recent balance
        if (checkpoints[tokenId][nCheckpoints - 1].timestamp <= timestamp) {
            return (nCheckpoints - 1);
        }
        // Next check implicit zero balance
        if (checkpoints[tokenId][0].timestamp > timestamp) {
            return 0;
        }

        uint lower = 0;
        uint upper = nCheckpoints - 1;
        while (upper > lower) {
            uint center = upper - (upper - lower) / 2; // ceil, avoiding overflow
            Checkpoint memory cp = checkpoints[tokenId][center];
            if (cp.timestamp == timestamp) {
                return center;
            } else if (cp.timestamp < timestamp) {
                lower = center;
            } else {
                upper = center - 1;
            }
        }
        return lower;
    }

    function getPriorSupplyIndex(uint timestamp) public view returns (uint) {
        uint nCheckpoints = supplyNumCheckpoints;
        if (nCheckpoints == 0) {
            return 0;
        }

        // First check most recent balance
        if (supplyCheckpoints[nCheckpoints - 1].timestamp <= timestamp) {
            return (nCheckpoints - 1);
        }

        // Next check implicit zero balance
        if (supplyCheckpoints[0].timestamp > timestamp) {
            return 0;
        }

        uint lower = 0;
        uint upper = nCheckpoints - 1;
        while (upper > lower) {
            uint center = upper - (upper - lower) / 2; // ceil, avoiding overflow
            SupplyCheckpoint memory cp = supplyCheckpoints[center];
            if (cp.timestamp == timestamp) {
                return center;
            } else if (cp.timestamp < timestamp) {
                lower = center;
            } else {
                upper = center - 1;
            }
        }
        return lower;
    }

    function _writeCheckpoint(uint tokenId, uint balance) internal {
        uint _timestamp = block.timestamp;
        uint _nCheckPoints = numCheckpoints[tokenId];
        if (_nCheckPoints > 0 && checkpoints[tokenId][_nCheckPoints - 1].timestamp == _timestamp) {
            checkpoints[tokenId][_nCheckPoints - 1].balanceOf = balance;
        } else {
            checkpoints[tokenId][_nCheckPoints] = Checkpoint(_timestamp, balance);
            numCheckpoints[tokenId] = _nCheckPoints + 1;
        }
    }

    function _writeSupplyCheckpoint() internal {
        uint _nCheckPoints = supplyNumCheckpoints;
        uint _timestamp = block.timestamp;

        if (_nCheckPoints > 0 && supplyCheckpoints[_nCheckPoints - 1].timestamp == _timestamp) {
            supplyCheckpoints[_nCheckPoints - 1].supply = totalSupply;
        } else {
            supplyCheckpoints[_nCheckPoints] = SupplyCheckpoint(_timestamp, totalSupply);
            supplyNumCheckpoints = _nCheckPoints + 1;
        }
    }

    function rewardsListLength() external view returns (uint) {
        return rewards.length;
    }

    // returns the last time the reward was modified or periodFinish if the reward has ended
    function lastTimeRewardApplicable(address token) public view returns (uint) {
        return Math.min(block.timestamp, periodFinish[token]);
    }

    // allows a user to claim rewards for a given token
    function getReward(uint tokenId, address[] memory tokens) external lock  {
        require(IVotingEscrow(_ve).isApprovedOrOwner(msg.sender, tokenId));
        for (uint i = 0; i < tokens.length; i++) {
            uint _reward = earned(tokens[i], tokenId);
            lastEarn[tokens[i]][tokenId] = block.timestamp;
            if (_reward > 0) _safeTransfer(tokens[i], msg.sender, _reward);

            emit ClaimRewards(msg.sender, tokens[i], _reward);
        }
    }

    // used by Voter to allow batched reward claims
    function getRewardForOwner(uint tokenId, address[] memory tokens) external lock  {
        require(msg.sender == voter);
        address _owner = IVotingEscrow(_ve).ownerOf(tokenId);
        for (uint i = 0; i < tokens.length; i++) {
            uint _reward = earned(tokens[i], tokenId);
            lastEarn[tokens[i]][tokenId] = block.timestamp;
            if (_reward > 0) _safeTransfer(tokens[i], _owner, _reward);

            emit ClaimRewards(_owner, tokens[i], _reward);
        }
    }

    function earned(address token, uint tokenId) public view returns (uint) {
        if (numCheckpoints[tokenId] == 0) {
            return 0;
        }

        uint reward = 0;
        uint _bal = 0;
        uint _supply = 1;
        uint _index = 0;
        uint _currTs = _bribeStart(lastEarn[token][tokenId]); // take epoch last claimed in as starting point

        _index = getPriorBalanceIndex(tokenId, _currTs);

        // accounts for case where lastEarn is before first checkpoint
        _currTs = Math.max(_currTs, _bribeStart(checkpoints[tokenId][_index].timestamp)); 

        // get epochs between current epoch and first checkpoint in same epoch as last claim
        uint numEpochs = (_bribeStart(block.timestamp) - _currTs) / DURATION;

        if (numEpochs > 0) {
            for (uint256 i = 0; i < numEpochs; i++) {
                // get index of last checkpoint in this epoch
                _index = getPriorBalanceIndex(tokenId, _currTs + DURATION); 
                // get checkpoint in this epoch
                _bal = checkpoints[tokenId][_index].balanceOf;
                // get supply of last checkpoint in this epoch
                _supply = supplyCheckpoints[getPriorSupplyIndex(_currTs + DURATION)].supply;
                if (_supply != 0) {
                    reward += _bal * tokenRewardsPerEpoch[token][_currTs] / _supply;
                }
                _currTs += DURATION;
            }
        }

        return reward;
    }

    // This is an external function, but internal notation is used since it can only be called "internally" from Gauges
    function _deposit(uint amount, uint tokenId) external {
        require(msg.sender == voter);

        totalSupply += amount;
        balanceOf[tokenId] += amount;

        _writeCheckpoint(tokenId, balanceOf[tokenId]);
        _writeSupplyCheckpoint();

        emit Deposit(msg.sender, tokenId, amount);
    }

    function _withdraw(uint amount, uint tokenId) external {
        require(msg.sender == voter);

        totalSupply -= amount;
        balanceOf[tokenId] -= amount;

        _writeCheckpoint(tokenId, balanceOf[tokenId]);
        _writeSupplyCheckpoint();

        emit Withdraw(msg.sender, tokenId, amount);
    }

    function left(address token) external view returns (uint) {
        uint adjustedTstamp = getEpochStart(block.timestamp);
        return tokenRewardsPerEpoch[token][adjustedTstamp];
    }

    function notifyRewardAmount(address token, uint amount) external lock {
        require(amount > 0);
        if (!isReward[token]) {
          require(IVoter(voter).isWhitelisted(token), "bribe tokens must be whitelisted");
          require(rewards.length < MAX_REWARD_TOKENS, "too many rewards tokens");
        }
        // bribes kick in at the start of next bribe period
        uint adjustedTstamp = getEpochStart(block.timestamp);
        uint epochRewards = tokenRewardsPerEpoch[token][adjustedTstamp];

        uint256 balanceBefore = IERC20(token).balanceOf(address(this));
        _safeTransferFrom(token, msg.sender, address(this), amount);
        uint256 balanceAfter = IERC20(token).balanceOf(address(this));

        amount = balanceAfter - balanceBefore;

        tokenRewardsPerEpoch[token][adjustedTstamp] = epochRewards + amount;

        periodFinish[token] = adjustedTstamp + DURATION;

        if (!isReward[token]) {
            isReward[token] = true;
            rewards.push(token);
        }

        emit NotifyReward(msg.sender, token, adjustedTstamp, amount);
    }

    // This is an external function that can only be called by teams to handle unclaimed rewards due to zero vote
    function handleLeftOverRewards(uint epochTimestamp, address[] memory tokens) external {
        require(msg.sender == IVotingEscrow(_ve).team(), "only team");

        // require that supply of that epoch to be ZERO
        uint epochStart = getEpochStart(epochTimestamp);
        SupplyCheckpoint memory sp0 = supplyCheckpoints[getPriorSupplyIndex(epochStart + DURATION)];
        if (epochStart + DURATION > _bribeStart(sp0.timestamp)) {
            require(sp0.supply == 0, "this epoch has votes");
        }

        // do sth like notifyRewardAmount
        uint length = tokens.length;
        for (uint i = 0; i < length;) {
            // check bribe amount 
            uint previousEpochRewards = tokenRewardsPerEpoch[tokens[i]][epochStart];
            require(previousEpochRewards != 0, "no bribes for this epoch");

            // get timestamp of current epoch
            uint adjustedTstamp = getEpochStart(block.timestamp);

            // get notified reward of current epoch
            uint currentEpochRewards = tokenRewardsPerEpoch[tokens[i]][adjustedTstamp];

            // add previous unclaimed rewards to current epoch
            tokenRewardsPerEpoch[tokens[i]][adjustedTstamp] = currentEpochRewards + previousEpochRewards;

            // remove token rewards from previous epoch
            tokenRewardsPerEpoch[tokens[i]][epochStart] = 0;

            // amend period finish
            periodFinish[tokens[i]] = adjustedTstamp + DURATION;

            emit HandleLeftOverRewards(tokens[i], epochStart, adjustedTstamp, previousEpochRewards);

            unchecked {
                ++i;
            }
        }
    }

    function swapOutRewardToken(uint i, address oldToken, address newToken) external {
        require(msg.sender == IVotingEscrow(_ve).team(), 'only team');
        require(rewards[i] == oldToken);
        isReward[oldToken] = false;
        isReward[newToken] = true;
        rewards[i] = newToken;
    }

    function _safeTransfer(address token, address to, uint256 value) internal {
        require(token.code.length > 0);
        (bool success, bytes memory data) =
        token.call(abi.encodeWithSelector(IERC20.transfer.selector, to, value));
        require(success && (data.length == 0 || abi.decode(data, (bool))));
    }

    function _safeTransferFrom(address token, address from, address to, uint256 value) internal {
        require(token.code.length > 0);
        (bool success, bytes memory data) =
        token.call(abi.encodeWithSelector(IERC20.transferFrom.selector, from, to, value));
        require(success && (data.length == 0 || abi.decode(data, (bool))));
    }
}

contract BribeFactory is IBribeFactory {
    address public last_external_bribe;

    function createExternalBribe(address[] memory allowedRewards) external returns (address) {
        last_external_bribe = address(new ExternalBribe(msg.sender, allowedRewards));
        return last_external_bribe;
    }
}
        

Contract ABI

[{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"createExternalBribe","inputs":[{"type":"address[]","name":"allowedRewards","internalType":"address[]"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"last_external_bribe","inputs":[]}]
              

Contract Creation Code

Verify & Publish
0x608060405234801561001057600080fd5b50612559806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c80636485f0c41461003b578063b0ad856f1461006a575b600080fd5b61004e610049366004610119565b61007d565b6040516001600160a01b03909116815260200160405180910390f35b60005461004e906001600160a01b031681565b6000338260405161008d906100da565b6100989291906101de565b604051809103906000f0801580156100b4573d6000803e3d6000fd5b50600080546001600160a01b0319166001600160a01b0392909216918217905592915050565b6122e98061023b83390190565b634e487b7160e01b600052604160045260246000fd5b80356001600160a01b038116811461011457600080fd5b919050565b6000602080838503121561012c57600080fd5b823567ffffffffffffffff8082111561014457600080fd5b818501915085601f83011261015857600080fd5b81358181111561016a5761016a6100e7565b8060051b604051601f19603f8301168101818110858211171561018f5761018f6100e7565b6040529182528482019250838101850191888311156101ad57600080fd5b938501935b828510156101d2576101c3856100fd565b845293850193928501926101b2565b98975050505050505050565b6001600160a01b038381168252604060208084018290528451918401829052600092858201929091906060860190855b8181101561022c57855185168352948301949183019160010161020e565b50909897505050505050505056fe60c06040526001600b553480156200001657600080fd5b50604051620022e9380380620022e98339810160408190526200003991620001f8565b6001600160a01b038216608081905260408051638dd598fb60e01b81529051638dd598fb916004808201926020929091908290030181865afa15801562000084573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620000aa9190620002e1565b6001600160a01b031660a05260005b8151811015620001bc5760006001600160a01b0316828281518110620000e357620000e362000306565b60200260200101516001600160a01b031614620001a75760016006600084848151811062000115576200011562000306565b60200260200101516001600160a01b03166001600160a01b0316815260200190815260200160002060006101000a81548160ff02191690831515021790555060058282815181106200016b576200016b62000306565b60209081029190910181015182546001810184556000938452919092200180546001600160a01b0319166001600160a01b039092169190911790555b80620001b3816200031c565b915050620000b9565b50505062000344565b80516001600160a01b0381168114620001dd57600080fd5b919050565b634e487b7160e01b600052604160045260246000fd5b600080604083850312156200020c57600080fd5b6200021783620001c5565b602084810151919350906001600160401b03808211156200023757600080fd5b818601915086601f8301126200024c57600080fd5b815181811115620002615762000261620001e2565b8060051b604051601f19603f83011681018181108582111715620002895762000289620001e2565b604052918252848201925083810185019189831115620002a857600080fd5b938501935b82851015620002d157620002c185620001c5565b84529385019392850192620002ad565b8096505050505050509250929050565b600060208284031215620002f457600080fd5b620002ff82620001c5565b9392505050565b634e487b7160e01b600052603260045260246000fd5b6000600182016200033d57634e487b7160e01b600052601160045260246000fd5b5060010190565b60805160a051611f47620003a260003960008181610309015281816104e801528181610c010152818161103b015261169b01526000818161020a01528181610dcf01528181610ff80152818161123601526115ae0152611f476000f3fe608060405234801561001057600080fd5b50600436106101a95760003560e01c806399bcc052116100f9578063e688639611610097578063f301af4211610071578063f301af4214610444578063f320772314610457578063f5f8d3651461046a578063f7412baf1461047d57600080fd5b8063e688639614610408578063e8111a1214610410578063f25e55a51461041957600080fd5b8063a28d4c9c116100d3578063a28d4c9c146103af578063a7852afa146103c2578063b66503cf146103d5578063da09d19d146103e857600080fd5b806399bcc052146103695780639cc7f7081461037c5780639e2bf22c1461039c57600080fd5b80634d5ce0381161016657806376f4be361161014057806376f4be36146102f15780638dd598fb1461030457806392777b291461032b5780639418f9391461035657600080fd5b80634d5ce0381461028b57806350589793146102be578063638634ee146102de57600080fd5b80630175e23b146101ae57806312a24cbb146101d457806318160ddd146101e95780633e491d47146101f257806346c96aac1461020557806349dcc20414610244575b600080fd5b6101c16101bc366004611bd8565b6104a4565b6040519081526020015b60405180910390f35b6101e76101e2366004611c2f565b6104e6565b005b6101c160005481565b6101c1610200366004611d00565b610903565b61022c7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020016101cb565b610276610252366004611d2c565b60076020908152600092835260408084209091529082529020805460019091015482565b604080519283526020830191909152016101cb565b6102ae610299366004611d4e565b60066020526000908152604090205460ff1681565b60405190151581526020016101cb565b6101c16102cc366004611bd8565b60086020526000908152604090205481565b6101c16102ec366004611d4e565b610aa7565b6101c16102ff366004611bd8565b610acb565b61022c7f000000000000000000000000000000000000000000000000000000000000000081565b6101c1610339366004611d00565b600260209081526000928352604080842090915290825290205481565b6101e7610364366004611d6b565b610bff565b6101c1610377366004611d4e565b610d8b565b6101c161038a366004611bd8565b60016020526000908152604090205481565b6101e76103aa366004611d2c565b610dc4565b6101c16103bd366004611d2c565b610e94565b6101e76103d0366004611c2f565b610fd9565b6101e76103e3366004611d00565b6111d9565b6101c16103f6366004611d4e565b60036020526000908152604090205481565b6005546101c1565b6101c1600a5481565b6101c1610427366004611d00565b600460209081526000928352604080842090915290825290205481565b61022c610452366004611bd8565b611579565b6101e7610465366004611d2c565b6115a3565b6101e7610478366004611c2f565b61166b565b61027661048b366004611bd8565b6009602052600090815260409020805460019091015482565b6000806104b08361183f565b905060006104c162093a8083611dc3565b90508084106104dc576104d78262093a80611dc3565b6104de565b815b949350505050565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166385f2aef26040518163ffffffff1660e01b81526004016020604051808303816000875af1158015610546573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061056a9190611ddb565b6001600160a01b0316336001600160a01b0316146105bb5760405162461bcd60e51b81526020600482015260096024820152686f6e6c79207465616d60b81b60448201526064015b60405180910390fd5b60006105c6836104a4565b905060006009816105dd6102ff62093a8086611dc3565b8152602001908152602001600020604051806040016040529081600082015481526020016001820154815250509050610619816000015161183f565b61062662093a8084611dc3565b1115610675576020810151156106755760405162461bcd60e51b8152602060048201526014602482015273746869732065706f63682068617320766f74657360601b60448201526064016105b2565b825160005b818110156108fb5760006002600087848151811061069a5761069a611df8565b60200260200101516001600160a01b03166001600160a01b031681526020019081526020016000206000868152602001908152602001600020549050806000036107265760405162461bcd60e51b815260206004820152601860248201527f6e6f2062726962657320666f7220746869732065706f6368000000000000000060448201526064016105b2565b6000610731426104a4565b905060006002600089868151811061074b5761074b611df8565b60200260200101516001600160a01b03166001600160a01b03168152602001908152602001600020600083815260200190815260200160002054905082816107939190611dc3565b600260008a87815181106107a9576107a9611df8565b60200260200101516001600160a01b03166001600160a01b031681526020019081526020016000206000848152602001908152602001600020819055506000600260008a87815181106107fe576107fe611df8565b6020908102919091018101516001600160a01b0316825281810192909252604090810160009081208b825290925290205561083c62093a8083611dc3565b600360008a878151811061085257610852611df8565b60200260200101516001600160a01b03166001600160a01b031681526020019081526020016000208190555087848151811061089057610890611df8565b60200260200101516001600160a01b03167f2f0115ef57ea10e148aca7cdcd12459faf11fec4747cc945b56ad984611d23988884866040516108e5939291909283526020830191909152604082015260600190565b60405180910390a283600101935050505061067a565b505050505050565b600081815260086020526040812054810361092057506000610aa1565b6001600160a01b03831660009081526004602090815260408083208584529091528120548190600190829081906109569061183f565b90506109628782610e94565b600088815260076020908152604080832084845290915290205490925061099390829061098e9061183f565b611858565b9050600062093a80826109a54261183f565b6109af9190611e0e565b6109b99190611e3b565b90508015610a985760005b81811015610a96576109dd896103bd62093a8086611dc3565b60008a81526007602090815260408083208484529091528120600101549750909450600990610a126102ff62093a8087611dc3565b815260200190815260200160002060010154945084600014610a75576001600160a01b038a1660009081526002602090815260408083208684529091529020548590610a5e9088611e4f565b610a689190611e3b565b610a729088611dc3565b96505b610a8262093a8084611dc3565b925080610a8e81611e6e565b9150506109c4565b505b50939450505050505b92915050565b6001600160a01b038116600090815260036020526040812054610aa190429061186e565b600a54600090808203610ae15750600092915050565b8260096000610af1600185611e0e565b81526020019081526020016000206000015411610b1a57610b13600182611e0e565b9392505050565b6000805260096020527fec8156718a8372b1db44bb411437d0870f3e3790d4a08526d024ce1b0b668f6b54831015610b555750600092915050565b600080610b63600184611e0e565b90505b81811115610bf75760006002610b7c8484611e0e565b610b869190611e3b565b610b909083611e0e565b600081815260096020908152604091829020825180840190935280548084526001909101549183019190915291925090879003610bd1575095945050505050565b8051871115610be257819350610bf0565b610bed600183611e0e565b92505b5050610b66565b509392505050565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166385f2aef26040518163ffffffff1660e01b81526004016020604051808303816000875af1158015610c5f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c839190611ddb565b6001600160a01b0316336001600160a01b031614610ccf5760405162461bcd60e51b81526020600482015260096024820152686f6e6c79207465616d60b81b60448201526064016105b2565b816001600160a01b031660058481548110610cec57610cec611df8565b6000918252602090912001546001600160a01b031614610d0b57600080fd5b6001600160a01b03808316600090815260066020526040808220805460ff1990811690915592841682529020805490911660011790556005805482919085908110610d5857610d58611df8565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550505050565b600080610d97426104a4565b6001600160a01b039093166000908152600260209081526040808320958352949052929092205492915050565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614610df957600080fd5b81600080828254610e0a9190611e0e565b909155505060008181526001602052604081208054849290610e2d908490611e0e565b9091555050600081815260016020526040902054610e4c90829061187d565b610e54611956565b604080518281526020810184905233917ff279e6a1f5e320cca91135676d9cb6e44ca8a08c0b88342bcdb1144f6511b56891015b60405180910390a25050565b600082815260086020526040812054808203610eb4576000915050610aa1565b60008481526007602052604081208491610ecf600185611e0e565b81526020019081526020016000206000015411610ef957610ef1600182611e0e565b915050610aa1565b6000848152600760209081526040808320838052909152902054831015610f24576000915050610aa1565b600080610f32600184611e0e565b90505b81811115610fd05760006002610f4b8484611e0e565b610f559190611e3b565b610f5f9083611e0e565b6000888152600760209081526040808320848452825291829020825180840190935280548084526001909101549183019190915291925090879003610faa57509350610aa192505050565b8051871115610fbb57819350610fc9565b610fc6600183611e0e565b92505b5050610f35565b50949350505050565b600b54600114610fe857600080fd5b6002600b55336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161461102257600080fd5b6040516331a9108f60e11b8152600481018390526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690636352211e90602401602060405180830381865afa15801561108a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110ae9190611ddb565b905060005b82518110156111ce5760006110e18483815181106110d3576110d3611df8565b602002602001015186610903565b905042600460008685815181106110fa576110fa611df8565b6020908102919091018101516001600160a01b03168252818101929092526040908101600090812089825290925290205580156111555761115584838151811061114657611146611df8565b602002602001015184836119f9565b83828151811061116757611167611df8565b60200260200101516001600160a01b0316836001600160a01b03167f9aa05b3d70a9e3e2f004f039648839560576334fb45c81f91b6db03ad9e2efc9836040516111b391815260200190565b60405180910390a350806111c681611e6e565b9150506110b3565b50506001600b555050565b600b546001146111e857600080fd5b6002600b55806111f757600080fd5b6001600160a01b03821660009081526006602052604090205460ff1661133f57604051633af32abf60e01b81526001600160a01b0383811660048301527f00000000000000000000000000000000000000000000000000000000000000001690633af32abf90602401602060405180830381865afa15801561127d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112a19190611e87565b6112ed5760405162461bcd60e51b815260206004820181905260248201527f627269626520746f6b656e73206d7573742062652077686974656c697374656460448201526064016105b2565b60055460101161133f5760405162461bcd60e51b815260206004820152601760248201527f746f6f206d616e79207265776172647320746f6b656e7300000000000000000060448201526064016105b2565b600061134a426104a4565b6001600160a01b03841660008181526002602090815260408083208584529091528082205490516370a0823160e01b8152306004820152939450929091906370a0823190602401602060405180830381865afa1580156113ae573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113d29190611ea9565b90506113e085333087611ae8565b6040516370a0823160e01b81523060048201526000906001600160a01b038716906370a0823190602401602060405180830381865afa158015611427573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061144b9190611ea9565b90506114578282611e0e565b94506114638584611dc3565b6001600160a01b038716600090815260026020908152604080832088845290915290205561149462093a8085611dc3565b6001600160a01b03871660009081526003602090815260408083209390935560069052205460ff16611526576001600160a01b0386166000818152600660205260408120805460ff191660019081179091556005805491820181559091527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db00180546001600160a01b03191690911790555b60408051858152602081018790526001600160a01b0388169133917f52977ea98a2220a03ee9ba5cb003ada08d394ea10155483c95dc2dc77a7eb24b910160405180910390a350506001600b5550505050565b6005818154811061158957600080fd5b6000918252602090912001546001600160a01b0316905081565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146115d857600080fd5b816000808282546115e99190611dc3565b90915550506000818152600160205260408120805484929061160c908490611dc3565b909155505060008181526001602052604090205461162b90829061187d565b611633611956565b604080518281526020810184905233917f90890809c654f11d6e72a28fa60149770a0d11ec6c92319d6ceb2bb0a4ea1a159101610e88565b600b5460011461167a57600080fd5b6002600b5560405163430c208160e01b8152336004820152602481018390527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063430c208190604401602060405180830381865afa1580156116ea573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061170e9190611e87565b61171757600080fd5b60005b815181101561183557600061174883838151811061173a5761173a611df8565b602002602001015185610903565b9050426004600085858151811061176157611761611df8565b6020908102919091018101516001600160a01b03168252818101929092526040908101600090812088825290925290205580156117bc576117bc8383815181106117ad576117ad611df8565b602002602001015133836119f9565b8282815181106117ce576117ce611df8565b60200260200101516001600160a01b0316336001600160a01b03167f9aa05b3d70a9e3e2f004f039648839560576334fb45c81f91b6db03ad9e2efc98360405161181a91815260200190565b60405180910390a3508061182d81611e6e565b91505061171a565b50506001600b5550565b600061184e62093a8083611ec2565b610aa19083611e0e565b60008183116118675781610b13565b5090919050565b60008183106118675781610b13565b600082815260086020526040902054429080158015906118c75750600084815260076020526040812083916118b3600185611e0e565b815260200190815260200160002060000154145b1561190057600084815260076020526040812084916118e7600185611e0e565b8152602081019190915260400160002060010155611950565b6040805180820182528381526020808201868152600088815260078352848120868252909252929020905181559051600191820155611940908290611dc3565b6000858152600860205260409020555b50505050565b600a5442811580159061198857508060096000611974600186611e0e565b815260200190815260200160002060000154145b156119b957600054600960006001856119a19190611e0e565b81526020810191909152604001600020600101555050565b604080518082018252828152600080546020808401918252868352600990529290209051815590516001918201556119f2908390611dc3565b600a555050565b6000836001600160a01b03163b11611a1057600080fd5b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180516001600160e01b031663a9059cbb60e01b1790529151600092839290871691611a6c9190611ed6565b6000604051808303816000865af19150503d8060008114611aa9576040519150601f19603f3d011682016040523d82523d6000602084013e611aae565b606091505b5091509150818015611ad8575080511580611ad8575080806020019051810190611ad89190611e87565b611ae157600080fd5b5050505050565b6000846001600160a01b03163b11611aff57600080fd5b604080516001600160a01b0385811660248301528481166044830152606480830185905283518084039091018152608490920183526020820180516001600160e01b03166323b872dd60e01b1790529151600092839290881691611b639190611ed6565b6000604051808303816000865af19150503d8060008114611ba0576040519150601f19603f3d011682016040523d82523d6000602084013e611ba5565b606091505b5091509150818015611bcf575080511580611bcf575080806020019051810190611bcf9190611e87565b6108fb57600080fd5b600060208284031215611bea57600080fd5b5035919050565b634e487b7160e01b600052604160045260246000fd5b6001600160a01b0381168114611c1c57600080fd5b50565b8035611c2a81611c07565b919050565b60008060408385031215611c4257600080fd5b8235915060208084013567ffffffffffffffff80821115611c6257600080fd5b818601915086601f830112611c7657600080fd5b813581811115611c8857611c88611bf1565b8060051b604051601f19603f83011681018181108582111715611cad57611cad611bf1565b604052918252848201925083810185019189831115611ccb57600080fd5b938501935b82851015611cf057611ce185611c1f565b84529385019392850192611cd0565b8096505050505050509250929050565b60008060408385031215611d1357600080fd5b8235611d1e81611c07565b946020939093013593505050565b60008060408385031215611d3f57600080fd5b50508035926020909101359150565b600060208284031215611d6057600080fd5b8135610b1381611c07565b600080600060608486031215611d8057600080fd5b833592506020840135611d9281611c07565b91506040840135611da281611c07565b809150509250925092565b634e487b7160e01b600052601160045260246000fd5b60008219821115611dd657611dd6611dad565b500190565b600060208284031215611ded57600080fd5b8151610b1381611c07565b634e487b7160e01b600052603260045260246000fd5b600082821015611e2057611e20611dad565b500390565b634e487b7160e01b600052601260045260246000fd5b600082611e4a57611e4a611e25565b500490565b6000816000190483118215151615611e6957611e69611dad565b500290565b600060018201611e8057611e80611dad565b5060010190565b600060208284031215611e9957600080fd5b81518015158114610b1357600080fd5b600060208284031215611ebb57600080fd5b5051919050565b600082611ed157611ed1611e25565b500690565b6000825160005b81811015611ef75760208186018101518583015201611edd565b81811115611f06576000828501525b50919091019291505056fea26469706673582212204f7435a280bdffecbea0ee7ddb14df42181fe6e1a0d595cd66898d91f610d69c64736f6c634300080d0033a26469706673582212205fd11251b709b968b4cba7717a50ffe5b58492fca303e076bd8333e25a48216864736f6c634300080d0033

Deployed ByteCode

0x608060405234801561001057600080fd5b50600436106100365760003560e01c80636485f0c41461003b578063b0ad856f1461006a575b600080fd5b61004e610049366004610119565b61007d565b6040516001600160a01b03909116815260200160405180910390f35b60005461004e906001600160a01b031681565b6000338260405161008d906100da565b6100989291906101de565b604051809103906000f0801580156100b4573d6000803e3d6000fd5b50600080546001600160a01b0319166001600160a01b0392909216918217905592915050565b6122e98061023b83390190565b634e487b7160e01b600052604160045260246000fd5b80356001600160a01b038116811461011457600080fd5b919050565b6000602080838503121561012c57600080fd5b823567ffffffffffffffff8082111561014457600080fd5b818501915085601f83011261015857600080fd5b81358181111561016a5761016a6100e7565b8060051b604051601f19603f8301168101818110858211171561018f5761018f6100e7565b6040529182528482019250838101850191888311156101ad57600080fd5b938501935b828510156101d2576101c3856100fd565b845293850193928501926101b2565b98975050505050505050565b6001600160a01b038381168252604060208084018290528451918401829052600092858201929091906060860190855b8181101561022c57855185168352948301949183019160010161020e565b50909897505050505050505056fe60c06040526001600b553480156200001657600080fd5b50604051620022e9380380620022e98339810160408190526200003991620001f8565b6001600160a01b038216608081905260408051638dd598fb60e01b81529051638dd598fb916004808201926020929091908290030181865afa15801562000084573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620000aa9190620002e1565b6001600160a01b031660a05260005b8151811015620001bc5760006001600160a01b0316828281518110620000e357620000e362000306565b60200260200101516001600160a01b031614620001a75760016006600084848151811062000115576200011562000306565b60200260200101516001600160a01b03166001600160a01b0316815260200190815260200160002060006101000a81548160ff02191690831515021790555060058282815181106200016b576200016b62000306565b60209081029190910181015182546001810184556000938452919092200180546001600160a01b0319166001600160a01b039092169190911790555b80620001b3816200031c565b915050620000b9565b50505062000344565b80516001600160a01b0381168114620001dd57600080fd5b919050565b634e487b7160e01b600052604160045260246000fd5b600080604083850312156200020c57600080fd5b6200021783620001c5565b602084810151919350906001600160401b03808211156200023757600080fd5b818601915086601f8301126200024c57600080fd5b815181811115620002615762000261620001e2565b8060051b604051601f19603f83011681018181108582111715620002895762000289620001e2565b604052918252848201925083810185019189831115620002a857600080fd5b938501935b82851015620002d157620002c185620001c5565b84529385019392850192620002ad565b8096505050505050509250929050565b600060208284031215620002f457600080fd5b620002ff82620001c5565b9392505050565b634e487b7160e01b600052603260045260246000fd5b6000600182016200033d57634e487b7160e01b600052601160045260246000fd5b5060010190565b60805160a051611f47620003a260003960008181610309015281816104e801528181610c010152818161103b015261169b01526000818161020a01528181610dcf01528181610ff80152818161123601526115ae0152611f476000f3fe608060405234801561001057600080fd5b50600436106101a95760003560e01c806399bcc052116100f9578063e688639611610097578063f301af4211610071578063f301af4214610444578063f320772314610457578063f5f8d3651461046a578063f7412baf1461047d57600080fd5b8063e688639614610408578063e8111a1214610410578063f25e55a51461041957600080fd5b8063a28d4c9c116100d3578063a28d4c9c146103af578063a7852afa146103c2578063b66503cf146103d5578063da09d19d146103e857600080fd5b806399bcc052146103695780639cc7f7081461037c5780639e2bf22c1461039c57600080fd5b80634d5ce0381161016657806376f4be361161014057806376f4be36146102f15780638dd598fb1461030457806392777b291461032b5780639418f9391461035657600080fd5b80634d5ce0381461028b57806350589793146102be578063638634ee146102de57600080fd5b80630175e23b146101ae57806312a24cbb146101d457806318160ddd146101e95780633e491d47146101f257806346c96aac1461020557806349dcc20414610244575b600080fd5b6101c16101bc366004611bd8565b6104a4565b6040519081526020015b60405180910390f35b6101e76101e2366004611c2f565b6104e6565b005b6101c160005481565b6101c1610200366004611d00565b610903565b61022c7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020016101cb565b610276610252366004611d2c565b60076020908152600092835260408084209091529082529020805460019091015482565b604080519283526020830191909152016101cb565b6102ae610299366004611d4e565b60066020526000908152604090205460ff1681565b60405190151581526020016101cb565b6101c16102cc366004611bd8565b60086020526000908152604090205481565b6101c16102ec366004611d4e565b610aa7565b6101c16102ff366004611bd8565b610acb565b61022c7f000000000000000000000000000000000000000000000000000000000000000081565b6101c1610339366004611d00565b600260209081526000928352604080842090915290825290205481565b6101e7610364366004611d6b565b610bff565b6101c1610377366004611d4e565b610d8b565b6101c161038a366004611bd8565b60016020526000908152604090205481565b6101e76103aa366004611d2c565b610dc4565b6101c16103bd366004611d2c565b610e94565b6101e76103d0366004611c2f565b610fd9565b6101e76103e3366004611d00565b6111d9565b6101c16103f6366004611d4e565b60036020526000908152604090205481565b6005546101c1565b6101c1600a5481565b6101c1610427366004611d00565b600460209081526000928352604080842090915290825290205481565b61022c610452366004611bd8565b611579565b6101e7610465366004611d2c565b6115a3565b6101e7610478366004611c2f565b61166b565b61027661048b366004611bd8565b6009602052600090815260409020805460019091015482565b6000806104b08361183f565b905060006104c162093a8083611dc3565b90508084106104dc576104d78262093a80611dc3565b6104de565b815b949350505050565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166385f2aef26040518163ffffffff1660e01b81526004016020604051808303816000875af1158015610546573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061056a9190611ddb565b6001600160a01b0316336001600160a01b0316146105bb5760405162461bcd60e51b81526020600482015260096024820152686f6e6c79207465616d60b81b60448201526064015b60405180910390fd5b60006105c6836104a4565b905060006009816105dd6102ff62093a8086611dc3565b8152602001908152602001600020604051806040016040529081600082015481526020016001820154815250509050610619816000015161183f565b61062662093a8084611dc3565b1115610675576020810151156106755760405162461bcd60e51b8152602060048201526014602482015273746869732065706f63682068617320766f74657360601b60448201526064016105b2565b825160005b818110156108fb5760006002600087848151811061069a5761069a611df8565b60200260200101516001600160a01b03166001600160a01b031681526020019081526020016000206000868152602001908152602001600020549050806000036107265760405162461bcd60e51b815260206004820152601860248201527f6e6f2062726962657320666f7220746869732065706f6368000000000000000060448201526064016105b2565b6000610731426104a4565b905060006002600089868151811061074b5761074b611df8565b60200260200101516001600160a01b03166001600160a01b03168152602001908152602001600020600083815260200190815260200160002054905082816107939190611dc3565b600260008a87815181106107a9576107a9611df8565b60200260200101516001600160a01b03166001600160a01b031681526020019081526020016000206000848152602001908152602001600020819055506000600260008a87815181106107fe576107fe611df8565b6020908102919091018101516001600160a01b0316825281810192909252604090810160009081208b825290925290205561083c62093a8083611dc3565b600360008a878151811061085257610852611df8565b60200260200101516001600160a01b03166001600160a01b031681526020019081526020016000208190555087848151811061089057610890611df8565b60200260200101516001600160a01b03167f2f0115ef57ea10e148aca7cdcd12459faf11fec4747cc945b56ad984611d23988884866040516108e5939291909283526020830191909152604082015260600190565b60405180910390a283600101935050505061067a565b505050505050565b600081815260086020526040812054810361092057506000610aa1565b6001600160a01b03831660009081526004602090815260408083208584529091528120548190600190829081906109569061183f565b90506109628782610e94565b600088815260076020908152604080832084845290915290205490925061099390829061098e9061183f565b611858565b9050600062093a80826109a54261183f565b6109af9190611e0e565b6109b99190611e3b565b90508015610a985760005b81811015610a96576109dd896103bd62093a8086611dc3565b60008a81526007602090815260408083208484529091528120600101549750909450600990610a126102ff62093a8087611dc3565b815260200190815260200160002060010154945084600014610a75576001600160a01b038a1660009081526002602090815260408083208684529091529020548590610a5e9088611e4f565b610a689190611e3b565b610a729088611dc3565b96505b610a8262093a8084611dc3565b925080610a8e81611e6e565b9150506109c4565b505b50939450505050505b92915050565b6001600160a01b038116600090815260036020526040812054610aa190429061186e565b600a54600090808203610ae15750600092915050565b8260096000610af1600185611e0e565b81526020019081526020016000206000015411610b1a57610b13600182611e0e565b9392505050565b6000805260096020527fec8156718a8372b1db44bb411437d0870f3e3790d4a08526d024ce1b0b668f6b54831015610b555750600092915050565b600080610b63600184611e0e565b90505b81811115610bf75760006002610b7c8484611e0e565b610b869190611e3b565b610b909083611e0e565b600081815260096020908152604091829020825180840190935280548084526001909101549183019190915291925090879003610bd1575095945050505050565b8051871115610be257819350610bf0565b610bed600183611e0e565b92505b5050610b66565b509392505050565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166385f2aef26040518163ffffffff1660e01b81526004016020604051808303816000875af1158015610c5f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c839190611ddb565b6001600160a01b0316336001600160a01b031614610ccf5760405162461bcd60e51b81526020600482015260096024820152686f6e6c79207465616d60b81b60448201526064016105b2565b816001600160a01b031660058481548110610cec57610cec611df8565b6000918252602090912001546001600160a01b031614610d0b57600080fd5b6001600160a01b03808316600090815260066020526040808220805460ff1990811690915592841682529020805490911660011790556005805482919085908110610d5857610d58611df8565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550505050565b600080610d97426104a4565b6001600160a01b039093166000908152600260209081526040808320958352949052929092205492915050565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614610df957600080fd5b81600080828254610e0a9190611e0e565b909155505060008181526001602052604081208054849290610e2d908490611e0e565b9091555050600081815260016020526040902054610e4c90829061187d565b610e54611956565b604080518281526020810184905233917ff279e6a1f5e320cca91135676d9cb6e44ca8a08c0b88342bcdb1144f6511b56891015b60405180910390a25050565b600082815260086020526040812054808203610eb4576000915050610aa1565b60008481526007602052604081208491610ecf600185611e0e565b81526020019081526020016000206000015411610ef957610ef1600182611e0e565b915050610aa1565b6000848152600760209081526040808320838052909152902054831015610f24576000915050610aa1565b600080610f32600184611e0e565b90505b81811115610fd05760006002610f4b8484611e0e565b610f559190611e3b565b610f5f9083611e0e565b6000888152600760209081526040808320848452825291829020825180840190935280548084526001909101549183019190915291925090879003610faa57509350610aa192505050565b8051871115610fbb57819350610fc9565b610fc6600183611e0e565b92505b5050610f35565b50949350505050565b600b54600114610fe857600080fd5b6002600b55336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161461102257600080fd5b6040516331a9108f60e11b8152600481018390526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690636352211e90602401602060405180830381865afa15801561108a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110ae9190611ddb565b905060005b82518110156111ce5760006110e18483815181106110d3576110d3611df8565b602002602001015186610903565b905042600460008685815181106110fa576110fa611df8565b6020908102919091018101516001600160a01b03168252818101929092526040908101600090812089825290925290205580156111555761115584838151811061114657611146611df8565b602002602001015184836119f9565b83828151811061116757611167611df8565b60200260200101516001600160a01b0316836001600160a01b03167f9aa05b3d70a9e3e2f004f039648839560576334fb45c81f91b6db03ad9e2efc9836040516111b391815260200190565b60405180910390a350806111c681611e6e565b9150506110b3565b50506001600b555050565b600b546001146111e857600080fd5b6002600b55806111f757600080fd5b6001600160a01b03821660009081526006602052604090205460ff1661133f57604051633af32abf60e01b81526001600160a01b0383811660048301527f00000000000000000000000000000000000000000000000000000000000000001690633af32abf90602401602060405180830381865afa15801561127d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112a19190611e87565b6112ed5760405162461bcd60e51b815260206004820181905260248201527f627269626520746f6b656e73206d7573742062652077686974656c697374656460448201526064016105b2565b60055460101161133f5760405162461bcd60e51b815260206004820152601760248201527f746f6f206d616e79207265776172647320746f6b656e7300000000000000000060448201526064016105b2565b600061134a426104a4565b6001600160a01b03841660008181526002602090815260408083208584529091528082205490516370a0823160e01b8152306004820152939450929091906370a0823190602401602060405180830381865afa1580156113ae573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113d29190611ea9565b90506113e085333087611ae8565b6040516370a0823160e01b81523060048201526000906001600160a01b038716906370a0823190602401602060405180830381865afa158015611427573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061144b9190611ea9565b90506114578282611e0e565b94506114638584611dc3565b6001600160a01b038716600090815260026020908152604080832088845290915290205561149462093a8085611dc3565b6001600160a01b03871660009081526003602090815260408083209390935560069052205460ff16611526576001600160a01b0386166000818152600660205260408120805460ff191660019081179091556005805491820181559091527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db00180546001600160a01b03191690911790555b60408051858152602081018790526001600160a01b0388169133917f52977ea98a2220a03ee9ba5cb003ada08d394ea10155483c95dc2dc77a7eb24b910160405180910390a350506001600b5550505050565b6005818154811061158957600080fd5b6000918252602090912001546001600160a01b0316905081565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146115d857600080fd5b816000808282546115e99190611dc3565b90915550506000818152600160205260408120805484929061160c908490611dc3565b909155505060008181526001602052604090205461162b90829061187d565b611633611956565b604080518281526020810184905233917f90890809c654f11d6e72a28fa60149770a0d11ec6c92319d6ceb2bb0a4ea1a159101610e88565b600b5460011461167a57600080fd5b6002600b5560405163430c208160e01b8152336004820152602481018390527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063430c208190604401602060405180830381865afa1580156116ea573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061170e9190611e87565b61171757600080fd5b60005b815181101561183557600061174883838151811061173a5761173a611df8565b602002602001015185610903565b9050426004600085858151811061176157611761611df8565b6020908102919091018101516001600160a01b03168252818101929092526040908101600090812088825290925290205580156117bc576117bc8383815181106117ad576117ad611df8565b602002602001015133836119f9565b8282815181106117ce576117ce611df8565b60200260200101516001600160a01b0316336001600160a01b03167f9aa05b3d70a9e3e2f004f039648839560576334fb45c81f91b6db03ad9e2efc98360405161181a91815260200190565b60405180910390a3508061182d81611e6e565b91505061171a565b50506001600b5550565b600061184e62093a8083611ec2565b610aa19083611e0e565b60008183116118675781610b13565b5090919050565b60008183106118675781610b13565b600082815260086020526040902054429080158015906118c75750600084815260076020526040812083916118b3600185611e0e565b815260200190815260200160002060000154145b1561190057600084815260076020526040812084916118e7600185611e0e565b8152602081019190915260400160002060010155611950565b6040805180820182528381526020808201868152600088815260078352848120868252909252929020905181559051600191820155611940908290611dc3565b6000858152600860205260409020555b50505050565b600a5442811580159061198857508060096000611974600186611e0e565b815260200190815260200160002060000154145b156119b957600054600960006001856119a19190611e0e565b81526020810191909152604001600020600101555050565b604080518082018252828152600080546020808401918252868352600990529290209051815590516001918201556119f2908390611dc3565b600a555050565b6000836001600160a01b03163b11611a1057600080fd5b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180516001600160e01b031663a9059cbb60e01b1790529151600092839290871691611a6c9190611ed6565b6000604051808303816000865af19150503d8060008114611aa9576040519150601f19603f3d011682016040523d82523d6000602084013e611aae565b606091505b5091509150818015611ad8575080511580611ad8575080806020019051810190611ad89190611e87565b611ae157600080fd5b5050505050565b6000846001600160a01b03163b11611aff57600080fd5b604080516001600160a01b0385811660248301528481166044830152606480830185905283518084039091018152608490920183526020820180516001600160e01b03166323b872dd60e01b1790529151600092839290881691611b639190611ed6565b6000604051808303816000865af19150503d8060008114611ba0576040519150601f19603f3d011682016040523d82523d6000602084013e611ba5565b606091505b5091509150818015611bcf575080511580611bcf575080806020019051810190611bcf9190611e87565b6108fb57600080fd5b600060208284031215611bea57600080fd5b5035919050565b634e487b7160e01b600052604160045260246000fd5b6001600160a01b0381168114611c1c57600080fd5b50565b8035611c2a81611c07565b919050565b60008060408385031215611c4257600080fd5b8235915060208084013567ffffffffffffffff80821115611c6257600080fd5b818601915086601f830112611c7657600080fd5b813581811115611c8857611c88611bf1565b8060051b604051601f19603f83011681018181108582111715611cad57611cad611bf1565b604052918252848201925083810185019189831115611ccb57600080fd5b938501935b82851015611cf057611ce185611c1f565b84529385019392850192611cd0565b8096505050505050509250929050565b60008060408385031215611d1357600080fd5b8235611d1e81611c07565b946020939093013593505050565b60008060408385031215611d3f57600080fd5b50508035926020909101359150565b600060208284031215611d6057600080fd5b8135610b1381611c07565b600080600060608486031215611d8057600080fd5b833592506020840135611d9281611c07565b91506040840135611da281611c07565b809150509250925092565b634e487b7160e01b600052601160045260246000fd5b60008219821115611dd657611dd6611dad565b500190565b600060208284031215611ded57600080fd5b8151610b1381611c07565b634e487b7160e01b600052603260045260246000fd5b600082821015611e2057611e20611dad565b500390565b634e487b7160e01b600052601260045260246000fd5b600082611e4a57611e4a611e25565b500490565b6000816000190483118215151615611e6957611e69611dad565b500290565b600060018201611e8057611e80611dad565b5060010190565b600060208284031215611e9957600080fd5b81518015158114610b1357600080fd5b600060208284031215611ebb57600080fd5b5051919050565b600082611ed157611ed1611e25565b500690565b6000825160005b81811015611ef75760208186018101518583015201611edd565b81811115611f06576000828501525b50919091019291505056fea26469706673582212204f7435a280bdffecbea0ee7ddb14df42181fe6e1a0d595cd66898d91f610d69c64736f6c634300080d0033a26469706673582212205fd11251b709b968b4cba7717a50ffe5b58492fca303e076bd8333e25a48216864736f6c634300080d0033