false
false
100

Contract Address Details

0xDe7917E632F3f7DC3555F3F82e9bdA746e62b024

Contract Name
SinkManager
Creator
0x871ea7ā€“388d63 at 0xb4c37cā€“16e0be
Balance
0 KAVA ( )
Tokens
Fetching tokens...
Transactions
Fetching transactions...
Transfers
Fetching transfers...
Gas Used
Fetching gas used...
Last Balance Update
11603417
Warning! Contract bytecode has been changed and doesn't match the verified one. Therefore, interaction with this smart contract may be risky.
Contract name:
SinkManager




Optimization enabled
true
Compiler version
v0.8.19+commit.7dd6d404




Optimization runs
200
EVM Version
default




Verified at
2023-10-05T14:30:40.266361Z

Constructor Arguments

0x000000000000000000000000c6eaf7d353d9901375be2d8f9ba38dd58524d8b300000000000000000000000087e7742fe8cb0ef71f6fc21344a20fb0ce9c03620000000000000000000000004c0846c484d7a6a9639b20e1828e3337bbc8d7d7000000000000000000000000842d477f8ce0efbb669ddc2ace21816b9a841ee4000000000000000000000000e1da44c0da55b075ae8e2e4b6986adc76ac77d73000000000000000000000000b6a075e9cf5dba476994b63b60217a5cf9ab272700000000000000000000000035361c9c2a324f5fb8f3aed2d7ba91ce1410893a0000000000000000000000004937fcfaf9fbd139b78d95a128425a19721c3072000000000000000000000000411bb8829a14d56356485df13cc6a00c9ddf19db

Arg [0] (address) : 0xc6eaf7d353d9901375be2d8f9ba38dd58524d8b3
Arg [1] (address) : 0x87e7742fe8cb0ef71f6fc21344a20fb0ce9c0362
Arg [2] (address) : 0x4c0846c484d7a6a9639b20e1828e3337bbc8d7d7
Arg [3] (address) : 0x842d477f8ce0efbb669ddc2ace21816b9a841ee4
Arg [4] (address) : 0xe1da44c0da55b075ae8e2e4b6986adc76ac77d73
Arg [5] (address) : 0xb6a075e9cf5dba476994b63b60217a5cf9ab2727
Arg [6] (address) : 0x35361c9c2a324f5fb8f3aed2d7ba91ce1410893a
Arg [7] (address) : 0x4937fcfaf9fbd139b78d95a128425a19721c3072
Arg [8] (address) : 0x411bb8829a14d56356485df13cc6a00c9ddf19db

              

contracts/v1/sink/SinkManager.sol

// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.19;

import { SinkManagerFacilitator } from "./SinkManagerFacilitator.sol";
import { ISinkManager } from "../../interfaces/ISinkManager.sol";
import { IMinter } from "../../interfaces/IMinter.sol";
import { IVotingEscrow } from "../../interfaces/IVotingEscrow.sol";
import { IVara } from "../../interfaces/IVara.sol";
import { IGaugeV1 } from "../../interfaces/v1/IGaugeV1.sol";
import { IVoterV1 } from "../../interfaces/v1/IVoterV1.sol";
import { IVotingEscrowV1 } from "../../interfaces/v1/IVotingEscrowV1.sol";
import { IRewardsDistributorV1 } from "../../interfaces/v1/IRewardsDistributorV1.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { Context } from "@openzeppelin/contracts/utils/Context.sol";
import { ERC2771Context } from "@openzeppelin/contracts/metatx/ERC2771Context.sol";
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { ERC721Holder } from "@openzeppelin/contracts/token/ERC721/utils/ERC721Holder.sol";
import { ReentrancyGuard } from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import { Clones } from "@openzeppelin/contracts/proxy/Clones.sol";
import { VelodromeTimeLibrary } from "../../libraries/VelodromeTimeLibrary.sol";

/// @title Velodrome Sink Manager
/// @notice Absorb v1 Vara and converting v1 veNFTs and VELO into v2
/// @author varadrome.finance, @pegahcarter
contract SinkManager is ISinkManager, ERC2771Context, Ownable, ERC721Holder, ReentrancyGuard {
  uint256 internal constant MAXTIME = 4 * 365 days;
  uint256 internal constant WEEK = 1 weeks;
  // @dev Additional salt for contract creation
  uint256 private counter;

  /// @dev tokenId => tokenIdV2
  mapping(uint256 => uint256) public conversions;
  /// @dev tokenId => facilitator address
  mapping(uint256 => address) public facilitators;

  /// @dev token id of veNFT owned by this contract to capture v1 VELO emissions
  uint256 public ownedTokenId;

  /// @dev Address of fake pool used to capture emissions
  address private sinkDrain;

  // @dev Address of deployed facilitator contract to clone
  address public facilitatorImplementation;

  /// @dev V1 Voting contract
  IVoterV1 public immutable voter;
  /// @dev V1 Vara contract
  IVara public immutable vara;
  /// @dev V2 Vara contract
  IVara public immutable varaV2;
  /// @dev V2 Minter contract
  IMinter public immutable minterV2;
  /// @dev V1 Voting Escrow contract
  IVotingEscrowV1 public immutable ve;
  /// @dev V2 Voting Escrow contract
  IVotingEscrow public immutable veV2;
  /// @dev V1 Rewards Distributor contract
  IRewardsDistributorV1 public immutable rewardsDistributor;
  /// @dev V1 sinkDrain gauge
  IGaugeV1 public gauge;
  /// @dev epoch start => vara captured
  mapping(uint256 => uint256) internal _captured;

  constructor(
    address _forwarder,
    address _sinkDrain,
    address _facilitatorImplementation,
    address _voter,
    address _vara,
    address _varaV2,
    address _ve,
    address _veV2,
    address _rewardsDistributor
  ) ERC2771Context(_forwarder) {
    sinkDrain = _sinkDrain;
    facilitatorImplementation = _facilitatorImplementation;
    voter = IVoterV1(_voter);
    vara = IVara(_vara);
    varaV2 = IVara(_varaV2);
    minterV2 = IMinter(IVara(_varaV2).minter());
    ve = IVotingEscrowV1(_ve);
    veV2 = IVotingEscrow(_veV2);
    rewardsDistributor = IRewardsDistributorV1(_rewardsDistributor);

    vara.approve(_ve, type(uint256).max);
    varaV2.approve(_veV2, type(uint256).max);
  }

  // --------------------------------------------------------------------
  // Conversion methods
  // --------------------------------------------------------------------

  /// @inheritdoc ISinkManager
  function convertVELO(uint256 amount) external {
    uint256 _ownedTokenId = ownedTokenId;
    if (_ownedTokenId == 0) revert TokenIdNotSet();
    address sender = _msgSender();

    // Mint emissions prior to conversion
    minterV2.updatePeriod();

    // Deposit old VELO
    vara.transferFrom(sender, address(this), amount);

    // Add VELO to owned escrow
    ve.increase_amount(_ownedTokenId, amount);

    // return new VELO
    varaV2.mint(sender, amount);
    _captured[VelodromeTimeLibrary.epochStart(block.timestamp)] += amount;

    emit ConvertVELO(sender, amount, block.timestamp);
  }

  /// @inheritdoc ISinkManager
  function convertVe(uint256 tokenId) external nonReentrant returns (uint256 tokenIdV2) {
    uint256 _ownedTokenId = ownedTokenId;
    if (_ownedTokenId == 0) revert TokenIdNotSet();

    // Ensure the veNFT was not converted
    if (conversions[tokenId] != 0) revert NFTAlreadyConverted();

    // Ensure this contract has been approved to transfer the veNFT
    if (!ve.isApprovedOrOwner(address(this), tokenId)) revert NFTNotApproved();

    // Ensure the veNFT has not expired
    if (ve.locked__end(tokenId) <= block.timestamp) revert NFTExpired();

    address sender = _msgSender();

    // Mint emissions prior to conversion
    minterV2.updatePeriod();

    // Create contract to facilitate the merge
    SinkManagerFacilitator facilitator = SinkManagerFacilitator(
      Clones.cloneDeterministic(
        facilitatorImplementation,
        keccak256(abi.encodePacked(++counter, blockhash(block.number - 1)))
      )
    );

    // Transfer the veNFT to the facilitator
    ve.safeTransferFrom(sender, address(facilitator), tokenId);

    /* Create new veNFT with same lock parameters */

    // Fetch lock information of v1 veNFT
    (int128 _lockAmount, uint256 lockEnd) = ve.locked(tokenId); // amount of v1 VELO locked, unlock timestamp
    uint256 lockAmount = uint256(int256(_lockAmount));
    // determine lockDuration based on current epoch start - see unlockTime in ve._createLock()
    uint256 lockDuration = lockEnd - (block.timestamp / WEEK) * WEEK;

    // mint v2 VELO to deposit into lock
    varaV2.mint(address(this), lockAmount);

    // Create v2 veNFT
    tokenIdV2 = veV2.createLockFor(lockAmount, lockDuration, sender);

    // Merge into the sinkManager veNFT
    ve.approve(address(facilitator), ownedTokenId);
    facilitator.merge(ve, tokenId, ownedTokenId);
    ve.approve(address(0), ownedTokenId);

    // poke vote to update voting balance to gauge
    voter.poke(_ownedTokenId);

    // event emission and storage of conversion
    conversions[tokenId] = tokenIdV2;
    facilitators[tokenId] = address(facilitator);
    _captured[VelodromeTimeLibrary.epochStart(block.timestamp)] += lockAmount;
    emit ConvertVe(sender, tokenId, tokenIdV2, lockAmount, lockEnd, block.timestamp);
  }

  // --------------------------------------------------------------------
  // Maintenance
  // --------------------------------------------------------------------
  /// @inheritdoc ISinkManager
  function claimRebaseAndGaugeRewards() external {
    if (address(gauge) == address(0)) revert GaugeNotSet();
    uint256 _ownedTokenId = ownedTokenId;

    // Claim gauge rewards and deposit into owned veNFT
    uint256 amountResidual = vara.balanceOf(address(this));
    address[] memory rewards = new address[](1);
    rewards[0] = address(vara);
    gauge.getReward(address(this), rewards);
    uint256 amountAfterReward = vara.balanceOf(address(this));
    uint256 amountRewarded = amountAfterReward - amountResidual;
    if (amountAfterReward > 0) {
      ve.increase_amount(_ownedTokenId, amountAfterReward);
    }

    // Claim rebases
    uint256 amountRebased = rewardsDistributor.claimable(_ownedTokenId);
    if (amountRebased > 0) {
      rewardsDistributor.claim(_ownedTokenId);
    }

    // increase locktime to max if possible
    uint256 unlockTime = ((block.timestamp + MAXTIME) / WEEK) * WEEK;
    (, uint256 end) = ve.locked(_ownedTokenId);
    if (unlockTime > end) {
      ve.increase_unlock_time(_ownedTokenId, MAXTIME);
    }

    // poke vote to update voting balance to gauge
    voter.poke(_ownedTokenId);

    emit ClaimRebaseAndGaugeRewards(
      _msgSender(),
      amountResidual,
      amountRewarded,
      amountRebased,
      block.timestamp
    );
  }

  // --------------------------------------------------------------------
  // Admin
  // --------------------------------------------------------------------
  /// @notice Initial setup of the ownedTokenId, the v1 veNFT which votes for the SinkDrain
  function setOwnedTokenId(uint256 tokenId) external onlyOwner {
    if (ownedTokenId != 0) revert TokenIdAlreadySet();
    if (ve.ownerOf(tokenId) != address(this)) revert ContractNotOwnerOfToken();
    ownedTokenId = tokenId;
  }

  /// @notice Deposit all of SinkDrain token to gauge to earn 100% of rewards
  /// And vote for the gauge to allocate rewards
  function setupSinkDrain(address _gauge) external onlyOwner {
    uint256 _ownedTokenId = ownedTokenId;
    if (_ownedTokenId == 0) revert TokenIdNotSet();
    if (address(gauge) != address(0)) revert GaugeAlreadySet();

    // Set gauge for future claims
    gauge = IGaugeV1(_gauge);

    // Approve gauge to transfer token
    IERC20 token = IERC20(gauge.stake());
    uint256 balance = token.balanceOf(address(this));
    token.approve(_gauge, balance);

    // Deposit SinkDrain to gauge
    gauge.deposit(balance, 0);

    // Initial vote
    address[] memory poolVote = new address[](1);
    uint256[] memory weights = new uint256[](1);
    poolVote[0] = gauge.stake();
    weights[0] = 1;
    voter.vote(_ownedTokenId, poolVote, weights);
  }

  /// @inheritdoc ISinkManager
  function captured(uint256 _timestamp) external view returns (uint256 _amount) {
    _amount = _captured[VelodromeTimeLibrary.epochStart(_timestamp)];
  }

  function _msgData() internal view override(ERC2771Context, Context) returns (bytes calldata) {
    return ERC2771Context._msgData();
  }

  function _msgSender() internal view override(ERC2771Context, Context) returns (address) {
    return ERC2771Context._msgSender();
  }
}
        

@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/IERC721Metadata.sol)

pragma solidity ^0.8.0;

import "../IERC721.sol";

/**
 * @title ERC-721 Non-Fungible Token Standard, optional metadata extension
 * @dev See https://eips.ethereum.org/EIPS/eip-721
 */
interface IERC721Metadata is IERC721 {
    /**
     * @dev Returns the token collection name.
     */
    function name() external view returns (string memory);

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

    /**
     * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
     */
    function tokenURI(uint256 tokenId) external view returns (string memory);
}
          

@openzeppelin/contracts/token/ERC721/utils/ERC721Holder.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC721/utils/ERC721Holder.sol)

pragma solidity ^0.8.0;

import "../IERC721Receiver.sol";

/**
 * @dev Implementation of the {IERC721Receiver} interface.
 *
 * Accepts all token transfers.
 * Make sure the contract is able to use its token with {IERC721-safeTransferFrom}, {IERC721-approve} or {IERC721-setApprovalForAll}.
 */
contract ERC721Holder is IERC721Receiver {
    /**
     * @dev See {IERC721Receiver-onERC721Received}.
     *
     * Always returns `IERC721Receiver.onERC721Received.selector`.
     */
    function onERC721Received(address, address, uint256, bytes memory) public virtual override returns (bytes4) {
        return this.onERC721Received.selector;
    }
}
          

contracts/governance/IVotes.sol

// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;

/// Modified IVotes interface for tokenId based voting
interface IVotes {
    /**
     * @dev Emitted when an account changes their delegate.
     */
    event DelegateChanged(address indexed delegator, uint256 indexed fromDelegate, uint256 indexed toDelegate);

    /**
     * @dev Emitted when a token transfer or delegate change results in changes to a delegate's number of votes.
     */
    event DelegateVotesChanged(address indexed delegate, uint256 previousBalance, uint256 newBalance);

    /**
     * @dev Returns the amount of votes that `tokenId` had at a specific moment in the past.
     *      If the account passed in is not the owner, returns 0.
     */
    function getPastVotes(address account, uint256 tokenId, uint256 timepoint) external view returns (uint256);

    /**
     * @dev Returns the total supply of votes available at a specific moment in the past. If the `clock()` is
     * configured to use block numbers, this will return the value the end of the corresponding block.
     *
     * NOTE: This value is the sum of all available votes, which is not necessarily the sum of all delegated votes.
     * Votes that have not been delegated are still part of total supply, even though they would not participate in a
     * vote.
     */
    function getPastTotalSupply(uint256 timepoint) external view returns (uint256);

    /**
     * @dev Returns the delegate that `tokenId` has chosen. Can never be equal to the delegator's `tokenId`.
     *      Returns 0 if not delegated.
     */
    function delegates(uint256 tokenId) external view returns (uint256);

