Contract Address Details

0xe653aC16B732876F58a1722d24801230fA96bc82

Contract Name
YieldFarm
Creator
0xd6cf49–0769d0 at 0xf3c602–fef225
Balance
0 ETN ( )
Tokens
Fetching tokens...
Transactions
85 Transactions
Transfers
240 Transfers
Gas Used
32,777,082
Last Balance Update
4503338
Contract name:
YieldFarm




Optimization enabled
true
Compiler version
v0.8.26+commit.8a97fa7a




Optimization runs
500
Verified at
2024-11-09T04:41:34.817491Z

Constructor Arguments

000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000021e19e0c9bab24000000000000000000000000000000000000000000000000000000005b605a7b06400000000000000000000000000138dafbda0ccb3d8e39c19edb0510fc31b7c1c77000000000000000000000000043faa1b5c5fc9a7dc35171f290c29ecde0ccff1000000000000000000000000203d550ed6fa9dab8a4190720cf9f65138abd15b000000000000000000000000072d4706f9a383d5608bd14b09b41683cb95ffd7000000000000000000000000bf6bcbe2be545135391777f3b4698be92e2eb8ca0000000000000000000000003a7f64c57433555b23dac4409a0ac7e84275398d000000000000000000000000a1a60e03139b65cda4f0d1d7ad833064bb3dfef2000000000000000000000000000000000000000000000000000000000000000644796e616d6f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000444594e4f00000000000000000000000000000000000000000000000000000000

Arg [0] (string) : Dynamo
Arg [1] (string) : DYNO
Arg [2] (uint256) : 10000000000000000000000
Arg [3] (uint256) : 1607510288000000
Arg [4] (address) : 0x138dafbda0ccb3d8e39c19edb0510fc31b7c1c77
Arg [5] (address) : 0x043faa1b5c5fc9a7dc35171f290c29ecde0ccff1
Arg [6] (address) : 0x203d550ed6fa9dab8a4190720cf9f65138abd15b
Arg [7] (address) : 0x072d4706f9a383d5608bd14b09b41683cb95ffd7
Arg [8] (address) : 0xbf6bcbe2be545135391777f3b4698be92e2eb8ca
Arg [9] (address) : 0x3a7f64c57433555b23dac4409a0ac7e84275398d
Arg [10] (address) : 0xa1a60e03139b65cda4f0d1d7ad833064bb3dfef2

              

contracts/YieldFarm.sol

// SPDX-License-Identifier: CC-BY-NC-ND-4.0
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "./interfaces.sol";
import "./RewardToken.sol";

