ETH Price: $3,488.27 (+2.40%)
Gas: 2 Gwei

Contract

0x954D5088d88291146CE58270Add820e809fF3d7e
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Token Holdings

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Withdraw Locked184617412023-10-30 8:19:59263 days ago1698653999IN
unshETH: unshETH Farm V1
0 ETH0.0067285811.91560876
Get Reward181737102023-09-20 1:04:11304 days ago1695171851IN
unshETH: unshETH Farm V1
0 ETH0.0036338711.28196003
Withdraw Locked181737082023-09-20 1:03:47304 days ago1695171827IN
unshETH: unshETH Farm V1
0 ETH0.0064205211.3700568
Withdraw Locked181400382023-09-15 7:08:23308 days ago1694761703IN
unshETH: unshETH Farm V1
0 ETH0.005510398.73894465
Withdraw Locked174064132023-06-04 9:38:59411 days ago1685871539IN
unshETH: unshETH Farm V1
0 ETH0.0087511519.14757501
Withdraw Locked173386772023-05-25 20:57:47421 days ago1685048267IN
unshETH: unshETH Farm V1
0 ETH0.012404427.14091193
Withdraw Locked173128872023-05-22 5:54:11424 days ago1684734851IN
unshETH: unshETH Farm V1
0 ETH0.0147172226.06261558
Withdraw Locked172408422023-05-12 1:19:11435 days ago1683854351IN
unshETH: unshETH Farm V1
0 ETH0.0512395880.373797
Withdraw Locked171543992023-04-29 21:25:59447 days ago1682803559IN
unshETH: unshETH Farm V1
0 ETH0.0189578934.22892261
Withdraw Locked171469562023-04-28 20:18:47448 days ago1682713127IN
unshETH: unshETH Farm V1
0 ETH0.0207628136.54351008
Withdraw Locked171427632023-04-28 6:11:35448 days ago1682662295IN
unshETH: unshETH Farm V1
0 ETH0.0217872239.33734832
Withdraw Locked171416612023-04-28 2:28:11448 days ago1682648891IN
unshETH: unshETH Farm V1
0 ETH0.0192982633.96583232
Withdraw Locked171141662023-04-24 5:47:47452 days ago1682315267IN
unshETH: unshETH Farm V1
0 ETH0.0214767137.8
Withdraw Locked170950462023-04-21 13:24:47455 days ago1682083487IN
unshETH: unshETH Farm V1
0 ETH0.0178163738.68777317
Withdraw Locked170943492023-04-21 11:03:23455 days ago1682075003IN
unshETH: unshETH Farm V1
0 ETH0.0227080941
Withdraw Locked170938252023-04-21 9:17:11455 days ago1682068631IN
unshETH: unshETH Farm V1
0 ETH0.0186263240.44654358
Recover ERC20170532352023-04-15 15:17:47461 days ago1681571867IN
unshETH: unshETH Farm V1
0 ETH0.0036279327.58006754
Withdraw Locked170526882023-04-15 13:25:11461 days ago1681565111IN
unshETH: unshETH Farm V1
0 ETH0.0121238226.32656168
Withdraw Locked170505152023-04-15 6:00:59461 days ago1681538459IN
unshETH: unshETH Farm V1
0 ETH0.0170450130
Withdraw Locked170487062023-04-14 23:46:35462 days ago1681515995IN
unshETH: unshETH Farm V1
0 ETH0.0109589223.79701396
Get Reward170487042023-04-14 23:46:11462 days ago1681515971IN
unshETH: unshETH Farm V1
0 ETH0.0103280125.33642663
Withdraw Locked170463352023-04-14 15:38:59462 days ago1681486739IN
unshETH: unshETH Farm V1
0 ETH0.0212992137.4876014
Withdraw Locked170429772023-04-14 4:07:47462 days ago1681445267IN
unshETH: unshETH Farm V1
0 ETH0.0302492628.99263437
Withdraw Locked170429742023-04-14 4:07:11462 days ago1681445231IN
unshETH: unshETH Farm V1
0 ETH0.030098329.05805489
Withdraw Locked170429682023-04-14 4:05:59462 days ago1681445159IN
unshETH: unshETH Farm V1
0 ETH0.0284270827
View all transactions

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
EuclideanFarm

Compiler Version
v0.8.13+commit.abaa5c0e

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion
File 1 of 13 : EuclideanFarm.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.13;

// ┬ ┬┌┐┌┌─┐┬ ┬╔═╗╔╦╗╦ ╦
// │ ││││└─┐├─┤║╣  ║ ╠═╣
// └─┘┘└┘└─┘┴ ┴╚═╝ ╩ ╩ ╩
// =======================
// EuclideanFarm.sol
/// @title Modified StakingRewards from Frax/Synthetix with a "coordination multiplier"
/// @author @EIP-A1tair
/// @notice Allows users to stake unshETH and earn USH, with a multiplier 
///         based on how well the pool confirms to a target ratio of tokens
/// @dev    Uses Euclidean distance to calculate the "coordination ratio"

import "communal/Math.sol";
import "communal/SafeMath.sol";
import "communal/SafeERC20.sol";
import 'communal/TransferHelper.sol';
import "communal/ReentrancyGuard.sol";

// Inheritance
import "communal/Owned.sol";
import "forge-std/console.sol";


import { UD60x18, ud, fromUD60x18, unwrap, mulDiv18, add, sub, mul, div, mulDiv, inv, powu, exp } from "@prb/math/UD60x18.sol";

// just do everything in signed, the casting ugliness isn't worth the aids
// import { UD60x18, ud, fromUD60x18, mul, div, sqrt, inv, powu } from "@prb/math/UD60x18.sol";

interface ILSDRegistry {
    function vaultAddress() external view returns (address);
    //ratios should be in the form of [0.25e18, 0.25e18, 0.25e18, 0.25e18] for 4 LSDs
    function targetRatio() external view returns (uint256[] calldata);
    function lsdAddresses() external view returns (address[] calldata);
    function shanghaiTime() external view returns (uint256);
    function nonce() external view returns (uint256);
}

interface ILSDVault {
    function balanceInUnderlying() external view returns (uint256);
    function stakedETHperunshETH() external view returns (uint256);  
}
interface IDarknet {
    function checkPrice(address lsd) external view returns(uint256);
}

contract EuclideanFarm is Owned, ReentrancyGuard {
    using SafeMath for uint256;
    using SafeERC20 for IERC20;


    /* ========== STATE VARIABLES ========== */

    // Instances
    IERC20 public stakingToken;
    
    // Constant for various precisions
    uint256 private constant MULTIPLIER_PRECISION = 1e18;

    // Time tracking
    uint256 public periodFinish;
    uint256 public lastUpdateTime;

    // Lock time and multiplier settings
    uint256 public lock_max_multiplier = uint256(3e18); // E18. 1x = e18
    uint256 public lock_time_for_max_multiplier; //set this to rewardsDuration - 6 hours at initialization
    uint256 public lock_time_min; //set this at initialization

    // Coordination multipliers
    uint256 public max_cord_multiplier = uint256(3e18);
    uint256 public min_cord_multiplier = uint256(1e18); 
    uint256 public nonce = 0;
    uint256 public max_nonce = 2; //no more than 2 changes to targetRatio
    // uint256 public lastRatioUpdate;
    // uint256 public min_cooldown = 21 days; 
    // uint256 public min_ratio_distance = 0.5e18;


    //there is only one lockTime - needs to be equal to shanghaiTime
    address public LSDRegistryAddress;
    address public LSDVaultAddress;
    uint256 public shanghaiTime;

    // Reward addresses, rates, and managers
    mapping(address => address) public rewardManagers; // token addr -> manager addr
    address[] public rewardTokens;
    uint256[] public rewardRates;
    string[] public rewardSymbols;
    mapping(address => uint256) public rewardTokenAddrToIdx; // token addr -> token index
    
    // Reward period
    uint256 public rewardsDuration; // from now until Shanghai
    
    // Reward tracking
    uint256[] private rewardsPerTokenStored;
    mapping(address => mapping(uint256 => uint256)) private userRewardsPerTokenPaid; // staker addr -> token id -> paid amount
    mapping(address => mapping(uint256 => uint256)) private rewards; // staker addr -> token id -> reward amount
    mapping(address => uint256) private lastRewardClaimTime; // staker addr -> timestamp

    // Balance tracking
    uint256 private _total_liquidity_locked;
    uint256 private _total_combined_weight;
    mapping(address => uint256) private _locked_liquidity;
    mapping(address => uint256) private _combined_weights;

    // Stake tracking
    mapping(address => LockedStake[]) private lockedStakes;

    // Greylisting of bad addresses
    mapping(address => bool) public greylist; //how long until this one is offensive too?

    // Administrative booleans
    bool public stakesUnlocked; // Release locked stakes in case of emergency
    bool public withdrawalsPaused; // For emergencies
    bool public rewardsCollectionPaused; // For emergencies
    bool public stakingPaused; // For emergencies

    //Price feed for LSDs for coordination multiplier calculations
    address public darknetAddress;

    /* ========== STRUCTS ========== */
    
    struct LockedStake {
        bytes32 kek_id;
        uint256 start_timestamp;
        uint256 liquidity;
        uint256 ending_timestamp;
        uint256 lock_multiplier; // 6 decimals of precision. 1x = 1000000
    }

    /* ========== MODIFIERS ========== */

    modifier onlyByOwner() {
        require(msg.sender == owner, "Not the owner");
        _;
    }

    modifier onlyTknMgrs(address reward_token_address) {
        require(msg.sender == owner || isTokenManagerFor(msg.sender, reward_token_address), "Not owner or tkn mgr");
        _;
    }

    modifier notStakingPaused() {
        require(stakingPaused == false, "Staking paused");
        _;
    }

    modifier updateRewardAndBalance(address account, bool sync_too) {
        _updateRewardAndBalance(account, sync_too);
        _;
    }
    
    /* ========== CONSTRUCTOR ========== */

    //this also needs to point to an LSDRegistry
    constructor (
        address _owner,
        address _stakingToken,
        address _LSDRegistry,
        uint256 _shanghaiTime,
        address _darknetAddress,
        string[] memory _rewardSymbols,
        address[] memory _rewardTokens,
        address[] memory _rewardManagers,
        uint256[] memory _rewardRates
    ) Owned(_owner){
        //check that LSDRegistry is not 0x0
        require(_LSDRegistry != address(0), "LSDRegistry is 0x0");
        LSDRegistryAddress = _LSDRegistry;
        LSDVaultAddress = ILSDRegistry(LSDRegistryAddress).vaultAddress();
        //check that LSDRegistry's shanghaiTime matches the given shanghaiTime
        require(_shanghaiTime == ILSDRegistry(LSDRegistryAddress).shanghaiTime(), "shanghaiTime mismatch");
        shanghaiTime = _shanghaiTime;
        stakingToken = IERC20(_stakingToken);
        darknetAddress = _darknetAddress;

        rewardTokens = _rewardTokens;
        rewardRates = _rewardRates;
        rewardSymbols = _rewardSymbols;
        rewardsDuration = shanghaiTime - block.timestamp;
        lock_time_for_max_multiplier = rewardsDuration - 24 hours;

        for (uint256 i = 0; i < _rewardTokens.length; i++){ 
            // For fast token address -> token ID lookups later
            rewardTokenAddrToIdx[_rewardTokens[i]] = i;

            // Initialize the stored rewards
            rewardsPerTokenStored.push(0);

            // Initialize the reward managers
            rewardManagers[_rewardTokens[i]] = _rewardManagers[i];
        }

        // Other booleans
        stakesUnlocked = false;

        // Initialization
        lastUpdateTime = block.timestamp;
        periodFinish = block.timestamp.add(rewardsDuration);
    }

    /* ========== VIEWS ========== */

    // Total locked liquidity tokens
    function totalLiquidityLocked() external view returns (uint256) {
        return _total_liquidity_locked;
    }

    // Locked liquidity for a given account
    function lockedLiquidityOf(address account) external view returns (uint256) {
        return _locked_liquidity[account];
    }

    // Total 'balance' used for calculating the percent of the pool the account owns
    // Takes into account the locked stake time multiplier
    function totalCombinedWeight() external view returns (uint256) {
        return _total_combined_weight;
    }

    // Combined weight for a specific account
    function combinedWeightOf(address account) external view returns (uint256) {
        return _combined_weights[account];
    }

    // Calculated the combined weight for an account
    function calcCurCombinedWeight(address account) public view
        returns (
            uint256 old_combined_weight,
            uint256 new_combined_weight
        )
    {
        // Get the old combined weight
        old_combined_weight = _combined_weights[account];

        // Loop through the locked stakes, first by getting the liquidity * lock_multiplier portion
        new_combined_weight = 0;
        for (uint256 i = 0; i < lockedStakes[account].length; i++) {
            LockedStake memory thisStake = lockedStakes[account][i];
            uint256 lock_multiplier = thisStake.lock_multiplier;
            //add coordination multiplier here
            uint256 cord_multiplier = coordinationMultiplier();
            require(cord_multiplier != 0, "Multiplier error");
            //set min_cord_multiplier < 1e18 if we want to shrink yield below base rate
            if (cord_multiplier <= min_cord_multiplier){
                cord_multiplier = min_cord_multiplier;
            }

            // If the lock is expired
            if (thisStake.ending_timestamp <= block.timestamp) {
                // If the lock expired in the time since the last claim, the weight needs to be proportionately averaged this time
                if (lastRewardClaimTime[account] < thisStake.ending_timestamp){
                    uint256 time_before_expiry = (thisStake.ending_timestamp).sub(lastRewardClaimTime[account]);
                    uint256 time_after_expiry = (block.timestamp).sub(thisStake.ending_timestamp);

                    // Get the weighted-average lock_multiplier
                    uint256 numerator = ((lock_multiplier).mul(time_before_expiry)).add(((MULTIPLIER_PRECISION).mul(time_after_expiry)));
                    lock_multiplier = numerator.div(time_before_expiry.add(time_after_expiry));
                }
                // Otherwise, it needs to just be 1x
                else {
                    lock_multiplier = MULTIPLIER_PRECISION;
                }
            }

            uint256 liquidity = thisStake.liquidity;
            uint256 combined_boosted_amount = liquidity.mul(lock_multiplier).div(MULTIPLIER_PRECISION);
            uint256 coordination_boosted_amount = combined_boosted_amount.mul(cord_multiplier).div(MULTIPLIER_PRECISION);
            new_combined_weight = new_combined_weight.add(coordination_boosted_amount);
        }
    }

    // All the locked stakes for a given account
    function lockedStakesOf(address account) external view returns (LockedStake[] memory) {
        return lockedStakes[account];
    }

    // All the locked stakes for a given account
    function getRewardSymbols() external view returns (string[] memory) {
        return rewardSymbols;
    }

    // All the reward tokens
    function getAllRewardTokens() external view returns (address[] memory) {
        return rewardTokens;
    }

    // All the reward rates
    function getAllRewardRates() external view returns (uint256[] memory) {
        return rewardRates;
    }
    
    // Multiplier amount, given the length of the lock
    function lockMultiplier(uint256 secs) public view returns (uint256) {
        uint256 lock_multiplier =
            uint256(MULTIPLIER_PRECISION).add(
                secs
                    .mul(lock_max_multiplier.sub(MULTIPLIER_PRECISION))
                    .div(lock_time_for_max_multiplier)
            );
        if (lock_multiplier > lock_max_multiplier) lock_multiplier = lock_max_multiplier;
        return lock_multiplier;
    }

    function sqrt(uint y) internal pure returns (uint z) {
        if (y > 3) {
            z = y;
            uint x = y / 2 + 1;
            while (x < z) {
                z = x;
                x = (y / x + x) / 2;
            }
        } else if (y != 0) {
            z = 1;
        }
        // else z = 0 (default value)
    }
   
    function euclideanDistance(uint256[] memory _a, uint256[] memory _b) internal pure returns (uint256) {
            require(_a.length == _b.length, "Array length mismatch");
            int256 a;
            int256 b;
            uint256 sumSqDiff;
            uint256 diff;
            for (uint256 i = 0; i < _a.length; i++) {
                a = int256(_a[i]/1e15);
                b = int256(_b[i]/1e15);
                diff = uint256(a > b ? a - b : b - a);
                uint256 squareDiff = diff**2;
                sumSqDiff += squareDiff;
            }
            return sqrt(sumSqDiff)*1e15;
    }

    function coordinationMultiplier() public view returns(uint256) {
        //get ratios
        //fetch the current targetRatio locally
        //ratios should be in the form of [0.25e18, 0.25e18, 0.25e18, 0.25e18] for 4 LSDs
        uint256[] memory targetRatio = ILSDRegistry(LSDRegistryAddress).targetRatio();
        address[] memory lsdAddresses = ILSDRegistry(LSDRegistryAddress).lsdAddresses();
        //make sure the lengths match
        require(targetRatio.length == lsdAddresses.length, "targetRatio and lsdAddresses length mismatch");
        uint256 tabs = lsdAddresses.length;
        console.log(tabs);
        uint256[] memory currentRatio = new uint256[](tabs);
        console.log(currentRatio.length);
        console.log(LSDVaultAddress);
        uint256 totalETHbalance = ILSDVault(LSDVaultAddress).balanceInUnderlying();
        console.log(totalETHbalance);
        //get the current balances of all LSDs in vault in terms of underlying ETH to compute ratio
        for (uint256 i = 0; i < tabs; i++) {
            uint256 balance = IERC20(lsdAddresses[i]).balanceOf(LSDVaultAddress);
            //convert to actual underlying amounts
            uint256 rate = IDarknet(darknetAddress).checkPrice(lsdAddresses[i]);
            uint256 ratio = mulDiv(balance, rate, totalETHbalance);
            currentRatio[i] = ratio;
        }
        //from here onwards these operations cost ~50k gas 
        uint256 _x = euclideanDistance(targetRatio, currentRatio);
        uint256 L = mulDiv18(_x, 2.5e18);
        UD60x18 l_scaled = div(ud(L), ud(1.0e18));
        UD60x18 e_L = exp(l_scaled);
        UD60x18 hell = ud(3.0e18).sub(inv(e_L)).sub(ud(L));
        UD60x18 hella = div(powu(hell, 3),ud(4.0e18));
        return unwrap(hella.add(ud(1.0e18)));
    }

    // Last time the reward was applicable
    function lastTimeRewardApplicable() internal view returns (uint256) {
        return Math.min(block.timestamp, periodFinish);
    }

    // Amount of reward tokens per LP token
    function rewardsPerToken() public view returns (uint256[] memory newRewardsPerTokenStored) {
        if (_total_liquidity_locked == 0 || _total_combined_weight == 0) {
            return rewardsPerTokenStored;
        }
        else {
            newRewardsPerTokenStored = new uint256[](rewardTokens.length);
            for (uint256 i = 0; i < rewardsPerTokenStored.length; i++){ 
                newRewardsPerTokenStored[i] = rewardsPerTokenStored[i].add(
                    lastTimeRewardApplicable().sub(lastUpdateTime).mul(rewardRates[i]).mul(1e18).div(_total_combined_weight)
                );
            }
            return newRewardsPerTokenStored;
        }
    }

    // Amount of reward tokens an account has earned / accrued
    // Note: In the edge-case of one of the account's stake expiring since the last claim, this will
    // return a slightly inflated number
    function earned(address account) public view returns (uint256[] memory new_earned) {
        uint256[] memory reward_arr = rewardsPerToken();
        new_earned = new uint256[](rewardTokens.length);

        if (_combined_weights[account] == 0){
            for (uint256 i = 0; i < rewardTokens.length; i++){ 
                new_earned[i] = 0;
            }
        }
        else {
            for (uint256 i = 0; i < rewardTokens.length; i++){ 
                new_earned[i] = (_combined_weights[account])
                    .mul(reward_arr[i].sub(userRewardsPerTokenPaid[account][i]))
                    .div(1e18)
                    .add(rewards[account][i]);
            }
        }
    }

    // Total reward tokens emitted in the given period
    function getRewardForDuration() external view returns (uint256[] memory rewards_per_duration_arr) {
        rewards_per_duration_arr = new uint256[](rewardRates.length);

        for (uint256 i = 0; i < rewardRates.length; i++){ 
            rewards_per_duration_arr[i] = rewardRates[i].mul(rewardsDuration);
        }
    }

    // See if the caller_addr is a manager for the reward token 
    function isTokenManagerFor(address caller_addr, address reward_token_addr) public view returns (bool){
        if (caller_addr == owner) return true; // Contract owner
        else if (rewardManagers[reward_token_addr] == caller_addr) return true; // Reward manager
        return false; 
    }

    /* ========== MUTATIVE FUNCTIONS ========== */
    function synchronize() external {
        uint256 proposedNonce = ILSDRegistry(LSDRegistryAddress).nonce();
        require(nonce != proposedNonce, "Already synchronized");
         _synchronize();
    }

    function _synchronize() internal {
        //set current coordination-boosted rate to the new min
        uint256 current_boosted_rate = coordinationMultiplier()*rewardRates[0]/1e18; //only for USH rewards, change later if needed
        rewardRates[0] = current_boosted_rate;
        //update to the new target ratio
    }

    function _updateRewardAndBalance(address account, bool sync_too) internal {
        // Need to retro-adjust some things if the period hasn't been renewed, then start a new one
        if (sync_too){
            sync();
        }
        
        if (account != address(0)) {
            // To keep the math correct, the user's combined weight must be recomputed
            (   
                uint256 old_combined_weight,
                uint256 new_combined_weight
            ) = calcCurCombinedWeight(account);

            // Calculate the earnings first
            _syncEarned(account);

            // Update the user's and the global combined weights
            if (new_combined_weight >= old_combined_weight) {
                uint256 weight_diff = new_combined_weight.sub(old_combined_weight);
                _total_combined_weight = _total_combined_weight.add(weight_diff);
                _combined_weights[account] = old_combined_weight.add(weight_diff);
            } else {
                uint256 weight_diff = old_combined_weight.sub(new_combined_weight);
                _total_combined_weight = _total_combined_weight.sub(weight_diff);
                _combined_weights[account] = old_combined_weight.sub(weight_diff);
            }

        }
    }

    function _syncEarned(address account) internal {
        if (account != address(0)) {
            // Calculate the earnings
            uint256[] memory earned_arr = earned(account);

            // Update the rewards array
            for (uint256 i = 0; i < earned_arr.length; i++){ 
                rewards[account][i] = earned_arr[i];
            }

            // Update the rewards paid array
            for (uint256 i = 0; i < earned_arr.length; i++){ 
                userRewardsPerTokenPaid[account][i] = rewardsPerTokenStored[i];
            }
        }
    }

    // Two different stake functions are needed because of delegateCall and msg.sender issues
    function stakeLocked(uint256 liquidity) nonReentrant public {

        _stakeLocked(msg.sender, msg.sender, liquidity, block.timestamp);
    }

    // If this were not internal, and source_address had an infinite approve, this could be exploitable
    // (pull funds from source_address and stake for an arbitrary staker_address)
    function _stakeLocked(
        address staker_address, 
        address source_address, 
        uint256 liquidity, 
        uint256 start_timestamp
    ) internal updateRewardAndBalance(staker_address, true) {
        require(!stakingPaused, "Staking paused");
        require(liquidity > 0, "Must stake more than zero");
        require(greylist[staker_address] == false, "Address has been greylisted");
        // require(secs >= lock_time_min, "Minimum stake time not met");
        // require(secs <= lock_time_for_max_multiplier,"Trying to lock for too long");
        uint256 secs = shanghaiTime - block.timestamp;
        uint256 lock_multiplier = lockMultiplier(secs);
        bytes32 kek_id = keccak256(abi.encodePacked(staker_address, start_timestamp, liquidity, _locked_liquidity[staker_address]));
        lockedStakes[staker_address].push(LockedStake(
            kek_id,
            start_timestamp,
            liquidity,
            shanghaiTime,//replaced start_timestamp.add(secs) w/ shanghaiTime
            lock_multiplier
        ));

        // Pull the tokens from the source_address
        TransferHelper.safeTransferFrom(address(stakingToken), source_address, address(this), liquidity);

        // Update liquidities
        _total_liquidity_locked = _total_liquidity_locked.add(liquidity);
        _locked_liquidity[staker_address] = _locked_liquidity[staker_address].add(liquidity);

        // Need to call to update the combined weights
        _updateRewardAndBalance(staker_address, false);

        // Needed for edge case if the staker only claims once, and after the lock expired
        if (lastRewardClaimTime[staker_address] == 0) lastRewardClaimTime[staker_address] = block.timestamp;

        emit StakeLocked(staker_address, liquidity, secs, kek_id, source_address);
    }

    // Two different withdrawLocked functions are needed because of delegateCall and msg.sender issues
    function withdrawLocked(bytes32 kek_id) nonReentrant public {
        require(withdrawalsPaused == false, "Withdrawals paused");
        _withdrawLocked(msg.sender, msg.sender, kek_id);
    }

    // No withdrawer == msg.sender check needed since this is only internally callable and the checks are done in the wrapper
    // functions like withdraw()
    function _withdrawLocked(address staker_address, address destination_address, bytes32 kek_id) internal  {
        // Collect rewards first and then update the balances
        _getReward(staker_address, destination_address);

        LockedStake memory thisStake;
        thisStake.liquidity = 0;
        uint theArrayIndex;
        for (uint256 i = 0; i < lockedStakes[staker_address].length; i++){ 
            if (kek_id == lockedStakes[staker_address][i].kek_id){
                thisStake = lockedStakes[staker_address][i];
                theArrayIndex = i;
                break;
            }
        }
        require(thisStake.kek_id == kek_id, "Stake not found");
        require(block.timestamp >= thisStake.ending_timestamp || stakesUnlocked == true, "Stake is still locked!");

        uint256 liquidity = thisStake.liquidity;

        if (liquidity > 0) {
            // Update liquidities
            _total_liquidity_locked = _total_liquidity_locked.sub(liquidity);
            _locked_liquidity[staker_address] = _locked_liquidity[staker_address].sub(liquidity);

            // Remove the stake from the array
            delete lockedStakes[staker_address][theArrayIndex];

            // Need to call to update the combined weights
            _updateRewardAndBalance(staker_address, false);

            // Give the tokens to the destination_address
            // Should throw if insufficient balance
            stakingToken.transfer(destination_address, liquidity);

            emit WithdrawLocked(staker_address, liquidity, kek_id, destination_address);
        }

    }
    
    // Two different getReward functions are needed because of delegateCall and msg.sender issues
    function getReward() external nonReentrant returns (uint256[] memory) {
        require(rewardsCollectionPaused == false,"Rewards collection paused");
        return _getReward(msg.sender, msg.sender);
    }

    // No withdrawer == msg.sender check needed since this is only internally callable
    function _getReward(address rewardee, address destination_address) internal updateRewardAndBalance(rewardee, true) returns (uint256[] memory rewards_before) {
        // Update the rewards array and distribute rewards
        rewards_before = new uint256[](rewardTokens.length);

        for (uint256 i = 0; i < rewardTokens.length; i++){ 
            rewards_before[i] = rewards[rewardee][i];
            rewards[rewardee][i] = 0;
            //use SafeERC20.transfer
            IERC20(rewardTokens[i]).transfer(destination_address, rewards_before[i]);
            emit RewardPaid(rewardee, rewards_before[i], rewardTokens[i], destination_address);
        }

        lastRewardClaimTime[rewardee] = block.timestamp;
    }

    // If the period expired, DON'T renew it
    function retroCatchUp() internal {
        // Failsafe check
        require(block.timestamp > periodFinish, "Period has not expired yet!");

        // Ensure the provided reward amount is not more than the balance in the contract.
        // This keeps the reward rate in the right range, preventing overflows due to
        // very high values of rewardRate in the earned and rewardsPerToken functions;
        // Reward + leftover must be less than 2^256 / 10^18 to avoid overflow.
        // uint256 num_periods_elapsed = uint256(block.timestamp.sub(periodFinish)) / rewardsDuration; // Floor division to the nearest period
        
        // // Make sure there are enough tokens to renew the reward period
        // for (uint256 i = 0; i < rewardTokens.length; i++){ 
        //     require(rewardRates[i].mul(rewardsDuration).mul(num_periods_elapsed + 1) <= IERC20(rewardTokens[i]).balanceOf(address(this)), string(abi.encodePacked("Not enough reward tokens available: ", rewardTokens[i])) );
        // }
        
        // uint256 old_lastUpdateTime = lastUpdateTime;
        // uint256 new_lastUpdateTime = block.timestamp;

        // lastUpdateTime = periodFinish;
        //periodFinish = periodFinish.add((num_periods_elapsed.add(1)).mul(rewardsDuration));

        _updateStoredRewardsAndTime();

        //emit RewardsPeriodRenewed(address(stakingToken));
    }

    function _updateStoredRewardsAndTime() internal {
        // Get the rewards
        uint256[] memory rewards_per_token = rewardsPerToken();

        // Update the rewardsPerTokenStored
        for (uint256 i = 0; i < rewardsPerTokenStored.length; i++){ 
            rewardsPerTokenStored[i] = rewards_per_token[i];
        }

        // Update the last stored time
        lastUpdateTime = lastTimeRewardApplicable();
    }

    function sync() public {
        if (block.timestamp > periodFinish) {
            retroCatchUp();
        }
        else {
            _updateStoredRewardsAndTime();
        }
    }

    /* ========== RESTRICTED FUNCTIONS ========== */

    // Added to support recovering LP Rewards and other mistaken tokens from other systems to be distributed to holders
    function recoverERC20(address tokenAddress, uint256 tokenAmount) external onlyTknMgrs(tokenAddress) {
        // Cannot rug the staking / LP tokens
        require(tokenAddress != address(stakingToken), "Cannot rug staking / LP tokens");

        // Check if the desired token is a reward token
        bool isRewardToken = false;
        for (uint256 i = 0; i < rewardTokens.length; i++){ 
            if (rewardTokens[i] == tokenAddress) {
                isRewardToken = true;
                break;
            }
        }

        // Only the reward managers can take back their reward tokens
        if (isRewardToken && rewardManagers[tokenAddress] == msg.sender){
            IERC20(tokenAddress).transfer(msg.sender, tokenAmount);
            emit Recovered(msg.sender, tokenAddress, tokenAmount);
            return;
        }

        // Other tokens, like airdrops or accidental deposits, can be withdrawn by the owner
        else if (!isRewardToken && (msg.sender == owner)){
            IERC20(tokenAddress).transfer(msg.sender, tokenAmount);
            emit Recovered(msg.sender, tokenAddress, tokenAmount);
            return;
        }

        // If none of the above conditions are true
        else {
            revert("No valid tokens to recover");
        }
    }

    function setRewardsDuration(uint256 _rewardsDuration) external onlyByOwner {
        require(_rewardsDuration >= 86400, "Rewards duration too short");
        require(
            periodFinish == 0 || block.timestamp > periodFinish,
            "Reward period incomplete"
        );
        rewardsDuration = _rewardsDuration;
        emit RewardsDurationUpdated(rewardsDuration);
    }

    function setMultipliers(uint256 _lock_max_multiplier) external onlyByOwner {
        require(_lock_max_multiplier >= uint256(1e18), "Multiplier must be greater than or equal to 1e18");
        lock_max_multiplier = _lock_max_multiplier;
        emit LockedStakeMaxMultiplierUpdated(lock_max_multiplier);
    }

    function setLockedStakeTimeForMinAndMaxMultiplier(uint256 _lock_time_for_max_multiplier, uint256 _lock_time_min) external onlyByOwner {
        require(_lock_time_for_max_multiplier >= 1, "Mul max time must be >= 1");
        require(_lock_time_min >= 1, "Mul min time must be >= 1");

        lock_time_for_max_multiplier = _lock_time_for_max_multiplier;
        lock_time_min = _lock_time_min;

        emit LockedStakeTimeForMaxMultiplier(lock_time_for_max_multiplier);
        emit LockedStakeMinTime(_lock_time_min);
    }

    function greylistAddress(address _address) external onlyByOwner {
        greylist[_address] = !(greylist[_address]);
    }

    function unlockStakes() external onlyByOwner {
        stakesUnlocked = !stakesUnlocked;
    }

    function toggleStaking() external onlyByOwner {
        stakingPaused = !stakingPaused;
    }

    function toggleWithdrawals() external onlyByOwner {
        withdrawalsPaused = !withdrawalsPaused;
    }

    function toggleRewardsCollection() external onlyByOwner {
        rewardsCollectionPaused = !rewardsCollectionPaused;
    }

    // The owner or the reward token managers can set reward rates 
    function setRewardRate(address reward_token_address, uint256 new_rate, bool sync_too) external onlyTknMgrs(reward_token_address) {
        rewardRates[rewardTokenAddrToIdx[reward_token_address]] = new_rate;
        
        if (sync_too){
            sync();
        }
    }

    // The owner or the reward token managers can change managers
    function changeTokenManager(address reward_token_address, address new_manager_address) external onlyTknMgrs(reward_token_address) {
        rewardManagers[reward_token_address] = new_manager_address;
    }

    /* ========== EVENTS ========== */

    event StakeLocked(address indexed user, uint256 amount, uint256 secs, bytes32 kek_id, address source_address);
    event WithdrawLocked(address indexed user, uint256 amount, bytes32 kek_id, address destination_address);
    event RewardPaid(address indexed user, uint256 reward, address token_address, address destination_address);
    event RewardsDurationUpdated(uint256 newDuration);
    event Recovered(address destination_address, address token, uint256 amount);
    event RewardsPeriodRenewed(address token);
    event LockedStakeMaxMultiplierUpdated(uint256 multiplier);
    event LockedStakeTimeForMaxMultiplier(uint256 secs);
    event LockedStakeMinTime(uint256 secs);
}