    /**
     * @dev Delegates votes from the sender to `delegatee`.
     */
    function delegate(uint256 delegator, uint256 delegatee) external;

    /**
     * @dev Delegates votes from `delegator` to `delegatee`. Signer must own `delegator`.
     */
    function delegateBySig(
        uint256 delegator,
        uint256 delegatee,
        uint256 nonce,
        uint256 expiry,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;
}
          

@openzeppelin/contracts/access/Ownable.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)

pragma solidity ^0.8.0;

import "../utils/Context.sol";

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

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

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor() {
        _transferOwnership(_msgSender());
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

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

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
    }

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

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

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}
          

@openzeppelin/contracts/interfaces/IERC165.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (interfaces/IERC165.sol)

pragma solidity ^0.8.0;

import "../utils/introspection/IERC165.sol";
          

@openzeppelin/contracts/interfaces/IERC4906.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (interfaces/IERC4906.sol)

pragma solidity ^0.8.0;

import "./IERC165.sol";
import "./IERC721.sol";

/// @title EIP-721 Metadata Update Extension
interface IERC4906 is IERC165, IERC721 {
    /// @dev This event emits when the metadata of a token is changed.
    /// So that the third-party platforms such as NFT market could
    /// timely update the images and related attributes of the NFT.
    event MetadataUpdate(uint256 _tokenId);

    /// @dev This event emits when the metadata of a range of tokens is changed.
    /// So that the third-party platforms such as NFT market could
    /// timely update the images and related attributes of the NFTs.
    event BatchMetadataUpdate(uint256 _fromTokenId, uint256 _toTokenId);
}
          

@openzeppelin/contracts/interfaces/IERC721.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (interfaces/IERC721.sol)

pragma solidity ^0.8.0;

import "../token/ERC721/IERC721.sol";
          

@openzeppelin/contracts/metatx/ERC2771Context.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.3) (metatx/ERC2771Context.sol)

pragma solidity ^0.8.9;

import "../utils/Context.sol";

/**
 * @dev Context variant with ERC2771 support.
 */
abstract contract ERC2771Context is Context {
    /// @custom:oz-upgrades-unsafe-allow state-variable-immutable
    address private immutable _trustedForwarder;

    /// @custom:oz-upgrades-unsafe-allow constructor
    constructor(address trustedForwarder) {
        _trustedForwarder = trustedForwarder;
    }

    function isTrustedForwarder(address forwarder) public view virtual returns (bool) {
        return forwarder == _trustedForwarder;
    }

    function _msgSender() internal view virtual override returns (address sender) {
        if (isTrustedForwarder(msg.sender) && msg.data.length >= 20) {
            // The assembly code is more direct than the Solidity version using `abi.decode`.
            /// @solidity memory-safe-assembly
            assembly {
                sender := shr(96, calldataload(sub(calldatasize(), 20)))
            }
        } else {
            return super._msgSender();
        }
    }

    function _msgData() internal view virtual override returns (bytes calldata) {
        if (isTrustedForwarder(msg.sender) && msg.data.length >= 20) {
            return msg.data[:msg.data.length - 20];
        } else {
            return super._msgData();
        }
    }
}
          

@openzeppelin/contracts/utils/Context.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

pragma solidity ^0.8.0;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }
}
          

@openzeppelin/contracts/proxy/Clones.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (proxy/Clones.sol)

pragma solidity ^0.8.0;

/**
 * @dev https://eips.ethereum.org/EIPS/eip-1167[EIP 1167] is a standard for
 * deploying minimal proxy contracts, also known as "clones".
 *
 * > To simply and cheaply clone contract functionality in an immutable way, this standard specifies
 * > a minimal bytecode implementation that delegates all calls to a known, fixed address.
 *
 * The library includes functions to deploy a proxy using either `create` (traditional deployment) or `create2`
 * (salted deterministic deployment). It also includes functions to predict the addresses of clones deployed using the
 * deterministic method.
 *
 * _Available since v3.4._
 */
library Clones {
    /**
     * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
     *
     * This function uses the create opcode, which should never revert.
     */
    function clone(address implementation) internal returns (address instance) {
        /// @solidity memory-safe-assembly
        assembly {
            // Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes
            // of the `implementation` address with the bytecode before the address.
            mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000))
            // Packs the remaining 17 bytes of `implementation` with the bytecode after the address.
            mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3))
            instance := create(0, 0x09, 0x37)
        }
        require(instance != address(0), "ERC1167: create failed");
    }

    /**
     * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
     *
     * This function uses the create2 opcode and a `salt` to deterministically deploy
     * the clone. Using the same `implementation` and `salt` multiple time will revert, since
     * the clones cannot be deployed twice at the same address.
     */
    function cloneDeterministic(address implementation, bytes32 salt) internal returns (address instance) {
        /// @solidity memory-safe-assembly
        assembly {
            // Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes
            // of the `implementation` address with the bytecode before the address.
            mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000))
            // Packs the remaining 17 bytes of `implementation` with the bytecode after the address.
            mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3))
            instance := create2(0, 0x09, 0x37, salt)
        }
        require(instance != address(0), "ERC1167: create2 failed");
    }

    /**
     * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
     */
    function predictDeterministicAddress(
        address implementation,
        bytes32 salt,
        address deployer
    ) internal pure returns (address predicted) {
        /// @solidity memory-safe-assembly
        assembly {
            let ptr := mload(0x40)
            mstore(add(ptr, 0x38), deployer)
            mstore(add(ptr, 0x24), 0x5af43d82803e903d91602b57fd5bf3ff)
            mstore(add(ptr, 0x14), implementation)
            mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73)
            mstore(add(ptr, 0x58), salt)
            mstore(add(ptr, 0x78), keccak256(add(ptr, 0x0c), 0x37))
            predicted := keccak256(add(ptr, 0x43), 0x55)
        }
    }

    /**
     * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
     */
    function predictDeterministicAddress(
        address implementation,
        bytes32 salt
    ) internal view returns (address predicted) {
        return predictDeterministicAddress(implementation, salt, address(this));
    }
}
          

@openzeppelin/contracts/security/ReentrancyGuard.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (security/ReentrancyGuard.sol)

pragma solidity ^0.8.0;

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

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

    uint256 private _status;

    constructor() {
        _status = _NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and making it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        _nonReentrantBefore();
        _;
        _nonReentrantAfter();
    }

    function _nonReentrantBefore() private {
        // On the first call to nonReentrant, _status will be _NOT_ENTERED
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

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

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

    /**
     * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
     * `nonReentrant` function in the call stack.
     */
    function _reentrancyGuardEntered() internal view returns (bool) {
        return _status == _ENTERED;
    }
}
          

@openzeppelin/contracts/token/ERC20/IERC20.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

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

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

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

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

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

@openzeppelin/contracts/token/ERC721/IERC721.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC721/IERC721.sol)

pragma solidity ^0.8.0;

import "../../utils/introspection/IERC165.sol";

/**
 * @dev Required interface of an ERC721 compliant contract.
 */
interface IERC721 is IERC165 {
    /**
     * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
     */
    event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
     */
    event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
     */
    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);

    /**
     * @dev Returns the number of tokens in ``owner``'s account.
     */
    function balanceOf(address owner) external view returns (uint256 balance);

    /**
     * @dev Returns the owner of the `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function ownerOf(uint256 tokenId) external view returns (address owner);

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
     * are aware of the ERC721 protocol to prevent tokens from being forever locked.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must have been allowed to move this token by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(address from, address to, uint256 tokenId) external;

    /**
     * @dev Transfers `tokenId` token from `from` to `to`.
     *
     * WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721
     * or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must
     * understand this adds an external call which potentially creates a reentrancy vulnerability.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 tokenId) external;

    /**
     * @dev Gives permission to `to` to transfer `tokenId` token to another account.
     * The approval is cleared when the token is transferred.
     *
     * Only a single account can be approved at a time, so approving the zero address clears previous approvals.
     *
     * Requirements:
     *
     * - The caller must own the token or be an approved operator.
     * - `tokenId` must exist.
     *
     * Emits an {Approval} event.
     */
    function approve(address to, uint256 tokenId) external;

    /**
     * @dev Approve or remove `operator` as an operator for the caller.
     * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
     *
     * Requirements:
     *
     * - The `operator` cannot be the caller.
     *
     * Emits an {ApprovalForAll} event.
     */
    function setApprovalForAll(address operator, bool approved) external;

    /**
     * @dev Returns the account approved for `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function getApproved(uint256 tokenId) external view returns (address operator);

    /**
     * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
     *
     * See {setApprovalForAll}
     */
    function isApprovedForAll(address owner, address operator) external view returns (bool);
}
          

@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC721/IERC721Receiver.sol)

pragma solidity ^0.8.0;

/**
 * @title ERC721 token receiver interface
 * @dev Interface for any contract that wants to support safeTransfers
 * from ERC721 asset contracts.
 */
interface IERC721Receiver {
    /**
     * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
     * by `operator` from `from`, this function is called.
     *
     * It must return its Solidity selector to confirm the token transfer.
     * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.
     *
     * The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`.
     */
    function onERC721Received(
        address operator,
        address from,
        uint256 tokenId,
        bytes calldata data
    ) external returns (bytes4);
}
          

@openzeppelin/contracts/utils/introspection/IERC165.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
          

contracts/interfaces/IMinter.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IMinter {
  error AlreadyNudged();
  error NotEpochGovernor();
  error TailEmissionsInactive();

  event Mint(
    address indexed _sender,
    uint256 _weekly,
    uint256 _circulating_supply,
    bool indexed _tail
  );
  event Nudge(uint256 indexed _period, uint256 _oldRate, uint256 _newRate);

  /// @notice Timestamp of start of epoch that updatePeriod was last called in
  function activePeriod() external returns (uint256);

  /// @notice Allows epoch governor to modify the tail emission rate by at most 1 basis point
  ///         per epoch to a maximum of 100 basis points or to a minimum of 1 basis point.
  ///         Note: the very first nudge proposal must take place the week prior
  ///         to the tail emission schedule starting.
  /// @dev Throws if not epoch governor.
  ///      Throws if not currently in tail emission schedule.
  ///      Throws if already nudged this epoch.
  ///      Throws if nudging above maximum rate.
  ///      Throws if nudging below minimum rate.
  ///      This contract is coupled to EpochGovernor as it requires three option simple majority voting.
  function nudge() external;

  /// @notice Calculates rebases according to the formula
  ///         weekly * (ve.totalSupply / vara.totalSupply) ^ 3 / 2
  ///         Note that ve.totalSupply is the locked ve supply
  ///         vara.totalSupply is the total ve supply minted
  /// @param _minted Amount of VELO minted this epoch
  /// @return _growth Rebases
  function calculateGrowth(uint256 _minted) external view returns (uint256 _growth);

  /// @notice Processes emissions and rebases. Callable once per epoch (1 week).
  /// @return _period Start of current epoch.
  function updatePeriod() external returns (uint256 _period);
}
          

contracts/interfaces/ISinkManager.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import { IVara } from "./IVara.sol";

interface ISinkManager {
  event ConvertVELO(address indexed who, uint256 amount, uint256 timestamp);
  event ConvertVe(
    address indexed who,
    uint256 indexed tokenId,
    uint256 indexed tokenIdV2,
    uint256 amount,
    uint256 lockEnd,
    uint256 timestamp
  );
  event ClaimRebaseAndGaugeRewards(
    address indexed who,
    uint256 amountResidual,
    uint256 amountRewarded,
    uint256 amountRebased,
    uint256 timestamp
  );

  error ContractNotOwnerOfToken();
  error GaugeAlreadySet();
  error GaugeNotSet();
  error GaugeNotSinkDrain();
  error NFTAlreadyConverted();
  error NFTNotApproved();
  error NFTExpired();
  error TokenIdNotSet();
  error TokenIdAlreadySet();

  function ownedTokenId() external view returns (uint256);

  function vara() external view returns (IVara);

  function varaV2() external view returns (IVara);

  /// @notice Helper utility that returns amount of token captured by epoch
  function captured(uint256 timestamp) external view returns (uint256 amount);

  /// @notice User converts their v1 VELO into v2 VELO
  /// @param amount Amount of VELO to convert
  function convertVELO(uint256 amount) external;

  /// @notice User converts their v1 ve into v2 ve
  /// @param tokenId      Token ID of v1 ve
  /// @return tokenIdV2   Token ID of v2 ve
  function convertVe(uint256 tokenId) external returns (uint256 tokenIdV2);

  /// @notice Claim SinkManager-eligible rebase and gauge rewards to lock into the SinkManager-owned tokenId
  /// @dev Callable by anyone
  function claimRebaseAndGaugeRewards() external;
}
          

contracts/interfaces/IVara.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

interface IVara is IERC20 {
  error NotMinter();
  error NotOwner();
  error NotMinterOrSinkManager();
  error SinkManagerAlreadySet();

  function mint(address, uint256) external returns (bool);

  function minter() external returns (address);
}
          

contracts/interfaces/IVotingEscrow.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IERC721, IERC721Metadata} from "@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol";
import {IERC4906} from "@openzeppelin/contracts/interfaces/IERC4906.sol";
import {IVotes} from "../governance/IVotes.sol";

interface IVotingEscrow is IVotes, IERC4906, IERC721Metadata {
    struct LockedBalance {
        int128 amount;
        uint256 end;
        bool isPermanent;
    }

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

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

    /// @notice A checkpoint for recorded delegated voting weights at a certain timestamp
    struct Checkpoint {
        uint256 fromTimestamp;
        address owner;
        uint256 delegatedBalance;
        uint256 delegatee;
    }

    enum DepositType {
        DEPOSIT_FOR_TYPE,
        CREATE_LOCK_TYPE,
        INCREASE_LOCK_AMOUNT,
        INCREASE_UNLOCK_TIME
    }

    /// @dev Different types of veNFTs:
    /// NORMAL  - typical veNFT
    /// LOCKED  - veNFT which is locked into a MANAGED veNFT
    /// MANAGED - veNFT which can accept the deposit of NORMAL veNFTs
    enum EscrowType {
        NORMAL,
        LOCKED,
        MANAGED
    }

    error AlreadyVoted();
    error AmountTooBig();
    error ERC721ReceiverRejectedTokens();
    error ERC721TransferToNonERC721ReceiverImplementer();
    error InvalidNonce();
    error InvalidSignature();
    error InvalidSignatureS();
    error InvalidManagedNFTId();
    error LockDurationNotInFuture();
    error LockDurationTooLong();
    error LockExpired();
    error LockNotExpired();
    error NoLockFound();
    error NonExistentToken();
    error NotApprovedOrOwner();
    error NotDistributor();
    error NotEmergencyCouncilOrGovernor();
    error NotGovernor();
    error NotGovernorOrManager();
    error NotManagedNFT();
    error NotManagedOrNormalNFT();
    error NotLockedNFT();
    error NotNormalNFT();
    error NotPermanentLock();
    error NotOwner();
    error NotTeam();
    error NotVoter();
    error OwnershipChange();
    error PermanentLock();
    error SameAddress();
    error SameNFT();
    error SameState();
    error SplitNoOwner();
    error SplitNotAllowed();
    error SignatureExpired();
    error TooManyTokenIDs();
    error ZeroAddress();
    error ZeroAmount();
    error ZeroBalance();