contract YieldFarm is Ownable, ReentrancyGuard, IERC721Receiver {

    struct Farm {
        uint256 id; // Farm ID
        uint8 version; // V2 or V3
        string name; // Descriptor of the farm
        address poolAddr; // Address of V2 pair or V3 pool
        uint256 liquidity; // Total liquidity deposited
        uint256 allocPoint; // Allocation points assigned to the pool

        uint256 lastCalcBlock; // Last block number the user had their rewards calculated
        uint256 accRewardsPerShare; // Accumulated rewards per share times CALC_PRECISION
        uint256 accThirdPartyRewardsPerShare; // Accumulated third party rewards per share times CALC_PRECISION
        
        address[] farmers; // Array of all farmer addresses in the farm
        uint256 farmerCount; // Array of all farmer addresses in the farm
        address token0; // Address of the 1st token in the pair
        address token1; // Address of the 2nd token in the pair

        // Only used for V3
        uint256 tokenId;
        int24 tickLower;
        int24 tickUpper;
        uint24 fee;
        uint256 accFees0PerShare; // Accumulated fees0 per share times CALC_PRECISION
        uint256 accFees1PerShare; // Accumulated fees1 per share times CALC_PRECISION

        bool active;
    }

    struct Farmer {
        address addr;
        uint256 liquidity; // How much liquidity the farmer has provided
        uint256 boltMultiplier; // Multiplier for deposited/staked BOLT, stored as percentage with two decimals
        uint256 boltDeposited; // Amount of BOLT depositted
        uint256 durationMultiplier; // Transient, populated by getFarmerByFarmIdAndAddress
        uint256 startingBlock; // Used in calculation of duration bonus multiplier

        uint256 rewards; // Transient
        uint256 rewardDebt; // The amount relative to farm.accRewardsPerShare the user can't get as reward
        uint256 thirdPartyRewards; // Transient
        uint256 thirdPartyRewardDebt; // The amount relative to farm.accThirdPartyRewardsPerShare the user can't get as reward

        // Only used for V3
        uint256 fees0; // Transient
        uint256 fees0Debt; // The amount relative to farm.accFees0PerShare the user can't claim
        uint256 fees1; // Transient
        uint256 fees1Debt; // The amount relative to farm.accFees1PerShare the user can't claim
    }

    struct ThirdPartyReward {
        address token;
        address tokenManager;
        uint256 tokensPerBlock;
        uint256 endBlock;
    }

    RewardToken public rewardToken; // Token to be minted as farming reward
    IElectroSwapV2Router internal v2Router; // For adding and removing V2 liquidity
    IElectroSwapV3Factory internal v3Factory; // Used to get V3 pool addresses
    INonfungiblePositionManager internal positionManager; // Used to get position info and add/remove liquidity
    IUncollectedFeeHelper internal feeHelper;
    address internal wetnAddress;
    address internal v2Factory; // Used to verify ElectroSwap V2 LP tokens
    address internal boltToken; // BOLT Token address

    uint256 public totalAllocPoint; // Tracks total allocation points across all pools
    uint256 public rewardPerBlock; // Number of rewards per block shared across all incentivized pools

    uint256 internal constant CALC_PRECISION = 1e18; // A big number to perform mul and div operations
    uint256 internal constant MAX_BLOCKS_FOR_BONUS = 6307200; // 1 year worth of blocks
    address internal constant DEAD_ADDRESS = 0x000000000000000000000000000000000000dEaD;
    
    // Tracks state of current farms and farmers
    uint256 public farmCount;
    mapping(address => uint256[]) internal poolFarmIds;  // mapping(address => farmId[])
    mapping(uint256 => Farm) internal farmInfo;  // mapping(farmId => Farm))
    mapping(uint256 => mapping(address => Farmer)) internal farmerInfo; // mapping(farmId => mapping(farmerAddr => Farmer))
    mapping(uint256 => ThirdPartyReward) internal thirdPartyRewards;  // mapping(farmId => ThirdPartyReward))
    mapping(uint256 => uint256) internal boltMultipliers; // mapping(valid BOLT deposit => bonus multiplier padded by 10000X)

    constructor(
        string memory _rewardName, string memory _rewardSymbol, uint256 _rewardSupply, uint256 _rewardPerBlock, // Reward related params
        address _wetnAddress, address _boltToken, // WETN and BOLT address
        address _v2Factory, address _v2Router, // V2 params
        address _v3Factory, address _positionManager, address _feeHelper // V3 params
    ) Ownable(msg.sender) {

        rewardToken = new RewardToken(_rewardName, _rewardSymbol);
        rewardToken.mint(msg.sender, _rewardSupply);
        rewardToken.renounceOwnership();

        rewardPerBlock = _rewardPerBlock;

        boltToken = _boltToken;

        v2Factory = _v2Factory;
        v2Router = IElectroSwapV2Router(_v2Router);
        positionManager = INonfungiblePositionManager(_positionManager);
        v3Factory = IElectroSwapV3Factory(_v3Factory);
        feeHelper = IUncollectedFeeHelper(_feeHelper);
        
        wetnAddress = _wetnAddress;

        boltMultipliers[0] = 10000; // 1.00X Reward multiplier (not changeable)
        boltMultipliers[50000 * 1e18] = 10500; // 1.05X Reward multiplier
        boltMultipliers[100000 * 1e18] = 11500; // 1.15X Reward multiplier
    }

    /** ==================== ADMIN FUNCTIONS ==================== */

    // Add a new incentivized V2 farm by passing an existing V2 pair address. Can only be called by the owner (ElectroSwap).
    function adminCreateFarm_V2(string memory _name, address _pairAddr, uint256 _allocPoint) external onlyOwner nonReentrant {
        require(_pairAddr != address(0), "Cannot add zero address");
        require(poolFarmIds[_pairAddr].length == 0, "Pool already added");
        require(IElectroSwapV2Pair(_pairAddr).factory() == v2Factory, "Invalid LP Token");

        _updateAllFarmRewardsAndFees();

        totalAllocPoint += _allocPoint;

        IElectroSwapV2Pair pair = IElectroSwapV2Pair(_pairAddr);
        address token0 = pair.token0();
        address token1 = pair.token1();

        farmCount++;
        poolFarmIds[_pairAddr].push(farmCount);

        address[] memory farmerAddresses; // empty array
        farmInfo[farmCount] = Farm({
            id: farmCount,
            version: 2,
            name: _name,
            poolAddr: _pairAddr,
            liquidity: 0,
            allocPoint: _allocPoint,
            
            lastCalcBlock: block.number,
            accRewardsPerShare: 0,
            accThirdPartyRewardsPerShare: 0,

            farmers: farmerAddresses,
            farmerCount: 0,
            token0: token0,
            token1: token1,

            tokenId: 0,
            tickLower: 0,
            tickUpper: 0,
            fee: 0,
            accFees0PerShare: 0,
            accFees1PerShare: 0,

            active: true
        });

        emit FarmCreated(farmCount, _pairAddr, 2, 0, _allocPoint, totalAllocPoint);
    }


    // Add a new incentivized V3 farm by passing a position token ID, which requires prior approval. Can only be called by the owner (ElectroSwap).
    function adminCreateFarm_V3(string memory _name, uint256 _tokenId, uint256 _allocPoint) external onlyOwner nonReentrant {
        
        Position memory position = _getPosition(_tokenId);
        require(position.token0 != address(0) && position.token1 != address(0), "Invalid tokenId");
        positionManager.safeTransferFrom(msg.sender, address(this), _tokenId);

        address poolAddr = v3Factory.getPool(position.token0, position.token1, position.fee);

        _updateAllFarmRewardsAndFees();

        totalAllocPoint += _allocPoint;
        farmCount++;
        poolFarmIds[poolAddr].push(farmCount);

        address[] memory farmerAddresses = new address[](1);
        farmerAddresses[0] = msg.sender; 

        farmInfo[farmCount] = Farm({
            id: farmCount,
            version: 3,
            name: _name,
            poolAddr: poolAddr,
            liquidity: position.liquidity,
            allocPoint: _allocPoint,

            lastCalcBlock: block.number,
            accRewardsPerShare: 0,
            accThirdPartyRewardsPerShare: 0,

            farmers: farmerAddresses,
            farmerCount: 1,

            tokenId: _tokenId,
            token0: position.token0,
            token1: position.token1,
            tickLower: position.tickLower,
            tickUpper: position.tickUpper,
            fee: position.fee,
            accFees0PerShare: 0,
            accFees1PerShare: 0,
            
            active: true
        });

        farmerInfo[farmCount][msg.sender] = Farmer({
            addr: msg.sender,
            liquidity: position.liquidity,
            boltMultiplier: boltMultipliers[0], // 1.00X 
            boltDeposited: 0,
            durationMultiplier: 0,
            startingBlock: block.number,
            
            rewards: 0,
            rewardDebt: 0,
            thirdPartyRewards: 0,
            thirdPartyRewardDebt: 0,
            fees0: 0,
            fees0Debt: 0,
            fees1: 0,
            fees1Debt: 0
        });

        emit FarmCreated(farmCount, poolAddr, 3, _tokenId, _allocPoint, totalAllocPoint);
    }

    // Update the farm's allocation points. Can only be called by the owner (ElectroSwap).
    function adminUpdateFarmAllocPoints(uint256 _farmId, uint256 _allocPoint) external onlyOwner nonReentrant {
        Farm storage farm = farmInfo[_farmId];
        require(farm.poolAddr != address(0), "Invalid pool");

        _updateAllFarmRewardsAndFees();

        totalAllocPoint = totalAllocPoint - farm.allocPoint + _allocPoint;
        farm.allocPoint = _allocPoint;

        emit FarmAllocationUpdated(_farmId, farm.poolAddr, _allocPoint, totalAllocPoint);
    }

    // Update the farm's name. Can only be called by the owner (ElectroSwap).
    function adminUpdateFarmName(uint256 _farmId, string memory _name) external onlyOwner nonReentrant {
        Farm storage farm = farmInfo[_farmId];
        require(farm.poolAddr != address(0), "Invalid pool");

        farm.name = _name;

        emit FarmNameUpdated(_farmId, farm.poolAddr, _name);
    }

    // Update the number of rewards per block. Can only be called by the owner (ElectroSwap).
    function adminUpdateRewardsPerBlock(uint256 _rewardPerBlock) external onlyOwner nonReentrant {
        uint256 prevRewardPerBlock = rewardPerBlock;
        rewardPerBlock = _rewardPerBlock;

        _updateAllFarmRewardsAndFees();
        
        emit RewardsPerBlockUpdated(rewardPerBlock, prevRewardPerBlock);
    }

    // Update the valid BOLT deposit amounts and corresponding multipliers. Can only be called by the owner (ElectroSwap).
    function adminUpdateBoltMultiplier(uint256 _amountBolt, uint256 _multiplier) external onlyOwner nonReentrant {
        require(_multiplier >= 10000, "Multiplier must be >= 10000");
        require(_amountBolt > 0, "Amount must be > 0");

        boltMultipliers[_amountBolt] = _multiplier;

        emit BoltMultiplierUpdated(_amountBolt, _multiplier);
    }
    
    // Remove an incentivized farm. Still allows withdrawls, but no deposits. Can only be called by the owner (ElectroSwap).
    function adminDeactivateFarm(uint256 _farmId) external onlyOwner nonReentrant {

        Farm storage farm = farmInfo[_farmId];
        require(farm.poolAddr != address(0), "Nothing to deactivate");

        _updateAllFarmRewardsAndFees();

        totalAllocPoint -= farm.allocPoint;

        emit FarmDeactivated(_farmId, farm.poolAddr, farm.version, farm.tokenId);

        farm.active = false;
        farm.allocPoint = 0;
    }

    // Add third party rewards to a farm. Can only be called by the owner (ElectroSwap).
    function adminAddThirdPartyReward(uint256 _farmId, address _token, address _tokenManager) external onlyOwner nonReentrant {
        require(_token != address(0) && _tokenManager != address(0), "Cannot be zero address");

        thirdPartyRewards[_farmId] = ThirdPartyReward({
            token: _token,
            tokenManager: _tokenManager, // Address of user that can update the tokensPerBlock
            tokensPerBlock: 0,
            endBlock: 0
        });

        emit ThirdPartyRewardAdded(_farmId, _token, _tokenManager);
    }

    /** ==================== PRIMARY FUNCTIONS ==================== */

    // Deposit tokens into yield farm for rewards allocation.
    function deposit(uint256 _farmId, uint256 _amount0, uint256 _amount1, uint256 _amountBolt) payable external nonReentrant {
        
        Farm storage farm = farmInfo[_farmId];
        require(farm.active, "Inactive farm");
        
        _updateFarmRewardsAndFees(_farmId);

        Farmer storage farmer = farmerInfo[_farmId][msg.sender];

        bool token0Native = false;
        bool token1Native = false;
        
        // Wrap native ETN and transfer other token from msg.sender to the yield farming contract. Requires prior approval
        if(msg.value > 0){
            require(farm.token0 == wetnAddress || farm.token1 == wetnAddress, "Native ETN sent to unsupporting farm");

            IWETN(wetnAddress).deposit{value: msg.value}();

            if(farm.token0 == wetnAddress){
                _amount0 = msg.value;
                token0Native = true;
                IERC20(farm.token1).transferFrom(msg.sender, address(this), _amount1);
            } else {
                _amount1 = msg.value;
                token1Native = true;
                IERC20(farm.token0).transferFrom(msg.sender, address(this), _amount0);
            }
        } 
        // Transfer both tokens from msg.sender to the yield farming contract. Requires prior approval
        else {
            IERC20(farm.token0).transferFrom(msg.sender, address(this), _amount0);
            IERC20(farm.token1).transferFrom(msg.sender, address(this), _amount1);
        }

        require(_amount0 > 0 && _amount1 > 0, "Both amounts must be positive");

        (uint256 liquidityAdded, uint256 amount0Added, uint256 amount1Added) = farm.version == 2 ? 
            _depositV2(farm, _amount0, _amount1) :  
            _depositV3(farm, _amount0, _amount1);

        // Refund whatever was not added to the position to msg.sender
        _refundIfNeeded(farm.token0, token0Native, _amount0, amount0Added);
        _refundIfNeeded(farm.token1, token1Native, _amount1, amount1Added);
        
        farm.liquidity += liquidityAdded;
        
        // New farmer
        if (farmer.liquidity == 0) {
            farm.farmers.push(msg.sender); // Add farmer to the pool's farmer list
            farm.farmerCount += 1;

            farmer.addr = msg.sender;
            farmer.liquidity = liquidityAdded;
            farmer.boltMultiplier = boltMultipliers[0]; // Default value
            farmer.startingBlock = block.number;

            farmer.rewardDebt = liquidityAdded * farm.accRewardsPerShare / CALC_PRECISION;
            farmer.thirdPartyRewardDebt = liquidityAdded * farm.accThirdPartyRewardsPerShare / CALC_PRECISION;
            farmer.fees0Debt = liquidityAdded * farm.accFees0PerShare / CALC_PRECISION;
            farmer.fees1Debt = liquidityAdded * farm.accFees1PerShare / CALC_PRECISION;

            emit FarmDeposit(_farmId, msg.sender, amount0Added, amount1Added, liquidityAdded);
        } 
        else {

            // Adjust starting block based on amount of liquidity added to prevent unfair duration multiplier manipulation
            uint256 increaseOfTotal = (liquidityAdded * 1e18) / (farmer.liquidity + liquidityAdded);
            uint256 blocksServed = block.number - farmer.startingBlock;
            uint256 adjustedBlocksServed = blocksServed - ((blocksServed * increaseOfTotal) / 1e18); // Resolves to zero if negative
            farmer.startingBlock = block.number - adjustedBlocksServed;
            
            uint256 rewardsEarned = (farmer.liquidity * farm.accRewardsPerShare / CALC_PRECISION) - farmer.rewardDebt;
            uint256 thirdPartyRewardsEarned = (farmer.liquidity * farm.accThirdPartyRewardsPerShare / CALC_PRECISION) - farmer.thirdPartyRewardDebt;
            uint256 fees0Earned = (farmer.liquidity * farm.accFees0PerShare / CALC_PRECISION) - farmer.fees0Debt;
            uint256 fees1Earned = (farmer.liquidity * farm.accFees1PerShare / CALC_PRECISION) - farmer.fees1Debt;
            
            farmer.liquidity += liquidityAdded;
            
            farmer.rewardDebt = (farmer.liquidity * farm.accRewardsPerShare / CALC_PRECISION) - rewardsEarned; 
            farmer.thirdPartyRewardDebt = (farmer.liquidity * farm.accThirdPartyRewardsPerShare / CALC_PRECISION) - thirdPartyRewardsEarned; 
            farmer.fees0Debt = (farmer.liquidity * farm.accFees0PerShare / CALC_PRECISION) - fees0Earned; 
            farmer.fees1Debt = (farmer.liquidity * farm.accFees1PerShare / CALC_PRECISION) - fees1Earned; 

            emit FarmIncrease(_farmId, msg.sender, amount0Added, amount1Added, liquidityAdded, farmer.startingBlock);
        }

        if(_amountBolt > 0){
            uint256 totalBolt = _amountBolt + farmer.boltDeposited;
            require(boltMultipliers[totalBolt] != 0, "Invalid BOLT deposit");
            IERC20(boltToken).transferFrom(msg.sender, address(this), _amountBolt);
            
            farmer.boltDeposited = totalBolt;
            farmer.boltMultiplier = boltMultipliers[totalBolt];
        }
    }

    // Withdraw tokens AND rewards. If _asNative is true AND the pair contains WETN, native ETN will be sent.
    function withdraw(uint256 _farmId, uint256 _liquidityAmt, bool _asNative) external nonReentrant {

        Farm storage farm = farmInfo[_farmId];
        Farmer storage farmer = farmerInfo[_farmId][msg.sender];

        require(farmer.liquidity >= _liquidityAmt, "Cannot withdraw more than deposited");

        (uint256 rewardsMinted, uint256 fees0Collected, uint256 fees1Collected, uint256 thirdPartyRewardsCollected) = _collectRewardsAndFees(_farmId, _asNative);

        farmer.liquidity -= _liquidityAmt;

        farmer.rewardDebt = farmer.liquidity * farm.accRewardsPerShare / CALC_PRECISION;
        if(farm.version == 3){
            farmer.fees0Debt = farmer.liquidity * farm.accFees0PerShare / CALC_PRECISION;
            farmer.fees1Debt = farmer.liquidity * farm.accFees1PerShare / CALC_PRECISION;
        }

        if(thirdPartyRewards[_farmId].token != address(0)){
            farmer.thirdPartyRewardDebt = farmer.liquidity * farm.accThirdPartyRewardsPerShare / CALC_PRECISION;
        }

        // Just withdraw rewards and fees
        if(_liquidityAmt == 0){
            emit FarmWithdrawl(_farmId, msg.sender, 0, 0, rewardsMinted, fees0Collected, fees1Collected, thirdPartyRewardsCollected);
            return;
        }

        if(_asNative){
            require(farm.token0 == wetnAddress || farm.token1 == wetnAddress, "Unsupporting farm");
        }

        // Includes removed liquidity and fees
        (uint256 amount0, uint256 amount1) = farm.version == 2 ? _withdrawV2(farm, _liquidityAmt, _asNative) : _withdrawV3(farm, _liquidityAmt, _asNative);

        // Clear state on removal of all liquidity
        if (farmer.liquidity == 0) {

            // Withdraw all BOLT 
            if(farmer.boltDeposited > 0){
                IERC20(boltToken).transfer(msg.sender, farmer.boltDeposited);
            }
            delete farmerInfo[_farmId][msg.sender];
            _removeFarmer(_farmId, msg.sender);
        }

        farm.liquidity -= _liquidityAmt;

        emit FarmWithdrawl(_farmId, msg.sender, amount0, amount1, rewardsMinted, fees0Collected, fees1Collected, thirdPartyRewardsCollected);
    }


    /** ==================== INTERNAL FUNCTIONS ==================== */
    
    // Deposits tokens into V2 yield farm for rewards allocation.
    function _depositV2(Farm memory _farm, uint256 _amount0, uint256 _amount1) internal returns (uint256 liquidityAdded, uint256 amount0Added, uint256 amount1Added) {

        // Approve the router to spend the tokens
        IERC20(_farm.token0).approve(address(v2Router), _amount0);
        IERC20(_farm.token1).approve(address(v2Router), _amount1);

        (amount0Added, amount1Added, liquidityAdded) = v2Router.addLiquidity(
            _farm.token0,
            _farm.token1,
            _amount0,
            _amount1,
            1,
            1,
            address(this),
            block.timestamp
        );
    }

    // Withdraw tokens AND rewards.
    function _withdrawV2(Farm memory _farm, uint256 _liquidityAmt, bool _asNative) internal returns (uint256 amount0, uint256 amount1){
        // Transfers token0 and token1 back to this contract
        IERC20(_farm.poolAddr).approve(address(v2Router), _liquidityAmt);
        (amount0, amount1) = v2Router.removeLiquidity(_farm.token0, _farm.token1, _liquidityAmt, 1, 1, address(this), block.timestamp);

        _transferTokens(_farm, amount0, amount1, _asNative);
    }

    // Deposit tokens into V3 yield farm for rewards allocation.
    function _depositV3(Farm memory _farm, uint256 _amount0, uint256 _amount1) internal returns (uint256 liquidityAdded, uint256 amount0Added, uint256 amount1Added) {

        // Approve the positionManager to spend the tokens
        IERC20(_farm.token0).approve(address(positionManager), _amount0);
        IERC20(_farm.token1).approve(address(positionManager), _amount1);

        // Add liquidity to the existing position
        (liquidityAdded, amount0Added, amount1Added) = positionManager.increaseLiquidity(
            INonfungiblePositionManager.IncreaseLiquidityParams({
                tokenId: _farm.tokenId,
                amount0Desired: _amount0,
                amount1Desired: _amount1,
                amount0Min: 1,
                amount1Min: 1,
                deadline: block.timestamp
            })
        );
    }

    function _withdrawV3(Farm storage _farm, uint256 _liquidityAmt, bool _asNative) internal returns (uint256 amount0, uint256 amount1) {
        // Decreases available liquidity before collecting
        (amount0, amount1) = positionManager.decreaseLiquidity(
            INonfungiblePositionManager.DecreaseLiquidityParams({
                tokenId: _farm.tokenId,
                liquidity: uint128(_liquidityAmt),
                amount0Min: 1,
                amount1Min: 1,
                deadline: block.timestamp
            })
        );

        // Transfers token0 and token back to this contract
        _collectV3(_farm.tokenId);

        _transferTokens(_farm, amount0, amount1, _asNative);
    }

    function _transferTokens(Farm memory _farm, uint256 amount0, uint256 amount1, bool _asNative) internal {
        if(_asNative && _farm.token0 == wetnAddress){
            _withdrawNativeETN(amount0);
            IERC20(_farm.token1).transfer(msg.sender, amount1);
        } 
        else if(_asNative && _farm.token1 == wetnAddress){
            _withdrawNativeETN(amount1);
            IERC20(_farm.token0).transfer(msg.sender, amount0);
        } 
        else {
            IERC20(_farm.token0).transfer(msg.sender, amount0);
            IERC20(_farm.token1).transfer(msg.sender, amount1);
        }
    }

    function _refundIfNeeded(address token, bool tokenIsNative, uint256 amountDeposited, uint256 amountAdded) internal {
        if(amountDeposited > amountAdded){
            uint256 refundAmount = amountDeposited - amountAdded;
            if(tokenIsNative){
                _withdrawNativeETN(refundAmount);
            } else {
                IERC20(token).transfer(msg.sender, refundAmount);
            }
        }
    }

    function _withdrawNativeETN(uint256 amount) internal {
        IWETN(wetnAddress).withdraw(amount);
        (bool success, ) = payable(msg.sender).call{value: amount}("");
        require(success, "ETN transfer failed");
    }

    function _collectV3(uint256 tokenId) internal returns (uint256 amount0Collected, uint256 amount1Collected){
        (amount0Collected, amount1Collected) = positionManager.collect(
            INonfungiblePositionManager.CollectParams({
                tokenId: tokenId,
                recipient: address(this),
                amount0Max: type(uint128).max,
                amount1Max: type(uint128).max
            })
        );
    }

    function _collectRewardsAndFees(uint256 _farmId, bool _asNative) internal returns (uint256 rewardsCollected, uint256 fees0Collected, uint256 fees1Collected, uint256 thirdPartyRewardsCollected) {
        _updateFarmRewardsAndFees(_farmId);
        
        Farmer storage farmer = farmerInfo[_farmId][msg.sender];

        if(farmer.liquidity > 0){
            
            Farm storage farm = farmInfo[_farmId];

            uint256 newRewardDebt = farmer.liquidity * farm.accRewardsPerShare / CALC_PRECISION;
            uint256 rewardsEarned = newRewardDebt - farmer.rewardDebt;

            // boltMultiplier and durationMultiplier are both multiplied by 10000, so we divide by 100000000
            rewardsCollected = (rewardsEarned * farmer.boltMultiplier * _calculateDurationMultiplier(block.number - farmer.startingBlock)) / 100000000;
            if (rewardsCollected != 0) {
                rewardToken.mint(msg.sender, rewardsCollected);
            }

            if(farm.version == 3){
                uint256 newFee0Debt = farmer.liquidity * farm.accFees0PerShare / CALC_PRECISION;
                fees0Collected = newFee0Debt - farmer.fees0Debt;

                uint256 newFee1Debt = farmer.liquidity * farm.accFees1PerShare / CALC_PRECISION;
                fees1Collected = newFee1Debt - farmer.fees1Debt;

                _transferTokens(farm, fees0Collected, fees1Collected, _asNative);
            }

            ThirdPartyReward memory thirdPartyReward = thirdPartyRewards[_farmId];
            if(thirdPartyReward.token != address(0)){

                uint256 newThirdPartyDebt = farmer.liquidity * farm.accThirdPartyRewardsPerShare / CALC_PRECISION;
                thirdPartyRewardsCollected = newThirdPartyDebt - farmer.thirdPartyRewardDebt;

                if (thirdPartyRewardsCollected != 0) {
                    IERC20(thirdPartyReward.token).transfer(msg.sender, thirdPartyRewardsCollected);
                }
            }
        }
    }

    // Called prior to modifying totalAllocPoint or rewardPerBlock (by admin)
    function _updateAllFarmRewardsAndFees() private {
        for (uint256 i = 1; i <= farmCount; i++) {
            _updateFarmRewardsAndFees(i);
        }
    }

    // Sets accumulated amounts for rewards, third party rewards and fees by share
    function _updateFarmRewardsAndFees(uint256 _farmId) private {
        Farm storage farm = farmInfo[_farmId];
        
        if (farm.liquidity == 0 || !farm.active) {
            farm.lastCalcBlock = block.number;
            return;
        }

        uint256 blocksSinceLastCalc = block.number - farm.lastCalcBlock;
        uint256 rewards = blocksSinceLastCalc * ((rewardPerBlock * farm.allocPoint) / totalAllocPoint);
        farm.accRewardsPerShare += rewards * CALC_PRECISION / farm.liquidity;

        if(farm.version == 3){
            (uint256 fees0Collected, uint256 fees1Collected) = _collectV3(farm.tokenId);
            farm.accFees0PerShare += fees0Collected * CALC_PRECISION / farm.liquidity;
            farm.accFees1PerShare += fees1Collected * CALC_PRECISION / farm.liquidity;
        }

        ThirdPartyReward storage thirdPartyReward = thirdPartyRewards[_farmId];
        if(thirdPartyReward.token != address(0) && thirdPartyReward.tokensPerBlock > 0){
            uint256 thirdPartyRewardTokens;

            // Third party rewards ended between last calculation and now
            if(farm.lastCalcBlock < thirdPartyReward.endBlock && thirdPartyReward.endBlock <= block.number){
                uint256 blocksAbleToPay = thirdPartyReward.endBlock - farm.lastCalcBlock;
                thirdPartyRewardTokens = blocksAbleToPay * thirdPartyReward.tokensPerBlock;
                thirdPartyReward.tokensPerBlock = 0;
            } 
            else {
                thirdPartyRewardTokens = blocksSinceLastCalc * thirdPartyReward.tokensPerBlock;
            }

            farm.accThirdPartyRewardsPerShare += thirdPartyRewardTokens * CALC_PRECISION / farm.liquidity;
        }

        farm.lastCalcBlock = block.number;
    }

    // Internal function that calculates duration bonus multiplier
    function _calculateDurationMultiplier(uint256 blocksServed) internal view returns (uint256) {
        // Occurs when farmer is undefined
        if(blocksServed == block.number){
            return 10000;
        }
        // If blockCount is greater than or equal to maxBlockCount, return 25000
        else if (blocksServed >= MAX_BLOCKS_FOR_BONUS) {
            return 25000;
        }

        // Calculate the linear yield
        return 10000 + (15000 * blocksServed) / MAX_BLOCKS_FOR_BONUS;
    }

    struct Position {
        address token0;
        address token1;
        uint24 fee;
        int24 tickLower;
        int24 tickUpper;
        uint128 liquidity;
    }

    function _getPosition(uint256 _tokenId) internal view returns (Position memory) {
        (, ,address token0, address token1, uint24 fee, int24 tickLower, int24 tickUpper, uint128 liquidity, , , ,) = positionManager.positions(_tokenId);

        return Position({
            token0: token0,
            token1: token1,
            fee: fee,
            tickLower: tickLower,
            tickUpper: tickUpper,
            liquidity: liquidity
        });
    }

    // Remove farmer address from a farm
    function _removeFarmer(uint256 _farmId, address _address) internal {

        Farm storage farm = farmInfo[_farmId];
        uint farmerCount = farm.farmers.length;
        uint indexToRemove = farmerCount;

        for (uint i = 0; i < farmerCount; i++) {
            if (farm.farmers[i] == _address) {
                indexToRemove = i;
                break;
            }
        }

        if (indexToRemove < farmerCount) {
            farm.farmers[indexToRemove] = farm.farmers[farmerCount - 1];
            farm.farmers.pop();
            farm.farmerCount -= 1;
        }
    }

    /** ==================== EXTERNAL HELPER FUNCTIONS ==================== */

    // Exposes function that calculates duration bonus multiplier - for gas savings
    function calculateDurationMultiplier(uint256 blocksServed) external view returns (uint256) {
        return _calculateDurationMultiplier(blocksServed);
    }

    // At least one farmer should be active when depositing, otherwise third party rewards go unaccounted for
    function depositThirdPartyReward(uint256 _farmId, uint256 _amountToken, uint256 _distributeOverBlockCount) external {

        ThirdPartyReward storage thirdPartyReward = thirdPartyRewards[_farmId];
        
        require(thirdPartyReward.token != address(0), "Third party reward not defined");
        require(owner() == msg.sender || thirdPartyReward.tokenManager == msg.sender, "Only token manager can deposit");
        
        _updateFarmRewardsAndFees(_farmId);

        IERC20(thirdPartyReward.token).transferFrom(msg.sender, address(this), _amountToken);
        
        uint256 blocksTillEnd = thirdPartyReward.endBlock >= block.number ? thirdPartyReward.endBlock - block.number : 0;
        uint256 unaccountedTokens = blocksTillEnd * thirdPartyReward.tokensPerBlock;

        thirdPartyReward.tokensPerBlock = (unaccountedTokens + _amountToken) / _distributeOverBlockCount;
        thirdPartyReward.endBlock = block.number + _distributeOverBlockCount;

        emit ThirdPartyRewardDeposited(_farmId, thirdPartyReward.token, thirdPartyReward.tokensPerBlock, thirdPartyReward.endBlock);
    }

    // Update the third party reward manager for a farm. Can only be called by the ElectroSwap or Third-Party token manager.
    function updateThirdPartyRewardManager(uint256 _farmId, address _tokenManager) external {
        require(_tokenManager != address(0), "Cannot be zero address");
        require(owner() == msg.sender || thirdPartyRewards[_farmId].tokenManager == msg.sender, "Only token manager can update");

        thirdPartyRewards[_farmId].tokenManager = _tokenManager;

        emit ThirdPartyRewardUpdated(_farmId, _tokenManager);
    }

    function getFarmById(uint256 _farmId) external view returns (Farm memory farm) {
        farm = farmInfo[_farmId];

        uint256 blocksSinceLastCalc = block.number - farm.lastCalcBlock;
        
        if(farm.liquidity > 0){
            uint256 rewards = blocksSinceLastCalc * ((rewardPerBlock * farm.allocPoint) / totalAllocPoint);
            farm.accRewardsPerShare += rewards * CALC_PRECISION / farm.liquidity;

            if(farm.version == 3){
                (uint256 uncollectedFee0, uint256 uncollectedFee1) = feeHelper.getUncollectedFees(farm.tokenId);
                farm.accFees0PerShare += uncollectedFee0 * CALC_PRECISION / farm.liquidity;
                farm.accFees1PerShare += uncollectedFee1 * CALC_PRECISION / farm.liquidity;
            }

            if(thirdPartyRewards[_farmId].token != address(0)){
                uint256 thirdPartyRewardTokens = blocksSinceLastCalc * thirdPartyRewards[_farmId].tokensPerBlock;
                farm.accThirdPartyRewardsPerShare += thirdPartyRewardTokens * CALC_PRECISION / farm.liquidity;
            }
        }
        
        // Prevent large responses by sending empty farmer array
        address[] memory emptyArr;
        farm.farmers = emptyArr;
    }

    function getFarmIdsByPoolAddress(address _poolAddr) external view returns (uint256[] memory) {
        return poolFarmIds[_poolAddr];
    }

    function getFarmerByFarmIdAndIndex(uint256 _farmId, uint256 _farmerIndex) external view returns (Farmer memory) {
        address farmerAddress = farmInfo[_farmId].farmers[_farmerIndex];
        return getFarmerByFarmIdAndAddress(_farmId, farmerAddress);
    }

    function getFarmerByFarmIdAndAddress(uint256 _farmId, address _farmerAddress) public view returns (Farmer memory farmer) {
        Farm memory farm = farmInfo[_farmId];
        farmer = farmerInfo[_farmId][_farmerAddress];
        farmer.durationMultiplier = _calculateDurationMultiplier(block.number - farmer.startingBlock);

        uint256 blocksSinceLastCalc = block.number - farm.lastCalcBlock;

        if(farm.liquidity > 0){
            uint256 rewards = blocksSinceLastCalc * ((rewardPerBlock * farm.allocPoint) / totalAllocPoint);
            farm.accRewardsPerShare += rewards * CALC_PRECISION / farm.liquidity;
        }
        
        uint256 rewardsEarned = (farmer.liquidity * farm.accRewardsPerShare / CALC_PRECISION) - farmer.rewardDebt;
        farmer.rewards = (rewardsEarned * farmer.boltMultiplier * farmer.durationMultiplier) / 100000000;

        ThirdPartyReward memory thirdPartyReward = thirdPartyRewards[_farmId];
        if(thirdPartyReward.token != address(0)){
            if(farm.liquidity > 0){
                
                uint256 thirdPartyRewardTokens;
                if(farm.lastCalcBlock < thirdPartyReward.endBlock && thirdPartyReward.endBlock <= block.number){
                    uint256 blocksAbleToPay = thirdPartyReward.endBlock - farm.lastCalcBlock;
                    thirdPartyRewardTokens = blocksAbleToPay * thirdPartyReward.tokensPerBlock;
                } 
                else {
                    thirdPartyRewardTokens = blocksSinceLastCalc * thirdPartyReward.tokensPerBlock;
                }
                farm.accThirdPartyRewardsPerShare += thirdPartyRewardTokens * CALC_PRECISION / farm.liquidity;
            }

            farmer.thirdPartyRewards = (farmer.liquidity * farm.accThirdPartyRewardsPerShare / CALC_PRECISION) - farmer.thirdPartyRewardDebt;
        }

        if(farm.version == 3){
            if(farm.liquidity > 0){
                (uint256 uncollectedFee0, uint256 uncollectedFee1) = feeHelper.getUncollectedFees(farm.tokenId);
                farm.accFees0PerShare += uncollectedFee0 * CALC_PRECISION / farm.liquidity;
                farm.accFees1PerShare += uncollectedFee1 * CALC_PRECISION / farm.liquidity;
            }
            
            farmer.fees0 = (farmer.liquidity * farm.accFees0PerShare / CALC_PRECISION) - farmer.fees0Debt;
            farmer.fees1 = (farmer.liquidity * farm.accFees1PerShare / CALC_PRECISION) - farmer.fees1Debt;
        }
    }

    function getThirdPartyRewardConfigByFarmId(uint256 _farmId) external view returns (ThirdPartyReward memory) {
        return thirdPartyRewards[_farmId];
    }


    /** ==================== EVENTS ==================== */
    
    event FarmCreated(uint256 indexed farmId, address indexed poolAddr, uint256 version, uint256 tokenId, uint256 allocPoints, uint256 totalAllocPoints);
    event FarmDeactivated(uint256 indexed farmId, address indexed poolAddr, uint256 version, uint256 tokenId);
    event FarmAllocationUpdated(uint256 indexed farmId, address indexed poolAddr, uint256 allocPoints, uint256 totalAllocPoints);
    event FarmNameUpdated(uint256 indexed farmId, address indexed poolAddr, string _name);

    event FarmDeposit(uint256 indexed farmId, address indexed farmer, uint256 amount0Added, uint256 amount1Added, uint256 liquidityAdded);
    event FarmIncrease(uint256 indexed farmId, address indexed farmer, uint256 amount0Added, uint256 amount1Added, uint256 liquidityAdded, uint256 adjustedStartingBlock);
    event FarmWithdrawl(uint256 indexed farmId, address indexed farmer, uint256 amount0Withdrawn, uint256 amount1Withdrawn, uint256 amountRewards, uint256 fees0Collected, uint256 fees1Collected, uint256 thirdPartyRewardsCollected);
    
    event ThirdPartyRewardAdded(uint256 indexed farmId, address indexed rewardToken, address indexed tokenManager);
    event ThirdPartyRewardUpdated(uint256 indexed farmId, address tokenManager);
    event ThirdPartyRewardDeposited(uint256 indexed farmId, address indexed rewardToken, uint256 tokensPerBlock, uint256 endBlock);

    event RewardsPerBlockUpdated(uint256 rewardPerBlock, uint256 prevRewardPerBlock);
    event BoltMultiplierUpdated(uint256 amountBolt, uint256 multiplier);

    function onERC721Received(address, address, uint256, bytes calldata) external pure returns (bytes4) {
        return this.onERC721Received.selector;
    }

    receive() external payable {}
    fallback() external payable {}
}
        