File 2 of 13 : Address.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.11 <0.9.0;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize, which returns 0 for contracts in
        // construction, since the code is only stored at the end of the
        // constructor execution.

        uint256 size;
        // solhint-disable-next-line no-inline-assembly
        assembly { size := extcodesize(account) }
        return size > 0;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
        (bool success, ) = recipient.call{ value: amount }("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain`call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
      return functionCall(target, data, "Address: low-level call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        require(isContract(target), "Address: call to non-contract");

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = target.call{ value: value }(data);
        return _verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) {
        require(isContract(target), "Address: static call to non-contract");

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = target.staticcall(data);
        return _verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
        require(isContract(target), "Address: delegate call to non-contract");

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return _verifyCallResult(success, returndata, errorMessage);
    }

    function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) {
        if (success) {
            return returndata;
        } else {
            // Look for revert reason and bubble it up if present
            if (returndata.length > 0) {
                // The easiest way to bubble the revert reason is using memory via assembly

                // solhint-disable-next-line no-inline-assembly
                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}

File 3 of 13 : Context.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.11;

/*
 * @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 GSN 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 payable) {
        return payable(msg.sender);
    }

    function _msgData() internal view virtual returns (bytes memory) {
        this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
        return msg.data;
    }
}

File 4 of 13 : IERC20.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.11;

import "./Context.sol";
import "./SafeMath.sol";

/**
 * @dev Interface of the ERC20 standard as defined in the EIP. Does not include
 * the optional functions; to access them see {ERC20Detailed}.
 */
interface IERC20 {
    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

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

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

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

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

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

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

File 5 of 13 : Math.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.11;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    /**
     * @dev Returns the largest of two numbers.
     */
    function max(uint256 a, uint256 b) internal pure returns (uint256) {
        return a >= b ? a : b;
    }

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

    /**
     * @dev Returns the average of two numbers. The result is rounded towards
     * zero.
     */
    function average(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b) / 2 can overflow, so we distribute
        return (a / 2) + (b / 2) + ((a % 2 + b % 2) / 2);
    }

    // babylonian method (https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method)
    function sqrt(uint y) internal pure returns (uint z) {
        if (y > 3) {
            z = y;
            uint x = y / 2 + 1;
            while (x < z) {
                z = x;
                x = (y / x + x) / 2;
            }
        } else if (y != 0) {
            z = 1;
        }
    }
}

File 6 of 13 : Owned.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.6.11;

// https://docs.synthetix.io/contracts/Owned
contract Owned {
    address public owner;
    address public nominatedOwner;

    constructor (address _owner) public {
        require(_owner != address(0), "Owner address cannot be 0");
        owner = _owner;
        emit OwnerChanged(address(0), _owner);
    }

    function nominateNewOwner(address _owner) external onlyOwner {
        nominatedOwner = _owner;
        emit OwnerNominated(_owner);
    }

    function acceptOwnership() external {
        require(msg.sender == nominatedOwner, "You must be nominated before you can accept ownership");
        emit OwnerChanged(owner, nominatedOwner);
        owner = nominatedOwner;
        nominatedOwner = address(0);
    }

    modifier onlyOwner {
        require(msg.sender == owner, "Only the contract owner may perform this action");
        _;
    }

    event OwnerNominated(address newOwner);
    event OwnerChanged(address oldOwner, address newOwner);
}

File 7 of 13 : ReentrancyGuard.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.11;

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

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

    uint256 private _status;

    constructor () internal {
        _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 make it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        // On the first call to nonReentrant, _notEntered will be true
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

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

        _;

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

File 8 of 13 : SafeERC20.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.11;

import "./IERC20.sol";
import "./SafeMath.sol";
import "./Address.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20 {
    using SafeMath for uint256;
    using Address for address;

    function safeTransfer(IERC20 token, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
    }

    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
    }

    /**
     * @dev Deprecated. This function has issues similar to the ones found in
     * {IERC20-approve}, and its usage is discouraged.
     *
     * Whenever possible, use {safeIncreaseAllowance} and
     * {safeDecreaseAllowance} instead.
     */
    function safeApprove(IERC20 token, address spender, uint256 value) internal {
        // safeApprove should only be called when setting an initial allowance,
        // or when resetting it to zero. To increase and decrease it, use
        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
        // solhint-disable-next-line max-line-length
        require((value == 0) || (token.allowance(address(this), spender) == 0),
            "SafeERC20: approve from non-zero to non-zero allowance"
        );
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
    }

    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 newAllowance = token.allowance(address(this), spender).add(value);
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
    }

    function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero");
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
        // the target address contains contract code and also asserts for success in the low-level call.

        bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
        if (returndata.length > 0) { // Return data is optional
            // solhint-disable-next-line max-line-length
            require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
        }
    }
}

File 9 of 13 : SafeMath.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.11;

/**
 * @dev Wrappers over Solidity's arithmetic operations with added overflow
 * checks.
 *
 * Arithmetic operations in Solidity wrap on overflow. This can easily result
 * in bugs, because programmers usually assume that an overflow raises an
 * error, which is the standard behavior in high level programming languages.
 * `SafeMath` restores this intuition by reverting the transaction when an
 * operation overflows.
 *
 * Using this library instead of the unchecked operations eliminates an entire
 * class of bugs, so it's recommended to use it always.
 */
library SafeMath {
    /**
     * @dev Returns the addition of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `+` operator.
     *
     * Requirements:
     * - Addition cannot overflow.
     */
    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a + b;
        require(c >= a, "SafeMath: addition overflow");

        return c;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting on
     * overflow (when the result is negative).
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        return sub(a, b, "SafeMath: subtraction overflow");
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
     * overflow (when the result is negative).
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     * - Subtraction cannot overflow.
     *
     * _Available since v2.4.0._
     */
    function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b <= a, errorMessage);
        uint256 c = a - b;

        return c;
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `*` operator.
     *
     * Requirements:
     * - Multiplication cannot overflow.
     */
    function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
        // benefit is lost if 'b' is also tested.
        // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
        if (a == 0) {
            return 0;
        }

        uint256 c = a * b;
        require(c / a == b, "SafeMath: multiplication overflow");

        return c;
    }

    /**
     * @dev Returns the integer division of two unsigned integers. Reverts on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        return div(a, b, "SafeMath: division by zero");
    }

    /**
     * @dev Returns the integer division of two unsigned integers. Reverts with custom message on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     * - The divisor cannot be zero.
     *
     * _Available since v2.4.0._
     */
    function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        // Solidity only automatically asserts when dividing by 0
        require(b > 0, errorMessage);
        uint256 c = a / b;
        // assert(a == b * c + a % b); // There is no case in which this doesn't hold

        return c;
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * Reverts when dividing by zero.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     * - The divisor cannot be zero.
     */
    function mod(uint256 a, uint256 b) internal pure returns (uint256) {
        return mod(a, b, "SafeMath: modulo by zero");
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * Reverts with custom message when dividing by zero.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     * - The divisor cannot be zero.
     *
     * _Available since v2.4.0._
     */
    function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b != 0, errorMessage);
        return a % b;
    }
}

File 10 of 13 : TransferHelper.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.11;

// helper methods for interacting with ERC20 tokens and sending ETH that do not consistently return true/false
library TransferHelper {
    function safeApprove(address token, address to, uint value) internal {
        // bytes4(keccak256(bytes('approve(address,uint256)')));
        (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x095ea7b3, to, value));
        require(success && (data.length == 0 || abi.decode(data, (bool))), 'TransferHelper: APPROVE_FAILED');
    }

    function safeTransfer(address token, address to, uint value) internal {
        // bytes4(keccak256(bytes('transfer(address,uint256)')));
        (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0xa9059cbb, to, value));
        require(success && (data.length == 0 || abi.decode(data, (bool))), 'TransferHelper: TRANSFER_FAILED');
    }

    function safeTransferFrom(address token, address from, address to, uint value) internal {
        // bytes4(keccak256(bytes('transferFrom(address,address,uint256)')));
        (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x23b872dd, from, to, value));
        require(success && (data.length == 0 || abi.decode(data, (bool))), 'TransferHelper: TRANSFER_FROM_FAILED');
    }

    function safeTransferETH(address to, uint value) internal {
        (bool success,) = to.call{value:value}(new bytes(0));
        require(success, 'TransferHelper: ETH_TRANSFER_FAILED');
    }
}

File 11 of 13 : console.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.4.22 <0.9.0;