    event Deposit(
        address indexed provider,
        uint256 indexed tokenId,
        DepositType indexed depositType,
        uint256 value,
        uint256 locktime,
        uint256 ts
    );
    event Withdraw(address indexed provider, uint256 indexed tokenId, uint256 value, uint256 ts);
    event LockPermanent(address indexed _owner, uint256 indexed _tokenId, uint256 amount, uint256 _ts);
    event UnlockPermanent(address indexed _owner, uint256 indexed _tokenId, uint256 amount, uint256 _ts);
    event Supply(uint256 prevSupply, uint256 supply);
    event Merge(
        address indexed _sender,
        uint256 indexed _from,
        uint256 indexed _to,
        uint256 _amountFrom,
        uint256 _amountTo,
        uint256 _amountFinal,
        uint256 _locktime,
        uint256 _ts
    );
    event Split(
        uint256 indexed _from,
        uint256 indexed _tokenId1,
        uint256 indexed _tokenId2,
        address _sender,
        uint256 _splitAmount1,
        uint256 _splitAmount2,
        uint256 _locktime,
        uint256 _ts
    );
    event CreateManaged(
        address indexed _to,
        uint256 indexed _mTokenId,
        address indexed _from,
        address _lockedManagedReward,
        address _freeManagedReward
    );
    event DepositManaged(
        address indexed _owner,
        uint256 indexed _tokenId,
        uint256 indexed _mTokenId,
        uint256 _weight,
        uint256 _ts
    );
    event WithdrawManaged(
        address indexed _owner,
        uint256 indexed _tokenId,
        uint256 indexed _mTokenId,
        uint256 _weight,
        uint256 _ts
    );
    event SetAllowedManager(address indexed _allowedManager);

    // State variables
    function factoryRegistry() external view returns (address);

    function token() external view returns (address);

    function distributor() external view returns (address);

    function voter() external view returns (address);

    function team() external view returns (address);

    function artProxy() external view returns (address);

    function allowedManager() external view returns (address);

    function tokenId() external view returns (uint256);

    /*///////////////////////////////////////////////////////////////
                            MANAGED NFT STORAGE
    //////////////////////////////////////////////////////////////*/

    /// @dev Mapping of token id to escrow type
    ///      Takes advantage of the fact default value is EscrowType.NORMAL
    function escrowType(uint256 tokenId) external view returns (EscrowType);

    /// @dev Mapping of token id to managed id
    function idToManaged(uint256 tokenId) external view returns (uint256 managedTokenId);

    /// @dev Mapping of user token id to managed token id to weight of token id
    function weights(uint256 tokenId, uint256 managedTokenId) external view returns (uint256 weight);

    /// @dev Mapping of managed id to deactivated state
    function deactivated(uint256 tokenId) external view returns (bool inactive);

    /// @dev Mapping from managed nft id to locked managed rewards
    ///      `token` denominated rewards (rebases/rewards) stored in locked managed rewards contract
    ///      to prevent co-mingling of assets
    function managedToLocked(uint256 tokenId) external view returns (address);

    /// @dev Mapping from managed nft id to free managed rewards contract
    ///      these rewards can be freely withdrawn by users
    function managedToFree(uint256 tokenId) external view returns (address);

    /*///////////////////////////////////////////////////////////////
                            MANAGED NFT LOGIC
    //////////////////////////////////////////////////////////////*/

    /// @notice Create managed NFT (a permanent lock) for use within ecosystem.
    /// @dev Throws if address already owns a managed NFT.
    /// @return _mTokenId managed token id.
    function createManagedLockFor(address _to) external returns (uint256 _mTokenId);

    /// @notice Delegates balance to managed nft
    ///         Note that NFTs deposited into a managed NFT will be re-locked
    ///         to the maximum lock time on withdrawal.
    ///         Permanent locks that are deposited will automatically unlock.
    /// @dev Managed nft will remain max-locked as long as there is at least one
    ///      deposit or withdrawal per week.
    ///      Throws if deposit nft is managed.
    ///      Throws if recipient nft is not managed.
    ///      Throws if deposit nft is already locked.
    ///      Throws if not called by voter.
    /// @param _tokenId tokenId of NFT being deposited
    /// @param _mTokenId tokenId of managed NFT that will receive the deposit
    function depositManaged(uint256 _tokenId, uint256 _mTokenId) external;

    /// @notice Retrieves locked rewards and withdraws balance from managed nft.
    ///         Note that the NFT withdrawn is re-locked to the maximum lock time.
    /// @dev Throws if NFT not locked.
    ///      Throws if not called by voter.
    /// @param _tokenId tokenId of NFT being deposited.
    function withdrawManaged(uint256 _tokenId) external;

    /// @notice Permit one address to call createManagedLockFor() that is not Voter.governor()
    function setAllowedManager(address _allowedManager) external;

    /// @notice Set Managed NFT state. Inactive NFTs cannot be deposited into.
    /// @param _mTokenId managed nft state to set
    /// @param _state true => inactive, false => active
    function setManagedState(uint256 _mTokenId, bool _state) external;

    /*///////////////////////////////////////////////////////////////
                             METADATA STORAGE
    //////////////////////////////////////////////////////////////*/

    function name() external view returns (string memory);

    function symbol() external view returns (string memory);

    function version() external view returns (string memory);

    function decimals() external view returns (uint8);

    function setTeam(address _team) external;

    function setArtProxy(address _proxy) external;

    /// @inheritdoc IERC721Metadata
    function tokenURI(uint256 tokenId) external view returns (string memory);

    /*//////////////////////////////////////////////////////////////
                      ERC721 BALANCE/OWNER STORAGE
    //////////////////////////////////////////////////////////////*/

    /// @dev Mapping from owner address to mapping of index to tokenId
    function ownerToNFTokenIdList(address _owner, uint256 _index) external view returns (uint256 _tokenId);

    /// @inheritdoc IERC721
    function ownerOf(uint256 tokenId) external view returns (address owner);

    /// @inheritdoc IERC721
    function balanceOf(address owner) external view returns (uint256 balance);

    /*//////////////////////////////////////////////////////////////
                         ERC721 APPROVAL STORAGE
    //////////////////////////////////////////////////////////////*/

    /// @inheritdoc IERC721
    function getApproved(uint256 _tokenId) external view returns (address operator);

    /// @inheritdoc IERC721
    function isApprovedForAll(address owner, address operator) external view returns (bool);

    /// @notice Check whether spender is owner or an approved user for a given veNFT
    /// @param _spender .
    /// @param _tokenId .
    function isApprovedOrOwner(address _spender, uint256 _tokenId) external returns (bool);

    /*//////////////////////////////////////////////////////////////
                              ERC721 LOGIC
    //////////////////////////////////////////////////////////////*/

    /// @inheritdoc IERC721
    function approve(address to, uint256 tokenId) external;

    /// @inheritdoc IERC721
    function setApprovalForAll(address operator, bool approved) external;

    /// @inheritdoc IERC721
    function transferFrom(address from, address to, uint256 tokenId) external;

    /// @inheritdoc IERC721
    function safeTransferFrom(address from, address to, uint256 tokenId) external;

    /// @inheritdoc IERC721
    function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;

    /*//////////////////////////////////////////////////////////////
                              ERC165 LOGIC
    //////////////////////////////////////////////////////////////*/

    function supportsInterface(bytes4 _interfaceID) external view returns (bool);

    /*//////////////////////////////////////////////////////////////
                             ESCROW STORAGE
    //////////////////////////////////////////////////////////////*/

    function epoch() external view returns (uint256);

    function supply() external view returns (uint256);

    function userPointEpoch(uint256 _tokenId) external view returns (uint256 _epoch);

    /// @notice time -> signed slope change
    function slopeChanges(uint256 _timestamp) external view returns (int128);

    /// @notice account -> can split
    function canSplit(address _account) external view returns (bool);

    /// @notice Global point history at a given index
    function pointHistory(uint256 _loc) external view returns (GlobalPoint memory);

    /// @notice Get the LockedBalance (amount, end) of a _tokenId
    /// @param _tokenId .
    /// @return LockedBalance of _tokenId
    function locked(uint256 _tokenId) external view returns (LockedBalance memory);

    /// @notice User -> UserPoint[userEpoch]
    function userPointHistory(uint256 _tokenId, uint256 _loc) external view returns (UserPoint memory);

    /*//////////////////////////////////////////////////////////////
                              ESCROW LOGIC
    //////////////////////////////////////////////////////////////*/

    /// @notice Record global data to checkpoint
    function checkpoint() external;

    /// @notice Deposit `_value` tokens for `_tokenId` and add to the lock
    /// @dev Anyone (even a smart contract) can deposit for someone else, but
    ///      cannot extend their locktime and deposit for a brand new user
    /// @param _tokenId lock NFT
    /// @param _value Amount to add to user's lock
    function depositFor(uint256 _tokenId, uint256 _value) external;

    /// @notice Deposit `_value` tokens for `msg.sender` and lock for `_lockDuration`
    /// @param _value Amount to deposit
    /// @param _lockDuration Number of seconds to lock tokens for (rounded down to nearest week)
    /// @return TokenId of created veNFT
    function createLock(uint256 _value, uint256 _lockDuration) external returns (uint256);

    /// @notice Deposit `_value` tokens for `_to` and lock for `_lockDuration`
    /// @param _value Amount to deposit
    /// @param _lockDuration Number of seconds to lock tokens for (rounded down to nearest week)
    /// @param _to Address to deposit
    /// @return TokenId of created veNFT
    function createLockFor(uint256 _value, uint256 _lockDuration, address _to) external returns (uint256);

    /// @notice Deposit `_value` additional tokens for `_tokenId` without modifying the unlock time
    /// @param _value Amount of tokens to deposit and add to the lock
    function increaseAmount(uint256 _tokenId, uint256 _value) external;

    /// @notice Extend the unlock time for `_tokenId`
    ///         Cannot extend lock time of permanent locks
    /// @param _lockDuration New number of seconds until tokens unlock
    function increaseUnlockTime(uint256 _tokenId, uint256 _lockDuration) external;

    /// @notice Withdraw all tokens for `_tokenId`
    /// @dev Only possible if the lock is both expired and not permanent
    ///      This will burn the veNFT. Any rebases or rewards that are unclaimed
    ///      will no longer be claimable. Claim all rebases and rewards prior to calling this.
    function withdraw(uint256 _tokenId) external;

    /// @notice Merges `_from` into `_to`.
    /// @dev Cannot merge `_from` locks that are permanent or have already voted this epoch.
    ///      Cannot merge `_to` locks that have already expired.
    ///      This will burn the veNFT. Any rebases or rewards that are unclaimed
    ///      will no longer be claimable. Claim all rebases and rewards prior to calling this.
    /// @param _from VeNFT to merge from.
    /// @param _to VeNFT to merge into.
    function merge(uint256 _from, uint256 _to) external;

    /// @notice Splits veNFT into two new veNFTS - one with oldLocked.amount - `_amount`, and the second with `_amount`
    /// @dev    This burns the tokenId of the target veNFT
    ///         Callable by approved or owner
    ///         If this is called by approved, approved will not have permissions to manipulate the newly created veNFTs
    ///         Returns the two new split veNFTs to owner
    ///         If `from` is permanent, will automatically dedelegate.
    ///         This will burn the veNFT. Any rebases or rewards that are unclaimed
    ///         will no longer be claimable. Claim all rebases and rewards prior to calling this.
    /// @param _from VeNFT to split.
    /// @param _amount Amount to split from veNFT.
    /// @return _tokenId1 Return tokenId of veNFT with oldLocked.amount - `_amount`.
    /// @return _tokenId2 Return tokenId of veNFT with `_amount`.
    function split(uint256 _from, uint256 _amount) external returns (uint256 _tokenId1, uint256 _tokenId2);

    /// @notice Toggle split for a specific veNFT.
    /// @dev Toggle split for address(0) to enable or disable for all.
    /// @param _account Address to toggle split permissions
    /// @param _bool True to allow, false to disallow
    function toggleSplit(address _account, bool _bool) external;

    /// @notice Permanently lock a veNFT. Voting power will be equal to
    ///         `LockedBalance.amount` with no decay. Required to delegate.
    /// @dev Only callable by unlocked normal veNFTs.
    /// @param _tokenId tokenId to lock.
    function lockPermanent(uint256 _tokenId) external;

    /// @notice Unlock a permanently locked veNFT. Voting power will decay.
    ///         Will automatically dedelegate if delegated.
    /// @dev Only callable by permanently locked veNFTs.
    ///      Cannot unlock if already voted this epoch.
    /// @param _tokenId tokenId to unlock.
    function unlockPermanent(uint256 _tokenId) external;

    /*///////////////////////////////////////////////////////////////
                           GAUGE VOTING STORAGE
    //////////////////////////////////////////////////////////////*/

    /// @notice Get the voting power for _tokenId at the current timestamp
    /// @dev Returns 0 if called in the same block as a transfer.
    /// @param _tokenId .
    /// @return Voting power
    function balanceOfNFT(uint256 _tokenId) external view returns (uint256);

    /// @notice Get the voting power for _tokenId at a given timestamp
    /// @param _tokenId .
    /// @param _t Timestamp to query voting power
    /// @return Voting power
    function balanceOfNFTAt(uint256 _tokenId, uint256 _t) external view returns (uint256);

    /// @notice Calculate total voting power at current timestamp
    /// @return Total voting power at current timestamp
    function totalSupply() external view returns (uint256);

    /// @notice Calculate total voting power at a given timestamp
    /// @param _t Timestamp to query total voting power
    /// @return Total voting power at given timestamp
    function totalSupplyAt(uint256 _t) external view returns (uint256);

    /*///////////////////////////////////////////////////////////////
                            GAUGE VOTING LOGIC
    //////////////////////////////////////////////////////////////*/

    /// @notice See if a queried _tokenId has actively voted
    /// @param _tokenId .
    /// @return True if voted, else false
    function voted(uint256 _tokenId) external view returns (bool);

    /// @notice Set the global state voter and distributor
    /// @dev This is only called once, at setup
    function setVoterAndDistributor(address _voter, address _distributor) external;

    /// @notice Set `voted` for _tokenId to true or false
    /// @dev Only callable by voter
    /// @param _tokenId .
    /// @param _voted .
    function voting(uint256 _tokenId, bool _voted) external;

    /*///////////////////////////////////////////////////////////////
                            DAO VOTING STORAGE
    //////////////////////////////////////////////////////////////*/

    /// @notice The number of checkpoints for each tokenId
    function numCheckpoints(uint256 tokenId) external view returns (uint48);

    /// @notice A record of states for signing / validating signatures
    function nonces(address account) external view returns (uint256);

    /// @inheritdoc IVotes
    function delegates(uint256 delegator) external view returns (uint256);

    /// @notice A record of delegated token checkpoints for each account, by index
    /// @param tokenId .
    /// @param index .
    /// @return Checkpoint
    function checkpoints(uint256 tokenId, uint48 index) external view returns (Checkpoint memory);

    /// @inheritdoc IVotes
    function getPastVotes(address account, uint256 tokenId, uint256 timestamp) external view returns (uint256);

    /// @inheritdoc IVotes
    function getPastTotalSupply(uint256 timestamp) external view returns (uint256);

    /*///////////////////////////////////////////////////////////////
                             DAO VOTING LOGIC
    //////////////////////////////////////////////////////////////*/

    /// @inheritdoc IVotes
    function delegate(uint256 delegator, uint256 delegatee) external;

