S Price: $0.661023 (-11.61%)
    /

    Contract Diff Checker

    Contract Name:
    UnifiedBribe

    Contract Source Code:

    File 1 of 1 : UnifiedBribe

    /**
     *Submitted for verification at SonicScan.org on 2024-12-18
    */
    
    /**
     *Submitted for verification at basescan.org on 2023-09-19
    */
    
    /**v1.0.0
     *0x8868ae6De5e723e6840Cdc21370e415bF5123684
     *Submitted for verification at FtmScan.com on 2022-11-04
    */
    
    
    
    /**
     *  EQUALIZER EXCHANGE
     *  The New Liquidity Hub of Fantom chain!
     *  https://equalizer.exchange  (Dapp)
     *  https://discord.gg/MaMhbgHMby   (Community)
     *
     *
     *  Version: 1.4.0
     *  - Remove the whole concept of Internal Bribes (Trade Fees Streamer), Unify.
     *  - Prevent breaking of rewards in earned() from reading 0-supply checkpoints
     *  - Voter.team() can siphon out unclaimed rewards.
     *    - This contract does not take or allow deposits of any user funds.
     *    - Only the Bribes can be rescue()'d.
     *
     *
     *  Contributors:
     *   -   Andre Cronje, Solidly.Exchange
     *   -   Team, Velodrome.finance
     *   -   @smartcoding51, Equalizer.exchange
     *   -   543#3017 (Sam), ftm.guru & Equalizer.exchange
     *
     *
     *	SPDX-License-Identifier: UNLICENSED
    */
    
    
    
    // File: contracts/interfaces/IGauge.sol
    
    
    pragma solidity 0.8.9;
    
    interface IGauge {
        function notifyRewardAmount(address token, uint amount) external;
        function getReward(address account, address[] memory tokens) external;
        function claimFees() external returns (uint claimed0, uint claimed1);
        function left(address token) external view returns (uint);
        function isForPair() external view returns (bool);
    }
    
    // File: contracts/interfaces/IVotingEscrow.sol
    
    
    pragma solidity 0.8.9;
    
    interface IVotingEscrow {
    
        struct Point {
            int128 bias;
            int128 slope; // # -dweight / dt
            uint256 ts;
            uint256 blk; // block
        }
    
        function team() external returns (address);
        function epoch() external view returns (uint);
        function token() external view returns (address);
        function name() external view returns (string memory);
        function symbol() external view returns (string memory);
        function decimals() 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 balanceOf(address) external view returns (uint);
        function balanceOfNFT(uint) external view returns (uint);
        function totalSupply() external view returns (uint);
        function tokenOfOwnerByIndex(address _owner, uint _tokenIndex) external view returns (uint);
    }
    
    // File: contracts/interfaces/IVoter.sol
    
    
    pragma solidity 0.8.9;
    
    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;
    }
    // File: contracts/interfaces/IERC20.sol
    
    
    pragma solidity 0.8.9;
    
    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);
    }
    
    // File: contracts/libraries/Math.sol
    
    
    pragma solidity 0.8.9;
    
    library Math {
        function max(uint a, uint b) internal pure returns (uint) {
            return a >= b ? a : b;
        }
        function min(uint a, uint b) internal pure returns (uint) {
            return a < b ? a : b;
        }
        function sqrt(uint y) internal pure returns (uint z) {
            if (y > 3) {
                z = y;
                uint x = y / 2 + 1;
                while (x < z) {
                    z = x;
                    x = (y / x + x) / 2;
                }
            } else if (y != 0) {
                z = 1;
            }
        }
        function cbrt(uint256 n) internal pure returns (uint256) { unchecked {
            uint256 x = 0;
            for (uint256 y = 1 << 255; y > 0; y >>= 3) {
                x <<= 1;
                uint256 z = 3 * x * (x + 1) + 1;
                if (n / y >= z) {
                    n -= y * z;
                    x += 1;
                }
            }
            return x;
        }}
    }
    
    // File: @openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol
    
    
    // OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol)
    
    pragma solidity ^0.8.1;
    
    /**
     * @dev Collection of functions related to the address type
     */
    library AddressUpgradeable {
        /**
         * @dev Returns true if `account` is a contract.
         *
         * [IMPORTANT]
         * ====
         * It is unsafe to assume that an address for which this function returns
         * false is an externally-owned account (EOA) and not a contract.
         *
         * Among others, `isContract` will return false for the following
         * types of addresses:
         *
         *  - an externally-owned account
         *  - a contract in construction
         *  - an address where a contract will be created
         *  - an address where a contract lived, but was destroyed
         * ====
         *
         * [IMPORTANT]
         * ====
         * You shouldn't rely on `isContract` to protect against flash loan attacks!
         *
         * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
         * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
         * constructor.
         * ====
         */
        function isContract(address account) internal view returns (bool) {
            // This method relies on extcodesize/address.code.length, which returns 0
            // for contracts in construction, since the code is only stored at the end
            // of the constructor execution.
    
            return account.code.length > 0;
        }
    
        /**
         * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
         * `recipient`, forwarding all available gas and reverting on errors.
         *
         * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
         * of certain opcodes, possibly making contracts go over the 2300 gas limit
         * imposed by `transfer`, making them unable to receive funds via
         * `transfer`. {sendValue} removes this limitation.
         *
         * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
         *
         * IMPORTANT: because control is transferred to `recipient`, care must be
         * taken to not create reentrancy vulnerabilities. Consider using
         * {ReentrancyGuard} or the
         * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
         */
        function sendValue(address payable recipient, uint256 amount) internal {
            require(address(this).balance >= amount, "Address: insufficient balance");
    
            (bool success, ) = recipient.call{value: amount}("");
            require(success, "Address: unable to send value, recipient may have reverted");
        }
    
        /**
         * @dev Performs a Solidity function call using a low level `call`. A
         * plain `call` is an unsafe replacement for a function call: use this
         * function instead.
         *
         * If `target` reverts with a revert reason, it is bubbled up by this
         * function (like regular Solidity function calls).
         *
         * Returns the raw returned data. To convert to the expected return value,
         * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
         *
         * Requirements:
         *
         * - `target` must be a contract.
         * - calling `target` with `data` must not revert.
         *
         * _Available since v3.1._
         */
        function functionCall(address target, bytes memory data) internal returns (bytes memory) {
            return functionCall(target, data, "Address: low-level call failed");
        }
    
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
         * `errorMessage` as a fallback revert reason when `target` reverts.
         *
         * _Available since v3.1._
         */
        function functionCall(
            address target,
            bytes memory data,
            string memory errorMessage
        ) internal returns (bytes memory) {
            return functionCallWithValue(target, data, 0, errorMessage);
        }
    
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
         * but also transferring `value` wei to `target`.
         *
         * Requirements:
         *
         * - the calling contract must have an ETH balance of at least `value`.
         * - the called Solidity function must be `payable`.
         *
         * _Available since v3.1._
         */
        function functionCallWithValue(
            address target,
            bytes memory data,
            uint256 value
        ) internal returns (bytes memory) {
            return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
        }
    
        /**
         * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
         * with `errorMessage` as a fallback revert reason when `target` reverts.
         *
         * _Available since v3.1._
         */
        function functionCallWithValue(
            address target,
            bytes memory data,
            uint256 value,
            string memory errorMessage
        ) internal returns (bytes memory) {
            require(address(this).balance >= value, "Address: insufficient balance for call");
            require(isContract(target), "Address: call to non-contract");
    
            (bool success, bytes memory returndata) = target.call{value: value}(data);
            return verifyCallResult(success, returndata, errorMessage);
        }
    
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
         * but performing a static call.
         *
         * _Available since v3.3._
         */
        function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
            return functionStaticCall(target, data, "Address: low-level static call failed");
        }
    
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
         * but performing a static call.
         *
         * _Available since v3.3._
         */
        function functionStaticCall(
            address target,
            bytes memory data,
            string memory errorMessage
        ) internal view returns (bytes memory) {
            require(isContract(target), "Address: static call to non-contract");
    
            (bool success, bytes memory returndata) = target.staticcall(data);
            return verifyCallResult(success, returndata, errorMessage);
        }
    
        /**
         * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
         * revert reason using the provided one.
         *
         * _Available since v4.3._
         */
        function verifyCallResult(
            bool success,
            bytes memory returndata,
            string memory errorMessage
        ) internal pure returns (bytes memory) {
            if (success) {
                return returndata;
            } else {
                // Look for revert reason and bubble it up if present
                if (returndata.length > 0) {
                    // The easiest way to bubble the revert reason is using memory via assembly
                    /// @solidity memory-safe-assembly
                    assembly {
                        let returndata_size := mload(returndata)
                        revert(add(32, returndata), returndata_size)
                    }
                } else {
                    revert(errorMessage);
                }
            }
        }
    }
    
    // File: @openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol
    
    
    // OpenZeppelin Contracts (last updated v4.7.0) (proxy/utils/Initializable.sol)
    
    pragma solidity ^0.8.2;
    
    
    /**
     * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
     * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
     * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
     * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
     *
     * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
     * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
     * case an upgrade adds a module that needs to be initialized.
     *
     * For example:
     *
     * [.hljs-theme-light.nopadding]
     * ```
     * contract MyToken is ERC20Upgradeable {
     *     function initialize() initializer public {
     *         __ERC20_init("MyToken", "MTK");
     *     }
     * }
     * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
     *     function initializeV2() reinitializer(2) public {
     *         __ERC20Permit_init("MyToken");
     *     }
     * }
     * ```
     *
     * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
     * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
     *
     * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
     * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
     *
     * [CAUTION]
     * ====
     * Avoid leaving a contract uninitialized.
     *
     * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
     * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
     * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
     *
     * [.hljs-theme-light.nopadding]
     * ```
     * /// @custom:oz-upgrades-unsafe-allow constructor
     * constructor() {
     *     _disableInitializers();
     * }
     * ```
     * ====
     */
    abstract contract Initializable {
        /**
         * @dev Indicates that the contract has been initialized.
         * @custom:oz-retyped-from bool
         */
        uint8 private _initialized;
    
        /**
         * @dev Indicates that the contract is in the process of being initialized.
         */
        bool private _initializing;
    
        /**
         * @dev Triggered when the contract has been initialized or reinitialized.
         */
        event Initialized(uint8 version);
    
        /**
         * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
         * `onlyInitializing` functions can be used to initialize parent contracts. Equivalent to `reinitializer(1)`.
         */
        modifier initializer() {
            bool isTopLevelCall = !_initializing;
            require(
                (isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1),
                "Initializable: contract is already initialized"
            );
            _initialized = 1;
            if (isTopLevelCall) {
                _initializing = true;
            }
            _;
            if (isTopLevelCall) {
                _initializing = false;
                emit Initialized(1);
            }
        }
    
        /**
         * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
         * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
         * used to initialize parent contracts.
         *
         * `initializer` is equivalent to `reinitializer(1)`, so a reinitializer may be used after the original
         * initialization step. This is essential to configure modules that are added through upgrades and that require
         * initialization.
         *
         * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
         * a contract, executing them in the right order is up to the developer or operator.
         */
        modifier reinitializer(uint8 version) {
            require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
            _initialized = version;
            _initializing = true;
            _;
            _initializing = false;
            emit Initialized(version);
        }
    
        /**
         * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
         * {initializer} and {reinitializer} modifiers, directly or indirectly.
         */
        modifier onlyInitializing() {
            require(_initializing, "Initializable: contract is not initializing");
            _;
        }
    
        /**
         * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
         * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
         * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
         * through proxies.
         */
        function _disableInitializers() internal virtual {
            require(!_initializing, "Initializable: contract is initializing");
            if (_initialized < type(uint8).max) {
                _initialized = type(uint8).max;
                emit Initialized(type(uint8).max);
            }
        }
    }
    
    // File: contracts/Bribe.sol
    
    
    pragma solidity 0.8.9;
    
    
    
    
    
    
    
    /**
     * @notice 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 UnifiedBribe is Initializable {
    
        /// @notice A checkpoint for marking balance
        struct Checkpoint {
            uint timestamp;
            uint balanceOf;
        }
    
        /// @notice A checkpoint for marking supply
        struct SupplyCheckpoint {
            uint timestamp;
            uint supply;
        }
    
        address public voter; // only voter can modify balances (since it only happens on vote())
        address public _ve;
    
        uint public constant DURATION = 7 days; // rewards are released over the voting period
    
        uint internal constant MAX_REWARD_TOKENS = 8;
    
        uint internal constant PRECISION = 10 ** 18;
    
        uint public totalSupply;
        mapping(uint => uint) public balanceOfId;
    
        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 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;
        /// @notice simple re-entrancy check
        bool internal _locked;
    
        /// Helper vars for analytics, read-only
        mapping(address => uint) public payouts;	/// token -> amountSinceGenesis
        mapping(address => uint) public payoutHistory;	/// token -> amountSinceGenesis
        mapping(address => mapping(address => uint)) public earnings;	/// user -> token -> amountSinceGenesis
        mapping(uint => mapping(address => uint)) public rewardHistory;	/// epoch -> token -> amountDuringThatEpoch
    
    
    
        event Transfer(address indexed from, address indexed to, uint value);
        event Deposit(address indexed voter, address indexed owner, uint indexed tokenId, uint amount);
        event Withdraw(address indexed voter, address indexed owner, uint indexed tokenId, uint amount);
        event NotifyReward(address indexed from, address indexed reward, uint indexed epoch, uint amount);
        event ClaimRewards(address indexed from, address indexed reward, uint indexed tokenId, uint amount);
        event ClaimRewardsForDelegate(address indexed owner, address indexed claimer, address indexed reward, uint tokenId, uint amount);
    
        modifier lock() {
            require(!_locked,  "No re-entrancy");
            _locked = true;
            _;
            _locked = false;
        }
    
        function initialize(address _voter, address[] memory _allowedRewardTokens) public initializer {
            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]);
                }
            }
        }
    
        function _bribeStart(uint timestamp) internal pure returns (uint) {
            return timestamp - (timestamp % (DURATION));
        }
    
        function getEpochStart(uint timestamp) public pure returns (uint) {
            uint bribeStart = _bribeStart(timestamp);
            uint bribeEnd = bribeStart + DURATION;
            return timestamp < bribeEnd ? bribeStart : bribeStart + DURATION;
        }
    
        /**
        * @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 name() external view returns (string memory) {
            return string(abi.encodePacked(IVotingEscrow(_ve).name()," Votes",unicode" 🗳️"));
        }
    
        function symbol() external view returns (string memory) {
            return string(abi.encodePacked(IVotingEscrow(_ve).symbol(),".votes"));
        }
    
        function decimals() external view returns (uint256) {
            return IVotingEscrow(_ve).decimals();
        }
    
        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), "Neither approved nor owner");
            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], msg.sender, _reward);
    
                emit ClaimRewards(_owner, tokens[i], tokenId, _reward);
                payouts[tokens[i]] += _reward;
                earnings[_owner][tokens[i]] += _reward;
    
                if(_owner != msg.sender) {
                    emit ClaimRewardsForDelegate(_owner, msg.sender, tokens[i], tokenId, _reward);
                }
            }
        }
    
        // used by Voter to allow batched reward claims
        function getRewardForOwner(uint tokenId, address[] memory tokens) external lock  {
            require(msg.sender == voter, "Not 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], tokenId, _reward);
                payouts[tokens[i]] += _reward;
                earnings[_owner][tokens[i]] += _reward;
            }
        }
    
        function earned(address token, uint tokenId) public view returns (uint) {
            if (numCheckpoints[tokenId] == 0) {
                return 0;
            }
    
            uint reward = 0;
            uint _ts = 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);
            _ts = checkpoints[tokenId][_index].timestamp;
            _bal = checkpoints[tokenId][_index].balanceOf;
            // accounts for case where lastEarn is before first checkpoint
            _currTs = Math.max(_currTs, _bribeStart(_ts));
    
            // 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
                    _ts = checkpoints[tokenId][_index].timestamp;
                    _bal = checkpoints[tokenId][_index].balanceOf;
                    // get supply of last checkpoint in this epoch
                    _supply = supplyCheckpoints[getPriorSupplyIndex(_currTs + DURATION)].supply;
                    /// Bribes but no voters? Let admin decide, via rescue.
                    /// Previously this bricked claims for all (division by Zero)
                    if (_supply > 0) {
                        reward += _bal * tokenRewardsPerEpoch[token][_currTs] / _supply;
                    }
                    _currTs += DURATION;
                }
            }
    
            return reward;
        }
    
        // Total User Votes
        function balanceOf(uint _vid) external view returns(uint _bal) {
        	_bal = balanceOfId[_vid];
        }
    
        // Total User Votes
        function balanceOf(address _usr) external view returns(uint _bal) {
        	IVotingEscrow _VE = IVotingEscrow(_ve);
        	uint _n = _VE.balanceOf(_usr);
        	for(uint i;i<_n;i++) {
        		_bal += balanceOfId[_VE.tokenOfOwnerByIndex(_usr, i)];
        	}
        }
    
        // 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, address _vtr, address _onr) external {
            require(msg.sender == voter, "Not voter");
            require(amount > 0, "Zero amount");
    
            totalSupply += amount;
            balanceOfId[tokenId] += amount;
    
            _writeCheckpoint(tokenId, balanceOfId[tokenId]);
            _writeSupplyCheckpoint();
    
            emit Deposit(_vtr, _onr, tokenId, amount);
            emit Transfer(address(0), _onr, amount);
        }
    
        function _withdraw(uint amount, uint tokenId, address _vtr, address _onr) external {
            require(msg.sender == voter, "Not voter");
    
            totalSupply -= amount;
            balanceOfId[tokenId] -= amount;
    
            _writeCheckpoint(tokenId, balanceOfId[tokenId]);
            _writeSupplyCheckpoint();
    
            emit Withdraw(_vtr, _onr, tokenId, amount);
            emit Transfer(_onr, address(0), 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 {
            if (!isReward[token] && !(IVotingEscrow(_ve).team()==msg.sender)) {
              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];
    
            uint rtbb = IERC20(token).balanceOf(address(this));
            _safeTransferFrom(token, msg.sender, address(this), amount);
            uint rtba = IERC20(token).balanceOf(address(this));
            amount = rtba - rtbb;
            require(amount > 0, "Amount must be greater than 0");
    
            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);
            rewardHistory[adjustedTstamp][token] += amount;
            payoutHistory[token] += amount;
        }
    
        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;
        }
    
        /// This can break claims of bribes! Useful during a platform-wide upgrade (optional)
        function rescue(uint _amt, address _token, address _to) external {
            require(msg.sender == IVotingEscrow(_ve).team(), "only team");
            IERC20(_token).transfer(_to, _amt);
        }
    
        function _safeTransfer(address token, address to, uint256 value) internal {
            require(token.code.length > 0, "Invalid token address");
            (bool success, bytes memory data) =
            token.call(abi.encodeWithSelector(IERC20.transfer.selector, to, value));
            require(success && (data.length == 0 || abi.decode(data, (bool))), "UnifiedBribe: TransferFrom failed");
        }
    
        function _safeTransferFrom(address token, address from, address to, uint256 value) internal {
            require(token.code.length > 0, "Invalid token address");
            (bool success, bytes memory data) =
            token.call(abi.encodeWithSelector(IERC20.transferFrom.selector, from, to, value));
            require(success && (data.length == 0 || abi.decode(data, (bool))), "UnifiedBribe: TransferFrom failed");
        }
    }
    
    
    // File: contracts/interfaces/IBribeFactory.sol
    
    
    pragma solidity 0.8.9;
    
    interface IBribeFactory {
        function createBribe(address[] memory) external returns (address);
    }
    
    // File: contracts/factories/BribeFactory.sol
    
    
    pragma solidity 0.8.9;
    
    
    
    
    contract BribeFactory is IBribeFactory {
        address public lastBribe;
    
        function createBribe(address[] memory _allowedRewards) external returns (address) {
            UnifiedBribe unifiedBribe = new UnifiedBribe();
            unifiedBribe.initialize(msg.sender, _allowedRewards);
            lastBribe = address(unifiedBribe);
            return address(unifiedBribe);
        }
    
    }

    Please enter a contract address above to load the contract details and source code.

    Context size (optional):