library console {
    address constant CONSOLE_ADDRESS = address(0x000000000000000000636F6e736F6c652e6c6f67);

    function _sendLogPayload(bytes memory payload) private view {
        uint256 payloadLength = payload.length;
        address consoleAddress = CONSOLE_ADDRESS;
        /// @solidity memory-safe-assembly
        assembly {
            let payloadStart := add(payload, 32)
            let r := staticcall(gas(), consoleAddress, payloadStart, payloadLength, 0, 0)
        }
    }

    function log() internal view {
        _sendLogPayload(abi.encodeWithSignature("log()"));
    }

    function logInt(int p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(int)", p0));
    }

    function logUint(uint p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint)", p0));
    }

    function logString(string memory p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string)", p0));
    }

    function logBool(bool p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool)", p0));
    }

    function logAddress(address p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address)", p0));
    }

    function logBytes(bytes memory p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes)", p0));
    }

    function logBytes1(bytes1 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes1)", p0));
    }

    function logBytes2(bytes2 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes2)", p0));
    }

    function logBytes3(bytes3 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes3)", p0));
    }

    function logBytes4(bytes4 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes4)", p0));
    }

    function logBytes5(bytes5 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes5)", p0));
    }

    function logBytes6(bytes6 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes6)", p0));
    }

    function logBytes7(bytes7 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes7)", p0));
    }

    function logBytes8(bytes8 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes8)", p0));
    }

    function logBytes9(bytes9 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes9)", p0));
    }

    function logBytes10(bytes10 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes10)", p0));
    }

    function logBytes11(bytes11 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes11)", p0));
    }

    function logBytes12(bytes12 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes12)", p0));
    }

    function logBytes13(bytes13 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes13)", p0));
    }

    function logBytes14(bytes14 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes14)", p0));
    }

    function logBytes15(bytes15 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes15)", p0));
    }

    function logBytes16(bytes16 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes16)", p0));
    }

    function logBytes17(bytes17 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes17)", p0));
    }

    function logBytes18(bytes18 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes18)", p0));
    }

    function logBytes19(bytes19 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes19)", p0));
    }

    function logBytes20(bytes20 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes20)", p0));
    }

    function logBytes21(bytes21 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes21)", p0));
    }

    function logBytes22(bytes22 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes22)", p0));
    }

    function logBytes23(bytes23 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes23)", p0));
    }

    function logBytes24(bytes24 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes24)", p0));
    }

    function logBytes25(bytes25 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes25)", p0));
    }

    function logBytes26(bytes26 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes26)", p0));
    }

    function logBytes27(bytes27 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes27)", p0));
    }

    function logBytes28(bytes28 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes28)", p0));
    }

    function logBytes29(bytes29 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes29)", p0));
    }

    function logBytes30(bytes30 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes30)", p0));
    }

    function logBytes31(bytes31 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes31)", p0));
    }

    function logBytes32(bytes32 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes32)", p0));
    }

    function log(uint p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint)", p0));
    }

    function log(string memory p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string)", p0));
    }

    function log(bool p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool)", p0));
    }

    function log(address p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address)", p0));
    }

    function log(uint p0, uint p1) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,uint)", p0, p1));
    }

    function log(uint p0, string memory p1) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,string)", p0, p1));
    }

    function log(uint p0, bool p1) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,bool)", p0, p1));
    }

    function log(uint p0, address p1) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,address)", p0, p1));
    }

    function log(string memory p0, uint p1) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint)", p0, p1));
    }

    function log(string memory p0, string memory p1) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,string)", p0, p1));
    }

    function log(string memory p0, bool p1) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool)", p0, p1));
    }

    function log(string memory p0, address p1) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,address)", p0, p1));
    }

    function log(bool p0, uint p1) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint)", p0, p1));
    }

    function log(bool p0, string memory p1) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string)", p0, p1));
    }

    function log(bool p0, bool p1) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool)", p0, p1));
    }

    function log(bool p0, address p1) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address)", p0, p1));
    }

    function log(address p0, uint p1) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint)", p0, p1));
    }

    function log(address p0, string memory p1) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,string)", p0, p1));
    }

    function log(address p0, bool p1) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool)", p0, p1));
    }

    function log(address p0, address p1) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,address)", p0, p1));
    }

    function log(uint p0, uint p1, uint p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint)", p0, p1, p2));
    }

    function log(uint p0, uint p1, string memory p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,uint,string)", p0, p1, p2));
    }

    function log(uint p0, uint p1, bool p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool)", p0, p1, p2));
    }

    function log(uint p0, uint p1, address p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,uint,address)", p0, p1, p2));
    }

    function log(uint p0, string memory p1, uint p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,string,uint)", p0, p1, p2));
    }

    function log(uint p0, string memory p1, string memory p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,string,string)", p0, p1, p2));
    }

    function log(uint p0, string memory p1, bool p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,string,bool)", p0, p1, p2));
    }

    function log(uint p0, string memory p1, address p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,string,address)", p0, p1, p2));
    }

    function log(uint p0, bool p1, uint p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint)", p0, p1, p2));
    }

    function log(uint p0, bool p1, string memory p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,bool,string)", p0, p1, p2));
    }

    function log(uint p0, bool p1, bool p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool)", p0, p1, p2));
    }

    function log(uint p0, bool p1, address p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,bool,address)", p0, p1, p2));
    }

    function log(uint p0, address p1, uint p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,address,uint)", p0, p1, p2));
    }

    function log(uint p0, address p1, string memory p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,address,string)", p0, p1, p2));
    }

    function log(uint p0, address p1, bool p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,address,bool)", p0, p1, p2));
    }

    function log(uint p0, address p1, address p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,address,address)", p0, p1, p2));
    }

    function log(string memory p0, uint p1, uint p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint,uint)", p0, p1, p2));
    }

    function log(string memory p0, uint p1, string memory p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint,string)", p0, p1, p2));
    }

    function log(string memory p0, uint p1, bool p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint,bool)", p0, p1, p2));
    }

    function log(string memory p0, uint p1, address p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint,address)", p0, p1, p2));
    }

    function log(string memory p0, string memory p1, uint p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,uint)", p0, p1, p2));
    }

    function log(string memory p0, string memory p1, string memory p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,string)", p0, p1, p2));
    }

    function log(string memory p0, string memory p1, bool p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,bool)", p0, p1, p2));
    }

    function log(string memory p0, string memory p1, address p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,address)", p0, p1, p2));
    }

    function log(string memory p0, bool p1, uint p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint)", p0, p1, p2));
    }

    function log(string memory p0, bool p1, string memory p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,string)", p0, p1, p2));
    }

    function log(string memory p0, bool p1, bool p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool)", p0, p1, p2));
    }

    function log(string memory p0, bool p1, address p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,address)", p0, p1, p2));
    }

    function log(string memory p0, address p1, uint p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,uint)", p0, p1, p2));
    }

    function log(string memory p0, address p1, string memory p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,string)", p0, p1, p2));
    }

    function log(string memory p0, address p1, bool p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,bool)", p0, p1, p2));
    }

    function log(string memory p0, address p1, address p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,address)", p0, p1, p2));
    }

    function log(bool p0, uint p1, uint p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint)", p0, p1, p2));
    }

    function log(bool p0, uint p1, string memory p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint,string)", p0, p1, p2));
    }

    function log(bool p0, uint p1, bool p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool)", p0, p1, p2));
    }

    function log(bool p0, uint p1, address p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint,address)", p0, p1, p2));
    }

    function log(bool p0, string memory p1, uint p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint)", p0, p1, p2));
    }

    function log(bool p0, string memory p1, string memory p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,string)", p0, p1, p2));
    }

    function log(bool p0, string memory p1, bool p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool)", p0, p1, p2));
    }

    function log(bool p0, string memory p1, address p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,address)", p0, p1, p2));
    }

    function log(bool p0, bool p1, uint p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint)", p0, p1, p2));
    }

    function log(bool p0, bool p1, string memory p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string)", p0, p1, p2));
    }

    function log(bool p0, bool p1, bool p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool)", p0, p1, p2));
    }

    function log(bool p0, bool p1, address p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address)", p0, p1, p2));
    }

    function log(bool p0, address p1, uint p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint)", p0, p1, p2));
    }

    function log(bool p0, address p1, string memory p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,string)", p0, p1, p2));
    }

    function log(bool p0, address p1, bool p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool)", p0, p1, p2));
    }

    function log(bool p0, address p1, address p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,address)", p0, p1, p2));
    }

    function log(address p0, uint p1, uint p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint,uint)", p0, p1, p2));
    }

    function log(address p0, uint p1, string memory p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint,string)", p0, p1, p2));
    }

    function log(address p0, uint p1, bool p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint,bool)", p0, p1, p2));
    }

    function log(address p0, uint p1, address p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint,address)", p0, p1, p2));
    }

    function log(address p0, string memory p1, uint p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,uint)", p0, p1, p2));
    }

    function log(address p0, string memory p1, string memory p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,string)", p0, p1, p2));
    }

    function log(address p0, string memory p1, bool p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,bool)", p0, p1, p2));
    }

    function log(address p0, string memory p1, address p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,address)", p0, p1, p2));
    }

    function log(address p0, bool p1, uint p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint)", p0, p1, p2));
    }

    function log(address p0, bool p1, string memory p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,string)", p0, p1, p2));
    }

    function log(address p0, bool p1, bool p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool)", p0, p1, p2));
    }

    function log(address p0, bool p1, address p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,address)", p0, p1, p2));
    }

    function log(address p0, address p1, uint p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,uint)", p0, p1, p2));
    }

    function log(address p0, address p1, string memory p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,string)", p0, p1, p2));
    }

    function log(address p0, address p1, bool p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,bool)", p0, p1, p2));
    }

    function log(address p0, address p1, address p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,address)", p0, p1, p2));
    }

    function log(uint p0, uint p1, uint p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint,uint)", p0, p1, p2, p3));
    }

    function log(uint p0, uint p1, uint p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint,string)", p0, p1, p2, p3));
    }

    function log(uint p0, uint p1, uint p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint,bool)", p0, p1, p2, p3));
    }

    function log(uint p0, uint p1, uint p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint,address)", p0, p1, p2, p3));
    }

    function log(uint p0, uint p1, string memory p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,uint,string,uint)", p0, p1, p2, p3));
    }

    function log(uint p0, uint p1, string memory p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,uint,string,string)", p0, p1, p2, p3));
    }

    function log(uint p0, uint p1, string memory p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,uint,string,bool)", p0, p1, p2, p3));
    }

    function log(uint p0, uint p1, string memory p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,uint,string,address)", p0, p1, p2, p3));
    }

    function log(uint p0, uint p1, bool p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool,uint)", p0, p1, p2, p3));
    }

    function log(uint p0, uint p1, bool p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool,string)", p0, p1, p2, p3));
    }

    function log(uint p0, uint p1, bool p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool,bool)", p0, p1, p2, p3));
    }

    function log(uint p0, uint p1, bool p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool,address)", p0, p1, p2, p3));
    }

    function log(uint p0, uint p1, address p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,uint,address,uint)", p0, p1, p2, p3));
    }

    function log(uint p0, uint p1, address p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,uint,address,string)", p0, p1, p2, p3));
    }

    function log(uint p0, uint p1, address p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,uint,address,bool)", p0, p1, p2, p3));
    }

    function log(uint p0, uint p1, address p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,uint,address,address)", p0, p1, p2, p3));
    }

    function log(uint p0, string memory p1, uint p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,string,uint,uint)", p0, p1, p2, p3));
    }

    function log(uint p0, string memory p1, uint p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,string,uint,string)", p0, p1, p2, p3));
    }

    function log(uint p0, string memory p1, uint p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,string,uint,bool)", p0, p1, p2, p3));
    }

    function log(uint p0, string memory p1, uint p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,string,uint,address)", p0, p1, p2, p3));
    }

    function log(uint p0, string memory p1, string memory p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,string,string,uint)", p0, p1, p2, p3));
    }

    function log(uint p0, string memory p1, string memory p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,string,string,string)", p0, p1, p2, p3));
    }

    function log(uint p0, string memory p1, string memory p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,string,string,bool)", p0, p1, p2, p3));
    }

    function log(uint p0, string memory p1, string memory p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,string,string,address)", p0, p1, p2, p3));
    }

    function log(uint p0, string memory p1, bool p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,string,bool,uint)", p0, p1, p2, p3));
    }

    function log(uint p0, string memory p1, bool p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,string,bool,string)", p0, p1, p2, p3));
    }

    function log(uint p0, string memory p1, bool p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,string,bool,bool)", p0, p1, p2, p3));
    }

    function log(uint p0, string memory p1, bool p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,string,bool,address)", p0, p1, p2, p3));
    }

    function log(uint p0, string memory p1, address p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,string,address,uint)", p0, p1, p2, p3));
    }

    function log(uint p0, string memory p1, address p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,string,address,string)", p0, p1, p2, p3));
    }

    function log(uint p0, string memory p1, address p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,string,address,bool)", p0, p1, p2, p3));
    }

    function log(uint p0, string memory p1, address p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,string,address,address)", p0, p1, p2, p3));
    }

    function log(uint p0, bool p1, uint p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint,uint)", p0, p1, p2, p3));
    }

    function log(uint p0, bool p1, uint p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint,string)", p0, p1, p2, p3));
    }

    function log(uint p0, bool p1, uint p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint,bool)", p0, p1, p2, p3));
    }

    function log(uint p0, bool p1, uint p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint,address)", p0, p1, p2, p3));
    }

    function log(uint p0, bool p1, string memory p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,bool,string,uint)", p0, p1, p2, p3));
    }

    function log(uint p0, bool p1, string memory p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,bool,string,string)", p0, p1, p2, p3));
    }

    function log(uint p0, bool p1, string memory p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,bool,string,bool)", p0, p1, p2, p3));
    }

    function log(uint p0, bool p1, string memory p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,bool,string,address)", p0, p1, p2, p3));
    }

    function log(uint p0, bool p1, bool p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool,uint)", p0, p1, p2, p3));
    }

    function log(uint p0, bool p1, bool p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool,string)", p0, p1, p2, p3));
    }

    function log(uint p0, bool p1, bool p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool,bool)", p0, p1, p2, p3));
    }

    function log(uint p0, bool p1, bool p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool,address)", p0, p1, p2, p3));
    }

    function log(uint p0, bool p1, address p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,bool,address,uint)", p0, p1, p2, p3));
    }

    function log(uint p0, bool p1, address p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,bool,address,string)", p0, p1, p2, p3));
    }

    function log(uint p0, bool p1, address p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,bool,address,bool)", p0, p1, p2, p3));
    }

    function log(uint p0, bool p1, address p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,bool,address,address)", p0, p1, p2, p3));
    }

    function log(uint p0, address p1, uint p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,address,uint,uint)", p0, p1, p2, p3));
    }

    function log(uint p0, address p1, uint p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,address,uint,string)", p0, p1, p2, p3));
    }

    function log(uint p0, address p1, uint p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,address,uint,bool)", p0, p1, p2, p3));
    }

    function log(uint p0, address p1, uint p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,address,uint,address)", p0, p1, p2, p3));
    }

    function log(uint p0, address p1, string memory p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,address,string,uint)", p0, p1, p2, p3));
    }

    function log(uint p0, address p1, string memory p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,address,string,string)", p0, p1, p2, p3));
    }

    function log(uint p0, address p1, string memory p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,address,string,bool)", p0, p1, p2, p3));
    }

    function log(uint p0, address p1, string memory p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,address,string,address)", p0, p1, p2, p3));
    }

    function log(uint p0, address p1, bool p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,address,bool,uint)", p0, p1, p2, p3));
    }

    function log(uint p0, address p1, bool p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,address,bool,string)", p0, p1, p2, p3));
    }

    function log(uint p0, address p1, bool p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,address,bool,bool)", p0, p1, p2, p3));
    }

    function log(uint p0, address p1, bool p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,address,bool,address)", p0, p1, p2, p3));
    }

    function log(uint p0, address p1, address p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,address,address,uint)", p0, p1, p2, p3));
    }

    function log(uint p0, address p1, address p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,address,address,string)", p0, p1, p2, p3));
    }

    function log(uint p0, address p1, address p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,address,address,bool)", p0, p1, p2, p3));
    }

    function log(uint p0, address p1, address p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,address,address,address)", p0, p1, p2, p3));
    }

    function log(string memory p0, uint p1, uint p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,uint)", p0, p1, p2, p3));
    }

    function log(string memory p0, uint p1, uint p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,string)", p0, p1, p2, p3));
    }

    function log(string memory p0, uint p1, uint p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,bool)", p0, p1, p2, p3));
    }

    function log(string memory p0, uint p1, uint p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,address)", p0, p1, p2, p3));
    }

    function log(string memory p0, uint p1, string memory p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint,string,uint)", p0, p1, p2, p3));
    }

    function log(string memory p0, uint p1, string memory p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint,string,string)", p0, p1, p2, p3));
    }

    function log(string memory p0, uint p1, string memory p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint,string,bool)", p0, p1, p2, p3));
    }

    function log(string memory p0, uint p1, string memory p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint,string,address)", p0, p1, p2, p3));
    }

    function log(string memory p0, uint p1, bool p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint,bool,uint)", p0, p1, p2, p3));
    }

    function log(string memory p0, uint p1, bool p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint,bool,string)", p0, p1, p2, p3));
    }

    function log(string memory p0, uint p1, bool p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint,bool,bool)", p0, p1, p2, p3));
    }

    function log(string memory p0, uint p1, bool p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint,bool,address)", p0, p1, p2, p3));
    }

    function log(string memory p0, uint p1, address p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint,address,uint)", p0, p1, p2, p3));
    }

    function log(string memory p0, uint p1, address p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint,address,string)", p0, p1, p2, p3));
    }

    function log(string memory p0, uint p1, address p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint,address,bool)", p0, p1, p2, p3));
    }

    function log(string memory p0, uint p1, address p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint,address,address)", p0, p1, p2, p3));
    }

    function log(string memory p0, string memory p1, uint p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,uint,uint)", p0, p1, p2, p3));
    }

    function log(string memory p0, string memory p1, uint p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,uint,string)", p0, p1, p2, p3));
    }

    function log(string memory p0, string memory p1, uint p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,uint,bool)", p0, p1, p2, p3));
    }

    function log(string memory p0, string memory p1, uint p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,uint,address)", p0, p1, p2, p3));
    }

    function log(string memory p0, string memory p1, string memory p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,string,uint)", p0, p1, p2, p3));
    }

    function log(string memory p0, string memory p1, string memory p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,string,string)", p0, p1, p2, p3));
    }

    function log(string memory p0, string memory p1, string memory p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,string,bool)", p0, p1, p2, p3));
    }

    function log(string memory p0, string memory p1, string memory p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,string,address)", p0, p1, p2, p3));
    }

    function log(string memory p0, string memory p1, bool p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,uint)", p0, p1, p2, p3));
    }

    function log(string memory p0, string memory p1, bool p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,string)", p0, p1, p2, p3));
    }

    function log(string memory p0, string memory p1, bool p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,bool)", p0, p1, p2, p3));
    }

    function log(string memory p0, string memory p1, bool p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,address)", p0, p1, p2, p3));
    }

    function log(string memory p0, string memory p1, address p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,address,uint)", p0, p1, p2, p3));
    }

    function log(string memory p0, string memory p1, address p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,address,string)", p0, p1, p2, p3));
    }

    function log(string memory p0, string memory p1, address p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,address,bool)", p0, p1, p2, p3));
    }

    function log(string memory p0, string memory p1, address p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,address,address)", p0, p1, p2, p3));
    }

    function log(string memory p0, bool p1, uint p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint,uint)", p0, p1, p2, p3));
    }

    function log(string memory p0, bool p1, uint p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint,string)", p0, p1, p2, p3));
    }

    function log(string memory p0, bool p1, uint p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint,bool)", p0, p1, p2, p3));
    }

    function log(string memory p0, bool p1, uint p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint,address)", p0, p1, p2, p3));
    }

    function log(string memory p0, bool p1, string memory p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,uint)", p0, p1, p2, p3));
    }

    function log(string memory p0, bool p1, string memory p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,string)", p0, p1, p2, p3));
    }

    function log(string memory p0, bool p1, string memory p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,bool)", p0, p1, p2, p3));
    }

    function log(string memory p0, bool p1, string memory p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,address)", p0, p1, p2, p3));
    }

    function log(string memory p0, bool p1, bool p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,uint)", p0, p1, p2, p3));
    }

    function log(string memory p0, bool p1, bool p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,string)", p0, p1, p2, p3));
    }

    function log(string memory p0, bool p1, bool p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,bool)", p0, p1, p2, p3));
    }

    function log(string memory p0, bool p1, bool p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,address)", p0, p1, p2, p3));
    }

    function log(string memory p0, bool p1, address p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,uint)", p0, p1, p2, p3));
    }

    function log(string memory p0, bool p1, address p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,string)", p0, p1, p2, p3));
    }

    function log(string memory p0, bool p1, address p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,bool)", p0, p1, p2, p3));
    }

    function log(string memory p0, bool p1, address p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,address)", p0, p1, p2, p3));
    }

    function log(string memory p0, address p1, uint p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,uint,uint)", p0, p1, p2, p3));
    }

    function log(string memory p0, address p1, uint p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,uint,string)", p0, p1, p2, p3));
    }

    function log(string memory p0, address p1, uint p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,uint,bool)", p0, p1, p2, p3));
    }

    function log(string memory p0, address p1, uint p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,uint,address)", p0, p1, p2, p3));
    }

    function log(string memory p0, address p1, string memory p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,string,uint)", p0, p1, p2, p3));
    }

    function log(string memory p0, address p1, string memory p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,string,string)", p0, p1, p2, p3));
    }

    function log(string memory p0, address p1, string memory p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,string,bool)", p0, p1, p2, p3));
    }

    function log(string memory p0, address p1, string memory p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,string,address)", p0, p1, p2, p3));
    }

    function log(string memory p0, address p1, bool p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,uint)", p0, p1, p2, p3));
    }

    function log(string memory p0, address p1, bool p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,string)", p0, p1, p2, p3));
    }

    function log(string memory p0, address p1, bool p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,bool)", p0, p1, p2, p3));
    }

    function log(string memory p0, address p1, bool p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,address)", p0, p1, p2, p3));
    }

    function log(string memory p0, address p1, address p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,address,uint)", p0, p1, p2, p3));
    }

    function log(string memory p0, address p1, address p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,address,string)", p0, p1, p2, p3));
    }

    function log(string memory p0, address p1, address p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,address,bool)", p0, p1, p2, p3));
    }

    function log(string memory p0, address p1, address p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,address,address)", p0, p1, p2, p3));
    }

    function log(bool p0, uint p1, uint p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint,uint)", p0, p1, p2, p3));
    }

    function log(bool p0, uint p1, uint p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint,string)", p0, p1, p2, p3));
    }

    function log(bool p0, uint p1, uint p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint,bool)", p0, p1, p2, p3));
    }

    function log(bool p0, uint p1, uint p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint,address)", p0, p1, p2, p3));
    }

    function log(bool p0, uint p1, string memory p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint,string,uint)", p0, p1, p2, p3));
    }

    function log(bool p0, uint p1, string memory p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint,string,string)", p0, p1, p2, p3));
    }

    function log(bool p0, uint p1, string memory p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint,string,bool)", p0, p1, p2, p3));
    }

    function log(bool p0, uint p1, string memory p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint,string,address)", p0, p1, p2, p3));
    }

    function log(bool p0, uint p1, bool p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool,uint)", p0, p1, p2, p3));
    }

    function log(bool p0, uint p1, bool p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool,string)", p0, p1, p2, p3));
    }

    function log(bool p0, uint p1, bool p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool,bool)", p0, p1, p2, p3));
    }

    function log(bool p0, uint p1, bool p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool,address)", p0, p1, p2, p3));
    }

    function log(bool p0, uint p1, address p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint,address,uint)", p0, p1, p2, p3));
    }

    function log(bool p0, uint p1, address p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint,address,string)", p0, p1, p2, p3));
    }

    function log(bool p0, uint p1, address p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint,address,bool)", p0, p1, p2, p3));
    }

    function log(bool p0, uint p1, address p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint,address,address)", p0, p1, p2, p3));
    }

    function log(bool p0, string memory p1, uint p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint,uint)", p0, p1, p2, p3));
    }

    function log(bool p0, string memory p1, uint p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint,string)", p0, p1, p2, p3));
    }

    function log(bool p0, string memory p1, uint p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint,bool)", p0, p1, p2, p3));
    }

    function log(bool p0, string memory p1, uint p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint,address)", p0, p1, p2, p3));
    }

    function log(bool p0, string memory p1, string memory p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,uint)", p0, p1, p2, p3));
    }

    function log(bool p0, string memory p1, string memory p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,string)", p0, p1, p2, p3));
    }

    function log(bool p0, string memory p1, string memory p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,bool)", p0, p1, p2, p3));
    }

    function log(bool p0, string memory p1, string memory p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,address)", p0, p1, p2, p3));
    }

    function log(bool p0, string memory p1, bool p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,uint)", p0, p1, p2, p3));
    }

    function log(bool p0, string memory p1, bool p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,string)", p0, p1, p2, p3));
    }

    function log(bool p0, string memory p1, bool p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,bool)", p0, p1, p2, p3));
    }

    function log(bool p0, string memory p1, bool p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,address)", p0, p1, p2, p3));
    }

    function log(bool p0, string memory p1, address p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,uint)", p0, p1, p2, p3));
    }

    function log(bool p0, string memory p1, address p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,string)", p0, p1, p2, p3));
    }

    function log(bool p0, string memory p1, address p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,bool)", p0, p1, p2, p3));
    }

    function log(bool p0, string memory p1, address p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,address)", p0, p1, p2, p3));
    }

    function log(bool p0, bool p1, uint p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint,uint)", p0, p1, p2, p3));
    }

    function log(bool p0, bool p1, uint p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint,string)", p0, p1, p2, p3));
    }

    function log(bool p0, bool p1, uint p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint,bool)", p0, p1, p2, p3));
    }

    function log(bool p0, bool p1, uint p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint,address)", p0, p1, p2, p3));
    }

    function log(bool p0, bool p1, string memory p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,uint)", p0, p1, p2, p3));
    }

    function log(bool p0, bool p1, string memory p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,string)", p0, p1, p2, p3));
    }

    function log(bool p0, bool p1, string memory p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,bool)", p0, p1, p2, p3));
    }

    function log(bool p0, bool p1, string memory p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,address)", p0, p1, p2, p3));
    }

    function log(bool p0, bool p1, bool p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,uint)", p0, p1, p2, p3));
    }

    function log(bool p0, bool p1, bool p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,string)", p0, p1, p2, p3));
    }

    function log(bool p0, bool p1, bool p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,bool)", p0, p1, p2, p3));
    }

    function log(bool p0, bool p1, bool p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,address)", p0, p1, p2, p3));
    }

    function log(bool p0, bool p1, address p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,uint)", p0, p1, p2, p3));
    }

    function log(bool p0, bool p1, address p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,string)", p0, p1, p2, p3));
    }

    function log(bool p0, bool p1, address p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,bool)", p0, p1, p2, p3));
    }

    function log(bool p0, bool p1, address p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,address)", p0, p1, p2, p3));
    }

    function log(bool p0, address p1, uint p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint,uint)", p0, p1, p2, p3));
    }

    function log(bool p0, address p1, uint p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint,string)", p0, p1, p2, p3));
    }

    function log(bool p0, address p1, uint p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint,bool)", p0, p1, p2, p3));
    }

    function log(bool p0, address p1, uint p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint,address)", p0, p1, p2, p3));
    }

    function log(bool p0, address p1, string memory p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,uint)", p0, p1, p2, p3));
    }

    function log(bool p0, address p1, string memory p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,string)", p0, p1, p2, p3));
    }

    function log(bool p0, address p1, string memory p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,bool)", p0, p1, p2, p3));
    }

    function log(bool p0, address p1, string memory p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,address)", p0, p1, p2, p3));
    }

    function log(bool p0, address p1, bool p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,uint)", p0, p1, p2, p3));
    }

    function log(bool p0, address p1, bool p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,string)", p0, p1, p2, p3));
    }

    function log(bool p0, address p1, bool p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,bool)", p0, p1, p2, p3));
    }

    function log(bool p0, address p1, bool p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,address)", p0, p1, p2, p3));
    }

    function log(bool p0, address p1, address p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,uint)", p0, p1, p2, p3));
    }

    function log(bool p0, address p1, address p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,string)", p0, p1, p2, p3));
    }

    function log(bool p0, address p1, address p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,bool)", p0, p1, p2, p3));
    }

    function log(bool p0, address p1, address p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,address)", p0, p1, p2, p3));
    }

    function log(address p0, uint p1, uint p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint,uint,uint)", p0, p1, p2, p3));
    }

    function log(address p0, uint p1, uint p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint,uint,string)", p0, p1, p2, p3));
    }

    function log(address p0, uint p1, uint p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint,uint,bool)", p0, p1, p2, p3));
    }

    function log(address p0, uint p1, uint p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint,uint,address)", p0, p1, p2, p3));
    }

    function log(address p0, uint p1, string memory p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint,string,uint)", p0, p1, p2, p3));
    }

    function log(address p0, uint p1, string memory p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint,string,string)", p0, p1, p2, p3));
    }

    function log(address p0, uint p1, string memory p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint,string,bool)", p0, p1, p2, p3));
    }

    function log(address p0, uint p1, string memory p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint,string,address)", p0, p1, p2, p3));
    }

    function log(address p0, uint p1, bool p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint,bool,uint)", p0, p1, p2, p3));
    }

    function log(address p0, uint p1, bool p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint,bool,string)", p0, p1, p2, p3));
    }

    function log(address p0, uint p1, bool p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint,bool,bool)", p0, p1, p2, p3));
    }

    function log(address p0, uint p1, bool p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint,bool,address)", p0, p1, p2, p3));
    }

    function log(address p0, uint p1, address p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint,address,uint)", p0, p1, p2, p3));
    }

    function log(address p0, uint p1, address p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint,address,string)", p0, p1, p2, p3));
    }

    function log(address p0, uint p1, address p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint,address,bool)", p0, p1, p2, p3));
    }

    function log(address p0, uint p1, address p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint,address,address)", p0, p1, p2, p3));
    }

    function log(address p0, string memory p1, uint p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,uint,uint)", p0, p1, p2, p3));
    }

    function log(address p0, string memory p1, uint p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,uint,string)", p0, p1, p2, p3));
    }

    function log(address p0, string memory p1, uint p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,uint,bool)", p0, p1, p2, p3));
    }

    function log(address p0, string memory p1, uint p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,uint,address)", p0, p1, p2, p3));
    }

    function log(address p0, string memory p1, string memory p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,string,uint)", p0, p1, p2, p3));
    }

    function log(address p0, string memory p1, string memory p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,string,string)", p0, p1, p2, p3));
    }

    function log(address p0, string memory p1, string memory p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,string,bool)", p0, p1, p2, p3));
    }

    function log(address p0, string memory p1, string memory p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,string,address)", p0, p1, p2, p3));
    }

    function log(address p0, string memory p1, bool p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,uint)", p0, p1, p2, p3));
    }

    function log(address p0, string memory p1, bool p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,string)", p0, p1, p2, p3));
    }

    function log(address p0, string memory p1, bool p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,bool)", p0, p1, p2, p3));
    }

    function log(address p0, string memory p1, bool p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,address)", p0, p1, p2, p3));
    }

    function log(address p0, string memory p1, address p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,address,uint)", p0, p1, p2, p3));
    }

    function log(address p0, string memory p1, address p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,address,string)", p0, p1, p2, p3));
    }

    function log(address p0, string memory p1, address p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,address,bool)", p0, p1, p2, p3));
    }

    function log(address p0, string memory p1, address p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,address,address)", p0, p1, p2, p3));
    }

    function log(address p0, bool p1, uint p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint,uint)", p0, p1, p2, p3));
    }

    function log(address p0, bool p1, uint p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint,string)", p0, p1, p2, p3));
    }

    function log(address p0, bool p1, uint p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint,bool)", p0, p1, p2, p3));
    }

    function log(address p0, bool p1, uint p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint,address)", p0, p1, p2, p3));
    }

    function log(address p0, bool p1, string memory p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,uint)", p0, p1, p2, p3));
    }

    function log(address p0, bool p1, string memory p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,string)", p0, p1, p2, p3));
    }

    function log(address p0, bool p1, string memory p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,bool)", p0, p1, p2, p3));
    }

    function log(address p0, bool p1, string memory p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,address)", p0, p1, p2, p3));
    }

    function log(address p0, bool p1, bool p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,uint)", p0, p1, p2, p3));
    }

    function log(address p0, bool p1, bool p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,string)", p0, p1, p2, p3));
    }

    function log(address p0, bool p1, bool p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,bool)", p0, p1, p2, p3));
    }

    function log(address p0, bool p1, bool p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,address)", p0, p1, p2, p3));
    }

    function log(address p0, bool p1, address p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,uint)", p0, p1, p2, p3));
    }

    function log(address p0, bool p1, address p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,string)", p0, p1, p2, p3));
    }

    function log(address p0, bool p1, address p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,bool)", p0, p1, p2, p3));
    }

    function log(address p0, bool p1, address p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,address)", p0, p1, p2, p3));
    }

    function log(address p0, address p1, uint p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,uint,uint)", p0, p1, p2, p3));
    }

    function log(address p0, address p1, uint p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,uint,string)", p0, p1, p2, p3));
    }

    function log(address p0, address p1, uint p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,uint,bool)", p0, p1, p2, p3));
    }

    function log(address p0, address p1, uint p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,uint,address)", p0, p1, p2, p3));
    }

    function log(address p0, address p1, string memory p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,string,uint)", p0, p1, p2, p3));
    }

    function log(address p0, address p1, string memory p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,string,string)", p0, p1, p2, p3));
    }

    function log(address p0, address p1, string memory p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,string,bool)", p0, p1, p2, p3));
    }

    function log(address p0, address p1, string memory p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,string,address)", p0, p1, p2, p3));
    }

    function log(address p0, address p1, bool p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,uint)", p0, p1, p2, p3));
    }

    function log(address p0, address p1, bool p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,string)", p0, p1, p2, p3));
    }

    function log(address p0, address p1, bool p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,bool)", p0, p1, p2, p3));
    }

    function log(address p0, address p1, bool p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,address)", p0, p1, p2, p3));
    }

    function log(address p0, address p1, address p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,address,uint)", p0, p1, p2, p3));
    }

    function log(address p0, address p1, address p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,address,string)", p0, p1, p2, p3));
    }

    function log(address p0, address p1, address p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,address,bool)", p0, p1, p2, p3));
    }

    function log(address p0, address p1, address p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,address,address)", p0, p1, p2, p3));
    }

}

File 12 of 13 : Core.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.13;

/// Common mathematical functions used in both SD59x18 and UD60x18. Note that these global functions do not
/// always operate with SD59x18 and UD60x18 numbers.

/*//////////////////////////////////////////////////////////////////////////
                                CUSTOM ERRORS
//////////////////////////////////////////////////////////////////////////*/

/// @notice Emitted when the ending result in the fixed-point version of `mulDiv` would overflow uint256.
error PRBMath__MulDiv18Overflow(uint256 x, uint256 y);

/// @notice Emitted when the ending result in `mulDiv` would overflow uint256.
error PRBMath__MulDivOverflow(uint256 x, uint256 y, uint256 denominator);

/// @notice Emitted when attempting to run `mulDiv` with one of the inputs `type(int256).min`.
error PRBMath__MulDivSignedInputTooSmall();

/// @notice Emitted when the ending result in the signed version of `mulDiv` would overflow int256.
error PRBMath__MulDivSignedOverflow(int256 x, int256 y);

/*//////////////////////////////////////////////////////////////////////////
                                    CONSTANTS
//////////////////////////////////////////////////////////////////////////*/

/// @dev How many trailing decimals can be represented.
uint256 constant UNIT = 1e18;

/// @dev Largest power of two that is a divisor of `UNIT`.
uint256 constant UNIT_LPOTD = 262144;

/// @dev The `UNIT` number inverted mod 2^256.
uint256 constant UNIT_INVERSE = 78156646155174841979727994598816262306175212592076161876661_508869554232690281;