@openzeppelin/contracts/access/Ownable.sol

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

pragma solidity ^0.8.20;

import {Context} from "../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.
 *
 * The initial owner is set to the address provided by the deployer. 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;

    /**
     * @dev The caller account is not authorized to perform an operation.
     */
    error OwnableUnauthorizedAccount(address account);

    /**
     * @dev The owner is not a valid owner account. (eg. `address(0)`)
     */
    error OwnableInvalidOwner(address owner);

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

    /**
     * @dev Initializes the contract setting the address provided by the deployer as the initial owner.
     */
    constructor(address initialOwner) {
        if (initialOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(initialOwner);
    }

    /**
     * @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 {
        if (owner() != _msgSender()) {
            revert OwnableUnauthorizedAccount(_msgSender());
        }
    }

    /**
     * @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 {
        if (newOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _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/draft-IERC6093.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/draft-IERC6093.sol)
pragma solidity ^0.8.20;

/**
 * @dev Standard ERC20 Errors
 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC20 tokens.
 */
interface IERC20Errors {
    /**
     * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     * @param balance Current balance for the interacting account.
     * @param needed Minimum amount required to perform a transfer.
     */
    error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed);

    /**
     * @dev Indicates a failure with the token `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     */
    error ERC20InvalidSender(address sender);

    /**
     * @dev Indicates a failure with the token `receiver`. Used in transfers.
     * @param receiver Address to which tokens are being transferred.
     */
    error ERC20InvalidReceiver(address receiver);

    /**
     * @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers.
     * @param spender Address that may be allowed to operate on tokens without being their owner.
     * @param allowance Amount of tokens a `spender` is allowed to operate with.
     * @param needed Minimum amount required to perform a transfer.
     */
    error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed);

    /**
     * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
     * @param approver Address initiating an approval operation.
     */
    error ERC20InvalidApprover(address approver);

    /**
     * @dev Indicates a failure with the `spender` to be approved. Used in approvals.
     * @param spender Address that may be allowed to operate on tokens without being their owner.
     */
    error ERC20InvalidSpender(address spender);
}