    /// @inheritdoc IVotes
    function delegateBySig(
        uint256 delegator,
        uint256 delegatee,
        uint256 nonce,
        uint256 expiry,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;
}
          

contracts/interfaces/v1/IGaugeV1.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IGaugeV1 {
    function balanceOf(address _account) external returns (uint256 _balance);

    function stake() external returns (address _stake);

    function totalSupply() external returns (uint256 _totalSupply);

    function getReward(address _account, address[] memory _tokens) external;

    function deposit(uint256 _amount, uint256 _tokenId) external;

    function notifyRewardAmount(address _token, uint256 _amount) external;
}
          

contracts/interfaces/v1/IRewardsDistributorV1.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IRewardsDistributorV1 {
    function claimable(uint256 _tokenId) external view returns (uint256 _claimable);

    function claim(uint256 _tokenId) external returns (uint256 _amount);
}
          

contracts/interfaces/v1/IVoterV1.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IVoterV1 {
    function governor() external returns (address _governor);

    function totalWeight() external returns (uint256 _totalWeight);

    function usedWeights(uint256 _tokenId) external returns (uint256 _weight);

    function votes(uint256 _tokenId, address _pool) external returns (uint256 _votes);

    function createGauge(address _pool) external returns (address _gauge);

    function gauges(address pool) external view returns (address);

    function distribute(address _gauge) external;

    function poke(uint256 _tokenId) external;

    function vote(uint256 _tokenId, address[] memory _pools, uint256[] memory _weights) external;
}
          

contracts/interfaces/v1/IVotingEscrowV1.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

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

    function getApproved(uint256 _tokenId) external view returns (address);

    function isApprovedOrOwner(address _spender, uint256 _tokenId) external returns (bool);

    function locked__end(uint256 _tokenId) external view returns (uint256 _locked);

    function locked(uint256 _tokenId) external view returns (int128 _amount, uint256 _end);

    function ownerOf(uint256 _tokenId) external view returns (address _owner);

    function increase_amount(uint256 _tokenId, uint256 _amount) external;

    function increase_unlock_time(uint256 _tokenId, uint256 _duration) external;

    function create_lock(uint256 _amount, uint256 _end) external returns (uint256 tokenId);

    function create_lock_for(uint256 _amount, uint256 _end, address _to) external returns (uint256 tokenId);

    function approve(address who, uint256 tokenId) external;

    function balanceOfNFT(uint256) external view returns (uint256 amount);

    function user_point_epoch(uint256) external view returns (uint256);

    function user_point_history(uint256, uint256) external view returns (Point memory);

    function merge(uint256 _from, uint256 _to) external;

    function safeTransferFrom(address _from, address _to, uint256 _tokenId) external;
}
          

contracts/libraries/VelodromeTimeLibrary.sol

// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.19;

library VelodromeTimeLibrary {
  uint256 internal constant WEEK = 7 days;

  /// @dev Returns start of epoch based on current timestamp
  function epochStart(uint256 timestamp) internal pure returns (uint256) {
    unchecked {
      return timestamp - (timestamp % WEEK);
    }
  }

  /// @dev Returns start of next epoch / end of current epoch
  function epochNext(uint256 timestamp) internal pure returns (uint256) {
    unchecked {
      return timestamp - (timestamp % WEEK) + WEEK;
    }
  }

  /// @dev Returns start of voting window
  function epochVoteStart(uint256 timestamp) internal pure returns (uint256) {
    unchecked {
      return timestamp - (timestamp % WEEK) + 1 hours;
    }
  }

  /// @dev Returns end of voting window / beginning of unrestricted voting window
  function epochVoteEnd(uint256 timestamp) internal pure returns (uint256) {
    unchecked {
      return timestamp - (timestamp % WEEK) + WEEK - 1 hours;
    }
  }
}
          

contracts/v1/sink/SinkManagerFacilitator.sol

// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.19;

import { IVotingEscrowV1 } from "../../interfaces/v1/IVotingEscrowV1.sol";
import { ERC721Holder } from "@openzeppelin/contracts/token/ERC721/utils/ERC721Holder.sol";

/// @notice This contract is used to support merging into the Velodrome SinkManager
contract SinkManagerFacilitator is ERC721Holder {
  constructor() {}

  function merge(IVotingEscrowV1 _ve, uint256 _from, uint256 _to) external {
    _ve.merge(_from, _to);
  }
}
          

@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/IERC721Metadata.sol)

pragma solidity ^0.8.0;

import "../IERC721.sol";

/**
 * @title ERC-721 Non-Fungible Token Standard, optional metadata extension
 * @dev See https://eips.ethereum.org/EIPS/eip-721
 */
interface IERC721Metadata is IERC721 {
    /**
     * @dev Returns the token collection name.
     */
    function name() external view returns (string memory);

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

    /**
     * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
     */
    function tokenURI(uint256 tokenId) external view returns (string memory);
}
          

@openzeppelin/contracts/token/ERC721/utils/ERC721Holder.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC721/utils/ERC721Holder.sol)

pragma solidity ^0.8.0;

import "../IERC721Receiver.sol";

/**
 * @dev Implementation of the {IERC721Receiver} interface.
 *
 * Accepts all token transfers.
 * Make sure the contract is able to use its token with {IERC721-safeTransferFrom}, {IERC721-approve} or {IERC721-setApprovalForAll}.
 */
contract ERC721Holder is IERC721Receiver {
    /**
     * @dev See {IERC721Receiver-onERC721Received}.
     *
     * Always returns `IERC721Receiver.onERC721Received.selector`.
     */
    function onERC721Received(address, address, uint256, bytes memory) public virtual override returns (bytes4) {
        return this.onERC721Received.selector;
    }
}
          

contracts/governance/IVotes.sol

// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;

/// Modified IVotes interface for tokenId based voting
interface IVotes {
    /**
     * @dev Emitted when an account changes their delegate.
     */
    event DelegateChanged(address indexed delegator, uint256 indexed fromDelegate, uint256 indexed toDelegate);

    /**
     * @dev Emitted when a token transfer or delegate change results in changes to a delegate's number of votes.
     */
    event DelegateVotesChanged(address indexed delegate, uint256 previousBalance, uint256 newBalance);

    /**
     * @dev Returns the amount of votes that `tokenId` had at a specific moment in the past.
     *      If the account passed in is not the owner, returns 0.
     */
    function getPastVotes(address account, uint256 tokenId, uint256 timepoint) external view returns (uint256);

    /**
     * @dev Returns the total supply of votes available at a specific moment in the past. If the `clock()` is
     * configured to use block numbers, this will return the value the end of the corresponding block.
     *
     * NOTE: This value is the sum of all available votes, which is not necessarily the sum of all delegated votes.
     * Votes that have not been delegated are still part of total supply, even though they would not participate in a
     * vote.
     */
    function getPastTotalSupply(uint256 timepoint) external view returns (uint256);

    /**
     * @dev Returns the delegate that `tokenId` has chosen. Can never be equal to the delegator's `tokenId`.
     *      Returns 0 if not delegated.
     */
    function delegates(uint256 tokenId) external view returns (uint256);

    /**
     * @dev Delegates votes from the sender to `delegatee`.
     */
    function delegate(uint256 delegator, uint256 delegatee) external;

    /**
     * @dev Delegates votes from `delegator` to `delegatee`. Signer must own `delegator`.
     */
    function delegateBySig(
        uint256 delegator,
        uint256 delegatee,
        uint256 nonce,
        uint256 expiry,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;
}
          

@openzeppelin/contracts/access/Ownable.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)

pragma solidity ^0.8.0;

import "../utils/Context.sol";

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

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

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor() {
        _transferOwnership(_msgSender());
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

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

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
    }

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

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

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}
          

@openzeppelin/contracts/interfaces/IERC165.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (interfaces/IERC165.sol)

pragma solidity ^0.8.0;

import "../utils/introspection/IERC165.sol";
          

@openzeppelin/contracts/interfaces/IERC4906.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (interfaces/IERC4906.sol)

pragma solidity ^0.8.0;

import "./IERC165.sol";
import "./IERC721.sol";

/// @title EIP-721 Metadata Update Extension
interface IERC4906 is IERC165, IERC721 {
    /// @dev This event emits when the metadata of a token is changed.
    /// So that the third-party platforms such as NFT market could
    /// timely update the images and related attributes of the NFT.
    event MetadataUpdate(uint256 _tokenId);

    /// @dev This event emits when the metadata of a range of tokens is changed.
    /// So that the third-party platforms such as NFT market could
    /// timely update the images and related attributes of the NFTs.
    event BatchMetadataUpdate(uint256 _fromTokenId, uint256 _toTokenId);
}
          

@openzeppelin/contracts/interfaces/IERC721.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (interfaces/IERC721.sol)

pragma solidity ^0.8.0;

import "../token/ERC721/IERC721.sol";
          

@openzeppelin/contracts/metatx/ERC2771Context.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.3) (metatx/ERC2771Context.sol)

pragma solidity ^0.8.9;

import "../utils/Context.sol";

/**
 * @dev Context variant with ERC2771 support.
 */
abstract contract ERC2771Context is Context {
    /// @custom:oz-upgrades-unsafe-allow state-variable-immutable
    address private immutable _trustedForwarder;

    /// @custom:oz-upgrades-unsafe-allow constructor
    constructor(address trustedForwarder) {
        _trustedForwarder = trustedForwarder;
    }

    function isTrustedForwarder(address forwarder) public view virtual returns (bool) {
        return forwarder == _trustedForwarder;
    }

    function _msgSender() internal view virtual override returns (address sender) {
        if (isTrustedForwarder(msg.sender) && msg.data.length >= 20) {
            // The assembly code is more direct than the Solidity version using `abi.decode`.
            /// @solidity memory-safe-assembly
            assembly {
                sender := shr(96, calldataload(sub(calldatasize(), 20)))
            }
        } else {
            return super._msgSender();
        }
    }

    function _msgData() internal view virtual override returns (bytes calldata) {
        if (isTrustedForwarder(msg.sender) && msg.data.length >= 20) {
            return msg.data[:msg.data.length - 20];
        } else {
            return super._msgData();
        }
    }
}
          

@openzeppelin/contracts/utils/Context.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

pragma solidity ^0.8.0;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }
}
          

@openzeppelin/contracts/proxy/Clones.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (proxy/Clones.sol)

pragma solidity ^0.8.0;

/**
 * @dev https://eips.ethereum.org/EIPS/eip-1167[EIP 1167] is a standard for
 * deploying minimal proxy contracts, also known as "clones".
 *
 * > To simply and cheaply clone contract functionality in an immutable way, this standard specifies
 * > a minimal bytecode implementation that delegates all calls to a known, fixed address.
 *
 * The library includes functions to deploy a proxy using either `create` (traditional deployment) or `create2`
 * (salted deterministic deployment). It also includes functions to predict the addresses of clones deployed using the
 * deterministic method.
 *
 * _Available since v3.4._
 */
library Clones {
    /**
     * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
     *
     * This function uses the create opcode, which should never revert.
     */
    function clone(address implementation) internal returns (address instance) {
        /// @solidity memory-safe-assembly
        assembly {
            // Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes
            // of the `implementation` address with the bytecode before the address.
            mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000))
            // Packs the remaining 17 bytes of `implementation` with the bytecode after the address.
            mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3))
            instance := create(0, 0x09, 0x37)
        }
        require(instance != address(0), "ERC1167: create failed");
    }

    /**
     * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
     *
     * This function uses the create2 opcode and a `salt` to deterministically deploy
     * the clone. Using the same `implementation` and `salt` multiple time will revert, since
     * the clones cannot be deployed twice at the same address.
     */
    function cloneDeterministic(address implementation, bytes32 salt) internal returns (address instance) {
        /// @solidity memory-safe-assembly
        assembly {
            // Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes
            // of the `implementation` address with the bytecode before the address.
            mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000))
            // Packs the remaining 17 bytes of `implementation` with the bytecode after the address.
            mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3))
            instance := create2(0, 0x09, 0x37, salt)
        }
        require(instance != address(0), "ERC1167: create2 failed");
    }

    /**
     * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
     */
    function predictDeterministicAddress(
        address implementation,
        bytes32 salt,
        address deployer
    ) internal pure returns (address predicted) {
        /// @solidity memory-safe-assembly
        assembly {
            let ptr := mload(0x40)
            mstore(add(ptr, 0x38), deployer)
            mstore(add(ptr, 0x24), 0x5af43d82803e903d91602b57fd5bf3ff)
            mstore(add(ptr, 0x14), implementation)
            mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73)
            mstore(add(ptr, 0x58), salt)
            mstore(add(ptr, 0x78), keccak256(add(ptr, 0x0c), 0x37))
            predicted := keccak256(add(ptr, 0x43), 0x55)
        }
    }

    /**
     * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
     */
    function predictDeterministicAddress(
        address implementation,
        bytes32 salt
    ) internal view returns (address predicted) {
        return predictDeterministicAddress(implementation, salt, address(this));
    }
}
          

@openzeppelin/contracts/security/ReentrancyGuard.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (security/ReentrancyGuard.sol)

pragma solidity ^0.8.0;

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

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

    uint256 private _status;

    constructor() {
        _status = _NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and making it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        _nonReentrantBefore();
        _;
        _nonReentrantAfter();
    }

    function _nonReentrantBefore() private {
        // On the first call to nonReentrant, _status will be _NOT_ENTERED
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

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

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

    /**
     * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
     * `nonReentrant` function in the call stack.
     */
    function _reentrancyGuardEntered() internal view returns (bool) {
        return _status == _ENTERED;
    }
}
          

@openzeppelin/contracts/token/ERC20/IERC20.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

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

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

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

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

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

@openzeppelin/contracts/token/ERC721/IERC721.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC721/IERC721.sol)

pragma solidity ^0.8.0;

import "../../utils/introspection/IERC165.sol";

/**
 * @dev Required interface of an ERC721 compliant contract.
 */
interface IERC721 is IERC165 {
    /**
     * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
     */
    event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
     */
    event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
     */
    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);

    /**
     * @dev Returns the number of tokens in ``owner``'s account.
     */
    function balanceOf(address owner) external view returns (uint256 balance);

    /**
     * @dev Returns the owner of the `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function ownerOf(uint256 tokenId) external view returns (address owner);

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
     * are aware of the ERC721 protocol to prevent tokens from being forever locked.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must have been allowed to move this token by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(address from, address to, uint256 tokenId) external;

    /**
     * @dev Transfers `tokenId` token from `from` to `to`.
     *
     * WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721
     * or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must
     * understand this adds an external call which potentially creates a reentrancy vulnerability.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 tokenId) external;

    /**
     * @dev Gives permission to `to` to transfer `tokenId` token to another account.
     * The approval is cleared when the token is transferred.
     *
     * Only a single account can be approved at a time, so approving the zero address clears previous approvals.
     *
     * Requirements:
     *
     * - The caller must own the token or be an approved operator.
     * - `tokenId` must exist.
     *
     * Emits an {Approval} event.
     */
    function approve(address to, uint256 tokenId) external;

    /**
     * @dev Approve or remove `operator` as an operator for the caller.
     * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
     *
     * Requirements:
     *
     * - The `operator` cannot be the caller.
     *
     * Emits an {ApprovalForAll} event.
     */
    function setApprovalForAll(address operator, bool approved) external;

    /**
     * @dev Returns the account approved for `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function getApproved(uint256 tokenId) external view returns (address operator);

    /**
     * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
     *
     * See {setApprovalForAll}
     */
    function isApprovedForAll(address owner, address operator) external view returns (bool);
}
          

@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC721/IERC721Receiver.sol)

pragma solidity ^0.8.0;

/**
 * @title ERC721 token receiver interface
 * @dev Interface for any contract that wants to support safeTransfers
 * from ERC721 asset contracts.
 */
interface IERC721Receiver {
    /**
     * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
     * by `operator` from `from`, this function is called.
     *
     * It must return its Solidity selector to confirm the token transfer.
     * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.
     *
     * The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`.
     */
    function onERC721Received(
        address operator,
        address from,
        uint256 tokenId,
        bytes calldata data
    ) external returns (bytes4);
}
          

@openzeppelin/contracts/utils/introspection/IERC165.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
          