/*//////////////////////////////////////////////////////////////////////////
                                    FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/

/// @notice Finds the zero-based index of the first one in the binary representation of x.
/// @dev See the note on msb in the "Find First Set" Wikipedia article https://en.wikipedia.org/wiki/Find_first_set
///
/// Each of the steps in this implementation is equivalent to this high-level code:
///
/// ```solidity
/// if (x >= 2 ** 128) {
///     x >>= 128;
///     result += 128;
/// }
/// ```
///
/// Where 128 is swapped with each respective power of two factor. See the full high-level implementation here:
/// https://gist.github.com/paulrberg/f932f8693f2733e30c4d479e8e980948
///
/// A list of the Yul instructions used below:
/// - "gt" is "greater than"
/// - "or" is the OR bitwise operator
/// - "shl" is "shift left"
/// - "shr" is "shift right"
///
/// @param x The uint256 number for which to find the index of the most significant bit.
/// @return result The index of the most significant bit as an uint256.
function msb(uint256 x) pure returns (uint256 result) {
    // 2^128
    assembly {
        let factor := shl(7, gt(x, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF))
        x := shr(factor, x)
        result := or(result, factor)
    }
    // 2^64
    assembly {
        let factor := shl(6, gt(x, 0xFFFFFFFFFFFFFFFF))
        x := shr(factor, x)
        result := or(result, factor)
    }
    // 2^32
    assembly {
        let factor := shl(5, gt(x, 0xFFFFFFFF))
        x := shr(factor, x)
        result := or(result, factor)
    }
    // 2^16
    assembly {
        let factor := shl(4, gt(x, 0xFFFF))
        x := shr(factor, x)
        result := or(result, factor)
    }
    // 2^8
    assembly {
        let factor := shl(3, gt(x, 0xFF))
        x := shr(factor, x)
        result := or(result, factor)
    }
    // 2^4
    assembly {
        let factor := shl(2, gt(x, 0xF))
        x := shr(factor, x)
        result := or(result, factor)
    }
    // 2^2
    assembly {
        let factor := shl(1, gt(x, 0x3))
        x := shr(factor, x)
        result := or(result, factor)
    }
    // 2^1
    // No need to shift x any more.
    assembly {
        let factor := gt(x, 0x1)
        result := or(result, factor)
    }
}

/// @notice Calculates floor(x*y÷denominator) with full precision.
///
/// @dev Credits to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv.
///
/// Requirements:
/// - The denominator cannot be zero.
/// - The result must fit within uint256.
///
/// Caveats:
/// - This function does not work with fixed-point numbers.
///
/// @param x The multiplicand as an uint256.
/// @param y The multiplier as an uint256.
/// @param denominator The divisor as an uint256.
/// @return result The result as an uint256.
function mulDiv(uint256 x, uint256 y, uint256 denominator) pure returns (uint256 result) {
    // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
    // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
    // variables such that product = prod1 * 2^256 + prod0.
    uint256 prod0; // Least significant 256 bits of the product
    uint256 prod1; // Most significant 256 bits of the product
    assembly {
        let mm := mulmod(x, y, not(0))
        prod0 := mul(x, y)
        prod1 := sub(sub(mm, prod0), lt(mm, prod0))
    }

    // Handle non-overflow cases, 256 by 256 division.
    if (prod1 == 0) {
        unchecked {
            return prod0 / denominator;
        }
    }

    // Make sure the result is less than 2^256. Also prevents denominator == 0.
    if (prod1 >= denominator) {
        revert PRBMath__MulDivOverflow(x, y, denominator);
    }

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

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

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

    // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
    // See https://cs.stackexchange.com/q/138556/92363.
    unchecked {
        // Does not overflow because the denominator cannot be zero at this stage in the function.
        uint256 lpotdod = denominator & (~denominator + 1);
        assembly {
            // Divide denominator by lpotdod.
            denominator := div(denominator, lpotdod)

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

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

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

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

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

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

/// @notice Calculates floor(x*y÷1e18) with full precision.
///
/// @dev Variant of `mulDiv` with constant folding, i.e. in which the denominator is always 1e18. Before returning the
/// final result, we add 1 if `(x * y) % UNIT >= HALF_UNIT`. Without this adjustment, 6.6e-19 would be truncated to 0
/// instead of being rounded to 1e-18. See "Listing 6" and text above it at https://accu.org/index.php/journals/1717.
///
/// Requirements:
/// - The result must fit within uint256.
///
/// Caveats:
/// - The body is purposely left uncommented; to understand how this works, see the NatSpec comments in `mulDiv`.
/// - It is assumed that the result can never be `type(uint256).max` when x and y solve the following two equations:
///     1. x * y = type(uint256).max * UNIT
///     2. (x * y) % UNIT >= UNIT / 2
///
/// @param x The multiplicand as an unsigned 60.18-decimal fixed-point number.
/// @param y The multiplier as an unsigned 60.18-decimal fixed-point number.
/// @return result The result as an unsigned 60.18-decimal fixed-point number.
function mulDiv18(uint256 x, uint256 y) pure returns (uint256 result) {
    uint256 prod0;
    uint256 prod1;
    assembly {
        let mm := mulmod(x, y, not(0))
        prod0 := mul(x, y)
        prod1 := sub(sub(mm, prod0), lt(mm, prod0))
    }

    if (prod1 >= UNIT) {
        revert PRBMath__MulDiv18Overflow(x, y);
    }

    uint256 remainder;
    assembly {
        remainder := mulmod(x, y, UNIT)
    }

    if (prod1 == 0) {
        unchecked {
            return prod0 / UNIT;
        }
    }

    assembly {
        result := mul(
            or(
                div(sub(prod0, remainder), UNIT_LPOTD),
                mul(sub(prod1, gt(remainder, prod0)), add(div(sub(0, UNIT_LPOTD), UNIT_LPOTD), 1))
            ),
            UNIT_INVERSE
        )
    }
}

/// @notice Calculates floor(x*y÷denominator) with full precision.
///
/// @dev An extension of `mulDiv` for signed numbers. Works by computing the signs and the absolute values separately.
///
/// Requirements:
/// - None of the inputs can be `type(int256).min`.
/// - The result must fit within int256.
///
/// @param x The multiplicand as an int256.
/// @param y The multiplier as an int256.
/// @param denominator The divisor as an int256.
/// @return result The result as an int256.
function mulDivSigned(int256 x, int256 y, int256 denominator) pure returns (int256 result) {
    if (x == type(int256).min || y == type(int256).min || denominator == type(int256).min) {
        revert PRBMath__MulDivSignedInputTooSmall();
    }

    // Get hold of the absolute values of x, y and the denominator.
    uint256 absX;
    uint256 absY;
    uint256 absD;
    unchecked {
        absX = x < 0 ? uint256(-x) : uint256(x);
        absY = y < 0 ? uint256(-y) : uint256(y);
        absD = denominator < 0 ? uint256(-denominator) : uint256(denominator);
    }

    // Compute the absolute value of (x*y)÷denominator. The result must fit within int256.
    uint256 rAbs = mulDiv(absX, absY, absD);
    if (rAbs > uint256(type(int256).max)) {
        revert PRBMath__MulDivSignedOverflow(x, y);
    }

    // Get the signs of x, y and the denominator.
    uint256 sx;
    uint256 sy;
    uint256 sd;
    assembly {
        // This works thanks to two's complement.
        // "sgt" stands for "signed greater than" and "sub(0,1)" is max uint256.
        sx := sgt(x, sub(0, 1))
        sy := sgt(y, sub(0, 1))
        sd := sgt(denominator, sub(0, 1))
    }

    // XOR over sx, sy and sd. What this does is to check whether there are 1 or 3 negative signs in the inputs.
    // If there are, the result should be negative. Otherwise, it should be positive.
    unchecked {
        result = sx ^ sy ^ sd == 0 ? -int256(rAbs) : int256(rAbs);
    }
}

/// @notice Calculates the binary exponent of x using the binary fraction method.
/// @dev Has to use 192.64-bit fixed-point numbers.
/// See https://ethereum.stackexchange.com/a/96594/24693.
/// @param x The exponent as an unsigned 192.64-bit fixed-point number.
/// @return result The result as an unsigned 60.18-decimal fixed-point number.
function prbExp2(uint256 x) pure returns (uint256 result) {
    unchecked {
        // Start from 0.5 in the 192.64-bit fixed-point format.
        result = 0x800000000000000000000000000000000000000000000000;

        // Multiply the result by root(2, 2^-i) when the bit at position i is 1. None of the intermediary results overflows
        // because the initial result is 2^191 and all magic factors are less than 2^65.
        if (x & 0xFF00000000000000 > 0) {
            if (x & 0x8000000000000000 > 0) {
                result = (result * 0x16A09E667F3BCC909) >> 64;
            }
            if (x & 0x4000000000000000 > 0) {
                result = (result * 0x1306FE0A31B7152DF) >> 64;
            }
            if (x & 0x2000000000000000 > 0) {
                result = (result * 0x1172B83C7D517ADCE) >> 64;
            }
            if (x & 0x1000000000000000 > 0) {
                result = (result * 0x10B5586CF9890F62A) >> 64;
            }
            if (x & 0x800000000000000 > 0) {
                result = (result * 0x1059B0D31585743AE) >> 64;
            }
            if (x & 0x400000000000000 > 0) {
                result = (result * 0x102C9A3E778060EE7) >> 64;
            }
            if (x & 0x200000000000000 > 0) {
                result = (result * 0x10163DA9FB33356D8) >> 64;
            }
            if (x & 0x100000000000000 > 0) {
                result = (result * 0x100B1AFA5ABCBED61) >> 64;
            }
        }

        if (x & 0xFF000000000000 > 0) {
            if (x & 0x80000000000000 > 0) {
                result = (result * 0x10058C86DA1C09EA2) >> 64;
            }
            if (x & 0x40000000000000 > 0) {
                result = (result * 0x1002C605E2E8CEC50) >> 64;
            }
            if (x & 0x20000000000000 > 0) {
                result = (result * 0x100162F3904051FA1) >> 64;
            }
            if (x & 0x10000000000000 > 0) {
                result = (result * 0x1000B175EFFDC76BA) >> 64;
            }
            if (x & 0x8000000000000 > 0) {
                result = (result * 0x100058BA01FB9F96D) >> 64;
            }
            if (x & 0x4000000000000 > 0) {
                result = (result * 0x10002C5CC37DA9492) >> 64;
            }
            if (x & 0x2000000000000 > 0) {
                result = (result * 0x1000162E525EE0547) >> 64;
            }
            if (x & 0x1000000000000 > 0) {
                result = (result * 0x10000B17255775C04) >> 64;
            }
        }

        if (x & 0xFF0000000000 > 0) {
            if (x & 0x800000000000 > 0) {
                result = (result * 0x1000058B91B5BC9AE) >> 64;
            }
            if (x & 0x400000000000 > 0) {
                result = (result * 0x100002C5C89D5EC6D) >> 64;
            }
            if (x & 0x200000000000 > 0) {
                result = (result * 0x10000162E43F4F831) >> 64;
            }
            if (x & 0x100000000000 > 0) {
                result = (result * 0x100000B1721BCFC9A) >> 64;
            }
            if (x & 0x80000000000 > 0) {
                result = (result * 0x10000058B90CF1E6E) >> 64;
            }
            if (x & 0x40000000000 > 0) {
                result = (result * 0x1000002C5C863B73F) >> 64;
            }
            if (x & 0x20000000000 > 0) {
                result = (result * 0x100000162E430E5A2) >> 64;
            }
            if (x & 0x10000000000 > 0) {
                result = (result * 0x1000000B172183551) >> 64;
            }
        }

        if (x & 0xFF00000000 > 0) {
            if (x & 0x8000000000 > 0) {
                result = (result * 0x100000058B90C0B49) >> 64;
            }
            if (x & 0x4000000000 > 0) {
                result = (result * 0x10000002C5C8601CC) >> 64;
            }
            if (x & 0x2000000000 > 0) {
                result = (result * 0x1000000162E42FFF0) >> 64;
            }
            if (x & 0x1000000000 > 0) {
                result = (result * 0x10000000B17217FBB) >> 64;
            }
            if (x & 0x800000000 > 0) {
                result = (result * 0x1000000058B90BFCE) >> 64;
            }
            if (x & 0x400000000 > 0) {
                result = (result * 0x100000002C5C85FE3) >> 64;
            }
            if (x & 0x200000000 > 0) {
                result = (result * 0x10000000162E42FF1) >> 64;
            }
            if (x & 0x100000000 > 0) {
                result = (result * 0x100000000B17217F8) >> 64;
            }
        }

        if (x & 0xFF00000000 > 0) {
            if (x & 0x80000000 > 0) {
                result = (result * 0x10000000058B90BFC) >> 64;
            }
            if (x & 0x40000000 > 0) {
                result = (result * 0x1000000002C5C85FE) >> 64;
            }
            if (x & 0x20000000 > 0) {
                result = (result * 0x100000000162E42FF) >> 64;
            }
            if (x & 0x10000000 > 0) {
                result = (result * 0x1000000000B17217F) >> 64;
            }
            if (x & 0x8000000 > 0) {
                result = (result * 0x100000000058B90C0) >> 64;
            }
            if (x & 0x4000000 > 0) {
                result = (result * 0x10000000002C5C860) >> 64;
            }
            if (x & 0x2000000 > 0) {
                result = (result * 0x1000000000162E430) >> 64;
            }
            if (x & 0x1000000 > 0) {
                result = (result * 0x10000000000B17218) >> 64;
            }
        }

        if (x & 0xFF0000 > 0) {
            if (x & 0x800000 > 0) {
                result = (result * 0x1000000000058B90C) >> 64;
            }
            if (x & 0x400000 > 0) {
                result = (result * 0x100000000002C5C86) >> 64;
            }
            if (x & 0x200000 > 0) {
                result = (result * 0x10000000000162E43) >> 64;
            }
            if (x & 0x100000 > 0) {
                result = (result * 0x100000000000B1721) >> 64;
            }
            if (x & 0x80000 > 0) {
                result = (result * 0x10000000000058B91) >> 64;
            }
            if (x & 0x40000 > 0) {
                result = (result * 0x1000000000002C5C8) >> 64;
            }
            if (x & 0x20000 > 0) {
                result = (result * 0x100000000000162E4) >> 64;
            }
            if (x & 0x10000 > 0) {
                result = (result * 0x1000000000000B172) >> 64;
            }
        }

        if (x & 0xFF00 > 0) {
            if (x & 0x8000 > 0) {
                result = (result * 0x100000000000058B9) >> 64;
            }
            if (x & 0x4000 > 0) {
                result = (result * 0x10000000000002C5D) >> 64;
            }
            if (x & 0x2000 > 0) {
                result = (result * 0x1000000000000162E) >> 64;
            }
            if (x & 0x1000 > 0) {
                result = (result * 0x10000000000000B17) >> 64;
            }
            if (x & 0x800 > 0) {
                result = (result * 0x1000000000000058C) >> 64;
            }
            if (x & 0x400 > 0) {
                result = (result * 0x100000000000002C6) >> 64;
            }
            if (x & 0x200 > 0) {
                result = (result * 0x10000000000000163) >> 64;
            }
            if (x & 0x100 > 0) {
                result = (result * 0x100000000000000B1) >> 64;
            }
        }

        if (x & 0xFF > 0) {
            if (x & 0x80 > 0) {
                result = (result * 0x10000000000000059) >> 64;
            }
            if (x & 0x40 > 0) {
                result = (result * 0x1000000000000002C) >> 64;
            }
            if (x & 0x20 > 0) {
                result = (result * 0x10000000000000016) >> 64;
            }
            if (x & 0x10 > 0) {
                result = (result * 0x1000000000000000B) >> 64;
            }
            if (x & 0x8 > 0) {
                result = (result * 0x10000000000000006) >> 64;
            }
            if (x & 0x4 > 0) {
                result = (result * 0x10000000000000003) >> 64;
            }
            if (x & 0x2 > 0) {
                result = (result * 0x10000000000000001) >> 64;
            }
            if (x & 0x1 > 0) {
                result = (result * 0x10000000000000001) >> 64;
            }
        }

        // We're doing two things at the same time:
        //
        //   1. Multiply the result by 2^n + 1, where "2^n" is the integer part and the one is added to account for
        //      the fact that we initially set the result to 0.5. This is accomplished by subtracting from 191
        //      rather than 192.
        //   2. Convert the result to the unsigned 60.18-decimal fixed-point format.
        //
        // This works because 2^(191-ip) = 2^ip / 2^191, where "ip" is the integer part "2^n".
        result *= UNIT;
        result >>= (191 - (x >> 64));
    }
}

/// @notice Calculates the square root of x, rounding down if x is not a perfect square.
/// @dev Uses the Babylonian method https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method.
/// Credits to OpenZeppelin for the explanations in code comments below.
///
/// Caveats:
/// - This function does not work with fixed-point numbers.
///
/// @param x The uint256 number for which to calculate the square root.
/// @return result The result as an uint256.
function prbSqrt(uint256 x) pure returns (uint256 result) {
    if (x == 0) {
        return 0;
    }

    // For our first guess, we get the biggest power of 2 which is smaller than the square root of x.
    //
    // We know that the "msb" (most significant bit) of x is a power of 2 such that we have:
    //
    // $$
    // msb(x) <= x <= 2*msb(x)$
    // $$
    //
    // We write $msb(x)$ as $2^k$ and we get:
    //
    // $$
    // k = log_2(x)
    // $$
    //
    // Thus we can write the initial inequality as:
    //
    // $$
    // 2^{log_2(x)} <= x <= 2*2^{log_2(x)+1} \\
    // sqrt(2^k) <= sqrt(x) < sqrt(2^{k+1}) \\
    // 2^{k/2} <= sqrt(x) < 2^{(k+1)/2} <= 2^{(k/2)+1}
    // $$
    //
    // Consequently, $2^{log_2(x) /2}` is a good first approximation of sqrt(x) with at least one correct bit.
    uint256 xAux = uint256(x);
    result = 1;
    if (xAux >= 2 ** 128) {
        xAux >>= 128;
        result <<= 64;
    }
    if (xAux >= 2 ** 64) {
        xAux >>= 64;
        result <<= 32;
    }
    if (xAux >= 2 ** 32) {
        xAux >>= 32;
        result <<= 16;
    }
    if (xAux >= 2 ** 16) {
        xAux >>= 16;
        result <<= 8;
    }
    if (xAux >= 2 ** 8) {
        xAux >>= 8;
        result <<= 4;
    }
    if (xAux >= 2 ** 4) {
        xAux >>= 4;
        result <<= 2;
    }
    if (xAux >= 2 ** 2) {
        result <<= 1;
    }

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

        // Round down the result in case x is not a perfect square.
        uint256 roundedDownResult = x / result;
        if (result >= roundedDownResult) {
            result = roundedDownResult;
        }
    }
}

File 13 of 13 : UD60x18.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.13;

import { msb, mulDiv, mulDiv18, prbExp2, prbSqrt } from "./Core.sol";

/// @notice The unsigned 60.18-decimal fixed-point number representation, which can have up to 60 digits and up to 18 decimals.
/// The values of this are bound by the minimum and the maximum values permitted by the Solidity type uint256.
type UD60x18 is uint256;

/*//////////////////////////////////////////////////////////////////////////
                                CUSTOM ERRORS
//////////////////////////////////////////////////////////////////////////*/

/// @notice Emitted when adding two numbers overflows UD60x18.
error PRBMathUD60x18__AddOverflow(uint256 x, UD60x18 y);

/// @notice Emitted when ceiling a number overflows UD60x18.
error PRBMathUD60x18__CeilOverflow(UD60x18 x);

/// @notice Emitted when taking the natural exponent of a base greater than 133.084258667509499441.
error PRBMathUD60x18__ExpInputTooBig(UD60x18 x);

/// @notice Emitted when taking the binary exponent of a base greater than 192.
error PRBMathUD60x18__Exp2InputTooBig(UD60x18 x);

/// @notice Emitted when taking the geometric mean of two numbers and multiplying them overflows UD60x18.
error PRBMathUD60x18__GmOverflow(UD60x18 x, UD60x18 y);

/// @notice Emitted when taking the logarithm of a number less than 1.
error PRBMathUD60x18__LogInputTooSmall(UD60x18 x);

/// @notice Emitted when calculating the square root overflows UD60x18.
error PRBMathUD60x18__SqrtOverflow(UD60x18 x);

/// @notice Emitted when subtracting one number from another underflows UD60x18.
error PRBMathUD60x18__SubUnderflow(UD60x18 x, UD60x18 y);

/// @notice Emitted when converting a basic integer to the fixed-point format overflows UD60x18.
error PRBMathUD60x18__ToUD60x18Overflow(uint256 x);

/*//////////////////////////////////////////////////////////////////////////
                                    CONSTANTS
//////////////////////////////////////////////////////////////////////////*/

/// NOTICE: the "u" prefix stands for "unwrapped".

/// @dev Euler's number as an UD60x18 number.
UD60x18 constant E = UD60x18.wrap(2_718281828459045235);

/// @dev Half the UNIT number.
uint256 constant uHALF_UNIT = 0.5e18;
UD60x18 constant HALF_UNIT = UD60x18.wrap(uHALF_UNIT);

/// @dev log2(10) as an UD60x18 number.
uint256 constant uLOG2_10 = 3_321928094887362347;
UD60x18 constant LOG2_10 = UD60x18.wrap(uLOG2_10);

/// @dev log2(e) as an UD60x18 number.
uint256 constant uLOG2_E = 1_442695040888963407;
UD60x18 constant LOG2_E = UD60x18.wrap(uLOG2_E);

/// @dev The maximum value an UD60x18 number can have.
uint256 constant uMAX_UD60x18 = 115792089237316195423570985008687907853269984665640564039457_584007913129639935;
UD60x18 constant MAX_UD60x18 = UD60x18.wrap(uMAX_UD60x18);

/// @dev The maximum whole value an UD60x18 number can have.
uint256 constant uMAX_WHOLE_UD60x18 = 115792089237316195423570985008687907853269984665640564039457_000000000000000000;
UD60x18 constant MAX_WHOLE_UD60x18 = UD60x18.wrap(uMAX_WHOLE_UD60x18);

/// @dev PI as an UD60x18 number.
UD60x18 constant PI = UD60x18.wrap(3_141592653589793238);

/// @dev The unit amount which implies how many trailing decimals can be represented.
uint256 constant uUNIT = 1e18;
UD60x18 constant UNIT = UD60x18.wrap(uUNIT);

/// @dev Zero as an UD60x18 number.
UD60x18 constant ZERO = UD60x18.wrap(0);