/**
 * @dev Standard ERC721 Errors
 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC721 tokens.
 */
interface IERC721Errors {
    /**
     * @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in EIP-20.
     * Used in balance queries.
     * @param owner Address of the current owner of a token.
     */
    error ERC721InvalidOwner(address owner);

    /**
     * @dev Indicates a `tokenId` whose `owner` is the zero address.
     * @param tokenId Identifier number of a token.
     */
    error ERC721NonexistentToken(uint256 tokenId);

    /**
     * @dev Indicates an error related to the ownership over a particular token. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     * @param tokenId Identifier number of a token.
     * @param owner Address of the current owner of a token.
     */
    error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner);

    /**
     * @dev Indicates a failure with the token `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     */
    error ERC721InvalidSender(address sender);

    /**
     * @dev Indicates a failure with the token `receiver`. Used in transfers.
     * @param receiver Address to which tokens are being transferred.
     */
    error ERC721InvalidReceiver(address receiver);

    /**
     * @dev Indicates a failure with the `operator`’s approval. Used in transfers.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     * @param tokenId Identifier number of a token.
     */
    error ERC721InsufficientApproval(address operator, uint256 tokenId);

    /**
     * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
     * @param approver Address initiating an approval operation.
     */
    error ERC721InvalidApprover(address approver);

    /**
     * @dev Indicates a failure with the `operator` to be approved. Used in approvals.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     */
    error ERC721InvalidOperator(address operator);
}

/**
 * @dev Standard ERC1155 Errors
 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC1155 tokens.
 */
interface IERC1155Errors {
    /**
     * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     * @param balance Current balance for the interacting account.
     * @param needed Minimum amount required to perform a transfer.
     * @param tokenId Identifier number of a token.
     */
    error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId);

    /**
     * @dev Indicates a failure with the token `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     */
    error ERC1155InvalidSender(address sender);

    /**
     * @dev Indicates a failure with the token `receiver`. Used in transfers.
     * @param receiver Address to which tokens are being transferred.
     */
    error ERC1155InvalidReceiver(address receiver);

    /**
     * @dev Indicates a failure with the `operator`’s approval. Used in transfers.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     * @param owner Address of the current owner of a token.
     */
    error ERC1155MissingApprovalForAll(address operator, address owner);

    /**
     * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
     * @param approver Address initiating an approval operation.
     */
    error ERC1155InvalidApprover(address approver);

    /**
     * @dev Indicates a failure with the `operator` to be approved. Used in approvals.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     */
    error ERC1155InvalidOperator(address operator);

    /**
     * @dev Indicates an array length mismatch between ids and values in a safeBatchTransferFrom operation.
     * Used in batch transfers.
     * @param idsLength Length of the array of token identifiers
     * @param valuesLength Length of the array of token amounts
     */
    error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength);
}
          

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

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/ERC20.sol)

pragma solidity ^0.8.20;

import {IERC20} from "./IERC20.sol";
import {IERC20Metadata} from "./extensions/IERC20Metadata.sol";
import {Context} from "../../utils/Context.sol";
import {IERC20Errors} from "../../interfaces/draft-IERC6093.sol";

/**
 * @dev Implementation of the {IERC20} interface.
 *
 * This implementation is agnostic to the way tokens are created. This means
 * that a supply mechanism has to be added in a derived contract using {_mint}.
 *
 * TIP: For a detailed writeup see our guide
 * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
 * to implement supply mechanisms].
 *
 * The default value of {decimals} is 18. To change this, you should override
 * this function so it returns a different value.
 *
 * We have followed general OpenZeppelin Contracts guidelines: functions revert
 * instead returning `false` on failure. This behavior is nonetheless
 * conventional and does not conflict with the expectations of ERC20
 * applications.
 *
 * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
 * This allows applications to reconstruct the allowance for all accounts just
 * by listening to said events. Other implementations of the EIP may not emit
 * these events, as it isn't required by the specification.
 */
abstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors {
    mapping(address account => uint256) private _balances;

    mapping(address account => mapping(address spender => uint256)) private _allowances;

    uint256 private _totalSupply;

    string private _name;
    string private _symbol;

    /**
     * @dev Sets the values for {name} and {symbol}.
     *
     * All two of these values are immutable: they can only be set once during
     * construction.
     */
    constructor(string memory name_, string memory symbol_) {
        _name = name_;
        _symbol = symbol_;
    }

    /**
     * @dev Returns the name of the token.
     */
    function name() public view virtual returns (string memory) {
        return _name;
    }

    /**
     * @dev Returns the symbol of the token, usually a shorter version of the
     * name.
     */
    function symbol() public view virtual returns (string memory) {
        return _symbol;
    }

    /**
     * @dev Returns the number of decimals used to get its user representation.
     * For example, if `decimals` equals `2`, a balance of `505` tokens should
     * be displayed to a user as `5.05` (`505 / 10 ** 2`).
     *
     * Tokens usually opt for a value of 18, imitating the relationship between
     * Ether and Wei. This is the default value returned by this function, unless
     * it's overridden.
     *
     * NOTE: This information is only used for _display_ purposes: it in
     * no way affects any of the arithmetic of the contract, including
     * {IERC20-balanceOf} and {IERC20-transfer}.
     */
    function decimals() public view virtual returns (uint8) {
        return 18;
    }

    /**
     * @dev See {IERC20-totalSupply}.
     */
    function totalSupply() public view virtual returns (uint256) {
        return _totalSupply;
    }

    /**
     * @dev See {IERC20-balanceOf}.
     */
    function balanceOf(address account) public view virtual returns (uint256) {
        return _balances[account];
    }

    /**
     * @dev See {IERC20-transfer}.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - the caller must have a balance of at least `value`.
     */
    function transfer(address to, uint256 value) public virtual returns (bool) {
        address owner = _msgSender();
        _transfer(owner, to, value);
        return true;
    }

    /**
     * @dev See {IERC20-allowance}.
     */
    function allowance(address owner, address spender) public view virtual returns (uint256) {
        return _allowances[owner][spender];
    }

    /**
     * @dev See {IERC20-approve}.
     *
     * NOTE: If `value` is the maximum `uint256`, the allowance is not updated on
     * `transferFrom`. This is semantically equivalent to an infinite approval.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function approve(address spender, uint256 value) public virtual returns (bool) {
        address owner = _msgSender();
        _approve(owner, spender, value);
        return true;
    }

    /**
     * @dev See {IERC20-transferFrom}.
     *
     * Emits an {Approval} event indicating the updated allowance. This is not
     * required by the EIP. See the note at the beginning of {ERC20}.
     *
     * NOTE: Does not update the allowance if the current allowance
     * is the maximum `uint256`.
     *
     * Requirements:
     *
     * - `from` and `to` cannot be the zero address.
     * - `from` must have a balance of at least `value`.
     * - the caller must have allowance for ``from``'s tokens of at least
     * `value`.
     */
    function transferFrom(address from, address to, uint256 value) public virtual returns (bool) {
        address spender = _msgSender();
        _spendAllowance(from, spender, value);
        _transfer(from, to, value);
        return true;
    }

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to`.
     *
     * This internal function is equivalent to {transfer}, and can be used to
     * e.g. implement automatic token fees, slashing mechanisms, etc.
     *
     * Emits a {Transfer} event.
     *
     * NOTE: This function is not virtual, {_update} should be overridden instead.
     */
    function _transfer(address from, address to, uint256 value) internal {
        if (from == address(0)) {
            revert ERC20InvalidSender(address(0));
        }
        if (to == address(0)) {
            revert ERC20InvalidReceiver(address(0));
        }
        _update(from, to, value);
    }

    /**
     * @dev Transfers a `value` amount of tokens from `from` to `to`, or alternatively mints (or burns) if `from`
     * (or `to`) is the zero address. All customizations to transfers, mints, and burns should be done by overriding
     * this function.
     *
     * Emits a {Transfer} event.
     */
    function _update(address from, address to, uint256 value) internal virtual {
        if (from == address(0)) {
            // Overflow check required: The rest of the code assumes that totalSupply never overflows
            _totalSupply += value;
        } else {
            uint256 fromBalance = _balances[from];
            if (fromBalance < value) {
                revert ERC20InsufficientBalance(from, fromBalance, value);
            }
            unchecked {
                // Overflow not possible: value <= fromBalance <= totalSupply.
                _balances[from] = fromBalance - value;
            }
        }

        if (to == address(0)) {
            unchecked {
                // Overflow not possible: value <= totalSupply or value <= fromBalance <= totalSupply.
                _totalSupply -= value;
            }
        } else {
            unchecked {
                // Overflow not possible: balance + value is at most totalSupply, which we know fits into a uint256.
                _balances[to] += value;
            }
        }

        emit Transfer(from, to, value);
    }

    /**
     * @dev Creates a `value` amount of tokens and assigns them to `account`, by transferring it from address(0).
     * Relies on the `_update` mechanism
     *
     * Emits a {Transfer} event with `from` set to the zero address.
     *
     * NOTE: This function is not virtual, {_update} should be overridden instead.
     */
    function _mint(address account, uint256 value) internal {
        if (account == address(0)) {
            revert ERC20InvalidReceiver(address(0));
        }
        _update(address(0), account, value);
    }

    /**
     * @dev Destroys a `value` amount of tokens from `account`, lowering the total supply.
     * Relies on the `_update` mechanism.
     *
     * Emits a {Transfer} event with `to` set to the zero address.
     *
     * NOTE: This function is not virtual, {_update} should be overridden instead
     */
    function _burn(address account, uint256 value) internal {
        if (account == address(0)) {
            revert ERC20InvalidSender(address(0));
        }
        _update(account, address(0), value);
    }

    /**
     * @dev Sets `value` as the allowance of `spender` over the `owner` s tokens.
     *
     * This internal function is equivalent to `approve`, and can be used to
     * e.g. set automatic allowances for certain subsystems, etc.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `owner` cannot be the zero address.
     * - `spender` cannot be the zero address.
     *
     * Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument.
     */
    function _approve(address owner, address spender, uint256 value) internal {
        _approve(owner, spender, value, true);
    }

    /**
     * @dev Variant of {_approve} with an optional flag to enable or disable the {Approval} event.
     *
     * By default (when calling {_approve}) the flag is set to true. On the other hand, approval changes made by
     * `_spendAllowance` during the `transferFrom` operation set the flag to false. This saves gas by not emitting any
     * `Approval` event during `transferFrom` operations.
     *
     * Anyone who wishes to continue emitting `Approval` events on the`transferFrom` operation can force the flag to
     * true using the following override:
     * ```
     * function _approve(address owner, address spender, uint256 value, bool) internal virtual override {
     *     super._approve(owner, spender, value, true);
     * }
     * ```
     *
     * Requirements are the same as {_approve}.
     */
    function _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual {
        if (owner == address(0)) {
            revert ERC20InvalidApprover(address(0));
        }
        if (spender == address(0)) {
            revert ERC20InvalidSpender(address(0));
        }
        _allowances[owner][spender] = value;
        if (emitEvent) {
            emit Approval(owner, spender, value);
        }
    }

    /**
     * @dev Updates `owner` s allowance for `spender` based on spent `value`.
     *
     * Does not update the allowance value in case of infinite allowance.
     * Revert if not enough allowance is available.
     *
     * Does not emit an {Approval} event.
     */
    function _spendAllowance(address owner, address spender, uint256 value) internal virtual {
        uint256 currentAllowance = allowance(owner, spender);
        if (currentAllowance != type(uint256).max) {
            if (currentAllowance < value) {
                revert ERC20InsufficientAllowance(spender, currentAllowance, value);
            }
            unchecked {
                _approve(owner, spender, currentAllowance - value, false);
            }
        }
    }
}
          

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

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

pragma solidity ^0.8.20;

/**
 * @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 value of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

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

    /**
     * @dev Moves a `value` amount of 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 value) 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 a `value` amount of tokens 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 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the
     * allowance mechanism. `value` 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 value) external returns (bool);
}
          

@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Metadata.sol)

pragma solidity ^0.8.20;

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

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

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

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

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

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

pragma solidity ^0.8.20;

/**
 * @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/Context.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)

pragma solidity ^0.8.20;

/**
 * @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;
    }

    function _contextSuffixLength() internal view virtual returns (uint256) {
        return 0;
    }
}
          

@openzeppelin/contracts/utils/ReentrancyGuard.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/ReentrancyGuard.sol)

pragma solidity ^0.8.20;

/**
 * @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;

    /**
     * @dev Unauthorized reentrant call.
     */
    error ReentrancyGuardReentrantCall();

    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
        if (_status == ENTERED) {
            revert ReentrancyGuardReentrantCall();
        }

        // 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;
    }
}
          

contracts/RewardToken.sol

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

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

contract RewardToken is ERC20, Ownable {

    // Mapping to store contracts that are allowed to mint
    mapping(address => bool) public minters;

    constructor(string memory name, string memory symbol) ERC20(name, symbol) Ownable(msg.sender) {
        minters[msg.sender] = true;
    }

    function mint(address _to, uint256 _amount) external {
        require(minters[msg.sender], "Caller is not authorized to mint");
        _mint(_to, _amount);
        emit TokensMinted(_to, _amount);
    }

    event TokensMinted(address indexed to, uint256 amount);
}
          

contracts/interfaces.sol

// SPDX-License-Identifier: CC-BY-NC-ND-4.0
pragma solidity ^0.8.0;

interface IWETN {
    function deposit() external payable;
    function withdraw(uint256 amount) external;
}

interface IElectroSwapV2Pair {
    function token0() external view returns (address);
    function token1() external view returns (address);
    function factory() external returns (address);
    function totalSupply() external returns (uint256);
}

interface IElectroSwapV2Router {
    function addLiquidity(address token0, address token1, uint amount0Desired, uint amount1Desired, uint amount0Min, uint amount1Min, address to, uint deadline) external returns (uint amount0, uint amount1, uint liquidity);
    function removeLiquidity(address token0, address token1, uint liquidity, uint amount0Min, uint amount1Min, address to, uint deadline) external returns (uint amount0, uint amount1);
    function WETH() external returns (address wetnAddress);
}

interface IElectroSwapV3Factory {
    function getPool(address, address, uint24) external view returns (address);
}

interface IUncollectedFeeHelper {
    function getUncollectedFees(uint256 positionTokenId) external view returns (uint256 uncollectedFee0, uint256 uncollectedFee1);
}

interface INonfungiblePositionManager {

    struct IncreaseLiquidityParams {
        uint256 tokenId;
        uint256 amount0Desired;
        uint256 amount1Desired;
        uint256 amount0Min;
        uint256 amount1Min;
        uint256 deadline;
    }

    function increaseLiquidity(IncreaseLiquidityParams calldata params) external payable returns (uint128 liquidity, uint256 amount0, uint256 amount1);

    struct DecreaseLiquidityParams {
        uint256 tokenId;
        uint128 liquidity;
        uint256 amount0Min;
        uint256 amount1Min;
        uint256 deadline;
    }

    function decreaseLiquidity(DecreaseLiquidityParams calldata params) external payable returns (uint256 amount0, uint256 amount1);

    struct CollectParams {
        uint256 tokenId;
        address recipient;
        uint128 amount0Max;
        uint128 amount1Max;
    }

    function collect(CollectParams calldata params) external payable returns (uint256 amount0, uint256 amount1);

    function positions(uint256 tokenId) external view returns (
        uint96 nonce,
        address operator,
        address token0,
        address token1,
        uint24 fee,
        int24 tickLower,
        int24 tickUpper,
        uint128 liquidity,
        uint256 feeGrowthInside0LastX128,
        uint256 feeGrowthInside1LastX128,
        uint128 tokensOwed0,
        uint128 tokensOwed1
    );

    function safeTransferFrom(address from, address to, uint256 tokenId) external;
    function factory() external view returns (address factory);
}
          

Contract ABI

[{"type":"constructor","stateMutability":"nonpayable","inputs":[{"type":"string","name":"_rewardName","internalType":"string"},{"type":"string","name":"_rewardSymbol","internalType":"string"},{"type":"uint256","name":"_rewardSupply","internalType":"uint256"},{"type":"uint256","name":"_rewardPerBlock","internalType":"uint256"},{"type":"address","name":"_wetnAddress","internalType":"address"},{"type":"address","name":"_boltToken","internalType":"address"},{"type":"address","name":"_v2Factory","internalType":"address"},{"type":"address","name":"_v2Router","internalType":"address"},{"type":"address","name":"_v3Factory","internalType":"address"},{"type":"address","name":"_positionManager","internalType":"address"},{"type":"address","name":"_feeHelper","internalType":"address"}]},{"type":"error","name":"OwnableInvalidOwner","inputs":[{"type":"address","name":"owner","internalType":"address"}]},{"type":"error","name":"OwnableUnauthorizedAccount","inputs":[{"type":"address","name":"account","internalType":"address"}]},{"type":"error","name":"ReentrancyGuardReentrantCall","inputs":[]},{"type":"event","name":"BoltMultiplierUpdated","inputs":[{"type":"uint256","name":"amountBolt","internalType":"uint256","indexed":false},{"type":"uint256","name":"multiplier","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"FarmAllocationUpdated","inputs":[{"type":"uint256","name":"farmId","internalType":"uint256","indexed":true},{"type":"address","name":"poolAddr","internalType":"address","indexed":true},{"type":"uint256","name":"allocPoints","internalType":"uint256","indexed":false},{"type":"uint256","name":"totalAllocPoints","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"FarmCreated","inputs":[{"type":"uint256","name":"farmId","internalType":"uint256","indexed":true},{"type":"address","name":"poolAddr","internalType":"address","indexed":true},{"type":"uint256","name":"version","internalType":"uint256","indexed":false},{"type":"uint256","name":"tokenId","internalType":"uint256","indexed":false},{"type":"uint256","name":"allocPoints","internalType":"uint256","indexed":false},{"type":"uint256","name":"totalAllocPoints","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"FarmDeactivated","inputs":[{"type":"uint256","name":"farmId","internalType":"uint256","indexed":true},{"type":"address","name":"poolAddr","internalType":"address","indexed":true},{"type":"uint256","name":"version","internalType":"uint256","indexed":false},{"type":"uint256","name":"tokenId","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"FarmDeposit","inputs":[{"type":"uint256","name":"farmId","internalType":"uint256","indexed":true},{"type":"address","name":"farmer","internalType":"address","indexed":true},{"type":"uint256","name":"amount0Added","internalType":"uint256","indexed":false},{"type":"uint256","name":"amount1Added","internalType":"uint256","indexed":false},{"type":"uint256","name":"liquidityAdded","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"FarmIncrease","inputs":[{"type":"uint256","name":"farmId","internalType":"uint256","indexed":true},{"type":"address","name":"farmer","internalType":"address","indexed":true},{"type":"uint256","name":"amount0Added","internalType":"uint256","indexed":false},{"type":"uint256","name":"amount1Added","internalType":"uint256","indexed":false},{"type":"uint256","name":"liquidityAdded","internalType":"uint256","indexed":false},{"type":"uint256","name":"adjustedStartingBlock","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"FarmNameUpdated","inputs":[{"type":"uint256","name":"farmId","internalType":"uint256","indexed":true},{"type":"address","name":"poolAddr","internalType":"address","indexed":true},{"type":"string","name":"_name","internalType":"string","indexed":false}],"anonymous":false},{"type":"event","name":"FarmWithdrawl","inputs":[{"type":"uint256","name":"farmId","internalType":"uint256","indexed":true},{"type":"address","name":"farmer","internalType":"address","indexed":true},{"type":"uint256","name":"amount0Withdrawn","internalType":"uint256","indexed":false},{"type":"uint256","name":"amount1Withdrawn","internalType":"uint256","indexed":false},{"type":"uint256","name":"amountRewards","internalType":"uint256","indexed":false},{"type":"uint256","name":"fees0Collected","internalType":"uint256","indexed":false},{"type":"uint256","name":"fees1Collected","internalType":"uint256","indexed":false},{"type":"uint256","name":"thirdPartyRewardsCollected","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":"event","name":"RewardsPerBlockUpdated","inputs":[{"type":"uint256","name":"rewardPerBlock","internalType":"uint256","indexed":false},{"type":"uint256","name":"prevRewardPerBlock","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"ThirdPartyRewardAdded","inputs":[{"type":"uint256","name":"farmId","internalType":"uint256","indexed":true},{"type":"address","name":"rewardToken","internalType":"address","indexed":true},{"type":"address","name":"tokenManager","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"ThirdPartyRewardDeposited","inputs":[{"type":"uint256","name":"farmId","internalType":"uint256","indexed":true},{"type":"address","name":"rewardToken","internalType":"address","indexed":true},{"type":"uint256","name":"tokensPerBlock","internalType":"uint256","indexed":false},{"type":"uint256","name":"endBlock","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"ThirdPartyRewardUpdated","inputs":[{"type":"uint256","name":"farmId","internalType":"uint256","indexed":true},{"type":"address","name":"tokenManager","internalType":"address","indexed":false}],"anonymous":false},{"type":"fallback","stateMutability":"payable"},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"adminAddThirdPartyReward","inputs":[{"type":"uint256","name":"_farmId","internalType":"uint256"},{"type":"address","name":"_token","internalType":"address"},{"type":"address","name":"_tokenManager","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"adminCreateFarm_V2","inputs":[{"type":"string","name":"_name","internalType":"string"},{"type":"address","name":"_pairAddr","internalType":"address"},{"type":"uint256","name":"_allocPoint","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"adminCreateFarm_V3","inputs":[{"type":"string","name":"_name","internalType":"string"},{"type":"uint256","name":"_tokenId","internalType":"uint256"},{"type":"uint256","name":"_allocPoint","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"adminDeactivateFarm","inputs":[{"type":"uint256","name":"_farmId","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"adminUpdateBoltMultiplier","inputs":[{"type":"uint256","name":"_amountBolt","internalType":"uint256"},{"type":"uint256","name":"_multiplier","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"adminUpdateFarmAllocPoints","inputs":[{"type":"uint256","name":"_farmId","internalType":"uint256"},{"type":"uint256","name":"_allocPoint","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"adminUpdateFarmName","inputs":[{"type":"uint256","name":"_farmId","internalType":"uint256"},{"type":"string","name":"_name","internalType":"string"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"adminUpdateRewardsPerBlock","inputs":[{"type":"uint256","name":"_rewardPerBlock","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"calculateDurationMultiplier","inputs":[{"type":"uint256","name":"blocksServed","internalType":"uint256"}]},{"type":"function","stateMutability":"payable","outputs":[],"name":"deposit","inputs":[{"type":"uint256","name":"_farmId","internalType":"uint256"},{"type":"uint256","name":"_amount0","internalType":"uint256"},{"type":"uint256","name":"_amount1","internalType":"uint256"},{"type":"uint256","name":"_amountBolt","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"depositThirdPartyReward","inputs":[{"type":"uint256","name":"_farmId","internalType":"uint256"},{"type":"uint256","name":"_amountToken","internalType":"uint256"},{"type":"uint256","name":"_distributeOverBlockCount","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"farmCount","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"tuple","name":"farm","internalType":"struct YieldFarm.Farm","components":[{"type":"uint256","name":"id","internalType":"uint256"},{"type":"uint8","name":"version","internalType":"uint8"},{"type":"string","name":"name","internalType":"string"},{"type":"address","name":"poolAddr","internalType":"address"},{"type":"uint256","name":"liquidity","internalType":"uint256"},{"type":"uint256","name":"allocPoint","internalType":"uint256"},{"type":"uint256","name":"lastCalcBlock","internalType":"uint256"},{"type":"uint256","name":"accRewardsPerShare","internalType":"uint256"},{"type":"uint256","name":"accThirdPartyRewardsPerShare","internalType":"uint256"},{"type":"address[]","name":"farmers","internalType":"address[]"},{"type":"uint256","name":"farmerCount","internalType":"uint256"},{"type":"address","name":"token0","internalType":"address"},{"type":"address","name":"token1","internalType":"address"},{"type":"uint256","name":"tokenId","internalType":"uint256"},{"type":"int24","name":"tickLower","internalType":"int24"},{"type":"int24","name":"tickUpper","internalType":"int24"},{"type":"uint24","name":"fee","internalType":"uint24"},{"type":"uint256","name":"accFees0PerShare","internalType":"uint256"},{"type":"uint256","name":"accFees1PerShare","internalType":"uint256"},{"type":"bool","name":"active","internalType":"bool"}]}],"name":"getFarmById","inputs":[{"type":"uint256","name":"_farmId","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256[]","name":"","internalType":"uint256[]"}],"name":"getFarmIdsByPoolAddress","inputs":[{"type":"address","name":"_poolAddr","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"tuple","name":"farmer","internalType":"struct YieldFarm.Farmer","components":[{"type":"address","name":"addr","internalType":"address"},{"type":"uint256","name":"liquidity","internalType":"uint256"},{"type":"uint256","name":"boltMultiplier","internalType":"uint256"},{"type":"uint256","name":"boltDeposited","internalType":"uint256"},{"type":"uint256","name":"durationMultiplier","internalType":"uint256"},{"type":"uint256","name":"startingBlock","internalType":"uint256"},{"type":"uint256","name":"rewards","internalType":"uint256"},{"type":"uint256","name":"rewardDebt","internalType":"uint256"},{"type":"uint256","name":"thirdPartyRewards","internalType":"uint256"},{"type":"uint256","name":"thirdPartyRewardDebt","internalType":"uint256"},{"type":"uint256","name":"fees0","internalType":"uint256"},{"type":"uint256","name":"fees0Debt","internalType":"uint256"},{"type":"uint256","name":"fees1","internalType":"uint256"},{"type":"uint256","name":"fees1Debt","internalType":"uint256"}]}],"name":"getFarmerByFarmIdAndAddress","inputs":[{"type":"uint256","name":"_farmId","internalType":"uint256"},{"type":"address","name":"_farmerAddress","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"tuple","name":"","internalType":"struct YieldFarm.Farmer","components":[{"type":"address","name":"addr","internalType":"address"},{"type":"uint256","name":"liquidity","internalType":"uint256"},{"type":"uint256","name":"boltMultiplier","internalType":"uint256"},{"type":"uint256","name":"boltDeposited","internalType":"uint256"},{"type":"uint256","name":"durationMultiplier","internalType":"uint256"},{"type":"uint256","name":"startingBlock","internalType":"uint256"},{"type":"uint256","name":"rewards","internalType":"uint256"},{"type":"uint256","name":"rewardDebt","internalType":"uint256"},{"type":"uint256","name":"thirdPartyRewards","internalType":"uint256"},{"type":"uint256","name":"thirdPartyRewardDebt","internalType":"uint256"},{"type":"uint256","name":"fees0","internalType":"uint256"},{"type":"uint256","name":"fees0Debt","internalType":"uint256"},{"type":"uint256","name":"fees1","internalType":"uint256"},{"type":"uint256","name":"fees1Debt","internalType":"uint256"}]}],"name":"getFarmerByFarmIdAndIndex","inputs":[{"type":"uint256","name":"_farmId","internalType":"uint256"},{"type":"uint256","name":"_farmerIndex","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"tuple","name":"","internalType":"struct YieldFarm.ThirdPartyReward","components":[{"type":"address","name":"token","internalType":"address"},{"type":"address","name":"tokenManager","internalType":"address"},{"type":"uint256","name":"tokensPerBlock","internalType":"uint256"},{"type":"uint256","name":"endBlock","internalType":"uint256"}]}],"name":"getThirdPartyRewardConfigByFarmId","inputs":[{"type":"uint256","name":"_farmId","internalType":"uint256"}]},{"type":"function","stateMutability":"pure","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":"address","name":"","internalType":"address"}],"name":"owner","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"renounceOwnership","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"rewardPerBlock","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract RewardToken"}],"name":"rewardToken","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"totalAllocPoint","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"transferOwnership","inputs":[{"type":"address","name":"newOwner","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"updateThirdPartyRewardManager","inputs":[{"type":"uint256","name":"_farmId","internalType":"uint256"},{"type":"address","name":"_tokenManager","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"withdraw","inputs":[{"type":"uint256","name":"_farmId","internalType":"uint256"},{"type":"uint256","name":"_liquidityAmt","internalType":"uint256"},{"type":"bool","name":"_asNative","internalType":"bool"}]},{"type":"receive","stateMutability":"payable"}]
            

Deployed ByteCode

0x608080604052600436101561001a575b50361561001857005b005b600090813560e01c9081630a8e214c14612fd857508063150b7a0214612f6257806317caf6f114612f445780632505c3d9146123325780632e12ba7d1461228e57806333cfcd3b146122525780633d3d2547146120415780633db6b35d14611f3b5780634c763f2e14611e86578063555d405c14611e385780635ddca72a14611d8e5780636980ff8814611c89578063704cf56d14611b8d578063715018a614611b3b5780637e15dc6014611b145780638ae39cac14611af65780638da5cb5b14611ad057806391e758fd146119d4578063a8aff3bc146117e0578063ae9679791461170a578063e11dd11014611020578063eb4bfb5014610fb4578063f2fde38b14610f21578063f3a65da0146105c7578063f7c618c1146105a05763fd3cb4510361000f573461059d57602036600319011261059d576004358161026060405161016581613089565b828152826020820152606060408201528260608201528260808201528260a08201528260c08201528260e0820152826101008201526060610120820152826101408201528261016082015282610180820152826101a0820152826101c0820152826101e08201528261020082015282610220820152826102408201520152808252600e6020526101f7604083206131db565b9160c083019161020883514361342c565b9060808501918251610379575b50509061012084016060815260405193602085528551602086015260ff6020870151166040860152610258604087015161028060608801526102a0870190613148565b936001600160a01b0360608801511660808701525160a086015260a086015160c08601525160e085015260e08501516101008501526101008501516101208501525191601f1984820301610140850152602080845192838152019301915b81811061035a5750505061026083610140849501516101608501526001600160a01b03610160820151166101808501526001600160a01b03610180820151166101a08501526101a08101516101c08501526101c081015160020b6101e08501526101e081015160020b61020085015262ffffff6102008201511661022085015261022081015161024085015261024081015182850152015115156102808301520390f35b82516001600160a01b03168452602093840193909201916001016102b6565b61039f610399610390600b5460a08a0151906133f9565b600a549061340c565b826133f9565b670de0b6b3a7640000810290808204670de0b6b3a764000014901517156105385783516103cb9161340c565b6103da60e088019182516133d6565b9052600360ff60208801511614610479575b81845260106020526001600160a01b03604085205416156102155761042091845260106020526002604085200154906133f9565b670de0b6b3a7640000810290808204670de0b6b3a7640000149015171561046557815161044c9161340c565b61045c61010086019182516133d6565b90523880610215565b634e487b7160e01b83526011600452602483fd5b6001600160a01b036006541660406101a088015160248251809481936363bcf82760e01b835260048301525afa9081156105925785908692610560575b50670de0b6b3a7640000810290808204670de0b6b3a7640000149015171561054c5784516104e39161340c565b6104f361022089019182516133d6565b9052670de0b6b3a7640000810290808204670de0b6b3a764000014901517156105385783516105219161340c565b61053161024088019182516133d6565b90526103ec565b634e487b7160e01b85526011600452602485fd5b634e487b7160e01b86526011600452602486fd5b9050610584915060403d60401161058b575b61057c81836130c3565b810190613ed6565b90386104b6565b503d610572565b6040513d87823e3d90fd5b80fd5b503461059d578060031936011261059d5760206001600160a01b0360025416604051908152f35b503461059d57606036600319011261059d5760043567ffffffffffffffff8111610df0576105f9903690600401613101565b6106016146e8565b610609614400565b8160a06040516106188161303b565b82815282602082015282604082015282606082015282608082015201526001600160a01b03600554166040519163133f757160e31b8352602435600484015261018083602481855afa908115610f1657849185908694879188978991610e43575b506001600160a01b036040519661068f8861303b565b168087526001600160801b036001600160a01b036020890196169283875262ffffff60408a019a168a52606089019560020b8652608089019a60020b8b521660a088015215159081610e39575b5015610df4578088913b15610df057604051632142170760e11b8152336004820152306024808301919091523560448201529082908290606490829084905af18015610de557610dcc575b50506001600160a01b03600454169460206001600160a01b0386511660646001600160a01b038651169162ffffff855116996040519a8b948593630b4c774160e11b85526004850152602484015260448301525afa958615610dc1578896610d81575b50610793614711565b6107a1604435600a546133d6565b600a556107d06107b2600c546143bc565b80600c556001600160a01b0388168a52600d60205260408a206143cb565b6040968751926107e089856130c3565b6001845260208401601f198a01368237845115610d6d57339052600c54946001600160801b0360a0890151169462ffffff6001600160a01b03808b5116935116935160020b945160020b955116958b519861083a8a613089565b888a52600360208b01528c8a01526001600160a01b038b1660608a0152608089015260443560a08901524360c08901528b60e08901528b61010089015261012088015260016101408801526101608701526101808601526024356101a08601526101c08501526101e0840152610200830152856102208301528561024083015260016102608301528552600e60205283852090805182556001820160ff60208301511660ff198254161790558481015180519067ffffffffffffffff8211610ca257819061090b60028601546131a1565b601f8111610d2f575b50602090601f8311600114610cc1578992610cb6575b50508160011b916000199060031b1c19161760028301555b6001600160a01b036060820151166001600160a01b036003840191166001600160a01b03198254161790556080810151600483015560a0810151600583015560c0810151600683015560e08101516007830155610100810151600883015561012081015180519067ffffffffffffffff8211610ca257600160401b8211610ca2576020906009850154836009870155808410610c85575b500160098401885260208820885b838110610c68575050505091610aed60a0926011610260866101406001600160801b03980151600a8501556001600160a01b03610160820151166001600160a01b03600b860191166001600160a01b03198254161790556001600160a01b03610180820151166001600160a01b03600c860191166001600160a01b03198254161790556101a0810151600d850155600e84016101c082015181546101e084015160181b9062ffffff68ffffff00000000000061020087015160301b1693169068ffffffffffffffffff1916179065ffffff0000001617179055610220810151600f85015561024081015160108501550151151591019060ff801983541691151516179055565b0151168380526011602052600d6101a084862054855190610b0d826130a6565b33825260208201948552868201908152606082018881526080830189815260a084019143835260c08501938b85528b60e08701528b6101008701528b6101208701528b6101408701528b6101608701528b6101808701528b87870152600c548c52600f6020528a808d206000906001600160a01b033316825260205220986001600160a01b0380885116166001600160a01b03198b5416178a555160018a015551600289015551600388015551600487015551600586015551600685015560e0810151600785015561010081015160088501556101208101516009850155610140810151600a850155610160810151600b850155610180810151600c85015501519101557f51e07fb38ff3282be5c5a25bb4097bc0b78ccdbf629103926bf82d7f8ea3715b60806001600160a01b03600c5493600a548651966003885260243560208901526044359088015260608701521693a36001805580f35b60019060206001600160a01b0385511694019381840155016109e7565b600986018a52828a20610c9c918101908501613ebf565b386109d9565b634e487b7160e01b88526041600452602488fd5b01519050388061092a565b92506002850189528089209089935b601f1984168510610d14576001945083601f19811610610cfb575b505050811b016002830155610942565b015160001960f88460031b161c19169055388080610ceb565b81810151835560209485019460019093019290910190610cd0565b610d5d90600287018b5260208b20601f850160051c81019160208610610d63575b601f0160051c0190613ebf565b38610914565b9091508190610d50565b634e487b7160e01b8b52603260045260248bfd5b9095506020813d602011610db9575b81610d9d602093836130c3565b81010312610db557610dae906143a8565b943861078a565b8780fd5b3d9150610d90565b6040513d8a823e3d90fd5b81610dd6916130c3565b610de1578638610727565b8680fd5b6040513d84823e3d90fd5b5080fd5b60405162461bcd60e51b815260206004820152600f60248201527f496e76616c696420746f6b656e496400000000000000000000000000000000006044820152606490fd5b90501515386106dc565b975050945050509050610180833d8211610f0e575b81610e6661018093836130c3565b81010312610f0a5782516bffffffffffffffffffffffff811603610f0a57610e90602084016143a8565b50610e9d604084016143a8565b90610eaa606085016143a8565b9160808501519062ffffff82168203610de157610ec960a0870161477d565b94610ed660c0880161477d565b91610efe610160610ee960e08b0161463e565b99610ef7610140820161463e565b500161463e565b50949295919638610679565b8380fd5b3d9150610e58565b6040513d86823e3d90fd5b503461059d57602036600319011261059d576001600160a01b03610f43612ff4565b610f4b6146e8565b168015610fa057815473ffffffffffffffffffffffffffffffffffffffff198116821783556001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08380a380f35b631e4fbdf760e01b82526004829052602482fd5b503461059d57602036600319011261059d57610fce6146e8565b610fd6614400565b7fd76943207bf317503ab19e2502c0c76842f97bcb94d539322b494e191551d2066040600b54600435600b5561100a614711565b600b549082519182526020820152a16001805580f35b503461059d57606036600319011261059d5760043567ffffffffffffffff8111610df057611052903690600401613101565b61105a61300f565b906110636146e8565b61106b614400565b6001600160a01b038216156116c5576001600160a01b0382168352600d60205260408320546116805760405163c45a015560e01b8152602081600481876001600160a01b0388165af1908115610f16578491611646575b506001600160a01b038060085416911603611601576110df614711565b6110ed604435600a546133d6565b600a55604051630dfe168160e01b81526020816004816001600160a01b0387165afa908115610f165784916115c7575b5060405163d21220a760e01b81526020816004816001600160a01b0388165afa8015610592578590611583575b6001600160a01b039150611179611162600c546143bc565b80600c558387168852600d602052604088206143cb565b81600c54936040519561118b87613089565b858752600260208801526040870152818716606087015287608087015260443560a08701524360c08701528760e0870152876101008701526060610120870152876101408701521661016085015216610180830152836101a0830152836101c0830152836101e083015283610200830152836102208301528361024083015260016102608301528352600e6020526040832090805182556001820160ff60208301511660ff19825416179055604081015180519067ffffffffffffffff82116114c357819061125d60028601546131a1565b601f8111611550575b50602090601f83116001146114e25787926114d7575b50508160011b916000199060031b1c19161760028301555b6001600160a01b036060820151166001600160a01b036003840191166001600160a01b03198254161790556080810151600483015560a0810151600583015560c0810151600683015560e08101516007830155610100810151600883015561012081015180519067ffffffffffffffff82116114c357600160401b82116114c35760209060098501548360098701558084106114a6575b500160098401865260208620865b8381106114895787876114328860116102608a610140810151600a8501556001600160a01b03610160820151166001600160a01b03600b860191166001600160a01b03198254161790556001600160a01b03610180820151166001600160a01b03600c860191166001600160a01b03198254161790556101a0810151600d850155600e84016101c082015181546101e084015160181b9062ffffff68ffffff00000000000061020087015160301b1693169068ffffffffffffffffff1916179065ffffff0000001617179055610220810151600f85015561024081015160108501550151151591019060ff801983541691151516179055565b600c547f51e07fb38ff3282be5c5a25bb4097bc0b78ccdbf629103926bf82d7f8ea3715b60806001600160a01b03600a54946040519560028752876020880152604435604088015260608701521693a36001805580f35b60019060206001600160a01b038551169401938184015501611339565b6009860188528288206114bd918101908501613ebf565b3861132b565b634e487b7160e01b86526041600452602486fd5b01519050388061127c565b92506002850187528087209087935b601f1984168510611535576001945083601f1981161061151c575b505050811b016002830155611294565b015160001960f88460031b161c1916905538808061150c565b818101518355602094850194600190930192909101906114f1565b61157d9060028701895260208920601f850160051c81019160208610610d6357601f0160051c0190613ebf565b38611266565b506020813d6020116115bf575b8161159d602093836130c3565b810103126115bb576115b66001600160a01b03916143a8565b61114a565b8480fd5b3d9150611590565b90506020813d6020116115f9575b816115e2602093836130c3565b81010312610f0a576115f3906143a8565b3861111d565b3d91506115d5565b60405162461bcd60e51b815260206004820152601060248201527f496e76616c6964204c5020546f6b656e000000000000000000000000000000006044820152606490fd5b90506020813d602011611678575b81611661602093836130c3565b81010312610f0a57611672906143a8565b386110c2565b3d9150611654565b60405162461bcd60e51b815260206004820152601260248201527f506f6f6c20616c726561647920616464656400000000000000000000000000006044820152606490fd5b60405162461bcd60e51b815260206004820152601760248201527f43616e6e6f7420616464207a65726f20616464726573730000000000000000006044820152606490fd5b503461059d57604036600319011261059d576117dc61173261172a61300f565b600435613eec565b604051918291829190916101a0806101c08301946001600160a01b0381511684526020810151602085015260408101516040850152606081015160608501526080810151608085015260a081015160a085015260c081015160c085015260e081015160e08501526101008101516101008501526101208101516101208501526101408101516101408501526101608101516101608501526101808101516101808501520151910152565b0390f35b503461059d57604036600319011261059d576004359060243567ffffffffffffffff8111610df057611816903690600401613101565b61181e6146e8565b611826614400565b828252600e60205260408220926002600385019461184f6001600160a01b038754161515613e84565b019382519467ffffffffffffffff86116119c05761186d81546131a1565b601f8111611990575b50602095601f811160011461190057916001600160a01b0391836118eb9488997f0b7d901ae393500dff5a3466e8f2830aafbb8e764beb43fc543c5a2b7ad6cf39979899916118f5575b508160011b916000199060031b1c19161790555b541693604051918291602083526020830190613148565b0390a36001805580f35b9050880151386118c0565b818652868620601f198216875b8181106119785750826001600160a01b0394927f0b7d901ae393500dff5a3466e8f2830aafbb8e764beb43fc543c5a2b7ad6cf399798999a6118eb97956001941061195f575b5050811b0190556118d4565b8a015160001960f88460031b161c191690553880611953565b878a015183556020998a01996001909301920161190d565b6119ba9082875260208720601f890160051c81019160208a10610d6357601f0160051c0190613ebf565b38611876565b634e487b7160e01b85526041600452602485fd5b503461059d57604036600319011261059d576004356001600160a01b036119f961300f565b16611a05811515613dd1565b6001600160a01b0383541633148015611aaf575b15611a6a5760207f25a540d6e0a0f5fef26dac1ae340f4965dbcbe19402ebda71353fdf92e44e229918385526010825260016040862001816001600160a01b0319825416179055604051908152a280f35b60405162461bcd60e51b815260206004820152601d60248201527f4f6e6c7920746f6b656e206d616e616765722063616e207570646174650000006044820152606490fd5b508183526010602052336001600160a01b0360016040862001541614611a19565b503461059d578060031936011261059d576001600160a01b036020915416604051908152f35b503461059d578060031936011261059d576020600b54604051908152f35b503461059d57602036600319011261059d576020611b33600435614737565b604051908152f35b503461059d578060031936011261059d57611b546146e8565b806001600160a01b038154811981168355167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a380f35b503461059d57611b9c36613025565b611ba46146e8565b611bac614400565b6127108110611c44578115611bff57816040917f9c1c6b832a81475c34b096f71606cd45a11db174926001426848d13ef06677bb9385526011602052808386205582519182526020820152a16001805580f35b60405162461bcd60e51b815260206004820152601260248201527f416d6f756e74206d757374206265203e203000000000000000000000000000006044820152606490fd5b60405162461bcd60e51b815260206004820152601b60248201527f4d756c7469706c696572206d757374206265203e3d20313030303000000000006044820152606490fd5b503461059d57602036600319011261059d57600435611ca66146e8565b611cae614400565b808252600e602052604082209060038201906001600160a01b0382541615611d49578392601191611cdd614711565b6001600160a01b036005830194611cf78654600a5461342c565b600a555416907ff117694fabc4ec9e484bdba96e172c17701ffc85e1e15a06940d1ad46d24ddb1604060ff600186015416600d86015482519182526020820152a301805460ff19169055556001805580f35b60405162461bcd60e51b815260206004820152601560248201527f4e6f7468696e6720746f206465616374697661746500000000000000000000006044820152606490fd5b503461059d57611d9d36613025565b90611da66146e8565b611dae614400565b808352600e6020527fa568a242f66b52a4969248e1bcfe826b40dee71c1a5dd3730ee4d2ec6175424e60406001600160a01b03818620856003820191611df8848454161515613e84565b611e00614711565b611e1c82611e176005600a5494019384549061342c565b6133d6565b600a5555541693600a5482519182526020820152a36001805580f35b503461059d576117326117dc916001600160a01b03611e7860406009611e5d36613025565b929095611e68613e1d565b50868152600e6020522001613439565b90549060031b1c1690613eec565b503461059d57602036600319011261059d576001600160a01b03611ea8612ff4565b168152600d60205260408120604051908160208254918281520190819285526020852090855b818110611f255750505082611ee49103836130c3565b604051928392602084019060208552518091526040840192915b818110611f0c575050500390f35b8251845285945060209384019390920191600101611efe565b8254845260209093019260019283019201611ece565b503461059d57606036600319011261059d57600435611f5861300f565b90604435916001600160a01b038316809303610f0a576001600160a01b0390611f7f6146e8565b611f87614400565b169081151580612038575b611f9b90613dd1565b604051611fa78161306d565b828152600360208201858152604083018781526001600160a01b036060850192898452868a526010602052818060408c2097511616821987541617865551166001600160a01b036001860191166001600160a01b0319825416179055516002840155519101557f7bbbf626944758d872c4ec0f49288af94151f90f27a0d41b4d90691d960c58168480a46001805580f35b50821515611f92565b503461059d57606036600319011261059d576004356024359060443591818452601060205260408420906001600160a01b038254161561220d576001600160a01b03855416331480156121f7575b156121b25761209d83614422565b81546040516323b872dd60e01b8152336004820152306024820152604481018390529190602090839060649082908a906001600160a01b03165af180156121a7577f28d96120359a20f2e5501040c185f0e0f5e3603aa58507e2b1be4cecf3936550936040936001600160a01b039261217a575b506003810180549097612157918143821061216b5761214961213761214e93439061342c565b975b611e1760028801998a54906133f9565b61340c565b809555436133d6565b80975554169482519182526020820152a380f35b61214e91506121498c97612139565b61219b9060203d6020116121a0575b61219381836130c3565b810190613189565b612111565b503d612189565b6040513d88823e3d90fd5b60405162461bcd60e51b815260206004820152601e60248201527f4f6e6c7920746f6b656e206d616e616765722063616e206465706f73697400006044820152606490fd5b50336001600160a01b036001840154161461208f565b60405162461bcd60e51b815260206004820152601e60248201527f546869726420706172747920726577617264206e6f7420646566696e656400006044820152606490fd5b503461059d57606036600319011261059d576044358015158103610df0576122879061227c614400565b602435600435613467565b6001805580f35b503461059d57602036600319011261059d57604081608092606083516122b38161306d565b8281528260208201528285820152015260043581526010602052206040516122da8161306d565b6001600160a01b03825416918282526001600160a01b03806001830154166020840190815260606003600285015494604087019586520154940193845260405194855251166020840152516040830152516060820152f35b50608036600319011261059d57602435606435604435600435612353614400565b8184828752600e602052604087209060ff60118301541615612f0f5761237884614422565b838852600f602052604088206001600160a01b033316895260205260408820968891898095341515600014612e50575050600b84016001600160a01b038154166001600160a01b03600754168091148015612e3a575b15612de9578b813b1561059d57604051630d0e30db60e41b8152918290600490829034905af18015612ad457612dce575b506001600160a01b03905416926001600160a01b03600754168414600014612d5f5750505050600c8101546040516323b872dd60e01b81523360048201523060248201526044810186905234929160019190602090829060649082908e906001600160a01b03165af18015612aac57612d40575b505b82151580612d37575b15612cf257600182015460ff16600203612afc5761249b826131db565b6101608101805160035460405163095ea7b360e01b81526001600160a01b039182166004820152602481018890529299938d929091602091859160449183918791165af1928315610de557612545936101809350612adf575b50016020838d6001600160a01b03845116906001600160a01b03600354169060405180978195829463095ea7b360e01b845260048401602090939291936001600160a01b0360408201951681520152565b03925af1918215612ad457606092612ab7575b506101046001600160a01b038080600354169b5116925116918d6040519b8c94859362e8e33760e81b85526004850152602484015289604484015286606484015260016084840152600160a48401523060c48401524260e48401525af1948515612aac578a958b988c91612a5d575b50906125ec6125ff94939297809a9788955b6001600160a01b03600b8a015416614652565b6001600160a01b03600c86015416614652565b6004810161260e8482546133d6565b90556001870180548061285a575060098201805490600160401b821015612846578161264291600161266094018155613439565b33906001600160a01b038084549260031b9316831b921b1916179055565b600a8201805490600182018092116128325792856126ff93601093670de0b6b3a764000096556001600160a01b0333166001600160a01b03198d5416178c55558a8052601160205260408b205460028b01554360058b0155836126c76007830154886133f9565b0460078b0155836126dc6008830154886133f9565b0460098b0155836126f1600f830154886133f9565b04600b8b01550154846133f9565b04600d870155604051938452602084015260408301527f141912e5b44b5d07ab89056d17b819c5672cbcb022c8997d64631719b3a9863960603393a35b8061274a575b826001805580f35b600382019061275a8254826133d6565b9182855260116020526040852054156127ed576009546040516323b872dd60e01b815233600482015230602482015260448101939093526020908390606490829089906001600160a01b03165af19182156105925783926127d0575b505582526011602052600260408320549101553880612742565b6127e89060203d6020116121a05761219381836130c3565b6127b6565b60405162461bcd60e51b815260206004820152601460248201527f496e76616c696420424f4c54206465706f7369740000000000000000000000006044820152606490fd5b634e487b7160e01b8b52601160045260248bfd5b634e487b7160e01b8b52604160045260248bfd5b670de0b6b3a76400008594939502848104670de0b6b3a764000014851517156128325791818a9361288c8780956133d6565b6128959161340c565b966005850197885443906128a89161342c565b906128b390826133f9565b670de0b6b3a764000090046128c79161342c565b6128d1904361342c565b809855600781019081546128e590846133f9565b670de0b6b3a76400009004600787019081546129009161342c565b906008830190815461291290876133f9565b670de0b6b3a764000090049160098a0192835461292e9161342c565b91600f8601948554612940908a6133f9565b670de0b6b3a7640000900497600b8d0198895461295c9161342c565b97601001998a5461296d90826133f9565b670de0b6b3a764000090049d600d019d8e546129889161342c565b9c612992916133d6565b809b55546129a0908b6133f9565b670de0b6b3a76400009004906129b59161342c565b9055546129c290886133f9565b670de0b6b3a76400009004906129d79161342c565b9055546129e490856133f9565b670de0b6b3a76400009004906129f99161342c565b905554612a05916133f9565b670de0b6b3a7640000900490612a1a9161342c565b9055604051948552602085015260408401526060830152339160807fb2f6073a8c991ae638b8825ad59ac07373632bbdb61cce0d6a9b9be4943f3c3291a361273c565b9650509650906060853d606011612aa4575b81612a7c606093836130c3565b81010312612aa05784516020860151604090960151959790959192916125ec6125c7565b8980fd5b3d9150612a6f565b6040513d8c823e3d90fd5b612acf9060203d6020116121a05761219381836130c3565b612558565b6040513d8e823e3d90fd5b612af79060203d6020116121a05761219381836130c3565b6124f4565b612b08829694966131db565b61016081015160055460405163095ea7b360e01b81526001600160a01b0391821660048201526024810187905292969291602091839116818e816044810103925af18015612cca57612cd5575b5061018085015160055460405163095ea7b360e01b81526001600160a01b0391821660048201526024810184905291602091839116818e816044810103925af18015612cca57612cad575b5060606101a06001600160a01b036005541696015160405190612bc28261303b565b815260c460208201918783528d6040820199868b5285830160018152608084016001815260a08501914283526040519d8e98899763219f5d1760e01b89525160048901525160248801525160448701525160648601525160848501525160a48401525af1968715612aac578a8b968c99612c56575b50906125ec6001600160801b036125ff9594931697809a9788956125d9565b9850509450906060873d606011612ca5575b81612c75606093836130c3565b81010312612aa0576125ff91612c8a8861463e565b602089015160409099015198969293509091906125ec612c37565b3d9150612c68565b612cc59060203d6020116121a05761219381836130c3565b612ba0565b6040513d8d823e3d90fd5b612ced9060203d6020116121a05761219381836130c3565b612b55565b60405162461bcd60e51b815260206004820152601d60248201527f426f746820616d6f756e7473206d75737420626520706f7369746976650000006044820152606490fd5b5085151561247e565b612d589060203d6020116121a05761219381836130c3565b5038612473565b6040516323b872dd60e01b81523360048201523060248201526044810193909352349750600195509093929091906020908290818d81606481015b03925af18015612aac57612daf575b50612475565b612dc79060203d6020116121a05761219381836130c3565b5038612da9565b9a612de2816001600160a01b03939d6130c3565b9a906123ff565b60405162461bcd60e51b8152602060048201526024808201527f4e61746976652045544e2073656e7420746f20756e737570706f7274696e67206044820152636661726d60e01b6064820152608490fd5b50806001600160a01b03600c88015416146123ce565b600b8601546040516323b872dd60e01b815233600482015230602482015260448101959095529295949391926020908390606490829087906001600160a01b03165af1908115612f0457602092612d9a92612ee9575b50600c8601546040516323b872dd60e01b81523360048201523060248201526044810192909252909384926001600160a01b039092169183919082906064820190565b612eff90843d86116121a05761219381836130c3565b612ea6565b6040513d85823e3d90fd5b60405162461bcd60e51b815260206004820152600d60248201526c496e616374697665206661726d60981b6044820152606490fd5b503461059d578060031936011261059d576020600a54604051908152f35b503461059d57608036600319011261059d57612f7c612ff4565b50612f8561300f565b5060643567ffffffffffffffff8111610df05736602382011215610df057806004013567ffffffffffffffff8111612fd4573691016024011161059d57604051630a85bd0160e11b8152602090f35b8280fd5b905034610df05781600319360112610df057602090600c548152f35b600435906001600160a01b038216820361300a57565b600080fd5b602435906001600160a01b038216820361300a57565b604090600319011261300a576004359060243590565b60c0810190811067ffffffffffffffff82111761305757604052565b634e487b7160e01b600052604160045260246000fd5b6080810190811067ffffffffffffffff82111761305757604052565b610280810190811067ffffffffffffffff82111761305757604052565b6101c0810190811067ffffffffffffffff82111761305757604052565b90601f8019910116810190811067ffffffffffffffff82111761305757604052565b67ffffffffffffffff811161305757601f01601f191660200190565b81601f8201121561300a57803590613118826130e5565b9261312660405194856130c3565b8284526020838301011161300a57816000926020809301838601378301015290565b919082519283825260005b848110613174575050826000602080949584010152601f8019910116010190565b80602080928401015182828601015201613153565b9081602091031261300a5751801515810361300a5790565b90600182811c921680156131d1575b60208310146131bb57565b634e487b7160e01b600052602260045260246000fd5b91607f16916131b0565b906040516131e881613089565b80928054825260ff60018201541660208301526002810160405190816000825492613212846131a1565b80845293600181169081156133b4575060011461336d575b50613237925003826130c3565b60408301526001600160a01b03600382015416606083015260048101546080830152600581015460a0830152600681015460c0830152600781015460e083015260088101546101008301526009810160405190816020825491828152019160005260206000209060005b81811061334e5750505061026092826132c060ff9460119403826130c3565b610120860152600a8101546101408601526001600160a01b03600b820154166101608601526001600160a01b03600c82015416610180860152600d8101546101a086015262ffffff600e8201548060020b6101c08801528060181c60020b6101e088015260301c16610200860152600f81015461022086015260108101546102408601520154161515910152565b82546001600160a01b03168452602090930192600192830192016132a1565b90506000929192526020600020906000915b818310613398575050906020613237928201013861322a565b602091935080600191548385880101520191019091839261337f565b90506020925061323794915060ff191682840152151560051b8201013861322a565b919082018092116133e357565b634e487b7160e01b600052601160045260246000fd5b818102929181159184041417156133e357565b8115613416570490565b634e487b7160e01b600052601260045260246000fd5b919082039182116133e357565b80548210156134515760005260206000200190600090565b634e487b7160e01b600052603260045260246000fd5b906000828152600e60205260408120838252600f602052604082206001600160a01b033316835260205260408220926001840181815410613d8057839184938580976134b28a614422565b898252600f602052604082206001600160a01b033316835260205260408220600181019081548c81613b2d575b505050506134ee84865461342c565b808655670de0b6b3a76400006135086007860154836133f9565b0460078301556001840190600360ff83541614613aee575b8b845260106020526001600160a01b03604085205416613aca575b508415613a7b578b6139f5575b5460ff166002036138e15761355c836131db565b606081015160035460405163095ea7b360e01b81526001600160a01b03918216600482015260248101889052929d92911681604481875a94602095f18015610f16576138c4575b506001600160a01b03600354169060408d60e46001600160a01b03610180816101608501511693015116918784519687948593635d5155ef60e11b8552600485015260248401528b604484015260016064840152600160848401523060a48401524260c48401525af19c8d15610f16578492859e61389b575b50908d8361362993614902565b9a945b541561368d575b50506004613644910191825461342c565b9055604051968752602087015260408601526060850152608084015260a08301527fc78493ea26eae4d9218158e9f1f6207ffe0f8926aa240f8c79a353b729ffb3f860c03393a3565b60038291015480613834575b50899052600f60205280600d60408083206000906001600160a01b03331682526020522082815582600182015582600282015582600382015582600482015582600582015582600682015582600782015582600882015582600982015582600a82015582600b82015582600c8201550155888152600e6020526040812060098101805480845b818110613802575b50808210613738575b505050613633565b6000198101908111610538579061376c6001600160a01b0361375d61378b9486613439565b90549060031b1c169184613439565b9091906001600160a01b038084549260031b9316831b921b1916179055565b805480156137ee5790600a929160001901906137a78282613439565b6001600160a01b0382549160031b1b19169055550180549160001983019283116137da5750556004613644388080613730565b634e487b7160e01b81526011600452602490fd5b634e487b7160e01b84526031600452602484fd5b6001600160a01b036138148286613439565b905460039190911b1c16331461382c5760010161371f565b915038613727565b60095460405163a9059cbb60e01b81523360048201526024810192909252909160209183916044918391906001600160a01b03165af18015610de55761387c575b8190613699565b6138949060203d6020116121a05761219381836130c3565b5038613875565b6138bb919e5061362992935060403d60401161058b5761057c81836130c3565b9d90929161361c565b6138dc9060203d6020116121a05761219381836130c3565b6135a3565b6001600160a01b03600554169a600d84019b8c546040519060a0820182811067ffffffffffffffff8211176139e1576040926001600160801b0395949260a49285528152876020820193878c168552858301600181526060840160018152608085019142835288519a8b988997630624e65f60e11b8952516004890152511660248701525160448601525160648501525160848401525af19c8d15610f16578492859e6139b1575b50906139986139aa925461478b565b50508d836139a5886131db565b614902565b9a9461362c565b6139aa92919e5061399893506139d59060403d60401161058b5761057c81836130c3565b9390939e919250613989565b634e487b7160e01b87526041600452602487fd5b6001600160a01b03600b850154166001600160a01b0360075416809114908115613a64575b506135485760405162461bcd60e51b815260206004820152601160248201527f556e737570706f7274696e67206661726d0000000000000000000000000000006044820152606490fd5b90506001600160a01b03600c860154161438613a1a565b5050985050505060405195808752602087015260408601526060850152608084015260a08301527fc78493ea26eae4d9218158e9f1f6207ffe0f8926aa240f8c79a353b729ffb3f860c03393a3565b613ae2670de0b6b3a7640000916008870154906133f9565b0460098301553861353b565b670de0b6b3a7640000613b05600f870154836133f9565b04600b840155670de0b6b3a7640000613b226010870154836133f9565b04600d840155613520565b60079950613b79613b6e670de0b6b3a7640000613b626305f5e10095613b95958b52600e60205260408b209e8f0154906133f9565b0460078601549061342c565b6002850154906133f9565b613b8f613b8a60058601544361342c565b614737565b906133f9565b049788613d1c575b8d600360ff60018401541614613ca9575b508c85526010602052604085209260405193613bc98561306d565b60036001600160a01b03825416918287526001600160a01b0360018201541660208801526002810154604088015201546060860152613c0a575b508c6134df565b85939c50670de0b6b3a7640000613c2e613c389493600860099454910154906133f9565b049101549061342c565b998a613c46575b8080613c03565b5160405163a9059cbb60e01b8152336004820152602481018c90529160209183916044918391906001600160a01b03165af18015612f0457613c8a575b8290613c3f565b613ca29060203d6020116121a05761219381836130c3565b5038613c83565b9a509850815498600f810154613cbf908b6133f9565b670de0b6b3a76400009004600b830154613cd89161342c565b809a6010830154613ce8916133f9565b670de0b6b3a76400009004600d840154613d019161342c565b9b8c613d0c846131db565b92613d1693614902565b8d613bae565b6001600160a01b0360025416803b15613d7c576040516340c10f1960e01b8152336004820152602481018b90529086908290604490829084905af180156121a757613d68575b50613b9d565b85613d75919692966130c3565b9338613d62565b8580fd5b60405162461bcd60e51b815260206004820152602360248201527f43616e6e6f74207769746864726177206d6f7265207468616e206465706f73696044820152621d195960ea1b6064820152608490fd5b15613dd857565b60405162461bcd60e51b815260206004820152601660248201527f43616e6e6f74206265207a65726f2061646472657373000000000000000000006044820152606490fd5b60405190613e2a826130a6565b60006101a0838281528260208201528260408201528260608201528260808201528260a08201528260c08201528260e082015282610100820152826101208201528261014082015282610160820152826101808201520152565b15613e8b57565b60405162461bcd60e51b815260206004820152600c60248201526b125b9d985b1a59081c1bdbdb60a21b6044820152606490fd5b818110613eca575050565b60008155600101613ebf565b919082604091031261300a576020825192015190565b9190613ef6613e1d565b5082600052600e6020526040600020613f0e906131db565b9083600052600f60205260406000206000916001600160a01b031682526020526040902090604051613f3f816130a6565b82546001600160a01b03168152600183015460208201908152600284015495604083019687526003850154606084015260048501549660808401978852846005870154988960a087015260068801549360c0870194855260078901549160e0880192835260088a015492610100890193845260098b0154926101208a01938452600a8c0154976101408b01988952600b8d0154978d6101608d01998a52600c01549d6101808d019e8f52600d01549b6101a081019c8d529f43906140029161342c565b61400b90614737565b855260c08801948551439061401f9161342c565b9460808a01998d8b51614315575b936140616140699493670de0b6b3a76400006140586140619560e06305f5e1009a51910151906133f9565b0490519061342c565b9051906133f9565b04905260005260106020526040600020906040516140868161306d565b6001600160a01b03835416918282526001600160a01b0360018501541660208301526002840154916060600360408301968588520154910193818552614249575b5050505050505050600360ff602088015116146140e8575b50505050505050565b8051614135575b5093610240836140589361411b61412798670de0b6b3a7640000614058819951610220890151906133f9565b905251910151906133f9565b9052388080808080806140df565b6001600160a01b0360069692939654169360406101a084015160248251809881936363bcf82760e01b835260048301525afa94851561423d57600090600096614219575b50670de0b6b3a7640000810290808204670de0b6b3a764000014901517156133e35782516141a69161340c565b6141b661022085019182516133d6565b9052670de0b6b3a7640000850294808604670de0b6b3a764000014901517156133e3576141279661411b614058956141fc670de0b6b3a76400009861024096519061340c565b61420a8688019182516133d6565b905294985050935093506140ef565b905061423591955060403d60401161058b5761057c81836130c3565b949038614179565b6040513d6000823e3d90fd5b8851614283575b50505050505061427490670de0b6b3a764000061405888516101008c0151906133f9565b905238808080808080806140c7565b80865110908161430a575b50156142f65750506142a792614061915190519061342c565b905b670de0b6b3a7640000820291808304670de0b6b3a764000014901517156133e3576142d96142749285519061340c565b6142e96101008b019182516133d6565b9052903880808080614250565b9150915061430492506133f9565b906142a9565b90504310153861428e565b506143316103906143379260a0600b96959654910151906133f9565b876133f9565b670de0b6b3a7640000810290808204670de0b6b3a764000014901517156133e3578f936140618f94670de0b6b3a76400006140588f9860e061406999614387614061996305f5e1009d519061340c565b6143958385019182516133d6565b9052969a5050955050509394505061402d565b51906001600160a01b038216820361300a57565b60001981146133e35760010190565b8054600160401b811015613057576143e891600182018155613439565b819291549060031b91821b91600019901b1916179055565b600260015414614411576002600155565b633ee5aeb560e01b60005260046000fd5b80600052600e60205260406000206004810180548015801561462f575b61462357600683019361445385544361342c565b9161447161446b610390600b546005890154906133f9565b846133f9565b90670de0b6b3a7640000820291808304670de0b6b3a764000014901517156133e35761449c9161340c565b6144ab600786019182546133d6565b9055600360ff60018601541614614597575b60005260106020526040600020906001600160a01b0382541615158061458a575b6144ec575b50505050439055565b84549060038301549081831080614580575b1561456a575060026145156000936145209361342c565b9301928354906133f9565b91555b670de0b6b3a7640000810290808204670de0b6b3a764000014901517156133e35761455560089161455f93549061340c565b92019182546133d6565b9055388080806144e3565b915050600261457b920154906133f9565b614523565b50438211156144fe565b50600282015415156144de565b6145a4600d85015461478b565b90670de0b6b3a7640000810290808204670de0b6b3a764000014901517156133e3576145d28554809261340c565b6145e1600f88019182546133d6565b9055670de0b6b3a7640000820291808304670de0b6b3a764000014901517156133e35761460d9161340c565b61461c601086019182546133d6565b90556144bd565b50509050600643910155565b5060ff6011840154161561443f565b51906001600160801b038216820361300a57565b919092808211614663575b50505050565b61466c9161342c565b9115614685575061467c9061482f565b3880808061465d565b60405163a9059cbb60e01b81523360048201526024810192909252602090829060449082906000906001600160a01b03165af1801561423d576146c9575b5061467c565b6146e19060203d6020116121a05761219381836130c3565b50386146c3565b6001600160a01b036000541633036146fc57565b63118cdaa760e01b6000523360045260246000fd5b60015b600c548111614734578061472a61472f92614422565b6143bc565b614714565b50565b438103614745575061271090565b62603d808110156147765780613a980290613a988204036133e35762603d8090046127100180612710116133e35790565b506161a890565b51908160020b820361300a57565b60406001600160a01b039160848360055416918351906147aa8261306d565b8152600060208201933085526001600160801b0386840181815281606086019181835289519a8b998a9863fc6f786560e01b8a525160048a01525116602488015251166044860152511660648401525af1801561423d5760009160009161481057509091565b905061482b915060403d60401161058b5761057c81836130c3565b9091565b6007546001600160a01b0360009116803b15610df057818091602460405180948193632e1a7d4d60e01b83528860048401525af18015610de5576148f2575b508080808094335af1903d156148ec573d90614889826130e5565b9161489760405193846130c3565b825260203d92013e5b156148a757565b60405162461bcd60e51b815260206004820152601360248201527f45544e207472616e73666572206661696c6564000000000000000000000000006044820152606490fd5b506148a0565b816148fc916130c3565b3861486e565b906000938080614a7d575b85146149895750916001600160a01b036101806149609361492f60209661482f565b015160405163a9059cbb60e01b81523360048201526024810193909352919485939190921691839182906044820190565b03925af1801561423d576149715750565b6147349060203d6020116121a05761219381836130c3565b90929080614a5b575b84146149b357916001600160a01b036101606149609361492f60209661482f565b61016082015160405163a9059cbb60e01b8152336004820152602481019490945292939092602091859160449183916001600160a01b03165af191821561423d576001600160a01b0361018061496093600096602096614a40575b50015160405163a9059cbb60e01b81523360048201526024810193909352919485939190921691839182906044820190565b614a5690873d89116121a05761219381836130c3565b614a0e565b506001600160a01b03610180830151166001600160a01b036007541614614992565b506001600160a01b03610160840151166001600160a01b03600754161461490d56fea2646970667358221220dee168380a7ec1ad80414ffe07d22c3e108ff8bde18d4161917093ed675dc21964736f6c634300081a0033