contracts/interfaces/IMinter.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IMinter {
  error AlreadyNudged();
  error NotEpochGovernor();
  error TailEmissionsInactive();

  event Mint(
    address indexed _sender,
    uint256 _weekly,
    uint256 _circulating_supply,
    bool indexed _tail
  );
  event Nudge(uint256 indexed _period, uint256 _oldRate, uint256 _newRate);

  /// @notice Timestamp of start of epoch that updatePeriod was last called in
  function activePeriod() external returns (uint256);

  /// @notice Allows epoch governor to modify the tail emission rate by at most 1 basis point
  ///         per epoch to a maximum of 100 basis points or to a minimum of 1 basis point.
  ///         Note: the very first nudge proposal must take place the week prior
  ///         to the tail emission schedule starting.
  /// @dev Throws if not epoch governor.
  ///      Throws if not currently in tail emission schedule.
  ///      Throws if already nudged this epoch.
  ///      Throws if nudging above maximum rate.
  ///      Throws if nudging below minimum rate.
  ///      This contract is coupled to EpochGovernor as it requires three option simple majority voting.
  function nudge() external;

  /// @notice Calculates rebases according to the formula
  ///         weekly * (ve.totalSupply / vara.totalSupply) ^ 3 / 2
  ///         Note that ve.totalSupply is the locked ve supply
  ///         vara.totalSupply is the total ve supply minted
  /// @param _minted Amount of VELO minted this epoch
  /// @return _growth Rebases
  function calculateGrowth(uint256 _minted) external view returns (uint256 _growth);

  /// @notice Processes emissions and rebases. Callable once per epoch (1 week).
  /// @return _period Start of current epoch.
  function updatePeriod() external returns (uint256 _period);
}
          

contracts/interfaces/ISinkManager.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import { IVara } from "./IVara.sol";

interface ISinkManager {
  event ConvertVELO(address indexed who, uint256 amount, uint256 timestamp);
  event ConvertVe(
    address indexed who,
    uint256 indexed tokenId,
    uint256 indexed tokenIdV2,
    uint256 amount,
    uint256 lockEnd,
    uint256 timestamp
  );
  event ClaimRebaseAndGaugeRewards(
    address indexed who,
    uint256 amountResidual,
    uint256 amountRewarded,
    uint256 amountRebased,
    uint256 timestamp
  );

  error ContractNotOwnerOfToken();
  error GaugeAlreadySet();
  error GaugeNotSet();
  error GaugeNotSinkDrain();
  error NFTAlreadyConverted();
  error NFTNotApproved();
  error NFTExpired();
  error TokenIdNotSet();
  error TokenIdAlreadySet();

  function ownedTokenId() external view returns (uint256);

  function vara() external view returns (IVara);

  function varaV2() external view returns (IVara);

  /// @notice Helper utility that returns amount of token captured by epoch
  function captured(uint256 timestamp) external view returns (uint256 amount);

  /// @notice User converts their v1 VELO into v2 VELO
  /// @param amount Amount of VELO to convert
  function convertVELO(uint256 amount) external;

  /// @notice User converts their v1 ve into v2 ve
  /// @param tokenId      Token ID of v1 ve
  /// @return tokenIdV2   Token ID of v2 ve
  function convertVe(uint256 tokenId) external returns (uint256 tokenIdV2);

  /// @notice Claim SinkManager-eligible rebase and gauge rewards to lock into the SinkManager-owned tokenId
  /// @dev Callable by anyone
  function claimRebaseAndGaugeRewards() external;
}
          

contracts/interfaces/IVara.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

interface IVara is IERC20 {
  error NotMinter();
  error NotOwner();
  error NotMinterOrSinkManager();
  error SinkManagerAlreadySet();

  function mint(address, uint256) external returns (bool);

  function minter() external returns (address);
}
          

contracts/interfaces/IVotingEscrow.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IERC721, IERC721Metadata} from "@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol";
import {IERC4906} from "@openzeppelin/contracts/interfaces/IERC4906.sol";
import {IVotes} from "../governance/IVotes.sol";

interface IVotingEscrow is IVotes, IERC4906, IERC721Metadata {
    struct LockedBalance {
        int128 amount;
        uint256 end;
        bool isPermanent;
    }

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

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

    /// @notice A checkpoint for recorded delegated voting weights at a certain timestamp
    struct Checkpoint {
        uint256 fromTimestamp;
        address owner;
        uint256 delegatedBalance;
        uint256 delegatee;
    }

    enum DepositType {
        DEPOSIT_FOR_TYPE,
        CREATE_LOCK_TYPE,
        INCREASE_LOCK_AMOUNT,
        INCREASE_UNLOCK_TIME
    }

    /// @dev Different types of veNFTs:
    /// NORMAL  - typical veNFT
    /// LOCKED  - veNFT which is locked into a MANAGED veNFT
    /// MANAGED - veNFT which can accept the deposit of NORMAL veNFTs
    enum EscrowType {
        NORMAL,
        LOCKED,
        MANAGED
    }

    error AlreadyVoted();
    error AmountTooBig();
    error ERC721ReceiverRejectedTokens();
    error ERC721TransferToNonERC721ReceiverImplementer();
    error InvalidNonce();
    error InvalidSignature();
    error InvalidSignatureS();
    error InvalidManagedNFTId();
    error LockDurationNotInFuture();
    error LockDurationTooLong();
    error LockExpired();
    error LockNotExpired();
    error NoLockFound();
    error NonExistentToken();
    error NotApprovedOrOwner();
    error NotDistributor();
    error NotEmergencyCouncilOrGovernor();
    error NotGovernor();
    error NotGovernorOrManager();
    error NotManagedNFT();
    error NotManagedOrNormalNFT();
    error NotLockedNFT();
    error NotNormalNFT();
    error NotPermanentLock();
    error NotOwner();
    error NotTeam();
    error NotVoter();
    error OwnershipChange();
    error PermanentLock();
    error SameAddress();
    error SameNFT();
    error SameState();
    error SplitNoOwner();
    error SplitNotAllowed();
    error SignatureExpired();
    error TooManyTokenIDs();
    error ZeroAddress();
    error ZeroAmount();
    error ZeroBalance();

    event Deposit(
        address indexed provider,
        uint256 indexed tokenId,
        DepositType indexed depositType,
        uint256 value,
        uint256 locktime,
        uint256 ts
    );
    event Withdraw(address indexed provider, uint256 indexed tokenId, uint256 value, uint256 ts);
    event LockPermanent(address indexed _owner, uint256 indexed _tokenId, uint256 amount, uint256 _ts);
    event UnlockPermanent(address indexed _owner, uint256 indexed _tokenId, uint256 amount, uint256 _ts);
    event Supply(uint256 prevSupply, uint256 supply);
    event Merge(
        address indexed _sender,
        uint256 indexed _from,
        uint256 indexed _to,
        uint256 _amountFrom,
        uint256 _amountTo,
        uint256 _amountFinal,
        uint256 _locktime,
        uint256 _ts
    );
    event Split(
        uint256 indexed _from,
        uint256 indexed _tokenId1,
        uint256 indexed _tokenId2,
        address _sender,
        uint256 _splitAmount1,
        uint256 _splitAmount2,
        uint256 _locktime,
        uint256 _ts
    );
    event CreateManaged(
        address indexed _to,
        uint256 indexed _mTokenId,
        address indexed _from,
        address _lockedManagedReward,
        address _freeManagedReward
    );
    event DepositManaged(
        address indexed _owner,
        uint256 indexed _tokenId,
        uint256 indexed _mTokenId,
        uint256 _weight,
        uint256 _ts
    );
    event WithdrawManaged(
        address indexed _owner,
        uint256 indexed _tokenId,
        uint256 indexed _mTokenId,
        uint256 _weight,
        uint256 _ts
    );
    event SetAllowedManager(address indexed _allowedManager);

    // State variables
    function factoryRegistry() external view returns (address);

    function token() external view returns (address);

    function distributor() external view returns (address);

    function voter() external view returns (address);

    function team() external view returns (address);

    function artProxy() external view returns (address);

    function allowedManager() external view returns (address);

    function tokenId() external view returns (uint256);

    /*///////////////////////////////////////////////////////////////
                            MANAGED NFT STORAGE
    //////////////////////////////////////////////////////////////*/

    /// @dev Mapping of token id to escrow type
    ///      Takes advantage of the fact default value is EscrowType.NORMAL
    function escrowType(uint256 tokenId) external view returns (EscrowType);

    /// @dev Mapping of token id to managed id
    function idToManaged(uint256 tokenId) external view returns (uint256 managedTokenId);

    /// @dev Mapping of user token id to managed token id to weight of token id
    function weights(uint256 tokenId, uint256 managedTokenId) external view returns (uint256 weight);

    /// @dev Mapping of managed id to deactivated state
    function deactivated(uint256 tokenId) external view returns (bool inactive);

    /// @dev Mapping from managed nft id to locked managed rewards
    ///      `token` denominated rewards (rebases/rewards) stored in locked managed rewards contract
    ///      to prevent co-mingling of assets
    function managedToLocked(uint256 tokenId) external view returns (address);

    /// @dev Mapping from managed nft id to free managed rewards contract
    ///      these rewards can be freely withdrawn by users
    function managedToFree(uint256 tokenId) external view returns (address);

    /*///////////////////////////////////////////////////////////////
                            MANAGED NFT LOGIC
    //////////////////////////////////////////////////////////////*/

    /// @notice Create managed NFT (a permanent lock) for use within ecosystem.
    /// @dev Throws if address already owns a managed NFT.
    /// @return _mTokenId managed token id.
    function createManagedLockFor(address _to) external returns (uint256 _mTokenId);

    /// @notice Delegates balance to managed nft
    ///         Note that NFTs deposited into a managed NFT will be re-locked
    ///         to the maximum lock time on withdrawal.
    ///         Permanent locks that are deposited will automatically unlock.
    /// @dev Managed nft will remain max-locked as long as there is at least one
    ///      deposit or withdrawal per week.
    ///      Throws if deposit nft is managed.
    ///      Throws if recipient nft is not managed.
    ///      Throws if deposit nft is already locked.
    ///      Throws if not called by voter.
    /// @param _tokenId tokenId of NFT being deposited
    /// @param _mTokenId tokenId of managed NFT that will receive the deposit
    function depositManaged(uint256 _tokenId, uint256 _mTokenId) external;

    /// @notice Retrieves locked rewards and withdraws balance from managed nft.
    ///         Note that the NFT withdrawn is re-locked to the maximum lock time.
    /// @dev Throws if NFT not locked.
    ///      Throws if not called by voter.
    /// @param _tokenId tokenId of NFT being deposited.
    function withdrawManaged(uint256 _tokenId) external;

    /// @notice Permit one address to call createManagedLockFor() that is not Voter.governor()
    function setAllowedManager(address _allowedManager) external;

    /// @notice Set Managed NFT state. Inactive NFTs cannot be deposited into.
    /// @param _mTokenId managed nft state to set
    /// @param _state true => inactive, false => active
    function setManagedState(uint256 _mTokenId, bool _state) external;

    /*///////////////////////////////////////////////////////////////
                             METADATA STORAGE
    //////////////////////////////////////////////////////////////*/

    function name() external view returns (string memory);

    function symbol() external view returns (string memory);

    function version() external view returns (string memory);

    function decimals() external view returns (uint8);

    function setTeam(address _team) external;

    function setArtProxy(address _proxy) external;

    /// @inheritdoc IERC721Metadata
    function tokenURI(uint256 tokenId) external view returns (string memory);

    /*//////////////////////////////////////////////////////////////
                      ERC721 BALANCE/OWNER STORAGE
    //////////////////////////////////////////////////////////////*/

    /// @dev Mapping from owner address to mapping of index to tokenId
    function ownerToNFTokenIdList(address _owner, uint256 _index) external view returns (uint256 _tokenId);

    /// @inheritdoc IERC721
    function ownerOf(uint256 tokenId) external view returns (address owner);

    /// @inheritdoc IERC721
    function balanceOf(address owner) external view returns (uint256 balance);

    /*//////////////////////////////////////////////////////////////
                         ERC721 APPROVAL STORAGE
    //////////////////////////////////////////////////////////////*/

    /// @inheritdoc IERC721
    function getApproved(uint256 _tokenId) external view returns (address operator);

    /// @inheritdoc IERC721
    function isApprovedForAll(address owner, address operator) external view returns (bool);

    /// @notice Check whether spender is owner or an approved user for a given veNFT
    /// @param _spender .
    /// @param _tokenId .
    function isApprovedOrOwner(address _spender, uint256 _tokenId) external returns (bool);

    /*//////////////////////////////////////////////////////////////
                              ERC721 LOGIC
    //////////////////////////////////////////////////////////////*/

    /// @inheritdoc IERC721
    function approve(address to, uint256 tokenId) external;

    /// @inheritdoc IERC721
    function setApprovalForAll(address operator, bool approved) external;

    /// @inheritdoc IERC721
    function transferFrom(address from, address to, uint256 tokenId) external;

    /// @inheritdoc IERC721
    function safeTransferFrom(address from, address to, uint256 tokenId) external;

    /// @inheritdoc IERC721
    function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;

    /*//////////////////////////////////////////////////////////////
                              ERC165 LOGIC
    //////////////////////////////////////////////////////////////*/

    function supportsInterface(bytes4 _interfaceID) external view returns (bool);

    /*//////////////////////////////////////////////////////////////
                             ESCROW STORAGE
    //////////////////////////////////////////////////////////////*/

    function epoch() external view returns (uint256);

    function supply() external view returns (uint256);

    function userPointEpoch(uint256 _tokenId) external view returns (uint256 _epoch);

    /// @notice time -> signed slope change
    function slopeChanges(uint256 _timestamp) external view returns (int128);

    /// @notice account -> can split
    function canSplit(address _account) external view returns (bool);

    /// @notice Global point history at a given index
    function pointHistory(uint256 _loc) external view returns (GlobalPoint memory);

    /// @notice Get the LockedBalance (amount, end) of a _tokenId
    /// @param _tokenId .
    /// @return LockedBalance of _tokenId
    function locked(uint256 _tokenId) external view returns (LockedBalance memory);

    /// @notice User -> UserPoint[userEpoch]
    function userPointHistory(uint256 _tokenId, uint256 _loc) external view returns (UserPoint memory);

    /*//////////////////////////////////////////////////////////////
                              ESCROW LOGIC
    //////////////////////////////////////////////////////////////*/

    /// @notice Record global data to checkpoint
    function checkpoint() external;

    /// @notice Deposit `_value` tokens for `_tokenId` and add to the lock
    /// @dev Anyone (even a smart contract) can deposit for someone else, but
    ///      cannot extend their locktime and deposit for a brand new user
    /// @param _tokenId lock NFT
    /// @param _value Amount to add to user's lock
    function depositFor(uint256 _tokenId, uint256 _value) external;

    /// @notice Deposit `_value` tokens for `msg.sender` and lock for `_lockDuration`
    /// @param _value Amount to deposit
    /// @param _lockDuration Number of seconds to lock tokens for (rounded down to nearest week)
    /// @return TokenId of created veNFT
    function createLock(uint256 _value, uint256 _lockDuration) external returns (uint256);

    /// @notice Deposit `_value` tokens for `_to` and lock for `_lockDuration`
    /// @param _value Amount to deposit
    /// @param _lockDuration Number of seconds to lock tokens for (rounded down to nearest week)
    /// @param _to Address to deposit
    /// @return TokenId of created veNFT
    function createLockFor(uint256 _value, uint256 _lockDuration, address _to) external returns (uint256);

    /// @notice Deposit `_value` additional tokens for `_tokenId` without modifying the unlock time
    /// @param _value Amount of tokens to deposit and add to the lock
    function increaseAmount(uint256 _tokenId, uint256 _value) external;