/*//////////////////////////////////////////////////////////////////////////
                            MATHEMATICAL FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/

using { avg, ceil, div, exp, exp2, floor, frac, gm, inv, ln, log10, log2, mul, pow, powu, sqrt } for UD60x18 global;

/// @notice Calculates the arithmetic average of x and y, rounding down.
///
/// @dev Based on the formula:
///
/// $$
/// avg(x, y) = (x & y) + ((xUint ^ yUint) / 2)
/// $$
//
/// In English, what this formula does is:
///
/// 1. AND x and y.
/// 2. Calculate half of XOR x and y.
/// 3. Add the two results together.
///
/// This technique is known as SWAR, which stands for "SIMD within a register". You can read more about it here:
/// https://devblogs.microsoft.com/oldnewthing/20220207-00/?p=106223
///
/// @param x The first operand as an UD60x18 number.
/// @param y The second operand as an UD60x18 number.
/// @return result The arithmetic average as an UD60x18 number.
function avg(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
    uint256 xUint = unwrap(x);
    uint256 yUint = unwrap(y);
    unchecked {
        result = wrap((xUint & yUint) + ((xUint ^ yUint) >> 1));
    }
}

/// @notice Yields the smallest whole UD60x18 number greater than or equal to x.
///
/// @dev This is optimized for fractional value inputs, because for every whole value there are "1e18 - 1" fractional
/// counterparts. See https://en.wikipedia.org/wiki/Floor_and_ceiling_functions.
///
/// Requirements:
/// - x must be less than or equal to `MAX_WHOLE_UD60x18`.
///
/// @param x The UD60x18 number to ceil.
/// @param result The least number greater than or equal to x, as an UD60x18 number.
function ceil(UD60x18 x) pure returns (UD60x18 result) {
    uint256 xUint = unwrap(x);
    if (xUint > uMAX_WHOLE_UD60x18) {
        revert PRBMathUD60x18__CeilOverflow(x);
    }

    assembly {
        // Equivalent to "x % UNIT" but faster.
        let remainder := mod(x, uUNIT)

        // Equivalent to "UNIT - remainder" but faster.
        let delta := sub(uUNIT, remainder)

        // Equivalent to "x + delta * (remainder > 0 ? 1 : 0)" but faster.
        result := add(x, mul(delta, gt(remainder, 0)))
    }
}

/// @notice Divides two UD60x18 numbers, returning a new UD60x18 number. Rounds towards zero.
///
/// @dev Uses `mulDiv` to enable overflow-safe multiplication and division.
///
/// Requirements:
/// - The denominator cannot be zero.
///
/// @param x The numerator as an UD60x18 number.
/// @param y The denominator as an UD60x18 number.
/// @param result The quotient as an UD60x18 number.
function div(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
    result = wrap(mulDiv(unwrap(x), uUNIT, unwrap(y)));
}

/// @notice Calculates the natural exponent of x.
///
/// @dev Based on the formula:
///
/// $$
/// e^x = 2^{x * log_2{e}}
/// $$
///
/// Requirements:
/// - All from `log2`.
/// - x must be less than 133.084258667509499441.
///
/// @param x The exponent as an UD60x18 number.
/// @return result The result as an UD60x18 number.
function exp(UD60x18 x) pure returns (UD60x18 result) {
    uint256 xUint = unwrap(x);

    // Without this check, the value passed to `exp2` would be greater than 192.
    if (xUint >= 133_084258667509499441) {
        revert PRBMathUD60x18__ExpInputTooBig(x);
    }

    unchecked {
        // We do the fixed-point multiplication inline rather than via the `mul` function to save gas.
        uint256 doubleUnitProduct = xUint * uLOG2_E;
        result = exp2(wrap(doubleUnitProduct / uUNIT));
    }
}

/// @notice Calculates the binary exponent of x using the binary fraction method.
///
/// @dev See https://ethereum.stackexchange.com/q/79903/24693.
///
/// Requirements:
/// - x must be 192 or less.
/// - The result must fit within `MAX_UD60x18`.
///
/// @param x The exponent as an UD60x18 number.
/// @return result The result as an UD60x18 number.
function exp2(UD60x18 x) pure returns (UD60x18 result) {
    uint256 xUint = unwrap(x);

    // Numbers greater than or equal to 2^192 don't fit within the 192.64-bit format.
    if (xUint >= 192e18) {
        revert PRBMathUD60x18__Exp2InputTooBig(x);
    }

    // Convert x to the 192.64-bit fixed-point format.
    uint256 x_192x64 = (xUint << 64) / uUNIT;

    // Pass x to the `prbExp2` function, which uses the 192.64-bit fixed-point number representation.
    result = wrap(prbExp2(x_192x64));
}

/// @notice Yields the greatest whole UD60x18 number less than or equal to x.
/// @dev Optimized for fractional value inputs, because for every whole value there are (1e18 - 1) fractional counterparts.
/// See https://en.wikipedia.org/wiki/Floor_and_ceiling_functions.
/// @param x The UD60x18 number to floor.
/// @param result The greatest integer less than or equal to x, as an UD60x18 number.
function floor(UD60x18 x) pure returns (UD60x18 result) {
    assembly {
        // Equivalent to "x % UNIT" but faster.
        let remainder := mod(x, uUNIT)

        // Equivalent to "x - remainder * (remainder > 0 ? 1 : 0)" but faster.
        result := sub(x, mul(remainder, gt(remainder, 0)))
    }
}

/// @notice Yields the excess beyond the floor of x.
/// @dev Based on the odd function definition https://en.wikipedia.org/wiki/Fractional_part.
/// @param x The UD60x18 number to get the fractional part of.
/// @param result The fractional part of x as an UD60x18 number.
function frac(UD60x18 x) pure returns (UD60x18 result) {
    assembly {
        result := mod(x, uUNIT)
    }
}

/// @notice Calculates the geometric mean of x and y, i.e. $$sqrt(x * y)$$, rounding down.
///
/// @dev Requirements:
/// - x * y must fit within `MAX_UD60x18`, lest it overflows.
///
/// @param x The first operand as an UD60x18 number.
/// @param y The second operand as an UD60x18 number.
/// @return result The result as an UD60x18 number.
function gm(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
    uint256 xUint = unwrap(x);
    uint256 yUint = unwrap(y);
    if (xUint == 0 || yUint == 0) {
        return ZERO;
    }

    unchecked {
        // Checking for overflow this way is faster than letting Solidity do it.
        uint256 xyUint = xUint * yUint;
        if (xyUint / xUint != yUint) {
            revert PRBMathUD60x18__GmOverflow(x, y);
        }

        // We don't need to multiply the result by `UNIT` here because the x*y product had picked up a factor of `UNIT`
        // during multiplication. See the comments in the `prbSqrt` function.
        result = wrap(prbSqrt(xyUint));
    }
}

/// @notice Calculates 1 / x, rounding toward zero.
///
/// @dev Requirements:
/// - x cannot be zero.
///
/// @param x The UD60x18 number for which to calculate the inverse.
/// @return result The inverse as an UD60x18 number.
function inv(UD60x18 x) pure returns (UD60x18 result) {
    unchecked {
        // 1e36 is UNIT * UNIT.
        result = wrap(1e36 / unwrap(x));
    }
}

/// @notice Calculates the natural logarithm of x.
///
/// @dev Based on the formula:
///
/// $$
/// ln{x} = log_2{x} / log_2{e}$$.
/// $$
///
/// Requirements:
/// - All from `log2`.
///
/// Caveats:
/// - All from `log2`.
/// - This doesn't return exactly 1 for 2.718281828459045235, for that more fine-grained precision is needed.
///
/// @param x The UD60x18 number for which to calculate the natural logarithm.
/// @return result The natural logarithm as an UD60x18 number.
function ln(UD60x18 x) pure returns (UD60x18 result) {
    unchecked {
        // We do the fixed-point multiplication inline to save gas. This is overflow-safe because the maximum value
        // that `log2` can return is 196.205294292027477728.
        result = wrap((unwrap(log2(x)) * uUNIT) / uLOG2_E);
    }
}

/// @notice Calculates the common logarithm of x.
///
/// @dev First checks if x is an exact power of ten and it stops if yes. If it's not, calculates the common
/// logarithm based on the formula:
///
/// $$
/// log_{10}{x} = log_2{x} / log_2{10}
/// $$
///
/// Requirements:
/// - All from `log2`.
///
/// Caveats:
/// - All from `log2`.
///
/// @param x The UD60x18 number for which to calculate the common logarithm.
/// @return result The common logarithm as an UD60x18 number.
function log10(UD60x18 x) pure returns (UD60x18 result) {
    uint256 xUint = unwrap(x);
    if (xUint < uUNIT) {
        revert PRBMathUD60x18__LogInputTooSmall(x);
    }

    // Note that the `mul` in this assembly block is the assembly multiplication operation, not the UD60x18 `mul`.
    // prettier-ignore
    assembly {
        switch x
        case 1 { result := mul(uUNIT, sub(0, 18)) }
        case 10 { result := mul(uUNIT, sub(1, 18)) }
        case 100 { result := mul(uUNIT, sub(2, 18)) }
        case 1000 { result := mul(uUNIT, sub(3, 18)) }
        case 10000 { result := mul(uUNIT, sub(4, 18)) }
        case 100000 { result := mul(uUNIT, sub(5, 18)) }
        case 1000000 { result := mul(uUNIT, sub(6, 18)) }
        case 10000000 { result := mul(uUNIT, sub(7, 18)) }
        case 100000000 { result := mul(uUNIT, sub(8, 18)) }
        case 1000000000 { result := mul(uUNIT, sub(9, 18)) }
        case 10000000000 { result := mul(uUNIT, sub(10, 18)) }
        case 100000000000 { result := mul(uUNIT, sub(11, 18)) }
        case 1000000000000 { result := mul(uUNIT, sub(12, 18)) }
        case 10000000000000 { result := mul(uUNIT, sub(13, 18)) }
        case 100000000000000 { result := mul(uUNIT, sub(14, 18)) }
        case 1000000000000000 { result := mul(uUNIT, sub(15, 18)) }
        case 10000000000000000 { result := mul(uUNIT, sub(16, 18)) }
        case 100000000000000000 { result := mul(uUNIT, sub(17, 18)) }
        case 1000000000000000000 { result := 0 }
        case 10000000000000000000 { result := uUNIT }
        case 100000000000000000000 { result := mul(uUNIT, 2) }
        case 1000000000000000000000 { result := mul(uUNIT, 3) }
        case 10000000000000000000000 { result := mul(uUNIT, 4) }
        case 100000000000000000000000 { result := mul(uUNIT, 5) }
        case 1000000000000000000000000 { result := mul(uUNIT, 6) }
        case 10000000000000000000000000 { result := mul(uUNIT, 7) }
        case 100000000000000000000000000 { result := mul(uUNIT, 8) }
        case 1000000000000000000000000000 { result := mul(uUNIT, 9) }
        case 10000000000000000000000000000 { result := mul(uUNIT, 10) }
        case 100000000000000000000000000000 { result := mul(uUNIT, 11) }
        case 1000000000000000000000000000000 { result := mul(uUNIT, 12) }
        case 10000000000000000000000000000000 { result := mul(uUNIT, 13) }
        case 100000000000000000000000000000000 { result := mul(uUNIT, 14) }
        case 1000000000000000000000000000000000 { result := mul(uUNIT, 15) }
        case 10000000000000000000000000000000000 { result := mul(uUNIT, 16) }
        case 100000000000000000000000000000000000 { result := mul(uUNIT, 17) }
        case 1000000000000000000000000000000000000 { result := mul(uUNIT, 18) }
        case 10000000000000000000000000000000000000 { result := mul(uUNIT, 19) }
        case 100000000000000000000000000000000000000 { result := mul(uUNIT, 20) }
        case 1000000000000000000000000000000000000000 { result := mul(uUNIT, 21) }
        case 10000000000000000000000000000000000000000 { result := mul(uUNIT, 22) }
        case 100000000000000000000000000000000000000000 { result := mul(uUNIT, 23) }
        case 1000000000000000000000000000000000000000000 { result := mul(uUNIT, 24) }
        case 10000000000000000000000000000000000000000000 { result := mul(uUNIT, 25) }
        case 100000000000000000000000000000000000000000000 { result := mul(uUNIT, 26) }
        case 1000000000000000000000000000000000000000000000 { result := mul(uUNIT, 27) }
        case 10000000000000000000000000000000000000000000000 { result := mul(uUNIT, 28) }
        case 100000000000000000000000000000000000000000000000 { result := mul(uUNIT, 29) }
        case 1000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 30) }
        case 10000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 31) }
        case 100000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 32) }
        case 1000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 33) }
        case 10000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 34) }
        case 100000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 35) }
        case 1000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 36) }
        case 10000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 37) }
        case 100000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 38) }
        case 1000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 39) }
        case 10000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 40) }
        case 100000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 41) }
        case 1000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 42) }
        case 10000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 43) }
        case 100000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 44) }
        case 1000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 45) }
        case 10000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 46) }
        case 100000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 47) }
        case 1000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 48) }
        case 10000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 49) }
        case 100000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 50) }
        case 1000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 51) }
        case 10000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 52) }
        case 100000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 53) }
        case 1000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 54) }
        case 10000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 55) }
        case 100000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 56) }
        case 1000000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 57) }
        case 10000000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 58) }
        case 100000000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 59) }
        default {
            result := uMAX_UD60x18
        }
    }

    if (unwrap(result) == uMAX_UD60x18) {
        unchecked {
            // Do the fixed-point division inline to save gas.
            result = wrap((unwrap(log2(x)) * uUNIT) / uLOG2_10);
        }
    }
}

/// @notice Calculates the binary logarithm of x.
///
/// @dev Based on the iterative approximation algorithm.
/// https://en.wikipedia.org/wiki/Binary_logarithm#Iterative_approximation
///
/// Requirements:
/// - x must be greater than or equal to UNIT, otherwise the result would be negative.
///
/// Caveats:
/// - The results are nor perfectly accurate to the last decimal, due to the lossy precision of the iterative approximation.
///
/// @param x The UD60x18 number for which to calculate the binary logarithm.
/// @return result The binary logarithm as an UD60x18 number.
function log2(UD60x18 x) pure returns (UD60x18 result) {
    uint256 xUint = unwrap(x);

    if (xUint < uUNIT) {
        revert PRBMathUD60x18__LogInputTooSmall(x);
    }

    unchecked {
        // Calculate the integer part of the logarithm, add it to the result and finally calculate y = x * 2^(-n).
        uint256 n = msb(xUint / uUNIT);

        // This is the integer part of the logarithm as an UD60x18 number. The operation can't overflow because n
        // n is maximum 255 and UNIT is 1e18.
        uint256 resultUint = n * uUNIT;

        // This is $y = x * 2^{-n}$.
        uint256 y = xUint >> n;

        // If y is 1, the fractional part is zero.
        if (y == uUNIT) {
            return wrap(resultUint);
        }

        // Calculate the fractional part via the iterative approximation.
        // The "delta.rshift(1)" part is equivalent to "delta /= 2", but shifting bits is faster.
        uint256 DOUBLE_UNIT = 2e18;
        for (uint256 delta = uHALF_UNIT; delta > 0; delta >>= 1) {
            y = (y * y) / uUNIT;

            // Is y^2 > 2 and so in the range [2,4)?
            if (y >= DOUBLE_UNIT) {
                // Add the 2^{-m} factor to the logarithm.
                resultUint += delta;

                // Corresponds to z/2 on Wikipedia.
                y >>= 1;
            }
        }
        result = wrap(resultUint);
    }
}

/// @notice Multiplies two UD60x18 numbers together, returning a new UD60x18 number.
/// @dev See the documentation for the `Core/mulDiv18` function.
/// @param x The multiplicand as an UD60x18 number.
/// @param y The multiplier as an UD60x18 number.
/// @return result The product as an UD60x18 number.
function mul(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
    result = wrap(mulDiv18(unwrap(x), unwrap(y)));
}

/// @notice Raises x to the power of y.
///
/// @dev Based on the formula:
///
/// $$
/// x^y = 2^{log_2{x} * y}
/// $$
///
/// Requirements:
/// - All from `exp2`, `log2` and `mul`.
///
/// Caveats:
/// - All from `exp2`, `log2` and `mul`.
/// - Assumes 0^0 is 1.
///
/// @param x Number to raise to given power y, as an UD60x18 number.
/// @param y Exponent to raise x to, as an UD60x18 number.
/// @return result x raised to power y, as an UD60x18 number.
function pow(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
    uint256 xUint = unwrap(x);
    uint256 yUint = unwrap(y);

    if (xUint == 0) {
        result = yUint == 0 ? UNIT : ZERO;
    } else {
        if (yUint == uUNIT) {
            result = x;
        } else {
            result = exp2(mul(log2(x), y));
        }
    }
}

/// @notice Raises x (an UD60x18 number) to the power y (unsigned basic integer) using the famous algorithm
/// "exponentiation by squaring".
///
/// @dev See https://en.wikipedia.org/wiki/Exponentiation_by_squaring
///
/// Requirements:
/// - The result must fit within `MAX_UD60x18`.
///
/// Caveats:
/// - All from "Core/mulDiv18".
/// - Assumes 0^0 is 1.
///
/// @param x The base as an UD60x18 number.
/// @param y The exponent as an uint256.
/// @return result The result as an UD60x18 number.
function powu(UD60x18 x, uint256 y) pure returns (UD60x18 result) {
    // Calculate the first iteration of the loop in advance.
    uint256 xUint = unwrap(x);
    uint256 resultUint = y & 1 > 0 ? xUint : uUNIT;

    // Equivalent to "for(y /= 2; y > 0; y /= 2)" but faster.
    for (y >>= 1; y > 0; y >>= 1) {
        xUint = mulDiv18(xUint, xUint);

        // Equivalent to "y % 2 == 1" but faster.
        if (y & 1 > 0) {
            resultUint = mulDiv18(resultUint, xUint);
        }
    }
    result = wrap(resultUint);
}

/// @notice Calculates the square root of x, rounding down.
/// @dev Uses the Babylonian method https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method.
///
/// Requirements:
/// - x must be less than `MAX_UD60x18` divided by `UNIT`.
///
/// @param x The UD60x18 number for which to calculate the square root.
/// @return result The result as an UD60x18 number.
function sqrt(UD60x18 x) pure returns (UD60x18 result) {
    uint256 xUint = unwrap(x);

    unchecked {
        if (xUint > uMAX_UD60x18 / uUNIT) {
            revert PRBMathUD60x18__SqrtOverflow(x);
        }
        // Multiply x by `UNIT` to account for the factor of `UNIT` that is picked up when multiplying two UD60x18
        // numbers together (in this case, the two numbers are both the square root).
        result = wrap(prbSqrt(xUint * uUNIT));
    }
}

/*//////////////////////////////////////////////////////////////////////////
                            CONVERSION FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/

/// @notice Converts an UD60x18 number to a simple integer by dividing it by `UNIT`. Rounds towards zero in the process.
/// @dev Rounds down in the process.
/// @param x The UD60x18 number to convert.
/// @return result The same number in basic integer form.
function fromUD60x18(UD60x18 x) pure returns (uint256 result) {
    result = unwrap(x) / uUNIT;
}

/// @notice Converts a simple integer to UD60x18 by multiplying it by `UNIT`.
///
/// @dev Requirements:
/// - x must be less than or equal to `MAX_UD60x18` divided by `UNIT`.
///
/// @param x The basic integer to convert.
/// @param result The same number converted to UD60x18.
function toUD60x18(uint256 x) pure returns (UD60x18 result) {
    if (x > uMAX_UD60x18 / uUNIT) {
        revert PRBMathUD60x18__ToUD60x18Overflow(x);
    }
    unchecked {
        result = wrap(x * uUNIT);
    }
}

/// @notice Wraps an unsigned integer into the UD60x18 type.
function ud(uint256 x) pure returns (UD60x18 result) {
    result = wrap(x);
}

/// @notice Wraps an unsigned integer into the UD60x18 type.
/// @dev Alias for the "ud" function defined above.
function ud60x18(uint256 x) pure returns (UD60x18 result) {
    result = wrap(x);
}

/// @notice Unwraps an UD60x18 number into the underlying unsigned integer.
function unwrap(UD60x18 x) pure returns (uint256 result) {
    result = UD60x18.unwrap(x);
}

/// @notice Wraps an unsigned integer into the UD60x18 type.
function wrap(uint256 x) pure returns (UD60x18 result) {
    result = UD60x18.wrap(x);
}

/*//////////////////////////////////////////////////////////////////////////
                        GLOBAL-SCOPED HELPER FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/

using {
    add,
    and,
    eq,
    gt,
    gte,
    isZero,
    lshift,
    lt,
    lte,
    mod,
    neq,
    or,
    rshift,
    sub,
    uncheckedAdd,
    uncheckedSub,
    xor
} for UD60x18 global;

/// @notice Implements the checked addition operation (+) in the UD60x18 type.
function add(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
    result = wrap(unwrap(x) + unwrap(y));
}

/// @notice Implements the AND (&) bitwise operation in the UD60x18 type.
function and(UD60x18 x, uint256 bits) pure returns (UD60x18 result) {
    result = wrap(unwrap(x) & bits);
}

/// @notice Implements the equal operation (==) in the UD60x18 type.
function eq(UD60x18 x, UD60x18 y) pure returns (bool result) {
    result = unwrap(x) == unwrap(y);
}

/// @notice Implements the greater than operation (>) in the UD60x18 type.
function gt(UD60x18 x, UD60x18 y) pure returns (bool result) {
    result = unwrap(x) > unwrap(y);
}

/// @notice Implements the greater than or equal to operation (>=) in the UD60x18 type.
function gte(UD60x18 x, UD60x18 y) pure returns (bool result) {
    result = unwrap(x) >= unwrap(y);
}

/// @notice Implements a zero comparison check function in the UD60x18 type.
function isZero(UD60x18 x) pure returns (bool result) {
    // This wouldn't work if x could be negative.
    result = unwrap(x) == 0;
}

/// @notice Implements the left shift operation (<<) in the UD60x18 type.
function lshift(UD60x18 x, uint256 bits) pure returns (UD60x18 result) {
    result = wrap(unwrap(x) << bits);
}

/// @notice Implements the lower than operation (<) in the UD60x18 type.
function lt(UD60x18 x, UD60x18 y) pure returns (bool result) {
    result = unwrap(x) < unwrap(y);
}

/// @notice Implements the lower than or equal to operation (<=) in the UD60x18 type.
function lte(UD60x18 x, UD60x18 y) pure returns (bool result) {
    result = unwrap(x) <= unwrap(y);
}

/// @notice Implements the checked modulo operation (%) in the UD60x18 type.
function mod(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
    result = wrap(unwrap(x) % unwrap(y));
}

/// @notice Implements the not equal operation (!=) in the UD60x18 type
function neq(UD60x18 x, UD60x18 y) pure returns (bool result) {
    result = unwrap(x) != unwrap(y);
}

/// @notice Implements the OR (|) bitwise operation in the UD60x18 type.
function or(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
    result = wrap(unwrap(x) | unwrap(y));
}

/// @notice Implements the right shift operation (>>) in the UD60x18 type.
function rshift(UD60x18 x, uint256 bits) pure returns (UD60x18 result) {
    result = wrap(unwrap(x) >> bits);
}

/// @notice Implements the checked subtraction operation (-) in the UD60x18 type.
function sub(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
    result = wrap(unwrap(x) - unwrap(y));
}

/// @notice Implements the unchecked addition operation (+) in the UD60x18 type.
function uncheckedAdd(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
    unchecked {
        result = wrap(unwrap(x) + unwrap(y));
    }
}

/// @notice Implements the unchecked subtraction operation (-) in the UD60x18 type.
function uncheckedSub(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
    unchecked {
        result = wrap(unwrap(x) - unwrap(y));
    }
}

/// @notice Implements the XOR (^) bitwise operation in the UD60x18 type.
function xor(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
    result = wrap(unwrap(x) ^ unwrap(y));
}

/*//////////////////////////////////////////////////////////////////////////
                        FILE-SCOPED HELPER FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/

using { uncheckedDiv, uncheckedMul } for UD60x18;

/// @notice Implements the unchecked standard division operation in the UD60x18 type.
function uncheckedDiv(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
    unchecked {
        result = wrap(unwrap(x) / unwrap(y));
    }
}

/// @notice Implements the unchecked standard multiplication operation in the UD60x18 type.
function uncheckedMul(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
    unchecked {
        result = wrap(unwrap(x) * unwrap(y));
    }
}

Settings
{
  "remappings": [
    "@prb/math/=lib/prb-math/src/",
    "@prb/test/=lib/prb-math/lib/prb-test/src/",
    "communal/=lib/communal/",
    "ds-test/=lib/forge-std/lib/ds-test/src/",
    "forge-std/=lib/forge-std/src/",
    "local/=src/",
    "openzeppelin/=lib/openzeppelin/",
    "prb-math/=lib/prb-math/src/",
    "prb-test/=lib/prb-math/lib/prb-test/src/",
    "src/=lib/prb-math/src/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "metadata": {
    "bytecodeHash": "ipfs"
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "london",
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"address","name":"_stakingToken","type":"address"},{"internalType":"address","name":"_LSDRegistry","type":"address"},{"internalType":"uint256","name":"_shanghaiTime","type":"uint256"},{"internalType":"address","name":"_darknetAddress","type":"address"},{"internalType":"string[]","name":"_rewardSymbols","type":"string[]"},{"internalType":"address[]","name":"_rewardTokens","type":"address[]"},{"internalType":"address[]","name":"_rewardManagers","type":"address[]"},{"internalType":"uint256[]","name":"_rewardRates","type":"uint256[]"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"UD60x18","name":"x","type":"uint256"}],"name":"PRBMathUD60x18__Exp2InputTooBig","type":"error"},{"inputs":[{"internalType":"UD60x18","name":"x","type":"uint256"}],"name":"PRBMathUD60x18__ExpInputTooBig","type":"error"},{"inputs":[{"internalType":"uint256","name":"x","type":"uint256"},{"internalType":"uint256","name":"y","type":"uint256"}],"name":"PRBMath__MulDiv18Overflow","type":"error"},{"inputs":[{"internalType":"uint256","name":"x","type":"uint256"},{"internalType":"uint256","name":"y","type":"uint256"},{"internalType":"uint256","name":"denominator","type":"uint256"}],"name":"PRBMath__MulDivOverflow","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"multiplier","type":"uint256"}],"name":"LockedStakeMaxMultiplierUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"secs","type":"uint256"}],"name":"LockedStakeMinTime","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"secs","type":"uint256"}],"name":"LockedStakeTimeForMaxMultiplier","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldOwner","type":"address"},{"indexed":false,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnerChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnerNominated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"destination_address","type":"address"},{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Recovered","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"reward","type":"uint256"},{"indexed":false,"internalType":"address","name":"token_address","type":"address"},{"indexed":false,"internalType":"address","name":"destination_address","type":"address"}],"name":"RewardPaid","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newDuration","type":"uint256"}],"name":"RewardsDurationUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"}],"name":"RewardsPeriodRenewed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"secs","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"kek_id","type":"bytes32"},{"indexed":false,"internalType":"address","name":"source_address","type":"address"}],"name":"StakeLocked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"kek_id","type":"bytes32"},{"indexed":false,"internalType":"address","name":"destination_address","type":"address"}],"name":"WithdrawLocked","type":"event"},{"inputs":[],"name":"LSDRegistryAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"LSDVaultAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"calcCurCombinedWeight","outputs":[{"internalType":"uint256","name":"old_combined_weight","type":"uint256"},{"internalType":"uint256","name":"new_combined_weight","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"reward_token_address","type":"address"},{"internalType":"address","name":"new_manager_address","type":"address"}],"name":"changeTokenManager","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"combinedWeightOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"coordinationMultiplier","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"darknetAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"earned","outputs":[{"internalType":"uint256[]","name":"new_earned","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getAllRewardRates","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getAllRewardTokens","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getReward","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getRewardForDuration","outputs":[{"internalType":"uint256[]","name":"rewards_per_duration_arr","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getRewardSymbols","outputs":[{"internalType":"string[]","name":"","type":"string[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"greylist","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_address","type":"address"}],"name":"greylistAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"caller_addr","type":"address"},{"internalType":"address","name":"reward_token_addr","type":"address"}],"name":"isTokenManagerFor","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastUpdateTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"secs","type":"uint256"}],"name":"lockMultiplier","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lock_max_multiplier","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lock_time_for_max_multiplier","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lock_time_min","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"lockedLiquidityOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"lockedStakesOf","outputs":[{"components":[{"internalType":"bytes32","name":"kek_id","type":"bytes32"},{"internalType":"uint256","name":"start_timestamp","type":"uint256"},{"internalType":"uint256","name":"liquidity","type":"uint256"},{"internalType":"uint256","name":"ending_timestamp","type":"uint256"},{"internalType":"uint256","name":"lock_multiplier","type":"uint256"}],"internalType":"struct EuclideanFarm.LockedStake[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"max_cord_multiplier","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"max_nonce","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"min_cord_multiplier","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"}],"name":"nominateNewOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"nominatedOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nonce","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"periodFinish","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"uint256","name":"tokenAmount","type":"uint256"}],"name":"recoverERC20","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"rewardManagers","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"rewardRates","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"rewardSymbols","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"rewardTokenAddrToIdx","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"rewardTokens","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rewardsCollectionPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rewardsDuration","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rewardsPerToken","outputs":[{"internalType":"uint256[]","name":"newRewardsPerTokenStored","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_lock_time_for_max_multiplier","type":"uint256"},{"internalType":"uint256","name":"_lock_time_min","type":"uint256"}],"name":"setLockedStakeTimeForMinAndMaxMultiplier","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_lock_max_multiplier","type":"uint256"}],"name":"setMultipliers","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"reward_token_address","type":"address"},{"internalType":"uint256","name":"new_rate","type":"uint256"},{"internalType":"bool","name":"sync_too","type":"bool"}],"name":"setRewardRate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_rewardsDuration","type":"uint256"}],"name":"setRewardsDuration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"shanghaiTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"liquidity","type":"uint256"}],"name":"stakeLocked","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"stakesUnlocked","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"stakingPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"stakingToken","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"sync","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"synchronize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"toggleRewardsCollection","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"toggleStaking","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"toggleWithdrawals","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"totalCombinedWeight","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalLiquidityLocked","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"unlockStakes","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"kek_id","type":"bytes32"}],"name":"withdrawLocked","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawalsPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"}]

60806040526729a2241af62c00006006556729a2241af62c0000600955670de0b6b3a7640000600a556000600b556002600c553480156200003f57600080fd5b5060405162005148380380620051488339810160408190526200006291620009ad565b886001600160a01b038116620000bf5760405162461bcd60e51b815260206004820152601960248201527f4f776e657220616464726573732063616e6e6f7420626520300000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b03831690811782556040805192835260208301919091527fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c910160405180910390a15060016002556001600160a01b038716620001695760405162461bcd60e51b815260206004820152601260248201527104c53445265676973747279206973203078360741b6044820152606401620000b6565b600d80546001600160a01b0319166001600160a01b03891690811790915560408051632185f84560e11b8152905163430bf08a916004808201926020929091908290030181865afa158015620001c3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620001e9919062000abc565b600e80546001600160a01b0319166001600160a01b03928316179055600d54604080516368fbd31560e01b8152905191909216916368fbd3159160048083019260209291908290030181865afa15801562000248573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200026e919062000ada565b8614620002be5760405162461bcd60e51b815260206004820152601560248201527f7368616e6768616954696d65206d69736d6174636800000000000000000000006044820152606401620000b6565b600f869055600380546001600160a01b0319166001600160a01b038a81169190911790915560208054600160201b600160c01b0319166401000000009288169290920291909117815583516200031b91601191908601906200052f565b5080516200033190601290602084019062000599565b50835162000347906013906020870190620005d7565b5042600f5462000358919062000b0a565b60158190556200036d90620151809062000b0a565b60075560005b83518110156200048a57806014600086848151811062000397576200039762000b24565b6020908102919091018101516001600160a01b03168252810191909152604001600090812091909155601680546001810182559082527fd833147d7dc355ba459fc788f669e58cfaf9dc25ddcd0702e87d69c7b51242890155825183908290811062000407576200040762000b24565b60200260200101516010600086848151811062000428576200042862000b24565b60200260200101516001600160a01b03166001600160a01b0316815260200190815260200160002060006101000a8154816001600160a01b0302191690836001600160a01b031602179055508080620004819062000b3a565b91505062000373565b506020805460ff19168155426005819055601554620004b2926200226e620004c5821b17901c565b6004555062000bad975050505050505050565b600080620004d4838562000b56565b905083811015620005285760405162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f7700000000006044820152606401620000b6565b9392505050565b82805482825590600052602060002090810192821562000587579160200282015b828111156200058757825182546001600160a01b0319166001600160a01b0390911617825560209092019160019091019062000550565b506200059592915062000637565b5090565b82805482825590600052602060002090810192821562000587579160200282015b8281111562000587578251825591602001919060010190620005ba565b82805482825590600052602060002090810192821562000629579160200282015b82811115620006295782518051620006189184916020909101906200064e565b5091602001919060010190620005f8565b5062000595929150620006ca565b5b8082111562000595576000815560010162000638565b8280546200065c9062000b71565b90600052602060002090601f01602090048101928262000680576000855562000587565b82601f106200069b57805160ff191683800117855562000587565b8280016001018555821562000587579182018281111562000587578251825591602001919060010190620005ba565b8082111562000595576000620006e18282620006eb565b50600101620006ca565b508054620006f99062000b71565b6000825580601f106200070a575050565b601f0160209004906000526020600020908101906200072a919062000637565b50565b80516001600160a01b03811681146200074557600080fd5b919050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b03811182821017156200078b576200078b6200074a565b604052919050565b60006001600160401b03821115620007af57620007af6200074a565b5060051b60200190565b6000601f8381840112620007cc57600080fd5b82516020620007e5620007df8362000793565b62000760565b82815260059290921b850181019181810190878411156200080557600080fd5b8287015b84811015620008c95780516001600160401b03808211156200082b5760008081fd5b818a0191508a603f830112620008415760008081fd5b85820151818111156200085857620008586200074a565b6200086b818a01601f1916880162000760565b915080825260408c81838601011115620008855760008081fd5b60005b82811015620008a5578481018201518482018a0152880162000888565b82811115620008b75760008984860101525b50505084525091830191830162000809565b50979650505050505050565b600082601f830112620008e757600080fd5b81516020620008fa620007df8362000793565b82815260059290921b840181019181810190868411156200091a57600080fd5b8286015b84811015620009405762000932816200072d565b83529183019183016200091e565b509695505050505050565b600082601f8301126200095d57600080fd5b8151602062000970620007df8362000793565b82815260059290921b840181019181810190868411156200099057600080fd5b8286015b8481101562000940578051835291830191830162000994565b60008060008060008060008060006101208a8c031215620009cd57600080fd5b620009d88a6200072d565b9850620009e860208b016200072d565b9750620009f860408b016200072d565b965060608a0151955062000a0f60808b016200072d565b60a08b01519095506001600160401b038082111562000a2d57600080fd5b62000a3b8d838e01620007b9565b955060c08c015191508082111562000a5257600080fd5b62000a608d838e01620008d5565b945060e08c015191508082111562000a7757600080fd5b62000a858d838e01620008d5565b93506101008c015191508082111562000a9d57600080fd5b5062000aac8c828d016200094b565b9150509295985092959850929598565b60006020828403121562000acf57600080fd5b62000528826200072d565b60006020828403121562000aed57600080fd5b5051919050565b634e487b7160e01b600052601160045260246000fd5b60008282101562000b1f5762000b1f62000af4565b500390565b634e487b7160e01b600052603260045260246000fd5b60006001820162000b4f5762000b4f62000af4565b5060010190565b6000821982111562000b6c5762000b6c62000af4565b500190565b600181811c9082168062000b8657607f821691505b60208210810362000ba757634e487b7160e01b600052602260045260246000fd5b50919050565b61458b8062000bbd6000396000f3fe608060405234801561001057600080fd5b50600436106103a35760003560e01c806379313225116101e9578063c8f33c911161010f578063e01f62bf116100ad578063f2caeb1e1161007c578063f2caeb1e146107cb578063f4315553146107de578063f64be1b1146107e6578063fff6cae9146107ee57600080fd5b8063e01f62bf146107a0578063e1ba95d2146107a8578063e9f2838e146107b0578063ebe2b12b146107c257600080fd5b8063d239f003116100e9578063d239f00314610749578063d9f96e8d14610751578063de1a65511461077a578063dfc117da1461078d57600080fd5b8063c8f33c9114610724578063cc1a378f1461072d578063cdc82e801461074057600080fd5b8063941d9f6511610187578063affed0e011610156578063affed0e0146106e3578063b669de8b146106ec578063b94c4dcb14610707578063bbb781cc1461071057600080fd5b8063941d9f65146106a85780639637927f146106bb5780639a97b349146106c8578063af00f4e2146106d057600080fd5b80637bb7bed1116101c35780637bb7bed1146106475780638980f11f1461065a5780638bad86a71461066d5780638da5cb5b1461069557600080fd5b8063793132251461062e57806379ba5097146106375780637b31c19a1461063f57600080fd5b80633d18b912116102ce57806362cc445c1161026c578063693392451161023b57806369339245146105ea5780636e27cef91461060a57806370641a361461061357806372f702f31461061b57600080fd5b806362cc445c146105b157806364f2c060146105c457806366e9e9b3146105cc57806368fbd315146105e157600080fd5b806350401ac0116102a857806350401ac01461056f578063507bd8031461057857806351e3fc171461058b57806353a47bb71461059e57600080fd5b80633d18b9121461051357806341a16f3f1461051b57806347b9bc221461055c57600080fd5b80631f5848de11610346578063323331ca11610315578063323331ca146104c657806336f89af2146104d9578063386a9525146105025780633b8105b31461050b57600080fd5b80631f5848de14610464578063231b68dc146104775780632d47cf221461049a57806331ca208c146104a357600080fd5b80631627540c116103825780631627540c146104075780631c1f78eb1461041c5780631e090f01146104245780631e3689801461044457600080fd5b80628cc262146103a85780630d7bac4f146103d157806312edb24c146103f2575b600080fd5b6103bb6103b6366004613df4565b6107f6565b6040516103c89190613e11565b60405180910390f35b6103e46103df366004613e55565b61099b565b6040519081526020016103c8565b6103fa6109f4565b6040516103c89190613e6e565b61041a610415366004613df4565b610a56565b005b6103bb610b22565b610437610432366004613df4565b610bde565b6040516103c89190613eaf565b610457610452366004613e55565b610c85565b6040516103c89190613f75565b61041a610472366004613f96565b610d31565b61048a610485366004613fd8565b610dbb565b60405190151581526020016103c8565b6103e4600a5481565b61048a6104b1366004613df4565b601f6020526000908152604090205460ff1681565b60205461048a9062010000900460ff1681565b6103e46104e7366004613df4565b6001600160a01b03166000908152601d602052604090205490565b6103e460155481565b61041a610e0f565b6103bb610e5a565b610544610529366004613df4565b6010602052600090815260409020546001600160a01b031681565b6040516001600160a01b0390911681526020016103c8565b61041a61056a366004613e55565b610eee565b6103e4600c5481565b600e54610544906001600160a01b031681565b61041a610599366004613e55565b610fbe565b600154610544906001600160a01b031681565b61041a6105bf366004613e55565b611043565b601b546103e4565b6105d4611074565b6040516103c89190614011565b6103e4600f5481565b6103e46105f8366004613df4565b60146020526000908152604090205481565b6103e460085481565b6103bb61114d565b600354610544906001600160a01b031681565b6103e460095481565b61041a6112b9565b61041a6113a3565b610544610655366004613e55565b6113ec565b61041a610668366004614073565b611416565b61068061067b366004613df4565b61169b565b604080519283526020830191909152016103c8565b600054610544906001600160a01b031681565b61041a6106b6366004613df4565b6118ce565b60205461048a9060ff1681565b61041a611921565b61041a6106de36600461409f565b6119e4565b6103e4600b5481565b6020546105449064010000000090046001600160a01b031681565b6103e460075481565b60205461048a906301000000900460ff1681565b6103e460055481565b61041a61073b366004613e55565b611b24565b6103e460065481565b61041a611c32565b6103e461075f366004613df4565b6001600160a01b03166000908152601c602052604090205490565b61041a610788366004613fd8565b611c79565b600d54610544906001600160a01b031681565b601a546103e4565b61041a611ce4565b60205461048a90610100900460ff1681565b6103e460045481565b6103e46107d9366004613e55565b611d22565b6103bb611d43565b6103e4611d99565b61041a612252565b6060600061080261114d565b60115490915067ffffffffffffffff811115610820576108206140c1565b604051908082528060200260200182016040528015610849578160200160208202803683370190505b506001600160a01b0384166000908152601d6020526040812054919350036108af5760005b6011548110156108a957600083828151811061088c5761088c6140d7565b6020908102919091010152806108a181614103565b91505061086e565b50610995565b60005b601154811015610993576001600160a01b0384166000818152601860209081526040808320858452825280832054938352601782528083208584529091529020548351610964929161095e91670de0b6b3a764000091610958916109399190899089908110610923576109236140d7565b60200260200101516122d490919063ffffffff16565b6001600160a01b038a166000908152601d602052604090205490612316565b90612398565b9061226e565b838281518110610976576109766140d7565b60209081029190910101528061098b81614103565b9150506108b2565b505b50919050565b6000806109dd6109ce6007546109586109c7670de0b6b3a76400006006546122d490919063ffffffff16565b8790612316565b670de0b6b3a76400009061226e565b90506006548111156109ee57506006545b92915050565b60606011805480602002602001604051908101604052809291908181526020018280548015610a4c57602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610a2e575b5050505050905090565b6000546001600160a01b03163314610acd5760405162461bcd60e51b815260206004820152602f60248201527f4f6e6c792074686520636f6e7472616374206f776e6572206d6179207065726660448201526e37b936903a3434b99030b1ba34b7b760891b60648201526084015b60405180910390fd5b600180546001600160a01b0319166001600160a01b0383169081179091556040519081527f906a1c6bd7e3091ea86693dd029a831c19049ce77f1dce2ce0bab1cacbabce22906020015b60405180910390a150565b60125460609067ffffffffffffffff811115610b4057610b406140c1565b604051908082528060200260200182016040528015610b69578160200160208202803683370190505b50905060005b601254811015610bda57610bab60155460128381548110610b9257610b926140d7565b906000526020600020015461231690919063ffffffff16565b828281518110610bbd57610bbd6140d7565b602090810291909101015280610bd281614103565b915050610b6f565b5090565b6001600160a01b0381166000908152601e60209081526040808320805482518185028101850190935280835260609492939192909184015b82821015610c7a57838290600052602060002090600502016040518060a00160405290816000820154815260200160018201548152602001600282015481526020016003820154815260200160048201548152505081526020019060010190610c16565b505050509050919050565b60138181548110610c9557600080fd5b906000526020600020016000915090508054610cb09061411c565b80601f0160208091040260200160405190810160405280929190818152602001828054610cdc9061411c565b8015610d295780601f10610cfe57610100808354040283529160200191610d29565b820191906000526020600020905b815481529060010190602001808311610d0c57829003601f168201915b505050505081565b60005483906001600160a01b0316331480610d515750610d513382610dbb565b610d6d5760405162461bcd60e51b8152600401610ac490614150565b6001600160a01b038416600090815260146020526040902054601280548592908110610d9b57610d9b6140d7565b6000918252602090912001558115610db557610db5612252565b50505050565b600080546001600160a01b0390811690841603610dda575060016109ee565b6001600160a01b03828116600090815260106020526040902054818516911603610e06575060016109ee565b50600092915050565b6000546001600160a01b03163314610e395760405162461bcd60e51b8152600401610ac49061417e565b6020805463ff00000019811663010000009182900460ff1615909102179055565b60606002805403610e7d5760405162461bcd60e51b8152600401610ac4906141a5565b6002805560205462010000900460ff1615610eda5760405162461bcd60e51b815260206004820152601960248201527f5265776172647320636f6c6c656374696f6e20706175736564000000000000006044820152606401610ac4565b610ee433336123da565b9050600160025590565b6000546001600160a01b03163314610f185760405162461bcd60e51b8152600401610ac49061417e565b670de0b6b3a7640000811015610f895760405162461bcd60e51b815260206004820152603060248201527f4d756c7469706c696572206d7573742062652067726561746572207468616e2060448201526f0dee440cae2eac2d840e8de4062ca62760831b6064820152608401610ac4565b60068190556040518181527fa1676084a9eea08c6f205b60799323b364a1bd8e10aba89f0fbd94cfbf68b5dd90602001610b17565b6002805403610fdf5760405162461bcd60e51b8152600401610ac4906141a5565b60028055602054610100900460ff16156110305760405162461bcd60e51b815260206004820152601260248201527115da5d1a191c985dd85b1cc81c185d5cd95960721b6044820152606401610ac4565b61103b33338361263a565b506001600255565b60028054036110645760405162461bcd60e51b8152600401610ac4906141a5565b6002805561103b33808342612994565b60606013805480602002602001604051908101604052809291908181526020016000905b828210156111445783829060005260206000200180546110b79061411c565b80601f01602080910402602001604051908101604052809291908181526020018280546110e39061411c565b80156111305780601f1061110557610100808354040283529160200191611130565b820191906000526020600020905b81548152906001019060200180831161111357829003601f168201915b505050505081526020019060010190611098565b50505050905090565b6060601a54600014806111605750601b54155b156111ba576016805480602002602001604051908101604052809291908181526020018280548015610a4c57602002820191906000526020600020905b81548152602001906001019080831161119d575050505050905090565b60115467ffffffffffffffff8111156111d5576111d56140c1565b6040519080825280602002602001820160405280156111fe578160200160208202803683370190505b50905060005b601654811015610bda5761128a61125e601b54610958670de0b6b3a764000061125860128781548110611239576112396140d7565b9060005260206000200154611258600554611252612cb0565b906122d4565b90612316565b60168381548110611271576112716140d7565b906000526020600020015461226e90919063ffffffff16565b82828151811061129c5761129c6140d7565b6020908102919091010152806112b181614103565b915050611204565b6001546001600160a01b031633146113315760405162461bcd60e51b815260206004820152603560248201527f596f75206d757374206265206e6f6d696e61746564206265666f726520796f7560448201527402063616e20616363657074206f776e65727368697605c1b6064820152608401610ac4565b600054600154604080516001600160a01b0393841681529290911660208301527fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c910160405180910390a160018054600080546001600160a01b03199081166001600160a01b03841617909155169055565b6000546001600160a01b031633146113cd5760405162461bcd60e51b8152600401610ac49061417e565b6020805462ff0000198116620100009182900460ff1615909102179055565b601181815481106113fc57600080fd5b6000918252602090912001546001600160a01b0316905081565b60005482906001600160a01b031633148061143657506114363382610dbb565b6114525760405162461bcd60e51b8152600401610ac490614150565b6003546001600160a01b03908116908416036114b05760405162461bcd60e51b815260206004820152601e60248201527f43616e6e6f7420727567207374616b696e67202f204c5020746f6b656e7300006044820152606401610ac4565b6000805b60115481101561151057846001600160a01b0316601182815481106114db576114db6140d7565b6000918252602090912001546001600160a01b0316036114fe5760019150611510565b8061150881614103565b9150506114b4565b5080801561153757506001600160a01b038481166000908152601060205260409020541633145b156115fe5760405163a9059cbb60e01b8152336004820152602481018490526001600160a01b0385169063a9059cbb906044015b6020604051808303816000875af115801561158a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115ae91906141dc565b50604080513381526001600160a01b03861660208201529081018490527ffff3b3844276f57024e0b42afec1a37f75db36511e43819a4f2a63ab7862b6489060600160405180910390a150505050565b8015801561161657506000546001600160a01b031633145b1561164e5760405163a9059cbb60e01b8152336004820152602481018490526001600160a01b0385169063a9059cbb9060440161156b565b60405162461bcd60e51b815260206004820152601a60248201527f4e6f2076616c696420746f6b656e7320746f207265636f7665720000000000006044820152606401610ac4565b505050565b6001600160a01b0381166000908152601d602052604081205490805b6001600160a01b0384166000908152601e60205260409020548110156118c8576001600160a01b0384166000908152601e60205260408120805483908110611701576117016140d7565b600091825260208083206040805160a08101825260059094029091018054845260018101549284019290925260028201549083015260038101546060830152600401546080820181905290925090611757611d99565b90508060000361179c5760405162461bcd60e51b815260206004820152601060248201526f26bab63a34b83634b2b91032b93937b960811b6044820152606401610ac4565b600a5481116117aa5750600a545b4283606001511161186a5760608301516001600160a01b038816600090815260196020526040902054101561185e576001600160a01b0387166000908152601960205260408120546060850151611800916122d4565b9050600061181b8560600151426122d490919063ffffffff16565b9050600061183e611834670de0b6b3a764000084612316565b61095e8786612316565b905061185461184d848461226e565b8290612398565b945050505061186a565b670de0b6b3a764000091505b60408301516000611887670de0b6b3a76400006109588487612316565b905060006118a1670de0b6b3a76400006109588487612316565b90506118ad888261226e565b975050505050505080806118c090614103565b9150506116b7565b50915091565b6000546001600160a01b031633146118f85760405162461bcd60e51b8152600401610ac49061417e565b6001600160a01b03166000908152601f60205260409020805460ff19811660ff90911615179055565b600d546040805163057ff68760e51b815290516000926001600160a01b03169163affed0e09160048083019260209291908290030181865afa15801561196b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061198f91906141f9565b905080600b54036119d95760405162461bcd60e51b8152602060048201526014602482015273105b1c9958591e481cde5b98da1c9bdb9a5e995960621b6044820152606401610ac4565b6119e1612cc3565b50565b6000546001600160a01b03163314611a0e5760405162461bcd60e51b8152600401610ac49061417e565b6001821015611a5f5760405162461bcd60e51b815260206004820152601960248201527f4d756c206d61782074696d65206d757374206265203e3d2031000000000000006044820152606401610ac4565b6001811015611ab05760405162461bcd60e51b815260206004820152601960248201527f4d756c206d696e2074696d65206d757374206265203e3d2031000000000000006044820152606401610ac4565b600782905560088190556040518281527f0e3e3fae480c6f92291358a02bc83f04ee1971d5488596bffda7929d57ab470f9060200160405180910390a16040518181527f0534d208d75dfdbfacc1204745dd9b3c4c37e8cfc05eb5e8e3ae538aedb0a9fa9060200160405180910390a15050565b6000546001600160a01b03163314611b4e5760405162461bcd60e51b8152600401610ac49061417e565b62015180811015611ba15760405162461bcd60e51b815260206004820152601a60248201527f52657761726473206475726174696f6e20746f6f2073686f72740000000000006044820152606401610ac4565b6004541580611bb1575060045442115b611bfd5760405162461bcd60e51b815260206004820152601860248201527f52657761726420706572696f6420696e636f6d706c65746500000000000000006044820152606401610ac4565b60158190556040518181527ffb46ca5a5e06d4540d6387b930a7c978bce0db5f449ec6b3f5d07c6e1d44f2d390602001610b17565b6000546001600160a01b03163314611c5c5760405162461bcd60e51b8152600401610ac49061417e565b6020805461ff001981166101009182900460ff1615909102179055565b60005482906001600160a01b0316331480611c995750611c993382610dbb565b611cb55760405162461bcd60e51b8152600401610ac490614150565b506001600160a01b03918216600090815260106020526040902080546001600160a01b03191691909216179055565b6000546001600160a01b03163314611d0e5760405162461bcd60e51b8152600401610ac49061417e565b6020805460ff19811660ff90911615179055565b60128181548110611d3257600080fd5b600091825260209091200154905081565b60606012805480602002602001604051908101604052809291908181526020018280548015610a4c576020028201919060005260206000209081548152602001906001019080831161119d575050505050905090565b600080600d60009054906101000a90046001600160a01b03166001600160a01b0316635a08bd176040518163ffffffff1660e01b8152600401600060405180830381865afa158015611def573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611e179190810190614267565b90506000600d60009054906101000a90046001600160a01b03166001600160a01b031663dad601f66040518163ffffffff1660e01b8152600401600060405180830381865afa158015611e6e573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611e9691908101906142f2565b90508051825114611efe5760405162461bcd60e51b815260206004820152602c60248201527f746172676574526174696f20616e64206c7364416464726573736573206c656e60448201526b0cee8d040dad2e6dac2e8c6d60a31b6064820152608401610ac4565b8051611f0981612d2f565b60008167ffffffffffffffff811115611f2457611f246140c1565b604051908082528060200260200182016040528015611f4d578160200160208202803683370190505b509050611f5a8151612d2f565b600e54611f6f906001600160a01b0316612d74565b600e546040805163357ce51160e01b815290516000926001600160a01b03169163357ce5119160048083019260209291908290030181865afa158015611fb9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611fdd91906141f9565b9050611fe881612d2f565b60005b83811015612173576000858281518110612007576120076140d7565b6020908102919091010151600e546040516370a0823160e01b81526001600160a01b0391821660048201529116906370a0823190602401602060405180830381865afa15801561205b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061207f91906141f9565b90506000602060049054906101000a90046001600160a01b03166001600160a01b031663d45d15be8885815181106120b9576120b96140d7565b60200260200101516040518263ffffffff1660e01b81526004016120ec91906001600160a01b0391909116815260200190565b602060405180830381865afa158015612109573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061212d91906141f9565b9050600061213c838387612dbc565b905080868581518110612151576121516140d7565b602002602001018181525050505050808061216b90614103565b915050611feb565b5060006121808684612e8f565b90506000612196826722b1c8c1227a0000612fbf565b905060006121bc6121a683613073565b6121b7670de0b6b3a7640000613073565b61307b565b905060006121c982613093565b905060006121fc6121d985613073565b6121f66121e5856130ef565b6121f66729a2241af62c0000613073565b90613112565b9050600061221f61220e836003613121565b6121b7673782dace9d900000613073565b905061224261223f612238670de0b6b3a7640000613073565b8390613186565b90565b9b50505050505050505050505090565b60045442111561226657612264613195565b565b6122646131e6565b60008061227b8385614381565b9050838110156122cd5760405162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f7700000000006044820152606401610ac4565b9392505050565b60006122cd83836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f770000815250613259565b600082600003612328575060006109ee565b60006123348385614399565b90508261234185836143ce565b146122cd5760405162461bcd60e51b815260206004820152602160248201527f536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f6044820152607760f81b6064820152608401610ac4565b60006122cd83836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f00000000000081525061328a565b60608260016123e982826132b8565b60115467ffffffffffffffff811115612404576124046140c1565b60405190808252806020026020018201604052801561242d578160200160208202803683370190505b50925060005b601154811015612617576001600160a01b03861660009081526018602090815260408083208484529091529020548451859083908110612475576124756140d7565b6020908102919091018101919091526001600160a01b03871660009081526018825260408082208483529092529081205560118054829081106124ba576124ba6140d7565b9060005260206000200160009054906101000a90046001600160a01b03166001600160a01b031663a9059cbb868684815181106124f9576124f96140d7565b60200260200101516040518363ffffffff1660e01b81526004016125329291906001600160a01b03929092168252602082015260400190565b6020604051808303816000875af1158015612551573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061257591906141dc565b50856001600160a01b03167f1d2f2ca53af5d2f333bd32fdd45f9c52ad8ebe31414f7792912077fcb3876dff8583815181106125b3576125b36140d7565b6020026020010151601184815481106125ce576125ce6140d7565b60009182526020918290200154604080519384526001600160a01b039182169284019290925289169082015260600160405180910390a28061260f81614103565b915050612433565b5050506001600160a01b0390921660009081526019602052604090204290555090565b61264483836123da565b5061267a6040518060a0016040528060008019168152602001600081526020016000815260200160008152602001600081525090565b600060408201819052805b6001600160a01b0386166000908152601e6020526040902054811015612778576001600160a01b0386166000908152601e602052604090208054829081106126cf576126cf6140d7565b9060005260206000209060050201600001548403612766576001600160a01b0386166000908152601e60205260409020805482908110612711576127116140d7565b90600052602060002090600502016040518060a0016040529081600082015481526020016001820154815260200160028201548152602001600382015481526020016004820154815250509250809150612778565b8061277081614103565b915050612685565b50815183146127bb5760405162461bcd60e51b815260206004820152600f60248201526e14dd185ad9481b9bdd08199bdd5b99608a1b6044820152606401610ac4565b8160600151421015806127d5575060205460ff1615156001145b61281a5760405162461bcd60e51b81526020600482015260166024820152755374616b65206973207374696c6c206c6f636b65642160501b6044820152606401610ac4565b6040820151801561298c57601a5461283290826122d4565b601a556001600160a01b0386166000908152601c602052604090205461285890826122d4565b6001600160a01b0387166000908152601c6020908152604080832093909355601e90522080548390811061288e5761288e6140d7565b6000918252602082206005909102018181556001810182905560028101829055600381018290556004018190556128c69087906132b8565b60035460405163a9059cbb60e01b81526001600160a01b038781166004830152602482018490529091169063a9059cbb906044016020604051808303816000875af1158015612919573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061293d91906141dc565b5060408051828152602081018690526001600160a01b03878116828401529151918816917f1d9308f6b22a2754a1c622bb30889e8f8f956c83e524d039e9d65d5f052eb9089181900360600190a25b505050505050565b8360016129a182826132b8565b6020546301000000900460ff16156129ec5760405162461bcd60e51b815260206004820152600e60248201526d14dd185ada5b99c81c185d5cd95960921b6044820152606401610ac4565b60008411612a3c5760405162461bcd60e51b815260206004820152601960248201527f4d757374207374616b65206d6f7265207468616e207a65726f000000000000006044820152606401610ac4565b6001600160a01b0386166000908152601f602052604090205460ff1615612aa55760405162461bcd60e51b815260206004820152601b60248201527f4164647265737320686173206265656e20677265796c697374656400000000006044820152606401610ac4565b600042600f54612ab591906143f0565b90506000612ac28261099b565b6001600160a01b0389166000908152601c602090815260408083205490516bffffffffffffffffffffffff1960608e901b169281019290925260348201899052605482018a905260748201529192509060940160408051601f1981840301815282825280516020918201206001600160a01b03808e166000908152601e845284812060a0870186528387528685018d81529587018e8152600f5460608901908152608089018b8152835460018181018655948652979094209851600590970290980195865595519085015593516002840155935160038084019190915592516004909201919091559054909250612bbc911689308a613387565b601a54612bc9908861226e565b601a556001600160a01b0389166000908152601c6020526040902054612bef908861226e565b6001600160a01b038a166000908152601c6020526040812091909155612c16908a906132b8565b6001600160a01b0389166000908152601960205260408120549003612c51576001600160a01b03891660009081526019602052604090204290555b60408051888152602081018590529081018290526001600160a01b0389811660608301528a16907ff400e72e69ef4402819dfc57eeddc66f5eb69bf405e0e8098b1946ec1ac14a229060800160405180910390a2505050505050505050565b6000612cbe426004546134af565b905090565b6000670de0b6b3a76400006012600081548110612ce257612ce26140d7565b9060005260206000200154612cf5611d99565b612cff9190614399565b612d0991906143ce565b9050806012600081548110612d2057612d206140d7565b60009182526020909120015550565b6119e181604051602401612d4591815260200190565b60408051601f198184030181529190526020810180516001600160e01b031663f5b1bba960e01b1790526134c5565b6040516001600160a01b03821660248201526119e19060440160408051601f198184030181529190526020810180516001600160e01b031663161765e160e11b1790526134c5565b6000808060001985870985870292508281108382030391505080600003612df657838281612dec57612dec6143b8565b04925050506122cd565b838110612e27576040516307639aaf60e41b8152600481018790526024810186905260448101859052606401610ac4565b600084868809600260036001881981018916988990049182028318808302840302808302840302808302840302808302840302808302840302918202909203026000889003889004909101858311909403939093029303949094049190911702949350505050565b60008151835114612eda5760405162461bcd60e51b8152602060048201526015602482015274082e4e4c2f240d8cadccee8d040dad2e6dac2e8c6d605b1b6044820152606401610ac4565b60008060008060005b8751811015612f995766038d7ea4c68000888281518110612f0657612f066140d7565b6020026020010151612f1891906143ce565b945066038d7ea4c68000878281518110612f3457612f346140d7565b6020026020010151612f4691906143ce565b9350838513612f5e57612f598585614407565b612f68565b612f688486614407565b91506000612f7760028461452a565b9050612f838185614381565b9350508080612f9190614103565b915050612ee3565b50612fa3826134e6565b612fb49066038d7ea4c68000614399565b979650505050505050565b60008080600019848609848602925082811083820303915050670de0b6b3a7640000811061300a5760405163090638d560e41b81526004810186905260248101859052604401610ac4565b6000670de0b6b3a7640000858709905081600003613036575050670de0b6b3a7640000900490506109ee565b620400008184030492109003600160ee1b02177faccb18165bd6fe31ae1cf318dc5b51eee0e1ba569b88cd74c1773b91fac1066902905092915050565b6000816109ee565b60006122cd61223f84670de0b6b3a764000085612dbc565b600081680736ea4425c11ac63181106130c25760405163062bb40d60e31b815260048101849052602401610ac4565b6714057b7ef767814f81026130e76130e2670de0b6b3a7640000835b0490565b613550565b949350505050565b60006109ee826ec097ce7bc90715b34b9f1000000000816130de576130de6143b8565b60006122cd61223f83856143f0565b600082816001841661313b57670de0b6b3a764000061313d565b815b9050600184901c93505b831561317b576131578283612fbf565b9150600184161561316f5761316c8183612fbf565b90505b600184901c9350613147565b805b95945050505050565b60006122cd61223f8385614381565b60045442116122665760405162461bcd60e51b815260206004820152601b60248201527f506572696f6420686173206e6f742065787069726564207965742100000000006044820152606401610ac4565b60006131f061114d565b905060005b60165481101561324a57818181518110613211576132116140d7565b60200260200101516016828154811061322c5761322c6140d7565b6000918252602090912001558061324281614103565b9150506131f5565b50613253612cb0565b60055550565b6000818484111561327d5760405162461bcd60e51b8152600401610ac49190613f75565b50600061317d84866143f0565b600081836132ab5760405162461bcd60e51b8152600401610ac49190613f75565b50600061317d84866143ce565b80156132c6576132c6612252565b6001600160a01b03821615613383576000806132e18461169b565b915091506132ee846135a5565b81811061333d57600061330182846122d4565b601b54909150613311908261226e565b601b5561331e838261226e565b6001600160a01b0386166000908152601d602052604090205550610db5565b600061334983836122d4565b601b5490915061335990826122d4565b601b5561336683826122d4565b6001600160a01b0386166000908152601d60205260409020555050505b5050565b604080516001600160a01b0385811660248301528481166044830152606480830185905283518084039091018152608490920183526020820180516001600160e01b03166323b872dd60e01b17905291516000928392908816916133eb9190614539565b6000604051808303816000865af19150503d8060008114613428576040519150601f19603f3d011682016040523d82523d6000602084013e61342d565b606091505b509150915081801561345757508051158061345757508080602001905181019061345791906141dc565b61298c5760405162461bcd60e51b8152602060048201526024808201527f5472616e7366657248656c7065723a205452414e534645525f46524f4d5f46416044820152631253115160e21b6064820152608401610ac4565b60008183106134be57816122cd565b5090919050565b80516a636f6e736f6c652e6c6f67602083016000808483855afa5050505050565b6000600382111561354157508060006135006002836143ce565b61350b906001614381565b90505b818110156109955790508060028161352681866143ce565b6135309190614381565b61353a91906143ce565b905061350e565b811561354b575060015b919050565b600081680a688906bd8b000000811061357f57604051634a4f26f160e01b815260048101849052602401610ac4565b6000613597670de0b6b3a7640000604084901b6143ce565b90506130e761223f8261367e565b6001600160a01b038116156119e15760006135bf826107f6565b905060005b8151811015613620578181815181106135df576135df6140d7565b6020908102919091018101516001600160a01b038516600090815260188352604080822085835290935291909120558061361881614103565b9150506135c4565b5060005b81518110156116965760168181548110613640576136406140d7565b60009182526020808320909101546001600160a01b03861683526017825260408084208585529092529120558061367681614103565b915050613624565b600160bf1b67ff0000000000000082161561378b576780000000000000008216156136b25768016a09e667f3bcc9090260401c5b6740000000000000008216156136d1576801306fe0a31b7152df0260401c5b6720000000000000008216156136f0576801172b83c7d517adce0260401c5b67100000000000000082161561370f5768010b5586cf9890f62a0260401c5b67080000000000000082161561372e576801059b0d31585743ae0260401c5b67040000000000000082161561374d57680102c9a3e778060ee70260401c5b67020000000000000082161561376c5768010163da9fb33356d80260401c5b67010000000000000082161561378b57680100b1afa5abcbed610260401c5b66ff00000000000082161561388a5766800000000000008216156137b85768010058c86da1c09ea20260401c5b66400000000000008216156137d6576801002c605e2e8cec500260401c5b66200000000000008216156137f457680100162f3904051fa10260401c5b6610000000000000821615613812576801000b175effdc76ba0260401c5b660800000000000082161561383057680100058ba01fb9f96d0260401c5b660400000000000082161561384e5768010002c5cc37da94920260401c5b660200000000000082161561386c576801000162e525ee05470260401c5b660100000000000082161561388a5768010000b17255775c040260401c5b65ff000000000082161561398057658000000000008216156138b5576801000058b91b5bc9ae0260401c5b654000000000008216156138d257680100002c5c89d5ec6d0260401c5b652000000000008216156138ef5768010000162e43f4f8310260401c5b6510000000000082161561390c57680100000b1721bcfc9a0260401c5b650800000000008216156139295768010000058b90cf1e6e0260401c5b65040000000000821615613946576801000002c5c863b73f0260401c5b6502000000000082161561396357680100000162e430e5a20260401c5b65010000000000821615613980576801000000b1721835510260401c5b64ff00000000821615613a6d576480000000008216156139a957680100000058b90c0b490260401c5b6440000000008216156139c55768010000002c5c8601cc0260401c5b6420000000008216156139e1576801000000162e42fff00260401c5b6410000000008216156139fd5768010000000b17217fbb0260401c5b640800000000821615613a19576801000000058b90bfce0260401c5b640400000000821615613a3557680100000002c5c85fe30260401c5b640200000000821615613a515768010000000162e42ff10260401c5b640100000000821615613a6d57680100000000b17217f80260401c5b64ff00000000821615613b52576380000000821615613a955768010000000058b90bfc0260401c5b6340000000821615613ab0576801000000002c5c85fe0260401c5b6320000000821615613acb57680100000000162e42ff0260401c5b6310000000821615613ae6576801000000000b17217f0260401c5b6308000000821615613b0157680100000000058b90c00260401c5b6304000000821615613b1c5768010000000002c5c8600260401c5b6302000000821615613b37576801000000000162e4300260401c5b6301000000821615613b525768010000000000b172180260401c5b62ff0000821615613c2d5762800000821615613b77576801000000000058b90c0260401c5b62400000821615613b9157680100000000002c5c860260401c5b62200000821615613bab5768010000000000162e430260401c5b62100000821615613bc557680100000000000b17210260401c5b62080000821615613bdf5768010000000000058b910260401c5b62040000821615613bf9576801000000000002c5c80260401c5b62020000821615613c1357680100000000000162e40260401c5b62010000821615613c2d576801000000000000b1720260401c5b61ff00821615613cff57618000821615613c5057680100000000000058b90260401c5b614000821615613c695768010000000000002c5d0260401c5b612000821615613c82576801000000000000162e0260401c5b611000821615613c9b5768010000000000000b170260401c5b610800821615613cb4576801000000000000058c0260401c5b610400821615613ccd57680100000000000002c60260401c5b610200821615613ce657680100000000000001630260401c5b610100821615613cff57680100000000000000b10260401c5b60ff821615613dc8576080821615613d2057680100000000000000590260401c5b6040821615613d38576801000000000000002c0260401c5b6020821615613d5057680100000000000000160260401c5b6010821615613d68576801000000000000000b0260401c5b6008821615613d8057680100000000000000060260401c5b6004821615613d9857680100000000000000030260401c5b6002821615613db057680100000000000000010260401c5b6001821615613dc857680100000000000000010260401c5b670de0b6b3a76400000260409190911c60bf031c90565b6001600160a01b03811681146119e157600080fd5b600060208284031215613e0657600080fd5b81356122cd81613ddf565b6020808252825182820181905260009190848201906040850190845b81811015613e4957835183529284019291840191600101613e2d565b50909695505050505050565b600060208284031215613e6757600080fd5b5035919050565b6020808252825182820181905260009190848201906040850190845b81811015613e495783516001600160a01b031683529284019291840191600101613e8a565b602080825282518282018190526000919060409081850190868401855b82811015613f105781518051855286810151878601528581015186860152606080820151908601526080908101519085015260a09093019290850190600101613ecc565b5091979650505050505050565b60005b83811015613f38578181015183820152602001613f20565b83811115610db55750506000910152565b60008151808452613f61816020860160208601613f1d565b601f01601f19169290920160200192915050565b6020815260006122cd6020830184613f49565b80151581146119e157600080fd5b600080600060608486031215613fab57600080fd5b8335613fb681613ddf565b9250602084013591506040840135613fcd81613f88565b809150509250925092565b60008060408385031215613feb57600080fd5b8235613ff681613ddf565b9150602083013561400681613ddf565b809150509250929050565b6000602080830181845280855180835260408601915060408160051b870101925083870160005b8281101561406657603f19888603018452614054858351613f49565b94509285019290850190600101614038565b5092979650505050505050565b6000806040838503121561408657600080fd5b823561409181613ddf565b946020939093013593505050565b600080604083850312156140b257600080fd5b50508035926020909101359150565b634e487b7160e01b600052604160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b600060018201614115576141156140ed565b5060010190565b600181811c9082168061413057607f821691505b60208210810361099557634e487b7160e01b600052602260045260246000fd5b6020808252601490820152732737ba1037bbb732b91037b9103a35b71036b3b960611b604082015260600190565b6020808252600d908201526c2737ba103a34329037bbb732b960991b604082015260600190565b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b6000602082840312156141ee57600080fd5b81516122cd81613f88565b60006020828403121561420b57600080fd5b5051919050565b604051601f8201601f1916810167ffffffffffffffff8111828210171561423b5761423b6140c1565b604052919050565b600067ffffffffffffffff82111561425d5761425d6140c1565b5060051b60200190565b6000602080838503121561427a57600080fd5b825167ffffffffffffffff81111561429157600080fd5b8301601f810185136142a257600080fd5b80516142b56142b082614243565b614212565b81815260059190911b820183019083810190878311156142d457600080fd5b928401925b82841015612fb4578351825292840192908401906142d9565b6000602080838503121561430557600080fd5b825167ffffffffffffffff81111561431c57600080fd5b8301601f8101851361432d57600080fd5b805161433b6142b082614243565b81815260059190911b8201830190838101908783111561435a57600080fd5b928401925b82841015612fb457835161437281613ddf565b8252928401929084019061435f565b60008219821115614394576143946140ed565b500190565b60008160001904831182151516156143b3576143b36140ed565b500290565b634e487b7160e01b600052601260045260246000fd5b6000826143eb57634e487b7160e01b600052601260045260246000fd5b500490565b600082821015614402576144026140ed565b500390565b60008083128015600160ff1b850184121615614425576144256140ed565b6001600160ff1b0384018313811615614440576144406140ed565b50500390565b600181815b80851115614481578160001904821115614467576144676140ed565b8085161561447457918102915b93841c939080029061444b565b509250929050565b600082614498575060016109ee565b816144a5575060006109ee565b81600181146144bb57600281146144c5576144e1565b60019150506109ee565b60ff8411156144d6576144d66140ed565b50506001821b6109ee565b5060208310610133831016604e8410600b8410161715614504575081810a6109ee565b61450e8383614446565b8060001904821115614522576145226140ed565b029392505050565b60006122cd60ff841683614489565b6000825161454b818460208701613f1d565b919091019291505056fea2646970667358221220ef25dd6c2a442ca803fe5bc13cc68dc2eb126a5956e64f7c7612b2537715a34d64736f6c634300080d0033000000000000000000000000d88e7d30f7548b7a7c6bfe513629724916449e6d000000000000000000000000846982c0a47b0e9f4c13f3251ba972bb8d32a8ca000000000000000000000000a857904691bbdeca2e768b318b5f6b9bfa698b7c00000000000000000000000000000000000000000000000000000000642773ff000000000000000000000000e8ef2e07e2fca3305372cb0345c686efbec75658000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000355534800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000e60779cc1b2c1d0580611c526a8df0e3f870ec480000000000000000000000000000000000000000000000000000000000000001000000000000000000000000d88e7d30f7548b7a7c6bfe513629724916449e6d00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000004ba24a1fe9e10000

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106103a35760003560e01c806379313225116101e9578063c8f33c911161010f578063e01f62bf116100ad578063f2caeb1e1161007c578063f2caeb1e146107cb578063f4315553146107de578063f64be1b1146107e6578063fff6cae9146107ee57600080fd5b8063e01f62bf146107a0578063e1ba95d2146107a8578063e9f2838e146107b0578063ebe2b12b146107c257600080fd5b8063d239f003116100e9578063d239f00314610749578063d9f96e8d14610751578063de1a65511461077a578063dfc117da1461078d57600080fd5b8063c8f33c9114610724578063cc1a378f1461072d578063cdc82e801461074057600080fd5b8063941d9f6511610187578063affed0e011610156578063affed0e0146106e3578063b669de8b146106ec578063b94c4dcb14610707578063bbb781cc1461071057600080fd5b8063941d9f65146106a85780639637927f146106bb5780639a97b349146106c8578063af00f4e2146106d057600080fd5b80637bb7bed1116101c35780637bb7bed1146106475780638980f11f1461065a5780638bad86a71461066d5780638da5cb5b1461069557600080fd5b8063793132251461062e57806379ba5097146106375780637b31c19a1461063f57600080fd5b80633d18b912116102ce57806362cc445c1161026c578063693392451161023b57806369339245146105ea5780636e27cef91461060a57806370641a361461061357806372f702f31461061b57600080fd5b806362cc445c146105b157806364f2c060146105c457806366e9e9b3146105cc57806368fbd315146105e157600080fd5b806350401ac0116102a857806350401ac01461056f578063507bd8031461057857806351e3fc171461058b57806353a47bb71461059e57600080fd5b80633d18b9121461051357806341a16f3f1461051b57806347b9bc221461055c57600080fd5b80631f5848de11610346578063323331ca11610315578063323331ca146104c657806336f89af2146104d9578063386a9525146105025780633b8105b31461050b57600080fd5b80631f5848de14610464578063231b68dc146104775780632d47cf221461049a57806331ca208c146104a357600080fd5b80631627540c116103825780631627540c146104075780631c1f78eb1461041c5780631e090f01146104245780631e3689801461044457600080fd5b80628cc262146103a85780630d7bac4f146103d157806312edb24c146103f2575b600080fd5b6103bb6103b6366004613df4565b6107f6565b6040516103c89190613e11565b60405180910390f35b6103e46103df366004613e55565b61099b565b6040519081526020016103c8565b6103fa6109f4565b6040516103c89190613e6e565b61041a610415366004613df4565b610a56565b005b6103bb610b22565b610437610432366004613df4565b610bde565b6040516103c89190613eaf565b610457610452366004613e55565b610c85565b6040516103c89190613f75565b61041a610472366004613f96565b610d31565b61048a610485366004613fd8565b610dbb565b60405190151581526020016103c8565b6103e4600a5481565b61048a6104b1366004613df4565b601f6020526000908152604090205460ff1681565b60205461048a9062010000900460ff1681565b6103e46104e7366004613df4565b6001600160a01b03166000908152601d602052604090205490565b6103e460155481565b61041a610e0f565b6103bb610e5a565b610544610529366004613df4565b6010602052600090815260409020546001600160a01b031681565b6040516001600160a01b0390911681526020016103c8565b61041a61056a366004613e55565b610eee565b6103e4600c5481565b600e54610544906001600160a01b031681565b61041a610599366004613e55565b610fbe565b600154610544906001600160a01b031681565b61041a6105bf366004613e55565b611043565b601b546103e4565b6105d4611074565b6040516103c89190614011565b6103e4600f5481565b6103e46105f8366004613df4565b60146020526000908152604090205481565b6103e460085481565b6103bb61114d565b600354610544906001600160a01b031681565b6103e460095481565b61041a6112b9565b61041a6113a3565b610544610655366004613e55565b6113ec565b61041a610668366004614073565b611416565b61068061067b366004613df4565b61169b565b604080519283526020830191909152016103c8565b600054610544906001600160a01b031681565b61041a6106b6366004613df4565b6118ce565b60205461048a9060ff1681565b61041a611921565b61041a6106de36600461409f565b6119e4565b6103e4600b5481565b6020546105449064010000000090046001600160a01b031681565b6103e460075481565b60205461048a906301000000900460ff1681565b6103e460055481565b61041a61073b366004613e55565b611b24565b6103e460065481565b61041a611c32565b6103e461075f366004613df4565b6001600160a01b03166000908152601c602052604090205490565b61041a610788366004613fd8565b611c79565b600d54610544906001600160a01b031681565b601a546103e4565b61041a611ce4565b60205461048a90610100900460ff1681565b6103e460045481565b6103e46107d9366004613e55565b611d22565b6103bb611d43565b6103e4611d99565b61041a612252565b6060600061080261114d565b60115490915067ffffffffffffffff811115610820576108206140c1565b604051908082528060200260200182016040528015610849578160200160208202803683370190505b506001600160a01b0384166000908152601d6020526040812054919350036108af5760005b6011548110156108a957600083828151811061088c5761088c6140d7565b6020908102919091010152806108a181614103565b91505061086e565b50610995565b60005b601154811015610993576001600160a01b0384166000818152601860209081526040808320858452825280832054938352601782528083208584529091529020548351610964929161095e91670de0b6b3a764000091610958916109399190899089908110610923576109236140d7565b60200260200101516122d490919063ffffffff16565b6001600160a01b038a166000908152601d602052604090205490612316565b90612398565b9061226e565b838281518110610976576109766140d7565b60209081029190910101528061098b81614103565b9150506108b2565b505b50919050565b6000806109dd6109ce6007546109586109c7670de0b6b3a76400006006546122d490919063ffffffff16565b8790612316565b670de0b6b3a76400009061226e565b90506006548111156109ee57506006545b92915050565b60606011805480602002602001604051908101604052809291908181526020018280548015610a4c57602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610a2e575b5050505050905090565b6000546001600160a01b03163314610acd5760405162461bcd60e51b815260206004820152602f60248201527f4f6e6c792074686520636f6e7472616374206f776e6572206d6179207065726660448201526e37b936903a3434b99030b1ba34b7b760891b60648201526084015b60405180910390fd5b600180546001600160a01b0319166001600160a01b0383169081179091556040519081527f906a1c6bd7e3091ea86693dd029a831c19049ce77f1dce2ce0bab1cacbabce22906020015b60405180910390a150565b60125460609067ffffffffffffffff811115610b4057610b406140c1565b604051908082528060200260200182016040528015610b69578160200160208202803683370190505b50905060005b601254811015610bda57610bab60155460128381548110610b9257610b926140d7565b906000526020600020015461231690919063ffffffff16565b828281518110610bbd57610bbd6140d7565b602090810291909101015280610bd281614103565b915050610b6f565b5090565b6001600160a01b0381166000908152601e60209081526040808320805482518185028101850190935280835260609492939192909184015b82821015610c7a57838290600052602060002090600502016040518060a00160405290816000820154815260200160018201548152602001600282015481526020016003820154815260200160048201548152505081526020019060010190610c16565b505050509050919050565b60138181548110610c9557600080fd5b906000526020600020016000915090508054610cb09061411c565b80601f0160208091040260200160405190810160405280929190818152602001828054610cdc9061411c565b8015610d295780601f10610cfe57610100808354040283529160200191610d29565b820191906000526020600020905b815481529060010190602001808311610d0c57829003601f168201915b505050505081565b60005483906001600160a01b0316331480610d515750610d513382610dbb565b610d6d5760405162461bcd60e51b8152600401610ac490614150565b6001600160a01b038416600090815260146020526040902054601280548592908110610d9b57610d9b6140d7565b6000918252602090912001558115610db557610db5612252565b50505050565b600080546001600160a01b0390811690841603610dda575060016109ee565b6001600160a01b03828116600090815260106020526040902054818516911603610e06575060016109ee565b50600092915050565b6000546001600160a01b03163314610e395760405162461bcd60e51b8152600401610ac49061417e565b6020805463ff00000019811663010000009182900460ff1615909102179055565b60606002805403610e7d5760405162461bcd60e51b8152600401610ac4906141a5565b6002805560205462010000900460ff1615610eda5760405162461bcd60e51b815260206004820152601960248201527f5265776172647320636f6c6c656374696f6e20706175736564000000000000006044820152606401610ac4565b610ee433336123da565b9050600160025590565b6000546001600160a01b03163314610f185760405162461bcd60e51b8152600401610ac49061417e565b670de0b6b3a7640000811015610f895760405162461bcd60e51b815260206004820152603060248201527f4d756c7469706c696572206d7573742062652067726561746572207468616e2060448201526f0dee440cae2eac2d840e8de4062ca62760831b6064820152608401610ac4565b60068190556040518181527fa1676084a9eea08c6f205b60799323b364a1bd8e10aba89f0fbd94cfbf68b5dd90602001610b17565b6002805403610fdf5760405162461bcd60e51b8152600401610ac4906141a5565b60028055602054610100900460ff16156110305760405162461bcd60e51b815260206004820152601260248201527115da5d1a191c985dd85b1cc81c185d5cd95960721b6044820152606401610ac4565b61103b33338361263a565b506001600255565b60028054036110645760405162461bcd60e51b8152600401610ac4906141a5565b6002805561103b33808342612994565b60606013805480602002602001604051908101604052809291908181526020016000905b828210156111445783829060005260206000200180546110b79061411c565b80601f01602080910402602001604051908101604052809291908181526020018280546110e39061411c565b80156111305780601f1061110557610100808354040283529160200191611130565b820191906000526020600020905b81548152906001019060200180831161111357829003601f168201915b505050505081526020019060010190611098565b50505050905090565b6060601a54600014806111605750601b54155b156111ba576016805480602002602001604051908101604052809291908181526020018280548015610a4c57602002820191906000526020600020905b81548152602001906001019080831161119d575050505050905090565b60115467ffffffffffffffff8111156111d5576111d56140c1565b6040519080825280602002602001820160405280156111fe578160200160208202803683370190505b50905060005b601654811015610bda5761128a61125e601b54610958670de0b6b3a764000061125860128781548110611239576112396140d7565b9060005260206000200154611258600554611252612cb0565b906122d4565b90612316565b60168381548110611271576112716140d7565b906000526020600020015461226e90919063ffffffff16565b82828151811061129c5761129c6140d7565b6020908102919091010152806112b181614103565b915050611204565b6001546001600160a01b031633146113315760405162461bcd60e51b815260206004820152603560248201527f596f75206d757374206265206e6f6d696e61746564206265666f726520796f7560448201527402063616e20616363657074206f776e65727368697605c1b6064820152608401610ac4565b600054600154604080516001600160a01b0393841681529290911660208301527fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c910160405180910390a160018054600080546001600160a01b03199081166001600160a01b03841617909155169055565b6000546001600160a01b031633146113cd5760405162461bcd60e51b8152600401610ac49061417e565b6020805462ff0000198116620100009182900460ff1615909102179055565b601181815481106113fc57600080fd5b6000918252602090912001546001600160a01b0316905081565b60005482906001600160a01b031633148061143657506114363382610dbb565b6114525760405162461bcd60e51b8152600401610ac490614150565b6003546001600160a01b03908116908416036114b05760405162461bcd60e51b815260206004820152601e60248201527f43616e6e6f7420727567207374616b696e67202f204c5020746f6b656e7300006044820152606401610ac4565b6000805b60115481101561151057846001600160a01b0316601182815481106114db576114db6140d7565b6000918252602090912001546001600160a01b0316036114fe5760019150611510565b8061150881614103565b9150506114b4565b5080801561153757506001600160a01b038481166000908152601060205260409020541633145b156115fe5760405163a9059cbb60e01b8152336004820152602481018490526001600160a01b0385169063a9059cbb906044015b6020604051808303816000875af115801561158a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115ae91906141dc565b50604080513381526001600160a01b03861660208201529081018490527ffff3b3844276f57024e0b42afec1a37f75db36511e43819a4f2a63ab7862b6489060600160405180910390a150505050565b8015801561161657506000546001600160a01b031633145b1561164e5760405163a9059cbb60e01b8152336004820152602481018490526001600160a01b0385169063a9059cbb9060440161156b565b60405162461bcd60e51b815260206004820152601a60248201527f4e6f2076616c696420746f6b656e7320746f207265636f7665720000000000006044820152606401610ac4565b505050565b6001600160a01b0381166000908152601d602052604081205490805b6001600160a01b0384166000908152601e60205260409020548110156118c8576001600160a01b0384166000908152601e60205260408120805483908110611701576117016140d7565b600091825260208083206040805160a08101825260059094029091018054845260018101549284019290925260028201549083015260038101546060830152600401546080820181905290925090611757611d99565b90508060000361179c5760405162461bcd60e51b815260206004820152601060248201526f26bab63a34b83634b2b91032b93937b960811b6044820152606401610ac4565b600a5481116117aa5750600a545b4283606001511161186a5760608301516001600160a01b038816600090815260196020526040902054101561185e576001600160a01b0387166000908152601960205260408120546060850151611800916122d4565b9050600061181b8560600151426122d490919063ffffffff16565b9050600061183e611834670de0b6b3a764000084612316565b61095e8786612316565b905061185461184d848461226e565b8290612398565b945050505061186a565b670de0b6b3a764000091505b60408301516000611887670de0b6b3a76400006109588487612316565b905060006118a1670de0b6b3a76400006109588487612316565b90506118ad888261226e565b975050505050505080806118c090614103565b9150506116b7565b50915091565b6000546001600160a01b031633146118f85760405162461bcd60e51b8152600401610ac49061417e565b6001600160a01b03166000908152601f60205260409020805460ff19811660ff90911615179055565b600d546040805163057ff68760e51b815290516000926001600160a01b03169163affed0e09160048083019260209291908290030181865afa15801561196b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061198f91906141f9565b905080600b54036119d95760405162461bcd60e51b8152602060048201526014602482015273105b1c9958591e481cde5b98da1c9bdb9a5e995960621b6044820152606401610ac4565b6119e1612cc3565b50565b6000546001600160a01b03163314611a0e5760405162461bcd60e51b8152600401610ac49061417e565b6001821015611a5f5760405162461bcd60e51b815260206004820152601960248201527f4d756c206d61782074696d65206d757374206265203e3d2031000000000000006044820152606401610ac4565b6001811015611ab05760405162461bcd60e51b815260206004820152601960248201527f4d756c206d696e2074696d65206d757374206265203e3d2031000000000000006044820152606401610ac4565b600782905560088190556040518281527f0e3e3fae480c6f92291358a02bc83f04ee1971d5488596bffda7929d57ab470f9060200160405180910390a16040518181527f0534d208d75dfdbfacc1204745dd9b3c4c37e8cfc05eb5e8e3ae538aedb0a9fa9060200160405180910390a15050565b6000546001600160a01b03163314611b4e5760405162461bcd60e51b8152600401610ac49061417e565b62015180811015611ba15760405162461bcd60e51b815260206004820152601a60248201527f52657761726473206475726174696f6e20746f6f2073686f72740000000000006044820152606401610ac4565b6004541580611bb1575060045442115b611bfd5760405162461bcd60e51b815260206004820152601860248201527f52657761726420706572696f6420696e636f6d706c65746500000000000000006044820152606401610ac4565b60158190556040518181527ffb46ca5a5e06d4540d6387b930a7c978bce0db5f449ec6b3f5d07c6e1d44f2d390602001610b17565b6000546001600160a01b03163314611c5c5760405162461bcd60e51b8152600401610ac49061417e565b6020805461ff001981166101009182900460ff1615909102179055565b60005482906001600160a01b0316331480611c995750611c993382610dbb565b611cb55760405162461bcd60e51b8152600401610ac490614150565b506001600160a01b03918216600090815260106020526040902080546001600160a01b03191691909216179055565b6000546001600160a01b03163314611d0e5760405162461bcd60e51b8152600401610ac49061417e565b6020805460ff19811660ff90911615179055565b60128181548110611d3257600080fd5b600091825260209091200154905081565b60606012805480602002602001604051908101604052809291908181526020018280548015610a4c576020028201919060005260206000209081548152602001906001019080831161119d575050505050905090565b600080600d60009054906101000a90046001600160a01b03166001600160a01b0316635a08bd176040518163ffffffff1660e01b8152600401600060405180830381865afa158015611def573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611e179190810190614267565b90506000600d60009054906101000a90046001600160a01b03166001600160a01b031663dad601f66040518163ffffffff1660e01b8152600401600060405180830381865afa158015611e6e573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611e9691908101906142f2565b90508051825114611efe5760405162461bcd60e51b815260206004820152602c60248201527f746172676574526174696f20616e64206c7364416464726573736573206c656e60448201526b0cee8d040dad2e6dac2e8c6d60a31b6064820152608401610ac4565b8051611f0981612d2f565b60008167ffffffffffffffff811115611f2457611f246140c1565b604051908082528060200260200182016040528015611f4d578160200160208202803683370190505b509050611f5a8151612d2f565b600e54611f6f906001600160a01b0316612d74565b600e546040805163357ce51160e01b815290516000926001600160a01b03169163357ce5119160048083019260209291908290030181865afa158015611fb9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611fdd91906141f9565b9050611fe881612d2f565b60005b83811015612173576000858281518110612007576120076140d7565b6020908102919091010151600e546040516370a0823160e01b81526001600160a01b0391821660048201529116906370a0823190602401602060405180830381865afa15801561205b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061207f91906141f9565b90506000602060049054906101000a90046001600160a01b03166001600160a01b031663d45d15be8885815181106120b9576120b96140d7565b60200260200101516040518263ffffffff1660e01b81526004016120ec91906001600160a01b0391909116815260200190565b602060405180830381865afa158015612109573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061212d91906141f9565b9050600061213c838387612dbc565b905080868581518110612151576121516140d7565b602002602001018181525050505050808061216b90614103565b915050611feb565b5060006121808684612e8f565b90506000612196826722b1c8c1227a0000612fbf565b905060006121bc6121a683613073565b6121b7670de0b6b3a7640000613073565b61307b565b905060006121c982613093565b905060006121fc6121d985613073565b6121f66121e5856130ef565b6121f66729a2241af62c0000613073565b90613112565b9050600061221f61220e836003613121565b6121b7673782dace9d900000613073565b905061224261223f612238670de0b6b3a7640000613073565b8390613186565b90565b9b50505050505050505050505090565b60045442111561226657612264613195565b565b6122646131e6565b60008061227b8385614381565b9050838110156122cd5760405162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f7700000000006044820152606401610ac4565b9392505050565b60006122cd83836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f770000815250613259565b600082600003612328575060006109ee565b60006123348385614399565b90508261234185836143ce565b146122cd5760405162461bcd60e51b815260206004820152602160248201527f536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f6044820152607760f81b6064820152608401610ac4565b60006122cd83836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f00000000000081525061328a565b60608260016123e982826132b8565b60115467ffffffffffffffff811115612404576124046140c1565b60405190808252806020026020018201604052801561242d578160200160208202803683370190505b50925060005b601154811015612617576001600160a01b03861660009081526018602090815260408083208484529091529020548451859083908110612475576124756140d7565b6020908102919091018101919091526001600160a01b03871660009081526018825260408082208483529092529081205560118054829081106124ba576124ba6140d7565b9060005260206000200160009054906101000a90046001600160a01b03166001600160a01b031663a9059cbb868684815181106124f9576124f96140d7565b60200260200101516040518363ffffffff1660e01b81526004016125329291906001600160a01b03929092168252602082015260400190565b6020604051808303816000875af1158015612551573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061257591906141dc565b50856001600160a01b03167f1d2f2ca53af5d2f333bd32fdd45f9c52ad8ebe31414f7792912077fcb3876dff8583815181106125b3576125b36140d7565b6020026020010151601184815481106125ce576125ce6140d7565b60009182526020918290200154604080519384526001600160a01b039182169284019290925289169082015260600160405180910390a28061260f81614103565b915050612433565b5050506001600160a01b0390921660009081526019602052604090204290555090565b61264483836123da565b5061267a6040518060a0016040528060008019168152602001600081526020016000815260200160008152602001600081525090565b600060408201819052805b6001600160a01b0386166000908152601e6020526040902054811015612778576001600160a01b0386166000908152601e602052604090208054829081106126cf576126cf6140d7565b9060005260206000209060050201600001548403612766576001600160a01b0386166000908152601e60205260409020805482908110612711576127116140d7565b90600052602060002090600502016040518060a0016040529081600082015481526020016001820154815260200160028201548152602001600382015481526020016004820154815250509250809150612778565b8061277081614103565b915050612685565b50815183146127bb5760405162461bcd60e51b815260206004820152600f60248201526e14dd185ad9481b9bdd08199bdd5b99608a1b6044820152606401610ac4565b8160600151421015806127d5575060205460ff1615156001145b61281a5760405162461bcd60e51b81526020600482015260166024820152755374616b65206973207374696c6c206c6f636b65642160501b6044820152606401610ac4565b6040820151801561298c57601a5461283290826122d4565b601a556001600160a01b0386166000908152601c602052604090205461285890826122d4565b6001600160a01b0387166000908152601c6020908152604080832093909355601e90522080548390811061288e5761288e6140d7565b6000918252602082206005909102018181556001810182905560028101829055600381018290556004018190556128c69087906132b8565b60035460405163a9059cbb60e01b81526001600160a01b038781166004830152602482018490529091169063a9059cbb906044016020604051808303816000875af1158015612919573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061293d91906141dc565b5060408051828152602081018690526001600160a01b03878116828401529151918816917f1d9308f6b22a2754a1c622bb30889e8f8f956c83e524d039e9d65d5f052eb9089181900360600190a25b505050505050565b8360016129a182826132b8565b6020546301000000900460ff16156129ec5760405162461bcd60e51b815260206004820152600e60248201526d14dd185ada5b99c81c185d5cd95960921b6044820152606401610ac4565b60008411612a3c5760405162461bcd60e51b815260206004820152601960248201527f4d757374207374616b65206d6f7265207468616e207a65726f000000000000006044820152606401610ac4565b6001600160a01b0386166000908152601f602052604090205460ff1615612aa55760405162461bcd60e51b815260206004820152601b60248201527f4164647265737320686173206265656e20677265796c697374656400000000006044820152606401610ac4565b600042600f54612ab591906143f0565b90506000612ac28261099b565b6001600160a01b0389166000908152601c602090815260408083205490516bffffffffffffffffffffffff1960608e901b169281019290925260348201899052605482018a905260748201529192509060940160408051601f1981840301815282825280516020918201206001600160a01b03808e166000908152601e845284812060a0870186528387528685018d81529587018e8152600f5460608901908152608089018b8152835460018181018655948652979094209851600590970290980195865595519085015593516002840155935160038084019190915592516004909201919091559054909250612bbc911689308a613387565b601a54612bc9908861226e565b601a556001600160a01b0389166000908152601c6020526040902054612bef908861226e565b6001600160a01b038a166000908152601c6020526040812091909155612c16908a906132b8565b6001600160a01b0389166000908152601960205260408120549003612c51576001600160a01b03891660009081526019602052604090204290555b60408051888152602081018590529081018290526001600160a01b0389811660608301528a16907ff400e72e69ef4402819dfc57eeddc66f5eb69bf405e0e8098b1946ec1ac14a229060800160405180910390a2505050505050505050565b6000612cbe426004546134af565b905090565b6000670de0b6b3a76400006012600081548110612ce257612ce26140d7565b9060005260206000200154612cf5611d99565b612cff9190614399565b612d0991906143ce565b9050806012600081548110612d2057612d206140d7565b60009182526020909120015550565b6119e181604051602401612d4591815260200190565b60408051601f198184030181529190526020810180516001600160e01b031663f5b1bba960e01b1790526134c5565b6040516001600160a01b03821660248201526119e19060440160408051601f198184030181529190526020810180516001600160e01b031663161765e160e11b1790526134c5565b6000808060001985870985870292508281108382030391505080600003612df657838281612dec57612dec6143b8565b04925050506122cd565b838110612e27576040516307639aaf60e41b8152600481018790526024810186905260448101859052606401610ac4565b600084868809600260036001881981018916988990049182028318808302840302808302840302808302840302808302840302808302840302918202909203026000889003889004909101858311909403939093029303949094049190911702949350505050565b60008151835114612eda5760405162461bcd60e51b8152602060048201526015602482015274082e4e4c2f240d8cadccee8d040dad2e6dac2e8c6d605b1b6044820152606401610ac4565b60008060008060005b8751811015612f995766038d7ea4c68000888281518110612f0657612f066140d7565b6020026020010151612f1891906143ce565b945066038d7ea4c68000878281518110612f3457612f346140d7565b6020026020010151612f4691906143ce565b9350838513612f5e57612f598585614407565b612f68565b612f688486614407565b91506000612f7760028461452a565b9050612f838185614381565b9350508080612f9190614103565b915050612ee3565b50612fa3826134e6565b612fb49066038d7ea4c68000614399565b979650505050505050565b60008080600019848609848602925082811083820303915050670de0b6b3a7640000811061300a5760405163090638d560e41b81526004810186905260248101859052604401610ac4565b6000670de0b6b3a7640000858709905081600003613036575050670de0b6b3a7640000900490506109ee565b620400008184030492109003600160ee1b02177faccb18165bd6fe31ae1cf318dc5b51eee0e1ba569b88cd74c1773b91fac1066902905092915050565b6000816109ee565b60006122cd61223f84670de0b6b3a764000085612dbc565b600081680736ea4425c11ac63181106130c25760405163062bb40d60e31b815260048101849052602401610ac4565b6714057b7ef767814f81026130e76130e2670de0b6b3a7640000835b0490565b613550565b949350505050565b60006109ee826ec097ce7bc90715b34b9f1000000000816130de576130de6143b8565b60006122cd61223f83856143f0565b600082816001841661313b57670de0b6b3a764000061313d565b815b9050600184901c93505b831561317b576131578283612fbf565b9150600184161561316f5761316c8183612fbf565b90505b600184901c9350613147565b805b95945050505050565b60006122cd61223f8385614381565b60045442116122665760405162461bcd60e51b815260206004820152601b60248201527f506572696f6420686173206e6f742065787069726564207965742100000000006044820152606401610ac4565b60006131f061114d565b905060005b60165481101561324a57818181518110613211576132116140d7565b60200260200101516016828154811061322c5761322c6140d7565b6000918252602090912001558061324281614103565b9150506131f5565b50613253612cb0565b60055550565b6000818484111561327d5760405162461bcd60e51b8152600401610ac49190613f75565b50600061317d84866143f0565b600081836132ab5760405162461bcd60e51b8152600401610ac49190613f75565b50600061317d84866143ce565b80156132c6576132c6612252565b6001600160a01b03821615613383576000806132e18461169b565b915091506132ee846135a5565b81811061333d57600061330182846122d4565b601b54909150613311908261226e565b601b5561331e838261226e565b6001600160a01b0386166000908152601d602052604090205550610db5565b600061334983836122d4565b601b5490915061335990826122d4565b601b5561336683826122d4565b6001600160a01b0386166000908152601d60205260409020555050505b5050565b604080516001600160a01b0385811660248301528481166044830152606480830185905283518084039091018152608490920183526020820180516001600160e01b03166323b872dd60e01b17905291516000928392908816916133eb9190614539565b6000604051808303816000865af19150503d8060008114613428576040519150601f19603f3d011682016040523d82523d6000602084013e61342d565b606091505b509150915081801561345757508051158061345757508080602001905181019061345791906141dc565b61298c5760405162461bcd60e51b8152602060048201526024808201527f5472616e7366657248656c7065723a205452414e534645525f46524f4d5f46416044820152631253115160e21b6064820152608401610ac4565b60008183106134be57816122cd565b5090919050565b80516a636f6e736f6c652e6c6f67602083016000808483855afa5050505050565b6000600382111561354157508060006135006002836143ce565b61350b906001614381565b90505b818110156109955790508060028161352681866143ce565b6135309190614381565b61353a91906143ce565b905061350e565b811561354b575060015b919050565b600081680a688906bd8b000000811061357f57604051634a4f26f160e01b815260048101849052602401610ac4565b6000613597670de0b6b3a7640000604084901b6143ce565b90506130e761223f8261367e565b6001600160a01b038116156119e15760006135bf826107f6565b905060005b8151811015613620578181815181106135df576135df6140d7565b6020908102919091018101516001600160a01b038516600090815260188352604080822085835290935291909120558061361881614103565b9150506135c4565b5060005b81518110156116965760168181548110613640576136406140d7565b60009182526020808320909101546001600160a01b03861683526017825260408084208585529092529120558061367681614103565b915050613624565b600160bf1b67ff0000000000000082161561378b576780000000000000008216156136b25768016a09e667f3bcc9090260401c5b6740000000000000008216156136d1576801306fe0a31b7152df0260401c5b6720000000000000008216156136f0576801172b83c7d517adce0260401c5b67100000000000000082161561370f5768010b5586cf9890f62a0260401c5b67080000000000000082161561372e576801059b0d31585743ae0260401c5b67040000000000000082161561374d57680102c9a3e778060ee70260401c5b67020000000000000082161561376c5768010163da9fb33356d80260401c5b67010000000000000082161561378b57680100b1afa5abcbed610260401c5b66ff00000000000082161561388a5766800000000000008216156137b85768010058c86da1c09ea20260401c5b66400000000000008216156137d6576801002c605e2e8cec500260401c5b66200000000000008216156137f457680100162f3904051fa10260401c5b6610000000000000821615613812576801000b175effdc76ba0260401c5b660800000000000082161561383057680100058ba01fb9f96d0260401c5b660400000000000082161561384e5768010002c5cc37da94920260401c5b660200000000000082161561386c576801000162e525ee05470260401c5b660100000000000082161561388a5768010000b17255775c040260401c5b65ff000000000082161561398057658000000000008216156138b5576801000058b91b5bc9ae0260401c5b654000000000008216156138d257680100002c5c89d5ec6d0260401c5b652000000000008216156138ef5768010000162e43f4f8310260401c5b6510000000000082161561390c57680100000b1721bcfc9a0260401c5b650800000000008216156139295768010000058b90cf1e6e0260401c5b65040000000000821615613946576801000002c5c863b73f0260401c5b6502000000000082161561396357680100000162e430e5a20260401c5b65010000000000821615613980576801000000b1721835510260401c5b64ff00000000821615613a6d576480000000008216156139a957680100000058b90c0b490260401c5b6440000000008216156139c55768010000002c5c8601cc0260401c5b6420000000008216156139e1576801000000162e42fff00260401c5b6410000000008216156139fd5768010000000b17217fbb0260401c5b640800000000821615613a19576801000000058b90bfce0260401c5b640400000000821615613a3557680100000002c5c85fe30260401c5b640200000000821615613a515768010000000162e42ff10260401c5b640100000000821615613a6d57680100000000b17217f80260401c5b64ff00000000821615613b52576380000000821615613a955768010000000058b90bfc0260401c5b6340000000821615613ab0576801000000002c5c85fe0260401c5b6320000000821615613acb57680100000000162e42ff0260401c5b6310000000821615613ae6576801000000000b17217f0260401c5b6308000000821615613b0157680100000000058b90c00260401c5b6304000000821615613b1c5768010000000002c5c8600260401c5b6302000000821615613b37576801000000000162e4300260401c5b6301000000821615613b525768010000000000b172180260401c5b62ff0000821615613c2d5762800000821615613b77576801000000000058b90c0260401c5b62400000821615613b9157680100000000002c5c860260401c5b62200000821615613bab5768010000000000162e430260401c5b62100000821615613bc557680100000000000b17210260401c5b62080000821615613bdf5768010000000000058b910260401c5b62040000821615613bf9576801000000000002c5c80260401c5b62020000821615613c1357680100000000000162e40260401c5b62010000821615613c2d576801000000000000b1720260401c5b61ff00821615613cff57618000821615613c5057680100000000000058b90260401c5b614000821615613c695768010000000000002c5d0260401c5b612000821615613c82576801000000000000162e0260401c5b611000821615613c9b5768010000000000000b170260401c5b610800821615613cb4576801000000000000058c0260401c5b610400821615613ccd57680100000000000002c60260401c5b610200821615613ce657680100000000000001630260401c5b610100821615613cff57680100000000000000b10260401c5b60ff821615613dc8576080821615613d2057680100000000000000590260401c5b6040821615613d38576801000000000000002c0260401c5b6020821615613d5057680100000000000000160260401c5b6010821615613d68576801000000000000000b0260401c5b6008821615613d8057680100000000000000060260401c5b6004821615613d9857680100000000000000030260401c5b6002821615613db057680100000000000000010260401c5b6001821615613dc857680100000000000000010260401c5b670de0b6b3a76400000260409190911c60bf031c90565b6001600160a01b03811681146119e157600080fd5b600060208284031215613e0657600080fd5b81356122cd81613ddf565b6020808252825182820181905260009190848201906040850190845b81811015613e4957835183529284019291840191600101613e2d565b50909695505050505050565b600060208284031215613e6757600080fd5b5035919050565b6020808252825182820181905260009190848201906040850190845b81811015613e495783516001600160a01b031683529284019291840191600101613e8a565b602080825282518282018190526000919060409081850190868401855b82811015613f105781518051855286810151878601528581015186860152606080820151908601526080908101519085015260a09093019290850190600101613ecc565b5091979650505050505050565b60005b83811015613f38578181015183820152602001613f20565b83811115610db55750506000910152565b60008151808452613f61816020860160208601613f1d565b601f01601f19169290920160200192915050565b6020815260006122cd6020830184613f49565b80151581146119e157600080fd5b600080600060608486031215613fab57600080fd5b8335613fb681613ddf565b9250602084013591506040840135613fcd81613f88565b809150509250925092565b60008060408385031215613feb57600080fd5b8235613ff681613ddf565b9150602083013561400681613ddf565b809150509250929050565b6000602080830181845280855180835260408601915060408160051b870101925083870160005b8281101561406657603f19888603018452614054858351613f49565b94509285019290850190600101614038565b5092979650505050505050565b6000806040838503121561408657600080fd5b823561409181613ddf565b946020939093013593505050565b600080604083850312156140b257600080fd5b50508035926020909101359150565b634e487b7160e01b600052604160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b600060018201614115576141156140ed565b5060010190565b600181811c9082168061413057607f821691505b60208210810361099557634e487b7160e01b600052602260045260246000fd5b6020808252601490820152732737ba1037bbb732b91037b9103a35b71036b3b960611b604082015260600190565b6020808252600d908201526c2737ba103a34329037bbb732b960991b604082015260600190565b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b6000602082840312156141ee57600080fd5b81516122cd81613f88565b60006020828403121561420b57600080fd5b5051919050565b604051601f8201601f1916810167ffffffffffffffff8111828210171561423b5761423b6140c1565b604052919050565b600067ffffffffffffffff82111561425d5761425d6140c1565b5060051b60200190565b6000602080838503121561427a57600080fd5b825167ffffffffffffffff81111561429157600080fd5b8301601f810185136142a257600080fd5b80516142b56142b082614243565b614212565b81815260059190911b820183019083810190878311156142d457600080fd5b928401925b82841015612fb4578351825292840192908401906142d9565b6000602080838503121561430557600080fd5b825167ffffffffffffffff81111561431c57600080fd5b8301601f8101851361432d57600080fd5b805161433b6142b082614243565b81815260059190911b8201830190838101908783111561435a57600080fd5b928401925b82841015612fb457835161437281613ddf565b8252928401929084019061435f565b60008219821115614394576143946140ed565b500190565b60008160001904831182151516156143b3576143b36140ed565b500290565b634e487b7160e01b600052601260045260246000fd5b6000826143eb57634e487b7160e01b600052601260045260246000fd5b500490565b600082821015614402576144026140ed565b500390565b60008083128015600160ff1b850184121615614425576144256140ed565b6001600160ff1b0384018313811615614440576144406140ed565b50500390565b600181815b80851115614481578160001904821115614467576144676140ed565b8085161561447457918102915b93841c939080029061444b565b509250929050565b600082614498575060016109ee565b816144a5575060006109ee565b81600181146144bb57600281146144c5576144e1565b60019150506109ee565b60ff8411156144d6576144d66140ed565b50506001821b6109ee565b5060208310610133831016604e8410600b8410161715614504575081810a6109ee565b61450e8383614446565b8060001904821115614522576145226140ed565b029392505050565b60006122cd60ff841683614489565b6000825161454b818460208701613f1d565b919091019291505056fea2646970667358221220ef25dd6c2a442ca803fe5bc13cc68dc2eb126a5956e64f7c7612b2537715a34d64736f6c634300080d0033

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