    /// @notice Extend the unlock time for `_tokenId`
    ///         Cannot extend lock time of permanent locks
    /// @param _lockDuration New number of seconds until tokens unlock
    function increaseUnlockTime(uint256 _tokenId, uint256 _lockDuration) external;

    /// @notice Withdraw all tokens for `_tokenId`
    /// @dev Only possible if the lock is both expired and not permanent
    ///      This will burn the veNFT. Any rebases or rewards that are unclaimed
    ///      will no longer be claimable. Claim all rebases and rewards prior to calling this.
    function withdraw(uint256 _tokenId) external;

    /// @notice Merges `_from` into `_to`.
    /// @dev Cannot merge `_from` locks that are permanent or have already voted this epoch.
    ///      Cannot merge `_to` locks that have already expired.
    ///      This will burn the veNFT. Any rebases or rewards that are unclaimed
    ///      will no longer be claimable. Claim all rebases and rewards prior to calling this.
    /// @param _from VeNFT to merge from.
    /// @param _to VeNFT to merge into.
    function merge(uint256 _from, uint256 _to) external;

    /// @notice Splits veNFT into two new veNFTS - one with oldLocked.amount - `_amount`, and the second with `_amount`
    /// @dev    This burns the tokenId of the target veNFT
    ///         Callable by approved or owner
    ///         If this is called by approved, approved will not have permissions to manipulate the newly created veNFTs
    ///         Returns the two new split veNFTs to owner
    ///         If `from` is permanent, will automatically dedelegate.
    ///         This will burn the veNFT. Any rebases or rewards that are unclaimed
    ///         will no longer be claimable. Claim all rebases and rewards prior to calling this.
    /// @param _from VeNFT to split.
    /// @param _amount Amount to split from veNFT.
    /// @return _tokenId1 Return tokenId of veNFT with oldLocked.amount - `_amount`.
    /// @return _tokenId2 Return tokenId of veNFT with `_amount`.
    function split(uint256 _from, uint256 _amount) external returns (uint256 _tokenId1, uint256 _tokenId2);

    /// @notice Toggle split for a specific veNFT.
    /// @dev Toggle split for address(0) to enable or disable for all.
    /// @param _account Address to toggle split permissions
    /// @param _bool True to allow, false to disallow
    function toggleSplit(address _account, bool _bool) external;

    /// @notice Permanently lock a veNFT. Voting power will be equal to
    ///         `LockedBalance.amount` with no decay. Required to delegate.
    /// @dev Only callable by unlocked normal veNFTs.
    /// @param _tokenId tokenId to lock.
    function lockPermanent(uint256 _tokenId) external;

    /// @notice Unlock a permanently locked veNFT. Voting power will decay.
    ///         Will automatically dedelegate if delegated.
    /// @dev Only callable by permanently locked veNFTs.
    ///      Cannot unlock if already voted this epoch.
    /// @param _tokenId tokenId to unlock.
    function unlockPermanent(uint256 _tokenId) external;

    /*///////////////////////////////////////////////////////////////
                           GAUGE VOTING STORAGE
    //////////////////////////////////////////////////////////////*/

    /// @notice Get the voting power for _tokenId at the current timestamp
    /// @dev Returns 0 if called in the same block as a transfer.
    /// @param _tokenId .
    /// @return Voting power
    function balanceOfNFT(uint256 _tokenId) external view returns (uint256);

    /// @notice Get the voting power for _tokenId at a given timestamp
    /// @param _tokenId .
    /// @param _t Timestamp to query voting power
    /// @return Voting power
    function balanceOfNFTAt(uint256 _tokenId, uint256 _t) external view returns (uint256);

    /// @notice Calculate total voting power at current timestamp
    /// @return Total voting power at current timestamp
    function totalSupply() external view returns (uint256);

    /// @notice Calculate total voting power at a given timestamp
    /// @param _t Timestamp to query total voting power
    /// @return Total voting power at given timestamp
    function totalSupplyAt(uint256 _t) external view returns (uint256);

    /*///////////////////////////////////////////////////////////////
                            GAUGE VOTING LOGIC
    //////////////////////////////////////////////////////////////*/

    /// @notice See if a queried _tokenId has actively voted
    /// @param _tokenId .
    /// @return True if voted, else false
    function voted(uint256 _tokenId) external view returns (bool);

    /// @notice Set the global state voter and distributor
    /// @dev This is only called once, at setup
    function setVoterAndDistributor(address _voter, address _distributor) external;

    /// @notice Set `voted` for _tokenId to true or false
    /// @dev Only callable by voter
    /// @param _tokenId .
    /// @param _voted .
    function voting(uint256 _tokenId, bool _voted) external;

    /*///////////////////////////////////////////////////////////////
                            DAO VOTING STORAGE
    //////////////////////////////////////////////////////////////*/

    /// @notice The number of checkpoints for each tokenId
    function numCheckpoints(uint256 tokenId) external view returns (uint48);

    /// @notice A record of states for signing / validating signatures
    function nonces(address account) external view returns (uint256);

    /// @inheritdoc IVotes
    function delegates(uint256 delegator) external view returns (uint256);

    /// @notice A record of delegated token checkpoints for each account, by index
    /// @param tokenId .
    /// @param index .
    /// @return Checkpoint
    function checkpoints(uint256 tokenId, uint48 index) external view returns (Checkpoint memory);

    /// @inheritdoc IVotes
    function getPastVotes(address account, uint256 tokenId, uint256 timestamp) external view returns (uint256);

    /// @inheritdoc IVotes
    function getPastTotalSupply(uint256 timestamp) external view returns (uint256);

    /*///////////////////////////////////////////////////////////////
                             DAO VOTING LOGIC
    //////////////////////////////////////////////////////////////*/

    /// @inheritdoc IVotes
    function delegate(uint256 delegator, uint256 delegatee) external;

    /// @inheritdoc IVotes
    function delegateBySig(
        uint256 delegator,
        uint256 delegatee,
        uint256 nonce,
        uint256 expiry,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;
}
          

contracts/interfaces/v1/IGaugeV1.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IGaugeV1 {
    function balanceOf(address _account) external returns (uint256 _balance);

    function stake() external returns (address _stake);

    function totalSupply() external returns (uint256 _totalSupply);

    function getReward(address _account, address[] memory _tokens) external;

    function deposit(uint256 _amount, uint256 _tokenId) external;

    function notifyRewardAmount(address _token, uint256 _amount) external;
}
          

contracts/interfaces/v1/IRewardsDistributorV1.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IRewardsDistributorV1 {
    function claimable(uint256 _tokenId) external view returns (uint256 _claimable);

    function claim(uint256 _tokenId) external returns (uint256 _amount);
}
          

contracts/interfaces/v1/IVoterV1.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IVoterV1 {
    function governor() external returns (address _governor);

    function totalWeight() external returns (uint256 _totalWeight);

    function usedWeights(uint256 _tokenId) external returns (uint256 _weight);

    function votes(uint256 _tokenId, address _pool) external returns (uint256 _votes);

    function createGauge(address _pool) external returns (address _gauge);

    function gauges(address pool) external view returns (address);

    function distribute(address _gauge) external;

    function poke(uint256 _tokenId) external;

    function vote(uint256 _tokenId, address[] memory _pools, uint256[] memory _weights) external;
}
          

contracts/interfaces/v1/IVotingEscrowV1.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

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

    function getApproved(uint256 _tokenId) external view returns (address);

    function isApprovedOrOwner(address _spender, uint256 _tokenId) external returns (bool);

    function locked__end(uint256 _tokenId) external view returns (uint256 _locked);

    function locked(uint256 _tokenId) external view returns (int128 _amount, uint256 _end);

    function ownerOf(uint256 _tokenId) external view returns (address _owner);

    function increase_amount(uint256 _tokenId, uint256 _amount) external;

    function increase_unlock_time(uint256 _tokenId, uint256 _duration) external;

    function create_lock(uint256 _amount, uint256 _end) external returns (uint256 tokenId);

    function create_lock_for(uint256 _amount, uint256 _end, address _to) external returns (uint256 tokenId);

    function approve(address who, uint256 tokenId) external;

    function balanceOfNFT(uint256) external view returns (uint256 amount);

    function user_point_epoch(uint256) external view returns (uint256);

    function user_point_history(uint256, uint256) external view returns (Point memory);

    function merge(uint256 _from, uint256 _to) external;

    function safeTransferFrom(address _from, address _to, uint256 _tokenId) external;
}
          

contracts/libraries/VelodromeTimeLibrary.sol

// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.19;

library VelodromeTimeLibrary {
  uint256 internal constant WEEK = 7 days;

  /// @dev Returns start of epoch based on current timestamp
  function epochStart(uint256 timestamp) internal pure returns (uint256) {
    unchecked {
      return timestamp - (timestamp % WEEK);
    }
  }

  /// @dev Returns start of next epoch / end of current epoch
  function epochNext(uint256 timestamp) internal pure returns (uint256) {
    unchecked {
      return timestamp - (timestamp % WEEK) + WEEK;
    }
  }

  /// @dev Returns start of voting window
  function epochVoteStart(uint256 timestamp) internal pure returns (uint256) {
    unchecked {
      return timestamp - (timestamp % WEEK) + 1 hours;
    }
  }

  /// @dev Returns end of voting window / beginning of unrestricted voting window
  function epochVoteEnd(uint256 timestamp) internal pure returns (uint256) {
    unchecked {
      return timestamp - (timestamp % WEEK) + WEEK - 1 hours;
    }
  }
}
          

contracts/v1/sink/SinkManagerFacilitator.sol

// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.19;

import { IVotingEscrowV1 } from "../../interfaces/v1/IVotingEscrowV1.sol";
import { ERC721Holder } from "@openzeppelin/contracts/token/ERC721/utils/ERC721Holder.sol";

/// @notice This contract is used to support merging into the Velodrome SinkManager
contract SinkManagerFacilitator is ERC721Holder {
  constructor() {}

  function merge(IVotingEscrowV1 _ve, uint256 _from, uint256 _to) external {
    _ve.merge(_from, _to);
  }
}
          

Compiler Settings

{"outputSelection":{"*":{"*":["abi","evm.bytecode","evm.deployedBytecode","evm.methodIdentifiers"]}},"optimizer":{"runs":200,"enabled":true},"libraries":{}}
              

Contract ABI

[{"type":"constructor","stateMutability":"nonpayable","inputs":[{"type":"address","name":"_forwarder","internalType":"address"},{"type":"address","name":"_sinkDrain","internalType":"address"},{"type":"address","name":"_facilitatorImplementation","internalType":"address"},{"type":"address","name":"_voter","internalType":"address"},{"type":"address","name":"_vara","internalType":"address"},{"type":"address","name":"_varaV2","internalType":"address"},{"type":"address","name":"_ve","internalType":"address"},{"type":"address","name":"_veV2","internalType":"address"},{"type":"address","name":"_rewardsDistributor","internalType":"address"}]},{"type":"error","name":"ContractNotOwnerOfToken","inputs":[]},{"type":"error","name":"GaugeAlreadySet","inputs":[]},{"type":"error","name":"GaugeNotSet","inputs":[]},{"type":"error","name":"GaugeNotSinkDrain","inputs":[]},{"type":"error","name":"NFTAlreadyConverted","inputs":[]},{"type":"error","name":"NFTExpired","inputs":[]},{"type":"error","name":"NFTNotApproved","inputs":[]},{"type":"error","name":"TokenIdAlreadySet","inputs":[]},{"type":"error","name":"TokenIdNotSet","inputs":[]},{"type":"event","name":"ClaimRebaseAndGaugeRewards","inputs":[{"type":"address","name":"who","internalType":"address","indexed":true},{"type":"uint256","name":"amountResidual","internalType":"uint256","indexed":false},{"type":"uint256","name":"amountRewarded","internalType":"uint256","indexed":false},{"type":"uint256","name":"amountRebased","internalType":"uint256","indexed":false},{"type":"uint256","name":"timestamp","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"ConvertVELO","inputs":[{"type":"address","name":"who","internalType":"address","indexed":true},{"type":"uint256","name":"amount","internalType":"uint256","indexed":false},{"type":"uint256","name":"timestamp","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"ConvertVe","inputs":[{"type":"address","name":"who","internalType":"address","indexed":true},{"type":"uint256","name":"tokenId","internalType":"uint256","indexed":true},{"type":"uint256","name":"tokenIdV2","internalType":"uint256","indexed":true},{"type":"uint256","name":"amount","internalType":"uint256","indexed":false},{"type":"uint256","name":"lockEnd","internalType":"uint256","indexed":false},{"type":"uint256","name":"timestamp","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"OwnershipTransferred","inputs":[{"type":"address","name":"previousOwner","internalType":"address","indexed":true},{"type":"address","name":"newOwner","internalType":"address","indexed":true}],"anonymous":false},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"_amount","internalType":"uint256"}],"name":"captured","inputs":[{"type":"uint256","name":"_timestamp","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"claimRebaseAndGaugeRewards","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"conversions","inputs":[{"type":"uint256","name":"","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"convertVELO","inputs":[{"type":"uint256","name":"amount","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256","name":"tokenIdV2","internalType":"uint256"}],"name":"convertVe","inputs":[{"type":"uint256","name":"tokenId","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"facilitatorImplementation","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"facilitators","inputs":[{"type":"uint256","name":"","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IGaugeV1"}],"name":"gauge","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"isTrustedForwarder","inputs":[{"type":"address","name":"forwarder","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IMinter"}],"name":"minterV2","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bytes4","name":"","internalType":"bytes4"}],"name":"onERC721Received","inputs":[{"type":"address","name":"","internalType":"address"},{"type":"address","name":"","internalType":"address"},{"type":"uint256","name":"","internalType":"uint256"},{"type":"bytes","name":"","internalType":"bytes"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"ownedTokenId","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"owner","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"renounceOwnership","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IRewardsDistributorV1"}],"name":"rewardsDistributor","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setOwnedTokenId","inputs":[{"type":"uint256","name":"tokenId","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setupSinkDrain","inputs":[{"type":"address","name":"_gauge","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"transferOwnership","inputs":[{"type":"address","name":"newOwner","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IVara"}],"name":"vara","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IVara"}],"name":"varaV2","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IVotingEscrowV1"}],"name":"ve","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IVotingEscrow"}],"name":"veV2","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IVoterV1"}],"name":"voter","inputs":[]}]
              

Contract Creation Code

0x6101806040523480156200001257600080fd5b50604051620024ef380380620024ef8339810160408190526200003591620002d9565b6001600160a01b038916608052620000566200005062000224565b62000235565b60018055600680546001600160a01b03808b166001600160a01b031992831617909255600780548a8416921691909117905586811660a05285811660c052841660e0819052604080516303aa30b960e11b8152905163075461729160048082019260209290919082900301816000875af1158015620000d9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620000ff919062000394565b6001600160a01b0390811661010052838116610120819052838216610140528282166101605260c05160405163095ea7b360e01b8152600481019290925260001960248301529091169063095ea7b3906044016020604051808303816000875af115801562000172573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620001989190620003b9565b5060e05160405163095ea7b360e01b81526001600160a01b03848116600483015260001960248301529091169063095ea7b3906044016020604051808303816000875af1158015620001ee573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620002149190620003b9565b50505050505050505050620003dd565b60006200023062000285565b905090565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6080516000906001600160a01b031633148015620002a4575060143610155b15620002b7575060131936013560601c90565b503390565b80516001600160a01b0381168114620002d457600080fd5b919050565b60008060008060008060008060006101208a8c031215620002f957600080fd5b620003048a620002bc565b98506200031460208b01620002bc565b97506200032460408b01620002bc565b96506200033460608b01620002bc565b95506200034460808b01620002bc565b94506200035460a08b01620002bc565b93506200036460c08b01620002bc565b92506200037460e08b01620002bc565b9150620003856101008b01620002bc565b90509295985092959850929598565b600060208284031215620003a757600080fd5b620003b282620002bc565b9392505050565b600060208284031215620003cc57600080fd5b81518015158114620003b257600080fd5b60805160a05160c05160e05161010051610120516101405161016051611fef620005006000396000818161021d015281816106f5015261078601526000818161037001526112aa0152600081816101c1015281816106770152818161084a015281816108df01528181610e6701528181610f11015281816110cf0152818161115001528181611344015281816113b80152818161144d01528181611768015261193b0152600081816102e901528181610fb0015261162e0152600081816103ee0152818161120701526117f00152600081816104150152818161047b01528181610516015281816105d001526116dd0152600081816102440152818161095a01528181610d7c01526114c70152600081816102890152611c280152611fef6000f3fe608060405234801561001057600080fd5b506004361061014d5760003560e01c80637482cb13116100c3578063d0de8ee21161007c578063d0de8ee214610392578063e3679450146103a5578063e4d7766b146103ae578063f2fde38b146103d6578063fcd4cf60146103e9578063fd84b53b1461041057600080fd5b80637482cb13146102e45780638da5cb5b1461030b578063a6f19c841461031c578063b1e991c11461032f578063b50c276514610342578063c865b7841461036b57600080fd5b80633f2a5540116101155780633f2a55401461021857806346c96aac1461023f5780634cab93ae14610266578063572b6c0514610279578063629b0ff4146102c9578063715018a6146102dc57600080fd5b8063150b7a02146101525780631c9893901461018e5780631f850716146101bc578063324528ff146101fb5780633b53d6f014610205575b600080fd5b610170610160366004611ca1565b630a85bd0160e11b949350505050565b6040516001600160e01b031990911681526020015b60405180910390f35b6101ae61019c366004611d81565b60036020526000908152604090205481565b604051908152602001610185565b6101e37f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b039091168152602001610185565b610203610437565b005b610203610213366004611d9a565b610a24565b6101e37f000000000000000000000000000000000000000000000000000000000000000081565b6101e37f000000000000000000000000000000000000000000000000000000000000000081565b6101ae610274366004611d81565b610def565b6102b9610287366004611d9a565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0390811691161490565b6040519015158152602001610185565b6102036102d7366004611d81565b6115fb565b6102036118de565b6101e37f000000000000000000000000000000000000000000000000000000000000000081565b6000546001600160a01b03166101e3565b6008546101e3906001600160a01b031681565b6007546101e3906001600160a01b031681565b6101e3610350366004611d81565b6004602052600090815260409020546001600160a01b031681565b6101e37f000000000000000000000000000000000000000000000000000000000000000081565b6102036103a0366004611d81565b6118f2565b6101ae60055481565b6101ae6103bc366004611d81565b62093a808106900360009081526009602052604090205490565b6102036103e4366004611d9a565b6119d2565b6101e37f000000000000000000000000000000000000000000000000000000000000000081565b6101e37f000000000000000000000000000000000000000000000000000000000000000081565b6008546001600160a01b0316610460576040516361877ef360e01b815260040160405180910390fd5b6005546040516370a0823160e01b81523060048201526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa1580156104ca573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104ee9190611dbe565b60408051600180825281830190925291925060009190602080830190803683370190505090507f00000000000000000000000000000000000000000000000000000000000000008160008151811061054857610548611dd7565b6001600160a01b0392831660209182029290920101526008546040516331279d3d60e01b81529116906331279d3d906105879030908590600401611e31565b600060405180830381600087803b1580156105a157600080fd5b505af11580156105b5573d6000803e3d6000fd5b50506040516370a0823160e01b8152306004820152600092507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031691506370a0823190602401602060405180830381865afa158015610620573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106449190611dbe565b905060006106528483611e73565b905081156106dc576040516350c1d7a960e11b815260048101869052602481018390527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063a183af5290604401600060405180830381600087803b1580156106c357600080fd5b505af11580156106d7573d6000803e3d6000fd5b505050505b60405163d1d58b2560e01b8152600481018690526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063d1d58b2590602401602060405180830381865afa158015610744573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107689190611dbe565b905080156107fd5760405163379607f560e01b8152600481018790527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063379607f5906024016020604051808303816000875af11580156107d7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107fb9190611dbe565b505b600062093a8080610812630784ce0042611e86565b61081c9190611e99565b6108269190611ebb565b604051635a2d1e0760e11b8152600481018990529091506000906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063b45a3c0e906024016040805180830381865afa158015610890573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108b49190611ed2565b915050808211156109445760405163a4d855df60e01b815260048101899052630784ce0060248201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063a4d855df90604401600060405180830381600087803b15801561092b57600080fd5b505af115801561093f573d6000803e3d6000fd5b505050505b60405163032145f960e41b8152600481018990527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906332145f9090602401600060405180830381600087803b1580156109a657600080fd5b505af11580156109ba573d6000803e3d6000fd5b505050506109c6611a50565b60408051898152602081018790529081018590524260608201526001600160a01b0391909116907fac162ad79ec08307123947685e1233c9f2e5d5b672fdd396127a0443a3ac1c8a9060800160405180910390a25050505050505050565b610a2c611a5f565b6005546000819003610a515760405163295fa13d60e11b815260040160405180910390fd5b6008546001600160a01b031615610a7b5760405163291f9deb60e01b815260040160405180910390fd5b600880546001600160a01b0319166001600160a01b03841690811790915560408051633a4b66f160e01b8152905160009291633a4b66f1916004808301926020929190829003018187875af1158015610ad8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610afc9190611f06565b6040516370a0823160e01b81523060048201529091506000906001600160a01b038316906370a0823190602401602060405180830381865afa158015610b46573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b6a9190611dbe565b60405163095ea7b360e01b81526001600160a01b038681166004830152602482018390529192509083169063095ea7b3906044016020604051808303816000875af1158015610bbd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610be19190611f23565b50600854604051631c57762b60e31b815260048101839052600060248201526001600160a01b039091169063e2bbb15890604401600060405180830381600087803b158015610c2f57600080fd5b505af1158015610c43573d6000803e3d6000fd5b506000925060019150610c539050565b604051908082528060200260200182016040528015610c7c578160200160208202803683370190505b506040805160018082528183019092529192506000919060208083019080368337505060085460408051633a4b66f160e01b815290519394506001600160a01b0390911692633a4b66f19250600480830192602092919082900301816000875af1158015610cee573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d129190611f06565b82600081518110610d2557610d25611dd7565b60200260200101906001600160a01b031690816001600160a01b031681525050600181600081518110610d5a57610d5a611dd7565b6020908102919091010152604051637ac09bf760e01b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690637ac09bf790610db590889086908690600401611f45565b600060405180830381600087803b158015610dcf57600080fd5b505af1158015610de3573d6000803e3d6000fd5b50505050505050505050565b6000610df9611ad8565b6005546000819003610e1e5760405163295fa13d60e11b815260040160405180910390fd5b60008381526003602052604090205415610e4b57604051632fc4601360e01b815260040160405180910390fd5b60405163430c208160e01b8152306004820152602481018490527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063430c2081906044016020604051808303816000875af1158015610eb8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610edc9190611f23565b610ef95760405163876dcc9f60e01b815260040160405180910390fd5b60405163f8a0576360e01b81526004810184905242907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063f8a0576390602401602060405180830381865afa158015610f60573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f849190611dbe565b11610fa257604051637bdb3cff60e11b815260040160405180910390fd5b6000610fac611a50565b90507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663a83627de6040518163ffffffff1660e01b81526004016020604051808303816000875af115801561100e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110329190611dbe565b506007546002805460009261109e926001600160a01b0390911691849061105890611fa0565b9182905550611068600143611e73565b40604051602001611083929190918252602082015260400190565b60405160208183030381529060405280519060200120611b31565b604051632142170760e11b81526001600160a01b0384811660048301528083166024830152604482018890529192507f0000000000000000000000000000000000000000000000000000000000000000909116906342842e0e90606401600060405180830381600087803b15801561111557600080fd5b505af1158015611129573d6000803e3d6000fd5b5050604051635a2d1e0760e11b815260048101889052600092508291506001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063b45a3c0e906024016040805180830381865afa158015611196573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111ba9190611ed2565b9092509050600f82900b600062093a806111d48142611e99565b6111de9190611ebb565b6111e89084611e73565b6040516340c10f1960e01b8152306004820152602481018490529091507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906340c10f19906044016020604051808303816000875af1158015611258573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061127c9190611f23565b5060405163ec32e6df60e01b815260048101839052602481018290526001600160a01b0387811660448301527f0000000000000000000000000000000000000000000000000000000000000000169063ec32e6df906064016020604051808303816000875af11580156112f3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113179190611dbe565b60055460405163095ea7b360e01b81526001600160a01b03888116600483015260248201929092529199507f0000000000000000000000000000000000000000000000000000000000000000169063095ea7b390604401600060405180830381600087803b15801561138857600080fd5b505af115801561139c573d6000803e3d6000fd5b505060055460405163231ee78560e11b81526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000081166004830152602482018e90526044820192909252908816925063463dcf0a9150606401600060405180830381600087803b15801561141657600080fd5b505af115801561142a573d6000803e3d6000fd5b505060055460405163095ea7b360e01b81526000600482015260248101919091527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316925063095ea7b39150604401600060405180830381600087803b15801561149b57600080fd5b505af11580156114af573d6000803e3d6000fd5b505060405163032145f960e41b8152600481018a90527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031692506332145f909150602401600060405180830381600087803b15801561151557600080fd5b505af1158015611529573d6000803e3d6000fd5b50505060008a81526003602090815260408083208c90556004909152812080546001600160a01b0319166001600160a01b0389161790558391506009906115754262093a808106900390565b815260200190815260200160002060008282546115929190611e86565b909155505060408051838152602081018590524281830152905189918b916001600160a01b038a16917f4a19e049996501b0c4add7667734ac63234004e7247d17e7df0d6dcce3674cef919081900360600190a4505050505050506115f660018055565b919050565b60055460008190036116205760405163295fa13d60e11b815260040160405180910390fd5b600061162a611a50565b90507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663a83627de6040518163ffffffff1660e01b81526004016020604051808303816000875af115801561168c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116b09190611dbe565b506040516323b872dd60e01b81526001600160a01b038281166004830152306024830152604482018590527f000000000000000000000000000000000000000000000000000000000000000016906323b872dd906064016020604051808303816000875af1158015611726573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061174a9190611f23565b506040516350c1d7a960e11b815260048101839052602481018490527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063a183af5290604401600060405180830381600087803b1580156117b457600080fd5b505af11580156117c8573d6000803e3d6000fd5b50506040516340c10f1960e01b81526001600160a01b038481166004830152602482018790527f00000000000000000000000000000000000000000000000000000000000000001692506340c10f1991506044016020604051808303816000875af115801561183b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061185f9190611f23565b5082600960006118744262093a808106900390565b815260200190815260200160002060008282546118919190611e86565b9091555050604080518481524260208201526001600160a01b038316917f57c3d16df660889ba8f21574179e9d1f0f795ff6f75b827ad0790583e321f8c0910160405180910390a2505050565b6118e6611a5f565b6118f06000611bd4565b565b6118fa611a5f565b6005541561191b5760405163403b2b6760e01b815260040160405180910390fd5b6040516331a9108f60e11b81526004810182905230906001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690636352211e90602401602060405180830381865afa158015611982573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119a69190611f06565b6001600160a01b0316146119cd5760405163160112d960e31b815260040160405180910390fd5b600555565b6119da611a5f565b6001600160a01b038116611a445760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b60648201526084015b60405180910390fd5b611a4d81611bd4565b50565b6000611a5a611c24565b905090565b611a67611a50565b6001600160a01b0316611a826000546001600160a01b031690565b6001600160a01b0316146118f05760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401611a3b565b600260015403611b2a5760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401611a3b565b6002600155565b6000763d602d80600a3d3981f3363d3d373d3d3d363d730000008360601b60e81c176000526e5af43d82803e903d91602b57fd5bf38360781b1760205281603760096000f590506001600160a01b038116611bce5760405162461bcd60e51b815260206004820152601760248201527f455243313136373a2063726561746532206661696c65640000000000000000006044820152606401611a3b565b92915050565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031633148015611c5f575060143610155b15611c71575060131936013560601c90565b503390565b6001600160a01b0381168114611a4d57600080fd5b634e487b7160e01b600052604160045260246000fd5b60008060008060808587031215611cb757600080fd5b8435611cc281611c76565b93506020850135611cd281611c76565b925060408501359150606085013567ffffffffffffffff80821115611cf657600080fd5b818701915087601f830112611d0a57600080fd5b813581811115611d1c57611d1c611c8b565b604051601f8201601f19908116603f01168101908382118183101715611d4457611d44611c8b565b816040528281528a6020848701011115611d5d57600080fd5b82602086016020830137600060208483010152809550505050505092959194509250565b600060208284031215611d9357600080fd5b5035919050565b600060208284031215611dac57600080fd5b8135611db781611c76565b9392505050565b600060208284031215611dd057600080fd5b5051919050565b634e487b7160e01b600052603260045260246000fd5b600081518084526020808501945080840160005b83811015611e265781516001600160a01b031687529582019590820190600101611e01565b509495945050505050565b6001600160a01b0383168152604060208201819052600090611e5590830184611ded565b949350505050565b634e487b7160e01b600052601160045260246000fd5b81810381811115611bce57611bce611e5d565b80820180821115611bce57611bce611e5d565b600082611eb657634e487b7160e01b600052601260045260246000fd5b500490565b8082028115828204841417611bce57611bce611e5d565b60008060408385031215611ee557600080fd5b825180600f0b8114611ef657600080fd5b6020939093015192949293505050565b600060208284031215611f1857600080fd5b8151611db781611c76565b600060208284031215611f3557600080fd5b81518015158114611db757600080fd5b83815260006020606081840152611f5f6060840186611ded565b838103604085015284518082528286019183019060005b81811015611f9257835183529284019291840191600101611f76565b509098975050505050505050565b600060018201611fb257611fb2611e5d565b506001019056fea2646970667358221220a961f0abf7985f09ae3097c1ce770fcbe6d4555a7eee6c5fd64242c87e74d9a764736f6c63430008130033000000000000000000000000c6eaf7d353d9901375be2d8f9ba38dd58524d8b300000000000000000000000087e7742fe8cb0ef71f6fc21344a20fb0ce9c03620000000000000000000000004c0846c484d7a6a9639b20e1828e3337bbc8d7d7000000000000000000000000842d477f8ce0efbb669ddc2ace21816b9a841ee4000000000000000000000000e1da44c0da55b075ae8e2e4b6986adc76ac77d73000000000000000000000000b6a075e9cf5dba476994b63b60217a5cf9ab272700000000000000000000000035361c9c2a324f5fb8f3aed2d7ba91ce1410893a0000000000000000000000004937fcfaf9fbd139b78d95a128425a19721c3072000000000000000000000000411bb8829a14d56356485df13cc6a00c9ddf19db

Deployed ByteCode