000000000000000000000000d88e7d30f7548b7a7c6bfe513629724916449e6d000000000000000000000000846982c0a47b0e9f4c13f3251ba972bb8d32a8ca000000000000000000000000a857904691bbdeca2e768b318b5f6b9bfa698b7c00000000000000000000000000000000000000000000000000000000642773ff000000000000000000000000e8ef2e07e2fca3305372cb0345c686efbec75658000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000355534800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000e60779cc1b2c1d0580611c526a8df0e3f870ec480000000000000000000000000000000000000000000000000000000000000001000000000000000000000000d88e7d30f7548b7a7c6bfe513629724916449e6d00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000004ba24a1fe9e10000

-----Decoded View---------------
Arg [0] : _owner (address): 0xD88e7D30f7548B7a7C6BFE513629724916449e6D
Arg [1] : _stakingToken (address): 0x846982C0a47b0e9f4c13F3251ba972Bb8D32a8cA
Arg [2] : _LSDRegistry (address): 0xA857904691bbdEca2e768B318B5f6b9bfA698b7C
Arg [3] : _shanghaiTime (uint256): 1680307199
Arg [4] : _darknetAddress (address): 0xE8EF2E07E2FCa3305372Cb0345C686EFbec75658
Arg [5] : _rewardSymbols (string[]): USH
Arg [6] : _rewardTokens (address[]): 0xE60779CC1b2c1d0580611c526a8DF0E3f870EC48
Arg [7] : _rewardManagers (address[]): 0xD88e7D30f7548B7a7C6BFE513629724916449e6D
Arg [8] : _rewardRates (uint256[]): 5450000000000000000

-----Encoded View---------------
19 Constructor Arguments found :
Arg [0] : 000000000000000000000000d88e7d30f7548b7a7c6bfe513629724916449e6d
Arg [1] : 000000000000000000000000846982c0a47b0e9f4c13f3251ba972bb8d32a8ca
Arg [2] : 000000000000000000000000a857904691bbdeca2e768b318b5f6b9bfa698b7c
Arg [3] : 00000000000000000000000000000000000000000000000000000000642773ff
Arg [4] : 000000000000000000000000e8ef2e07e2fca3305372cb0345c686efbec75658
Arg [5] : 0000000000000000000000000000000000000000000000000000000000000120
Arg [6] : 00000000000000000000000000000000000000000000000000000000000001a0
Arg [7] : 00000000000000000000000000000000000000000000000000000000000001e0
Arg [8] : 0000000000000000000000000000000000000000000000000000000000000220
Arg [9] : 0000000000000000000000000000000000000000000000000000000000000001
Arg [10] : 0000000000000000000000000000000000000000000000000000000000000020
Arg [11] : 0000000000000000000000000000000000000000000000000000000000000003
Arg [12] : 5553480000000000000000000000000000000000000000000000000000000000
Arg [13] : 0000000000000000000000000000000000000000000000000000000000000001
Arg [14] : 000000000000000000000000e60779cc1b2c1d0580611c526a8df0e3f870ec48
Arg [15] : 0000000000000000000000000000000000000000000000000000000000000001
Arg [16] : 000000000000000000000000d88e7d30f7548b7a7c6bfe513629724916449e6d
Arg [17] : 0000000000000000000000000000000000000000000000000000000000000001
Arg [18] : 0000000000000000000000000000000000000000000000004ba24a1fe9e10000


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading
[ Download: CSV Export  ]

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.