0x608060405234801561001057600080fd5b506004361061014d5760003560e01c80637482cb13116100c3578063d0de8ee21161007c578063d0de8ee214610392578063e3679450146103a5578063e4d7766b146103ae578063f2fde38b146103d6578063fcd4cf60146103e9578063fd84b53b1461041057600080fd5b80637482cb13146102e45780638da5cb5b1461030b578063a6f19c841461031c578063b1e991c11461032f578063b50c276514610342578063c865b7841461036b57600080fd5b80633f2a5540116101155780633f2a55401461021857806346c96aac1461023f5780634cab93ae14610266578063572b6c0514610279578063629b0ff4146102c9578063715018a6146102dc57600080fd5b8063150b7a02146101525780631c9893901461018e5780631f850716146101bc578063324528ff146101fb5780633b53d6f014610205575b600080fd5b610170610160366004611ca1565b630a85bd0160e11b949350505050565b6040516001600160e01b031990911681526020015b60405180910390f35b6101ae61019c366004611d81565b60036020526000908152604090205481565b604051908152602001610185565b6101e37f00000000000000000000000035361c9c2a324f5fb8f3aed2d7ba91ce1410893a81565b6040516001600160a01b039091168152602001610185565b610203610437565b005b610203610213366004611d9a565b610a24565b6101e37f000000000000000000000000411bb8829a14d56356485df13cc6a00c9ddf19db81565b6101e37f000000000000000000000000842d477f8ce0efbb669ddc2ace21816b9a841ee481565b6101ae610274366004611d81565b610def565b6102b9610287366004611d9a565b7f000000000000000000000000c6eaf7d353d9901375be2d8f9ba38dd58524d8b36001600160a01b0390811691161490565b6040519015158152602001610185565b6102036102d7366004611d81565b6115fb565b6102036118de565b6101e37f000000000000000000000000871ea7a1be3dae44e5fa72eb0980a31870388d6381565b6000546001600160a01b03166101e3565b6008546101e3906001600160a01b031681565b6007546101e3906001600160a01b031681565b6101e3610350366004611d81565b6004602052600090815260409020546001600160a01b031681565b6101e37f0000000000000000000000004937fcfaf9fbd139b78d95a128425a19721c307281565b6102036103a0366004611d81565b6118f2565b6101ae60055481565b6101ae6103bc366004611d81565b62093a808106900360009081526009602052604090205490565b6102036103e4366004611d9a565b6119d2565b6101e37f000000000000000000000000b6a075e9cf5dba476994b63b60217a5cf9ab272781565b6101e37f000000000000000000000000e1da44c0da55b075ae8e2e4b6986adc76ac77d7381565b6008546001600160a01b0316610460576040516361877ef360e01b815260040160405180910390fd5b6005546040516370a0823160e01b81523060048201526000907f000000000000000000000000e1da44c0da55b075ae8e2e4b6986adc76ac77d736001600160a01b0316906370a0823190602401602060405180830381865afa1580156104ca573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104ee9190611dbe565b60408051600180825281830190925291925060009190602080830190803683370190505090507f000000000000000000000000e1da44c0da55b075ae8e2e4b6986adc76ac77d738160008151811061054857610548611dd7565b6001600160a01b0392831660209182029290920101526008546040516331279d3d60e01b81529116906331279d3d906105879030908590600401611e31565b600060405180830381600087803b1580156105a157600080fd5b505af11580156105b5573d6000803e3d6000fd5b50506040516370a0823160e01b8152306004820152600092507f000000000000000000000000e1da44c0da55b075ae8e2e4b6986adc76ac77d736001600160a01b031691506370a0823190602401602060405180830381865afa158015610620573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106449190611dbe565b905060006106528483611e73565b905081156106dc576040516350c1d7a960e11b815260048101869052602481018390527f00000000000000000000000035361c9c2a324f5fb8f3aed2d7ba91ce1410893a6001600160a01b03169063a183af5290604401600060405180830381600087803b1580156106c357600080fd5b505af11580156106d7573d6000803e3d6000fd5b505050505b60405163d1d58b2560e01b8152600481018690526000907f000000000000000000000000411bb8829a14d56356485df13cc6a00c9ddf19db6001600160a01b03169063d1d58b2590602401602060405180830381865afa158015610744573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107689190611dbe565b905080156107fd5760405163379607f560e01b8152600481018790527f000000000000000000000000411bb8829a14d56356485df13cc6a00c9ddf19db6001600160a01b03169063379607f5906024016020604051808303816000875af11580156107d7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107fb9190611dbe565b505b600062093a8080610812630784ce0042611e86565b61081c9190611e99565b6108269190611ebb565b604051635a2d1e0760e11b8152600481018990529091506000906001600160a01b037f00000000000000000000000035361c9c2a324f5fb8f3aed2d7ba91ce1410893a169063b45a3c0e906024016040805180830381865afa158015610890573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108b49190611ed2565b915050808211156109445760405163a4d855df60e01b815260048101899052630784ce0060248201527f00000000000000000000000035361c9c2a324f5fb8f3aed2d7ba91ce1410893a6001600160a01b03169063a4d855df90604401600060405180830381600087803b15801561092b57600080fd5b505af115801561093f573d6000803e3d6000fd5b505050505b60405163032145f960e41b8152600481018990527f000000000000000000000000842d477f8ce0efbb669ddc2ace21816b9a841ee46001600160a01b0316906332145f9090602401600060405180830381600087803b1580156109a657600080fd5b505af11580156109ba573d6000803e3d6000fd5b505050506109c6611a50565b60408051898152602081018790529081018590524260608201526001600160a01b0391909116907fac162ad79ec08307123947685e1233c9f2e5d5b672fdd396127a0443a3ac1c8a9060800160405180910390a25050505050505050565b610a2c611a5f565b6005546000819003610a515760405163295fa13d60e11b815260040160405180910390fd5b6008546001600160a01b031615610a7b5760405163291f9deb60e01b815260040160405180910390fd5b600880546001600160a01b0319166001600160a01b03841690811790915560408051633a4b66f160e01b8152905160009291633a4b66f1916004808301926020929190829003018187875af1158015610ad8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610afc9190611f06565b6040516370a0823160e01b81523060048201529091506000906001600160a01b038316906370a0823190602401602060405180830381865afa158015610b46573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b6a9190611dbe565b60405163095ea7b360e01b81526001600160a01b038681166004830152602482018390529192509083169063095ea7b3906044016020604051808303816000875af1158015610bbd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610be19190611f23565b50600854604051631c57762b60e31b815260048101839052600060248201526001600160a01b039091169063e2bbb15890604401600060405180830381600087803b158015610c2f57600080fd5b505af1158015610c43573d6000803e3d6000fd5b506000925060019150610c539050565b604051908082528060200260200182016040528015610c7c578160200160208202803683370190505b506040805160018082528183019092529192506000919060208083019080368337505060085460408051633a4b66f160e01b815290519394506001600160a01b0390911692633a4b66f19250600480830192602092919082900301816000875af1158015610cee573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d129190611f06565b82600081518110610d2557610d25611dd7565b60200260200101906001600160a01b031690816001600160a01b031681525050600181600081518110610d5a57610d5a611dd7565b6020908102919091010152604051637ac09bf760e01b81526001600160a01b037f000000000000000000000000842d477f8ce0efbb669ddc2ace21816b9a841ee41690637ac09bf790610db590889086908690600401611f45565b600060405180830381600087803b158015610dcf57600080fd5b505af1158015610de3573d6000803e3d6000fd5b50505050505050505050565b6000610df9611ad8565b6005546000819003610e1e5760405163295fa13d60e11b815260040160405180910390fd5b60008381526003602052604090205415610e4b57604051632fc4601360e01b815260040160405180910390fd5b60405163430c208160e01b8152306004820152602481018490527f00000000000000000000000035361c9c2a324f5fb8f3aed2d7ba91ce1410893a6001600160a01b03169063430c2081906044016020604051808303816000875af1158015610eb8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610edc9190611f23565b610ef95760405163876dcc9f60e01b815260040160405180910390fd5b60405163f8a0576360e01b81526004810184905242907f00000000000000000000000035361c9c2a324f5fb8f3aed2d7ba91ce1410893a6001600160a01b03169063f8a0576390602401602060405180830381865afa158015610f60573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f849190611dbe565b11610fa257604051637bdb3cff60e11b815260040160405180910390fd5b6000610fac611a50565b90507f000000000000000000000000871ea7a1be3dae44e5fa72eb0980a31870388d636001600160a01b031663a83627de6040518163ffffffff1660e01b81526004016020604051808303816000875af115801561100e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110329190611dbe565b506007546002805460009261109e926001600160a01b0390911691849061105890611fa0565b9182905550611068600143611e73565b40604051602001611083929190918252602082015260400190565b60405160208183030381529060405280519060200120611b31565b604051632142170760e11b81526001600160a01b0384811660048301528083166024830152604482018890529192507f00000000000000000000000035361c9c2a324f5fb8f3aed2d7ba91ce1410893a909116906342842e0e90606401600060405180830381600087803b15801561111557600080fd5b505af1158015611129573d6000803e3d6000fd5b5050604051635a2d1e0760e11b815260048101889052600092508291506001600160a01b037f00000000000000000000000035361c9c2a324f5fb8f3aed2d7ba91ce1410893a169063b45a3c0e906024016040805180830381865afa158015611196573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111ba9190611ed2565b9092509050600f82900b600062093a806111d48142611e99565b6111de9190611ebb565b6111e89084611e73565b6040516340c10f1960e01b8152306004820152602481018490529091507f000000000000000000000000b6a075e9cf5dba476994b63b60217a5cf9ab27276001600160a01b0316906340c10f19906044016020604051808303816000875af1158015611258573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061127c9190611f23565b5060405163ec32e6df60e01b815260048101839052602481018290526001600160a01b0387811660448301527f0000000000000000000000004937fcfaf9fbd139b78d95a128425a19721c3072169063ec32e6df906064016020604051808303816000875af11580156112f3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113179190611dbe565b60055460405163095ea7b360e01b81526001600160a01b03888116600483015260248201929092529199507f00000000000000000000000035361c9c2a324f5fb8f3aed2d7ba91ce1410893a169063095ea7b390604401600060405180830381600087803b15801561138857600080fd5b505af115801561139c573d6000803e3d6000fd5b505060055460405163231ee78560e11b81526001600160a01b037f00000000000000000000000035361c9c2a324f5fb8f3aed2d7ba91ce1410893a81166004830152602482018e90526044820192909252908816925063463dcf0a9150606401600060405180830381600087803b15801561141657600080fd5b505af115801561142a573d6000803e3d6000fd5b505060055460405163095ea7b360e01b81526000600482015260248101919091527f00000000000000000000000035361c9c2a324f5fb8f3aed2d7ba91ce1410893a6001600160a01b0316925063095ea7b39150604401600060405180830381600087803b15801561149b57600080fd5b505af11580156114af573d6000803e3d6000fd5b505060405163032145f960e41b8152600481018a90527f000000000000000000000000842d477f8ce0efbb669ddc2ace21816b9a841ee46001600160a01b031692506332145f909150602401600060405180830381600087803b15801561151557600080fd5b505af1158015611529573d6000803e3d6000fd5b50505060008a81526003602090815260408083208c90556004909152812080546001600160a01b0319166001600160a01b0389161790558391506009906115754262093a808106900390565b815260200190815260200160002060008282546115929190611e86565b909155505060408051838152602081018590524281830152905189918b916001600160a01b038a16917f4a19e049996501b0c4add7667734ac63234004e7247d17e7df0d6dcce3674cef919081900360600190a4505050505050506115f660018055565b919050565b60055460008190036116205760405163295fa13d60e11b815260040160405180910390fd5b600061162a611a50565b90507f000000000000000000000000871ea7a1be3dae44e5fa72eb0980a31870388d636001600160a01b031663a83627de6040518163ffffffff1660e01b81526004016020604051808303816000875af115801561168c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116b09190611dbe565b506040516323b872dd60e01b81526001600160a01b038281166004830152306024830152604482018590527f000000000000000000000000e1da44c0da55b075ae8e2e4b6986adc76ac77d7316906323b872dd906064016020604051808303816000875af1158015611726573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061174a9190611f23565b506040516350c1d7a960e11b815260048101839052602481018490527f00000000000000000000000035361c9c2a324f5fb8f3aed2d7ba91ce1410893a6001600160a01b03169063a183af5290604401600060405180830381600087803b1580156117b457600080fd5b505af11580156117c8573d6000803e3d6000fd5b50506040516340c10f1960e01b81526001600160a01b038481166004830152602482018790527f000000000000000000000000b6a075e9cf5dba476994b63b60217a5cf9ab27271692506340c10f1991506044016020604051808303816000875af115801561183b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061185f9190611f23565b5082600960006118744262093a808106900390565b815260200190815260200160002060008282546118919190611e86565b9091555050604080518481524260208201526001600160a01b038316917f57c3d16df660889ba8f21574179e9d1f0f795ff6f75b827ad0790583e321f8c0910160405180910390a2505050565b6118e6611a5f565b6118f06000611bd4565b565b6118fa611a5f565b6005541561191b5760405163403b2b6760e01b815260040160405180910390fd5b6040516331a9108f60e11b81526004810182905230906001600160a01b037f00000000000000000000000035361c9c2a324f5fb8f3aed2d7ba91ce1410893a1690636352211e90602401602060405180830381865afa158015611982573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119a69190611f06565b6001600160a01b0316146119cd5760405163160112d960e31b815260040160405180910390fd5b600555565b6119da611a5f565b6001600160a01b038116611a445760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b60648201526084015b60405180910390fd5b611a4d81611bd4565b50565b6000611a5a611c24565b905090565b611a67611a50565b6001600160a01b0316611a826000546001600160a01b031690565b6001600160a01b0316146118f05760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401611a3b565b600260015403611b2a5760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401611a3b565b6002600155565b6000763d602d80600a3d3981f3363d3d373d3d3d363d730000008360601b60e81c176000526e5af43d82803e903d91602b57fd5bf38360781b1760205281603760096000f590506001600160a01b038116611bce5760405162461bcd60e51b815260206004820152601760248201527f455243313136373a2063726561746532206661696c65640000000000000000006044820152606401611a3b565b92915050565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b60007f000000000000000000000000c6eaf7d353d9901375be2d8f9ba38dd58524d8b36001600160a01b031633148015611c5f575060143610155b15611c71575060131936013560601c90565b503390565b6001600160a01b0381168114611a4d57600080fd5b634e487b7160e01b600052604160045260246000fd5b60008060008060808587031215611cb757600080fd5b8435611cc281611c76565b93506020850135611cd281611c76565b925060408501359150606085013567ffffffffffffffff80821115611cf657600080fd5b818701915087601f830112611d0a57600080fd5b813581811115611d1c57611d1c611c8b565b604051601f8201601f19908116603f01168101908382118183101715611d4457611d44611c8b565b816040528281528a6020848701011115611d5d57600080fd5b82602086016020830137600060208483010152809550505050505092959194509250565b600060208284031215611d9357600080fd5b5035919050565b600060208284031215611dac57600080fd5b8135611db781611c76565b9392505050565b600060208284031215611dd057600080fd5b5051919050565b634e487b7160e01b600052603260045260246000fd5b600081518084526020808501945080840160005b83811015611e265781516001600160a01b031687529582019590820190600101611e01565b509495945050505050565b6001600160a01b0383168152604060208201819052600090611e5590830184611ded565b949350505050565b634e487b7160e01b600052601160045260246000fd5b81810381811115611bce57611bce611e5d565b80820180821115611bce57611bce611e5d565b600082611eb657634e487b7160e01b600052601260045260246000fd5b500490565b8082028115828204841417611bce57611bce611e5d565b60008060408385031215611ee557600080fd5b825180600f0b8114611ef657600080fd5b6020939093015192949293505050565b600060208284031215611f1857600080fd5b8151611db781611c76565b600060208284031215611f3557600080fd5b81518015158114611db757600080fd5b83815260006020606081840152611f5f6060840186611ded565b838103604085015284518082528286019183019060005b81811015611f9257835183529284019291840191600101611f76565b509098975050505050505050565b600060018201611fb257611fb2611e5d565b506001019056fea2646970667358221220a961f0abf7985f09ae3097c1ce770fcbe6d4555a7eee6c5fd64242c87e74d9a764736f6c63430008130033