ETH Price: $2,272.44 (-4.68%)

Token

Unagii Wrapped Ether Vault v3 (uWETHv3)
 

Overview

Max Total Supply

55.573454223189319713 uWETHv3

Holders

13

Market

Onchain Market Cap

$0.00

Circulating Supply Market Cap

-

Other Info

Token Contract (WITH 18 Decimals)

Filtered by Token Holder
Null: 0x000...000
Balance
0 uWETHv3

Value
$0.00
0x0000000000000000000000000000000000000000
Loading...
Loading
Loading...
Loading
Loading...
Loading

Click here to update the token information / general information
# Exchange Pair Price  24H Volume % Volume

Contract Source Code Verified (Exact Match)

Contract Name:
Vault

Compiler Version
v0.8.9+commit.e5eed63a

Optimization Enabled:
Yes with 10000 runs

Other Settings:
default evmVersion
File 1 of 8 : Vault.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.9;

import 'solmate/tokens/ERC20.sol';
import 'solmate/utils/SafeTransferLib.sol';
import 'solmate/utils/FixedPointMathLib.sol';

import './libraries/Ownership.sol';
import './libraries/BlockDelay.sol';
import './interfaces/IERC4626.sol';
import './Strategy.sol';

contract Vault is ERC20, IERC4626, Ownership, BlockDelay {
	using SafeTransferLib for ERC20;
	using FixedPointMathLib for uint256;

	/// @notice token which the vault uses and accumulates
	ERC20 public immutable asset;

	/// @notice whether deposits and withdrawals are paused
	bool public paused;

	uint256 private _lockedProfit;
	/// @notice timestamp of last report, used for locked profit calculations
	uint256 public lastReport;
	/// @notice period over which profits are gradually unlocked, defense against sandwich attacks
	uint256 public lockedProfitDuration = 24 hours;
	uint256 internal constant MAX_LOCKED_PROFIT_DURATION = 3 days;

	/// @dev maximum user can deposit in a single tx
	uint256 private _maxDeposit = type(uint256).max;

	struct StrategyParams {
		bool added;
		uint256 debt;
		uint256 debtRatio;
	}

	Strategy[] private _queue;
	mapping(Strategy => StrategyParams) public strategies;

	uint8 internal constant MAX_QUEUE_LENGTH = 20;

	uint256 public totalDebt;
	/// @dev proportion of funds kept in vault to facilitate user withdrawals
	uint256 public floatDebtRatio;
	uint256 public totalDebtRatio;
	uint256 internal constant MAX_TOTAL_DEBT_RATIO = 1_000;

	/*//////////////////
	/      Events      /
	//////////////////*/

	event Report(Strategy indexed strategy, uint256 harvested, uint256 gain, uint256 loss);
	event Lend(Strategy indexed strategy, uint256 assets, uint256 slippage);
	event Collect(Strategy indexed strategy, uint256 received, uint256 slippage, uint256 bonus);

	event StrategyAdded(Strategy indexed strategy, uint256 debtRatio);
	event StrategyDebtRatioChanged(Strategy indexed strategy, uint256 newDebtRatio);
	event StrategyRemoved(Strategy indexed strategy);
	event StrategyQueuePositionsSwapped(uint8 i, uint8 j, Strategy indexed newI, Strategy indexed newJ);

	event LockedProfitDurationChanged(uint256 newDuration);
	event MaxDepositChanged(uint256 newMaxDeposit);
	event FloatDebtRatioChanged(uint256 newFloatDebtRatio);

	/*//////////////////
	/      Errors      /
	//////////////////*/

	error Zero();
	error BelowMinimum(uint256);
	error AboveMaximum(uint256);

	error AboveMaxDeposit();

	error AlreadyStrategy();
	error NotStrategy();
	error StrategyDoesNotBelongToQueue();
	error StrategyQueueFull();

	error AlreadyValue();

	error Paused();

	/// @dev e.g. USDC becomes 'Unagii USD Coin Vault v3' and 'uUSDCv3'
	constructor(
		ERC20 _asset,
		uint8 _blockDelay,
		uint256 _floatDebtRatio,
		address _nominatedOwner,
		address _admin,
		address[] memory _authorized
	)
		ERC20(
			string(abi.encodePacked('Unagii ', _asset.name(), ' Vault v3')),
			string(abi.encodePacked('u', _asset.symbol(), 'v3')),
			_asset.decimals()
		)
		Ownership(_nominatedOwner, _admin, _authorized)
		BlockDelay(_blockDelay)
	{
		asset = _asset;
		_setFloatDebtRatio(_floatDebtRatio);
	}

	/*///////////////////////
	/      Public View      /
	///////////////////////*/

	function queue() external view returns (Strategy[] memory) {
		return _queue;
	}

	function totalAssets() public view returns (uint256 assets) {
		return asset.balanceOf(address(this)) + totalDebt;
	}

	function lockedProfit() public view returns (uint256 lockedAssets) {
		uint256 last = lastReport;
		uint256 duration = lockedProfitDuration;

		unchecked {
			// won't overflow since time is nowhere near uint256.max
			if (block.timestamp >= last + duration) return 0;
			// can overflow if _lockedProfit * difference > uint256.max but in practice should never happen
			return _lockedProfit - _lockedProfit.mulDivDown(block.timestamp - last, duration);
		}
	}

	function freeAssets() public view returns (uint256 assets) {
		return totalAssets() - lockedProfit();
	}

	function convertToShares(uint256 _assets) public view returns (uint256 shares) {
		uint256 supply = totalSupply;
		return supply == 0 ? _assets : _assets.mulDivDown(supply, totalAssets());
	}

	function convertToAssets(uint256 _shares) public view returns (uint256 assets) {
		uint256 supply = totalSupply;
		return supply == 0 ? _shares : _shares.mulDivDown(totalAssets(), supply);
	}

	function maxDeposit(address) external view returns (uint256 assets) {
		return _maxDeposit;
	}

	function previewDeposit(uint256 _assets) public view returns (uint256 shares) {
		return convertToShares(_assets);
	}

	function maxMint(address) external view returns (uint256 shares) {
		return convertToShares(_maxDeposit);
	}

	function previewMint(uint256 shares) public view returns (uint256 assets) {
		uint256 supply = totalSupply;
		return supply == 0 ? shares : shares.mulDivUp(totalAssets(), supply);
	}

	function maxWithdraw(address owner) external view returns (uint256 assets) {
		return convertToAssets(balanceOf[owner]);
	}

	function previewWithdraw(uint256 assets) public view returns (uint256 shares) {
		uint256 supply = totalSupply;

		return supply == 0 ? assets : assets.mulDivUp(supply, freeAssets());
	}

	function maxRedeem(address _owner) external view returns (uint256 shares) {
		return balanceOf[_owner];
	}

	function previewRedeem(uint256 shares) public view returns (uint256 assets) {
		uint256 supply = totalSupply;
		return supply == 0 ? shares : shares.mulDivDown(freeAssets(), supply);
	}

	/*////////////////////////////
	/      Public Functions      /
	////////////////////////////*/

	function safeDeposit(
		uint256 _assets,
		address _receiver,
		uint256 _minShares
	) external returns (uint256 shares) {
		shares = deposit(_assets, _receiver);
		if (shares < _minShares) revert BelowMinimum(shares);
	}

	function safeMint(
		uint256 _shares,
		address _receiver,
		uint256 _maxAssets
	) external returns (uint256 assets) {
		assets = mint(_shares, _receiver);
		if (assets > _maxAssets) revert AboveMaximum(assets);
	}

	function safeWithdraw(
		uint256 _assets,
		address _receiver,
		address _owner,
		uint256 _maxShares
	) external returns (uint256 shares) {
		shares = withdraw(_assets, _receiver, _owner);
		if (shares > _maxShares) revert AboveMaximum(shares);
	}

	function safeRedeem(
		uint256 _shares,
		address _receiver,
		address _owner,
		uint256 _minAssets
	) external returns (uint256 assets) {
		assets = redeem(_shares, _receiver, _owner);
		if (assets < _minAssets) revert BelowMinimum(assets);
	}

	/*////////////////////////////////////
	/      ERC4626 Public Functions      /
	////////////////////////////////////*/

	function deposit(uint256 _assets, address _receiver) public whenNotPaused returns (uint256 shares) {
		if ((shares = previewDeposit(_assets)) == 0) revert Zero();

		_deposit(_assets, shares, _receiver);
	}

	function mint(uint256 _shares, address _receiver) public whenNotPaused returns (uint256 assets) {
		if (_shares == 0) revert Zero();
		assets = previewMint(_shares);

		_deposit(assets, _shares, _receiver);
	}

	function withdraw(
		uint256 _assets,
		address _receiver,
		address _owner
	) public returns (uint256 shares) {
		if (_assets == 0) revert Zero();
		shares = previewWithdraw(_assets);

		_withdraw(_assets, shares, _owner, _receiver);
	}

	function redeem(
		uint256 _shares,
		address _receiver,
		address _owner
	) public returns (uint256 assets) {
		if ((assets = previewRedeem(_shares)) == 0) revert Zero();

		return _withdraw(assets, _shares, _owner, _receiver);
	}

	/*///////////////////////////////////////////
	/      Restricted Functions: onlyOwner      /
	///////////////////////////////////////////*/

	function addStrategy(Strategy _strategy, uint256 _debtRatio) external onlyOwner {
		if (_strategy.vault() != this) revert StrategyDoesNotBelongToQueue();
		if (strategies[_strategy].added) revert AlreadyStrategy();
		if (_queue.length >= MAX_QUEUE_LENGTH) revert StrategyQueueFull();

		totalDebtRatio += _debtRatio;
		if (totalDebtRatio > MAX_TOTAL_DEBT_RATIO) revert AboveMaximum(totalDebtRatio);

		strategies[_strategy] = StrategyParams({added: true, debt: 0, debtRatio: _debtRatio});
		_queue.push(_strategy);

		emit StrategyAdded(_strategy, _debtRatio);
	}

	/*////////////////////////////////////////////
	/      Restricted Functions: onlyAdmins      /
	////////////////////////////////////////////*/

	function removeStrategy(
		Strategy _strategy,
		bool _shouldHarvest,
		uint256 _minReceived
	) external onlyAdmins returns (uint256 received) {
		if (!strategies[_strategy].added) revert NotStrategy();

		_setDebtRatio(_strategy, 0);

		uint256 balanceBefore = asset.balanceOf(address(this));

		if (_shouldHarvest) _harvest(_strategy);
		else _report(_strategy, 0);

		received = asset.balanceOf(address(this)) - balanceBefore;

		if (received < _minReceived) revert BelowMinimum(received);

		// reorganize queue, filling in the empty strategy
		Strategy[] memory newQueue = new Strategy[](_queue.length - 1);

		bool found;
		uint8 length = uint8(newQueue.length);
		for (uint8 i = 0; i < length; ++i) {
			if (_queue[i] == _strategy) found = true;

			if (found) newQueue[i] = _queue[i + 1];
			else newQueue[i] = _queue[i];
		}

		delete strategies[_strategy];
		_queue = newQueue;

		emit StrategyRemoved(_strategy);
	}

	function swapQueuePositions(uint8 _i, uint8 _j) external onlyAdmins {
		Strategy s1 = _queue[_i];
		Strategy s2 = _queue[_j];

		_queue[_i] = s2;
		_queue[_j] = s1;

		emit StrategyQueuePositionsSwapped(_i, _j, s2, s1);
	}

	function setDebtRatio(Strategy _strategy, uint256 _newDebtRatio) external onlyAdmins {
		if (!strategies[_strategy].added) revert NotStrategy();
		_setDebtRatio(_strategy, _newDebtRatio);
	}

	/// @dev locked profit duration can be 0
	function setLockedProfitDuration(uint256 _newDuration) external onlyAdmins {
		if (_newDuration > MAX_LOCKED_PROFIT_DURATION) revert AboveMaximum(_newDuration);
		if (_newDuration == lockedProfitDuration) revert AlreadyValue();
		lockedProfitDuration = _newDuration;
		emit LockedProfitDurationChanged(_newDuration);
	}

	function setBlockDelay(uint8 _newDelay) external onlyAdmins {
		_setBlockDelay(_newDelay);
	}

	/*///////////////////////////////////////////////
	/      Restricted Functions: onlyAuthorized     /
	///////////////////////////////////////////////*/

	function suspendStrategy(Strategy _strategy) external onlyAuthorized {
		if (!strategies[_strategy].added) revert NotStrategy();
		_setDebtRatio(_strategy, 0);
	}

	function collectFromStrategy(
		Strategy _strategy,
		uint256 _assets,
		uint256 _minReceived
	) external onlyAuthorized returns (uint256 received) {
		if (!strategies[_strategy].added) revert NotStrategy();
		(received, ) = _collect(_strategy, _assets, address(this));
		if (received < _minReceived) revert BelowMinimum(received);
	}

	function pause() external onlyAuthorized {
		if (paused) revert AlreadyValue();
		paused = true;
	}

	function unpause() external onlyAuthorized {
		if (!paused) revert AlreadyValue();
		paused = false;
	}

	function setMaxDeposit(uint256 _newMaxDeposit) external onlyAuthorized {
		if (_maxDeposit == _newMaxDeposit) revert AlreadyValue();
		_maxDeposit = _newMaxDeposit;
		emit MaxDepositChanged(_newMaxDeposit);
	}

	/// @dev costs less gas than multiple harvests if active strategies > 1
	function harvestAll() external onlyAuthorized updateLastReport {
		uint8 length = uint8(_queue.length);
		for (uint8 i = 0; i < length; ++i) {
			_harvest(_queue[i]);
		}
	}

	/// @dev costs less gas than multiple reports if active strategies > 1
	function reportAll() external onlyAuthorized updateLastReport {
		uint8 length = uint8(_queue.length);
		for (uint8 i = 0; i < length; ++i) {
			_report(_queue[i], 0);
		}
	}

	function harvest(Strategy _strategy) external onlyAuthorized updateLastReport {
		if (!strategies[_strategy].added) revert NotStrategy();

		_harvest(_strategy);
	}

	function report(Strategy _strategy) external onlyAuthorized updateLastReport {
		if (!strategies[_strategy].added) revert NotStrategy();

		_report(_strategy, 0);
	}

	function setFloatDebtRatio(uint256 _floatDebtRatio) external onlyAuthorized {
		_setFloatDebtRatio(_floatDebtRatio);
	}

	/*///////////////////////////////////////////
	/      Internal Override: useBlockDelay     /
	///////////////////////////////////////////*/

	/// @dev address cannot mint/burn/send/receive share tokens on same block, defense against flash loan exploits
	function _mint(address _to, uint256 _amount) internal override useBlockDelay(_to) {
		if (_to == address(0)) revert Zero();
		ERC20._mint(_to, _amount);
	}

	/// @dev address cannot mint/burn/send/receive share tokens on same block, defense against flash loan exploits
	function _burn(address _from, uint256 _amount) internal override useBlockDelay(_from) {
		ERC20._burn(_from, _amount);
	}

	/// @dev address cannot mint/burn/send/receive share tokens on same block, defense against flash loan exploits
	function transfer(address _to, uint256 _amount)
		public
		override
		useBlockDelay(msg.sender)
		useBlockDelay(_to)
		returns (bool)
	{
		return ERC20.transfer(_to, _amount);
	}

	/// @dev address cannot mint/burn/send/receive share tokens on same block, defense against flash loan exploits
	function transferFrom(
		address _from,
		address _to,
		uint256 _amount
	) public override useBlockDelay(_from) useBlockDelay(_to) returns (bool) {
		return ERC20.transferFrom(_from, _to, _amount);
	}

	/*//////////////////////////////
	/      Internal Functions      /
	//////////////////////////////*/

	function _deposit(
		uint256 _assets,
		uint256 _shares,
		address _receiver
	) internal {
		if (_assets > _maxDeposit) revert AboveMaxDeposit();

		asset.safeTransferFrom(msg.sender, address(this), _assets);
		_mint(_receiver, _shares);
		emit Deposit(msg.sender, _receiver, _assets, _shares);
	}

	function _withdraw(
		uint256 _assets,
		uint256 _shares,
		address _owner,
		address _receiver
	) internal returns (uint256 received) {
		if (msg.sender != _owner) {
			uint256 allowed = allowance[_owner][msg.sender];
			if (allowed != type(uint256).max) allowance[_owner][msg.sender] = allowed - _shares;
		}

		_burn(_owner, _shares);

		emit Withdraw(msg.sender, _receiver, _owner, _assets, _shares);

		// first, withdraw from balance
		uint256 balance = asset.balanceOf(address(this));

		if (balance > 0) {
			uint256 amount = _assets > balance ? balance : _assets;
			asset.safeTransfer(_receiver, amount);
			_assets -= amount;
			received += amount;
		}

		// next, withdraw from strategies
		uint8 length = uint8(_queue.length);
		for (uint8 i = 0; i < length; ++i) {
			if (_assets == 0) break;
			(uint256 receivedFromStrategy, uint256 slippage) = _collect(_queue[i], _assets, _receiver);
			_assets -= receivedFromStrategy + slippage; // user pays for slippage, if any
			received += receivedFromStrategy;
		}
	}

	/// @dev do not touch debt outside of _lend(), _collect() and _report()
	function _lend(Strategy _strategy, uint256 _assets) internal {
		uint256 balance = asset.balanceOf(address(this));
		uint256 amount = _assets > balance ? balance : _assets;

		asset.safeTransfer(address(_strategy), amount);
		_strategy.invest();

		uint256 debtBefore = strategies[_strategy].debt;
		uint256 debtAfter = _strategy.totalAssets();

		uint256 diff = debtAfter - debtBefore;

		uint256 slippage = amount > diff ? amount - diff : 0;

		// ignore bonus if diff > amount, safeguard against imprecise `strategy.totalAsset()` calculations that open vault to being drained
		uint256 debt = amount - slippage;

		strategies[_strategy].debt += debt;
		totalDebt += debt;

		emit Lend(_strategy, amount, slippage);
	}

	function _collect(
		Strategy _strategy,
		uint256 _assets,
		address _receiver
	) internal returns (uint256 received, uint256 slippage) {
		uint256 bonus;
		(received, slippage, bonus) = _strategy.withdraw(_assets);

		uint256 total = received + slippage;

		uint256 debt = strategies[_strategy].debt;

		uint256 amount = debt > total ? received : total;

		strategies[_strategy].debt -= amount;
		totalDebt -= amount;

		// do not pass bonuses on to users withdrawing, prevents exploits draining vault
		if (_receiver == address(this)) emit Collect(_strategy, received, slippage, bonus);
		else asset.safeTransfer(_receiver, received);
	}

	function _harvest(Strategy _strategy) internal {
		_report(_strategy, _strategy.harvest());
	}

	/// @dev do not touch debt outside of _lend(), _collect() and _report()
	function _report(Strategy _strategy, uint256 _harvested) internal {
		uint256 assets = _strategy.totalAssets();
		uint256 debt = strategies[_strategy].debt;

		strategies[_strategy].debt = assets; // update debt

		uint256 gain;
		uint256 loss;

		uint256 lockedProfitBefore = lockedProfit();

		if (assets > debt) {
			unchecked {
				gain = assets - debt;
			}
			totalDebt += gain;

			_lockedProfit = lockedProfitBefore + gain + _harvested;
		} else if (debt > assets) {
			unchecked {
				loss = debt - assets;
				totalDebt -= loss;

				_lockedProfit = lockedProfitBefore + _harvested > loss ? lockedProfitBefore + _harvested - loss : 0;
			}
		}

		uint256 possibleDebt = totalDebtRatio == 0
			? 0
			: totalAssets().mulDivDown(strategies[_strategy].debtRatio, totalDebtRatio);

		if (possibleDebt > assets) _lend(_strategy, possibleDebt - assets);
		else if (assets > possibleDebt) _collect(_strategy, assets - possibleDebt, address(this));

		emit Report(_strategy, _harvested, gain, loss);
	}

	function _setDebtRatio(Strategy _strategy, uint256 _newDebtRatio) internal {
		uint256 currentDebtRatio = strategies[_strategy].debtRatio;
		if (_newDebtRatio == currentDebtRatio) revert AlreadyValue();

		uint256 newTotalDebtRatio = totalDebtRatio + _newDebtRatio - currentDebtRatio;
		if (newTotalDebtRatio > MAX_TOTAL_DEBT_RATIO) revert AboveMaximum(newTotalDebtRatio);

		strategies[_strategy].debtRatio = _newDebtRatio;
		totalDebtRatio = newTotalDebtRatio;

		emit StrategyDebtRatioChanged(_strategy, _newDebtRatio);
	}

	function _setFloatDebtRatio(uint256 _floatDebtRatio) internal {
		uint256 newTotalDebtRatio = totalDebtRatio + _floatDebtRatio - floatDebtRatio;
		if (newTotalDebtRatio > MAX_TOTAL_DEBT_RATIO) revert AboveMaximum(newTotalDebtRatio);

		floatDebtRatio = _floatDebtRatio;
		totalDebtRatio = newTotalDebtRatio;

		emit FloatDebtRatioChanged(_floatDebtRatio);
	}

	/*/////////////////////
	/      Modifiers      /
	/////////////////////*/

	modifier updateLastReport() {
		_;
		lastReport = block.timestamp;
	}

	modifier whenNotPaused() {
		if (paused) revert Paused();
		_;
	}
}

File 2 of 8 : ERC20.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

/// @notice Modern and gas efficient ERC20 + EIP-2612 implementation.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol)
/// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)
/// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.
abstract contract ERC20 {
    /*//////////////////////////////////////////////////////////////
                                 EVENTS
    //////////////////////////////////////////////////////////////*/

    event Transfer(address indexed from, address indexed to, uint256 amount);

    event Approval(address indexed owner, address indexed spender, uint256 amount);

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

    string public name;

    string public symbol;

    uint8 public immutable decimals;

    /*//////////////////////////////////////////////////////////////
                              ERC20 STORAGE
    //////////////////////////////////////////////////////////////*/

    uint256 public totalSupply;

    mapping(address => uint256) public balanceOf;

    mapping(address => mapping(address => uint256)) public allowance;

    /*//////////////////////////////////////////////////////////////
                            EIP-2612 STORAGE
    //////////////////////////////////////////////////////////////*/

    uint256 internal immutable INITIAL_CHAIN_ID;

    bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;

    mapping(address => uint256) public nonces;

    /*//////////////////////////////////////////////////////////////
                               CONSTRUCTOR
    //////////////////////////////////////////////////////////////*/

    constructor(
        string memory _name,
        string memory _symbol,
        uint8 _decimals
    ) {
        name = _name;
        symbol = _symbol;
        decimals = _decimals;

        INITIAL_CHAIN_ID = block.chainid;
        INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
    }

    /*//////////////////////////////////////////////////////////////
                               ERC20 LOGIC
    //////////////////////////////////////////////////////////////*/

    function approve(address spender, uint256 amount) public virtual returns (bool) {
        allowance[msg.sender][spender] = amount;

        emit Approval(msg.sender, spender, amount);

        return true;
    }

    function transfer(address to, uint256 amount) public virtual returns (bool) {
        balanceOf[msg.sender] -= amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

        emit Transfer(msg.sender, to, amount);

        return true;
    }

    function transferFrom(
        address from,
        address to,
        uint256 amount
    ) public virtual returns (bool) {
        uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.

        if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;

        balanceOf[from] -= amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

        emit Transfer(from, to, amount);

        return true;
    }

    /*//////////////////////////////////////////////////////////////
                             EIP-2612 LOGIC
    //////////////////////////////////////////////////////////////*/

    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) public virtual {
        require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");

        // Unchecked because the only math done is incrementing
        // the owner's nonce which cannot realistically overflow.
        unchecked {
            address recoveredAddress = ecrecover(
                keccak256(
                    abi.encodePacked(
                        "\x19\x01",
                        DOMAIN_SEPARATOR(),
                        keccak256(
                            abi.encode(
                                keccak256(
                                    "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
                                ),
                                owner,
                                spender,
                                value,
                                nonces[owner]++,
                                deadline
                            )
                        )
                    )
                ),
                v,
                r,
                s
            );

            require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");

            allowance[recoveredAddress][spender] = value;
        }

        emit Approval(owner, spender, value);
    }

    function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
        return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
    }

    function computeDomainSeparator() internal view virtual returns (bytes32) {
        return
            keccak256(
                abi.encode(
                    keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
                    keccak256(bytes(name)),
                    keccak256("1"),
                    block.chainid,
                    address(this)
                )
            );
    }

    /*//////////////////////////////////////////////////////////////
                        INTERNAL MINT/BURN LOGIC
    //////////////////////////////////////////////////////////////*/

    function _mint(address to, uint256 amount) internal virtual {
        totalSupply += amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

        emit Transfer(address(0), to, amount);
    }

    function _burn(address from, uint256 amount) internal virtual {
        balanceOf[from] -= amount;

        // Cannot underflow because a user's balance
        // will never be larger than the total supply.
        unchecked {
            totalSupply -= amount;
        }

        emit Transfer(from, address(0), amount);
    }
}

File 3 of 8 : FixedPointMathLib.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

/// @notice Arithmetic library with operations for fixed-point numbers.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/FixedPointMathLib.sol)
/// @author Inspired by USM (https://github.com/usmfum/USM/blob/master/contracts/WadMath.sol)
library FixedPointMathLib {
    /*//////////////////////////////////////////////////////////////
                    SIMPLIFIED FIXED POINT OPERATIONS
    //////////////////////////////////////////////////////////////*/

    uint256 internal constant MAX_UINT256 = 2**256 - 1;

    uint256 internal constant WAD = 1e18; // The scalar of ETH and most ERC20s.

    function mulWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivDown(x, y, WAD); // Equivalent to (x * y) / WAD rounded down.
    }

    function mulWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivUp(x, y, WAD); // Equivalent to (x * y) / WAD rounded up.
    }

    function divWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivDown(x, WAD, y); // Equivalent to (x * WAD) / y rounded down.
    }

    function divWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivUp(x, WAD, y); // Equivalent to (x * WAD) / y rounded up.
    }

    /*//////////////////////////////////////////////////////////////
                    LOW LEVEL FIXED POINT OPERATIONS
    //////////////////////////////////////////////////////////////*/

    function mulDivDown(
        uint256 x,
        uint256 y,
        uint256 denominator
    ) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y))
            if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) {
                revert(0, 0)
            }

            // Divide x * y by the denominator.
            z := div(mul(x, y), denominator)
        }
    }

    function mulDivUp(
        uint256 x,
        uint256 y,
        uint256 denominator
    ) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y))
            if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) {
                revert(0, 0)
            }

            // If x * y modulo the denominator is strictly greater than 0,
            // 1 is added to round up the division of x * y by the denominator.
            z := add(gt(mod(mul(x, y), denominator), 0), div(mul(x, y), denominator))
        }
    }

    function rpow(
        uint256 x,
        uint256 n,
        uint256 scalar
    ) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            switch x
            case 0 {
                switch n
                case 0 {
                    // 0 ** 0 = 1
                    z := scalar
                }
                default {
                    // 0 ** n = 0
                    z := 0
                }
            }
            default {
                switch mod(n, 2)
                case 0 {
                    // If n is even, store scalar in z for now.
                    z := scalar
                }
                default {
                    // If n is odd, store x in z for now.
                    z := x
                }

                // Shifting right by 1 is like dividing by 2.
                let half := shr(1, scalar)

                for {
                    // Shift n right by 1 before looping to halve it.
                    n := shr(1, n)
                } n {
                    // Shift n right by 1 each iteration to halve it.
                    n := shr(1, n)
                } {
                    // Revert immediately if x ** 2 would overflow.
                    // Equivalent to iszero(eq(div(xx, x), x)) here.
                    if shr(128, x) {
                        revert(0, 0)
                    }

                    // Store x squared.
                    let xx := mul(x, x)

                    // Round to the nearest number.
                    let xxRound := add(xx, half)

                    // Revert if xx + half overflowed.
                    if lt(xxRound, xx) {
                        revert(0, 0)
                    }

                    // Set x to scaled xxRound.
                    x := div(xxRound, scalar)

                    // If n is even:
                    if mod(n, 2) {
                        // Compute z * x.
                        let zx := mul(z, x)

                        // If z * x overflowed:
                        if iszero(eq(div(zx, x), z)) {
                            // Revert if x is non-zero.
                            if iszero(iszero(x)) {
                                revert(0, 0)
                            }
                        }

                        // Round to the nearest number.
                        let zxRound := add(zx, half)

                        // Revert if zx + half overflowed.
                        if lt(zxRound, zx) {
                            revert(0, 0)
                        }

                        // Return properly scaled zxRound.
                        z := div(zxRound, scalar)
                    }
                }
            }
        }
    }

    /*//////////////////////////////////////////////////////////////
                        GENERAL NUMBER UTILITIES
    //////////////////////////////////////////////////////////////*/

    function sqrt(uint256 x) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            let y := x // We start y at x, which will help us make our initial estimate.

            z := 181 // The "correct" value is 1, but this saves a multiplication later.

            // This segment is to get a reasonable initial estimate for the Babylonian method. With a bad
            // start, the correct # of bits increases ~linearly each iteration instead of ~quadratically.

            // We check y >= 2^(k + 8) but shift right by k bits
            // each branch to ensure that if x >= 256, then y >= 256.
            if iszero(lt(y, 0x10000000000000000000000000000000000)) {
                y := shr(128, y)
                z := shl(64, z)
            }
            if iszero(lt(y, 0x1000000000000000000)) {
                y := shr(64, y)
                z := shl(32, z)
            }
            if iszero(lt(y, 0x10000000000)) {
                y := shr(32, y)
                z := shl(16, z)
            }
            if iszero(lt(y, 0x1000000)) {
                y := shr(16, y)
                z := shl(8, z)
            }

            // Goal was to get z*z*y within a small factor of x. More iterations could
            // get y in a tighter range. Currently, we will have y in [256, 256*2^16).
            // We ensured y >= 256 so that the relative difference between y and y+1 is small.
            // That's not possible if x < 256 but we can just verify those cases exhaustively.

            // Now, z*z*y <= x < z*z*(y+1), and y <= 2^(16+8), and either y >= 256, or x < 256.
            // Correctness can be checked exhaustively for x < 256, so we assume y >= 256.
            // Then z*sqrt(y) is within sqrt(257)/sqrt(256) of sqrt(x), or about 20bps.

            // For s in the range [1/256, 256], the estimate f(s) = (181/1024) * (s+1) is in the range
            // (1/2.84 * sqrt(s), 2.84 * sqrt(s)), with largest error when s = 1 and when s = 256 or 1/256.

            // Since y is in [256, 256*2^16), let a = y/65536, so that a is in [1/256, 256). Then we can estimate
            // sqrt(y) using sqrt(65536) * 181/1024 * (a + 1) = 181/4 * (y + 65536)/65536 = 181 * (y + 65536)/2^18.

            // There is no overflow risk here since y < 2^136 after the first branch above.
            z := shr(18, mul(z, add(y, 65536))) // A mul() is saved from starting z at 181.

            // Given the worst case multiplicative error of 2.84 above, 7 iterations should be enough.
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))

            // If x+1 is a perfect square, the Babylonian method cycles between
            // floor(sqrt(x)) and ceil(sqrt(x)). This statement ensures we return floor.
            // See: https://en.wikipedia.org/wiki/Integer_square_root#Using_only_integer_division
            // Since the ceil is rare, we save gas on the assignment and repeat division in the rare case.
            // If you don't care whether the floor or ceil square root is returned, you can remove this statement.
            z := sub(z, lt(div(x, z), z))
        }
    }

    function unsafeMod(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Mod x by y. Note this will return
            // 0 instead of reverting if y is zero.
            z := mod(x, y)
        }
    }

    function unsafeDiv(uint256 x, uint256 y) internal pure returns (uint256 r) {
        /// @solidity memory-safe-assembly
        assembly {
            // Divide x by y. Note this will return
            // 0 instead of reverting if y is zero.
            r := div(x, y)
        }
    }

    function unsafeDivUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Add 1 to x * y if x % y > 0. Note this will
            // return 0 instead of reverting if y is zero.
            z := add(gt(mod(x, y), 0), div(x, y))
        }
    }
}

File 4 of 8 : SafeTransferLib.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

import {ERC20} from "../tokens/ERC20.sol";

/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
/// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer.
/// @dev Note that none of the functions in this library check that a token has code at all! That responsibility is delegated to the caller.
library SafeTransferLib {
    /*//////////////////////////////////////////////////////////////
                             ETH OPERATIONS
    //////////////////////////////////////////////////////////////*/

    function safeTransferETH(address to, uint256 amount) internal {
        bool success;

        /// @solidity memory-safe-assembly
        assembly {
            // Transfer the ETH and store if it succeeded or not.
            success := call(gas(), to, amount, 0, 0, 0, 0)
        }

        require(success, "ETH_TRANSFER_FAILED");
    }

    /*//////////////////////////////////////////////////////////////
                            ERC20 OPERATIONS
    //////////////////////////////////////////////////////////////*/

    function safeTransferFrom(
        ERC20 token,
        address from,
        address to,
        uint256 amount
    ) internal {
        bool success;

        /// @solidity memory-safe-assembly
        assembly {
            // Get a pointer to some free memory.
            let freeMemoryPointer := mload(0x40)

            // Write the abi-encoded calldata into memory, beginning with the function selector.
            mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000)
            mstore(add(freeMemoryPointer, 4), from) // Append the "from" argument.
            mstore(add(freeMemoryPointer, 36), to) // Append the "to" argument.
            mstore(add(freeMemoryPointer, 68), amount) // Append the "amount" argument.

            success := and(
                // Set success to whether the call reverted, if not we check it either
                // returned exactly 1 (can't just be non-zero data), or had no return data.
                or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                // We use 100 because the length of our calldata totals up like so: 4 + 32 * 3.
                // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                // Counterintuitively, this call must be positioned second to the or() call in the
                // surrounding and() call or else returndatasize() will be zero during the computation.
                call(gas(), token, 0, freeMemoryPointer, 100, 0, 32)
            )
        }

        require(success, "TRANSFER_FROM_FAILED");
    }

    function safeTransfer(
        ERC20 token,
        address to,
        uint256 amount
    ) internal {
        bool success;

        /// @solidity memory-safe-assembly
        assembly {
            // Get a pointer to some free memory.
            let freeMemoryPointer := mload(0x40)

            // Write the abi-encoded calldata into memory, beginning with the function selector.
            mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)
            mstore(add(freeMemoryPointer, 4), to) // Append the "to" argument.
            mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument.

            success := and(
                // Set success to whether the call reverted, if not we check it either
                // returned exactly 1 (can't just be non-zero data), or had no return data.
                or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
                // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                // Counterintuitively, this call must be positioned second to the or() call in the
                // surrounding and() call or else returndatasize() will be zero during the computation.
                call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
            )
        }

        require(success, "TRANSFER_FAILED");
    }

    function safeApprove(
        ERC20 token,
        address to,
        uint256 amount
    ) internal {
        bool success;

        /// @solidity memory-safe-assembly
        assembly {
            // Get a pointer to some free memory.
            let freeMemoryPointer := mload(0x40)

            // Write the abi-encoded calldata into memory, beginning with the function selector.
            mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000)
            mstore(add(freeMemoryPointer, 4), to) // Append the "to" argument.
            mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument.

            success := and(
                // Set success to whether the call reverted, if not we check it either
                // returned exactly 1 (can't just be non-zero data), or had no return data.
                or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
                // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                // Counterintuitively, this call must be positioned second to the or() call in the
                // surrounding and() call or else returndatasize() will be zero during the computation.
                call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
            )
        }

        require(success, "APPROVE_FAILED");
    }
}

File 5 of 8 : Strategy.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.9;

import './Vault.sol';

/** @dev
 * Strategies have to implement the following virtual functions:
 *
 * totalAssets()
 * _withdraw(uint256, address)
 * _harvest()
 * _invest()
 */
abstract contract Strategy is Ownership {
	using SafeTransferLib for ERC20;
	using FixedPointMathLib for uint256;

	Vault public immutable vault;
	ERC20 public immutable asset;

	/// @notice address which performance fees are sent to
	address public treasury;
	/// @notice performance fee sent to treasury / FEE_BASIS of 10_000
	uint16 public fee = 1_000;
	uint16 internal constant MAX_FEE = 1_000;
	uint16 internal constant FEE_BASIS = 10_000;

	/// @notice used to calculate slippage / SLIP_BASIS of 10_000
	/// @dev default to 99% (or 1%)
	uint16 public slip = 9_900;
	uint16 internal constant SLIP_BASIS = 10_000;

	/*//////////////////
	/      Events      /
	//////////////////*/

	event FeeChanged(uint16 newFee);
	event SlipChanged(uint16 newSlip);
	event TreasuryChanged(address indexed newTreasury);

	/*//////////////////
	/      Errors      /
	//////////////////*/

	error Zero();
	error NotVault();
	error InvalidValue();
	error AlreadyValue();

	constructor(
		Vault _vault,
		address _treasury,
		address _nominatedOwner,
		address _admin,
		address[] memory _authorized
	) Ownership(_nominatedOwner, _admin, _authorized) {
		vault = _vault;
		asset = vault.asset();
		treasury = _treasury;
	}

	/*//////////////////////////
	/      Public Virtual      /
	//////////////////////////*/

	/// @notice amount of 'asset' currently managed by strategy
	function totalAssets() public view virtual returns (uint256);

	/*///////////////////////////////////////////
	/      Restricted Functions: onlyVault      /
	///////////////////////////////////////////*/

	function withdraw(uint256 _assets)
		external
		onlyVault
		returns (
			uint256 received,
			uint256 slippage,
			uint256 bonus
		)
	{
		uint256 total = totalAssets();
		if (total == 0) revert Zero();

		uint256 assets = _assets > total ? total : _assets;

		received = _withdraw(assets);

		unchecked {
			if (assets > received) slippage = assets - received;
			else if (received > assets) {
				bonus = received - assets;
				// received cannot > assets for vault calcuations
				received = assets;
			}
		}
	}

	/*//////////////////////////////////////////////////
	/      Restricted Functions: onlyAdminOrVault      /
	//////////////////////////////////////////////////*/

	function harvest() external onlyAdminOrVault returns (uint256 received) {
		_harvest();

		received = asset.balanceOf(address(this));

		if (fee > 0) {
			uint256 feeAmount = _calculateFee(received);
			received -= feeAmount;
			asset.safeTransfer(treasury, feeAmount);
		}

		asset.safeTransfer(address(vault), received);
	}

	function invest() external onlyAdminOrVault {
		_invest();
	}

	/*///////////////////////////////////////////
	/      Restricted Functions: onlyOwner      /
	///////////////////////////////////////////*/

	function setFee(uint16 _fee) external onlyOwner {
		if (_fee > MAX_FEE) revert InvalidValue();
		if (_fee == fee) revert AlreadyValue();
		fee = _fee;
		emit FeeChanged(_fee);
	}

	function setTreasury(address _treasury) external onlyOwner {
		if (_treasury == treasury) revert AlreadyValue();
		treasury = _treasury;
		emit TreasuryChanged(_treasury);
	}

	/*////////////////////////////////////////////
	/      Restricted Functions: onlyAdmins      /
	////////////////////////////////////////////*/

	function setSlip(uint16 _slip) external onlyAdmins {
		if (_slip > SLIP_BASIS) revert InvalidValue();
		if (_slip == slip) revert AlreadyValue();
		slip = _slip;
		emit SlipChanged(_slip);
	}

	/*////////////////////////////
	/      Internal Virtual      /
	////////////////////////////*/

	function _withdraw(uint256 _assets) internal virtual returns (uint256 received);

	/// @dev return harvested assets
	function _harvest() internal virtual;

	function _invest() internal virtual;

	/*//////////////////////////////
	/      Internal Functions      /
	//////////////////////////////*/

	function _calculateSlippage(uint256 _amount) internal view returns (uint256) {
		return _amount.mulDivDown(slip, SLIP_BASIS);
	}

	function _calculateFee(uint256 _amount) internal view returns (uint256) {
		return _amount.mulDivDown(fee, FEE_BASIS);
	}

	modifier onlyVault() {
		if (msg.sender != address(vault)) revert NotVault();
		_;
	}

	/*//////////////////////////////
	/      Internal Functions      /
	//////////////////////////////*/

	modifier onlyAdminOrVault() {
		if (msg.sender != owner && msg.sender != admin && msg.sender != address(vault)) revert Unauthorized();
		_;
	}
}

File 6 of 8 : IERC4626.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.9;

import 'solmate/tokens/ERC20.sol';

/// @notice https://eips.ethereum.org/EIPS/eip-4626
interface IERC4626 {
	event Deposit(address indexed caller, address indexed owner, uint256 assets, uint256 shares);

	event Withdraw(
		address indexed caller,
		address indexed receiver,
		address indexed owner,
		uint256 assets,
		uint256 shares
	);

	function asset() external view returns (ERC20);

	function totalAssets() external view returns (uint256 assets);

	function convertToShares(uint256 assets) external view returns (uint256 shares);

	function convertToAssets(uint256 shares) external view returns (uint256 assets);

	function maxDeposit(address receiver) external view returns (uint256 assets);

	function previewDeposit(uint256 assets) external view returns (uint256 shares);

	function deposit(uint256 assets, address receiver) external returns (uint256 shares);

	function maxMint(address receiver) external view returns (uint256 shares);

	function previewMint(uint256 shares) external view returns (uint256 assets);

	function mint(uint256 shares, address receiver) external returns (uint256 assets);

	function maxWithdraw(address owner) external view returns (uint256 assets);

	function previewWithdraw(uint256 assets) external view returns (uint256 shares);

	function withdraw(
		uint256 assets,
		address receiver,
		address owner
	) external returns (uint256 shares);

	function maxRedeem(address owner) external view returns (uint256 shares);

	function previewRedeem(uint256 shares) external view returns (uint256 assets);

	function redeem(
		uint256 shares,
		address receiver,
		address owner
	) external returns (uint256 assets);
}

File 7 of 8 : BlockDelay.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.9;

abstract contract BlockDelay {
	/// @notice delay before functions with 'useBlockDelay' can be called by the same address
	/// @dev 0 means no delay
	uint256 public blockDelay;
	uint256 internal constant MAX_BLOCK_DELAY = 10;

	mapping(address => uint256) public lastBlock;

	error AboveMaxBlockDelay();
	error BeforeBlockDelay();

	constructor(uint8 _delay) {
		_setBlockDelay(_delay);
	}

	function _setBlockDelay(uint8 _newDelay) internal {
		if (_newDelay > MAX_BLOCK_DELAY) revert AboveMaxBlockDelay();
		blockDelay = _newDelay;
	}

	modifier useBlockDelay(address _address) {
		if (block.number < lastBlock[_address] + blockDelay) revert BeforeBlockDelay();
		lastBlock[_address] = block.number;
		_;
	}
}

File 8 of 8 : Ownership.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.9;

abstract contract Ownership {
	address public owner;
	address public nominatedOwner;

	address public admin;

	mapping(address => bool) public authorized;

	event OwnerChanged(address indexed previousOwner, address indexed newOwner);
	event AuthAdded(address indexed newAuth);
	event AuthRemoved(address indexed oldAuth);

	error Unauthorized();
	error AlreadyRole();
	error NotRole();

	/// @param _authorized maximum of 256 addresses in constructor
	constructor(
		address _nominatedOwner,
		address _admin,
		address[] memory _authorized
	) {
		owner = msg.sender;
		nominatedOwner = _nominatedOwner;
		admin = _admin;
		for (uint8 i = 0; i < _authorized.length; ++i) {
			authorized[_authorized[i]] = true;
			emit AuthAdded(_authorized[i]);
		}
	}

	// Public Functions

	function acceptOwnership() external {
		if (msg.sender != nominatedOwner) revert Unauthorized();
		emit OwnerChanged(owner, msg.sender);
		owner = msg.sender;
		nominatedOwner = address(0);
	}

	// Restricted Functions: onlyOwner

	/// @dev nominating zero address revokes a pending nomination
	function nominateOwnership(address _newOwner) external onlyOwner {
		nominatedOwner = _newOwner;
	}

	function setAdmin(address _newAdmin) external onlyOwner {
		if (admin == _newAdmin) revert AlreadyRole();
		admin = _newAdmin;
	}

	// Restricted Functions: onlyAdmins

	function addAuthorized(address _authorized) external onlyAdmins {
		if (authorized[_authorized]) revert AlreadyRole();
		authorized[_authorized] = true;
		emit AuthAdded(_authorized);
	}

	function removeAuthorized(address _authorized) external onlyAdmins {
		if (!authorized[_authorized]) revert NotRole();
		authorized[_authorized] = false;
		emit AuthRemoved(_authorized);
	}

	// Modifiers

	modifier onlyOwner() {
		if (msg.sender != owner) revert Unauthorized();
		_;
	}

	modifier onlyAdmins() {
		if (msg.sender != owner && msg.sender != admin) revert Unauthorized();
		_;
	}

	modifier onlyAuthorized() {
		if (msg.sender != owner && msg.sender != admin && !authorized[msg.sender]) revert Unauthorized();
		_;
	}
}

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

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"contract ERC20","name":"_asset","type":"address"},{"internalType":"uint8","name":"_blockDelay","type":"uint8"},{"internalType":"uint256","name":"_floatDebtRatio","type":"uint256"},{"internalType":"address","name":"_nominatedOwner","type":"address"},{"internalType":"address","name":"_admin","type":"address"},{"internalType":"address[]","name":"_authorized","type":"address[]"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AboveMaxBlockDelay","type":"error"},{"inputs":[],"name":"AboveMaxDeposit","type":"error"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"AboveMaximum","type":"error"},{"inputs":[],"name":"AlreadyRole","type":"error"},{"inputs":[],"name":"AlreadyStrategy","type":"error"},{"inputs":[],"name":"AlreadyValue","type":"error"},{"inputs":[],"name":"BeforeBlockDelay","type":"error"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"BelowMinimum","type":"error"},{"inputs":[],"name":"NotRole","type":"error"},{"inputs":[],"name":"NotStrategy","type":"error"},{"inputs":[],"name":"Paused","type":"error"},{"inputs":[],"name":"StrategyDoesNotBelongToQueue","type":"error"},{"inputs":[],"name":"StrategyQueueFull","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"inputs":[],"name":"Zero","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"newAuth","type":"address"}],"name":"AuthAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldAuth","type":"address"}],"name":"AuthRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"contract Strategy","name":"strategy","type":"address"},{"indexed":false,"internalType":"uint256","name":"received","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"slippage","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"bonus","type":"uint256"}],"name":"Collect","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint256","name":"assets","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newFloatDebtRatio","type":"uint256"}],"name":"FloatDebtRatioChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"contract Strategy","name":"strategy","type":"address"},{"indexed":false,"internalType":"uint256","name":"assets","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"slippage","type":"uint256"}],"name":"Lend","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newDuration","type":"uint256"}],"name":"LockedProfitDurationChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newMaxDeposit","type":"uint256"}],"name":"MaxDepositChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnerChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"contract Strategy","name":"strategy","type":"address"},{"indexed":false,"internalType":"uint256","name":"harvested","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"gain","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"loss","type":"uint256"}],"name":"Report","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"contract Strategy","name":"strategy","type":"address"},{"indexed":false,"internalType":"uint256","name":"debtRatio","type":"uint256"}],"name":"StrategyAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"contract Strategy","name":"strategy","type":"address"},{"indexed":false,"internalType":"uint256","name":"newDebtRatio","type":"uint256"}],"name":"StrategyDebtRatioChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"i","type":"uint8"},{"indexed":false,"internalType":"uint8","name":"j","type":"uint8"},{"indexed":true,"internalType":"contract Strategy","name":"newI","type":"address"},{"indexed":true,"internalType":"contract Strategy","name":"newJ","type":"address"}],"name":"StrategyQueuePositionsSwapped","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"contract Strategy","name":"strategy","type":"address"}],"name":"StrategyRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint256","name":"assets","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"}],"name":"Withdraw","type":"event"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_authorized","type":"address"}],"name":"addAuthorized","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract Strategy","name":"_strategy","type":"address"},{"internalType":"uint256","name":"_debtRatio","type":"uint256"}],"name":"addStrategy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"admin","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"asset","outputs":[{"internalType":"contract ERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"authorized","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"blockDelay","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract Strategy","name":"_strategy","type":"address"},{"internalType":"uint256","name":"_assets","type":"uint256"},{"internalType":"uint256","name":"_minReceived","type":"uint256"}],"name":"collectFromStrategy","outputs":[{"internalType":"uint256","name":"received","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_shares","type":"uint256"}],"name":"convertToAssets","outputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_assets","type":"uint256"}],"name":"convertToShares","outputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_assets","type":"uint256"},{"internalType":"address","name":"_receiver","type":"address"}],"name":"deposit","outputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"floatDebtRatio","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"freeAssets","outputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract Strategy","name":"_strategy","type":"address"}],"name":"harvest","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"harvestAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"lastBlock","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastReport","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lockedProfit","outputs":[{"internalType":"uint256","name":"lockedAssets","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lockedProfitDuration","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"maxDeposit","outputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"maxMint","outputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"}],"name":"maxRedeem","outputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"maxWithdraw","outputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_shares","type":"uint256"},{"internalType":"address","name":"_receiver","type":"address"}],"name":"mint","outputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_newOwner","type":"address"}],"name":"nominateOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"nominatedOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"nonces","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":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"permit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_assets","type":"uint256"}],"name":"previewDeposit","outputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"name":"previewMint","outputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"name":"previewRedeem","outputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"name":"previewWithdraw","outputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"queue","outputs":[{"internalType":"contract Strategy[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_shares","type":"uint256"},{"internalType":"address","name":"_receiver","type":"address"},{"internalType":"address","name":"_owner","type":"address"}],"name":"redeem","outputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_authorized","type":"address"}],"name":"removeAuthorized","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract Strategy","name":"_strategy","type":"address"},{"internalType":"bool","name":"_shouldHarvest","type":"bool"},{"internalType":"uint256","name":"_minReceived","type":"uint256"}],"name":"removeStrategy","outputs":[{"internalType":"uint256","name":"received","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract Strategy","name":"_strategy","type":"address"}],"name":"report","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"reportAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_assets","type":"uint256"},{"internalType":"address","name":"_receiver","type":"address"},{"internalType":"uint256","name":"_minShares","type":"uint256"}],"name":"safeDeposit","outputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_shares","type":"uint256"},{"internalType":"address","name":"_receiver","type":"address"},{"internalType":"uint256","name":"_maxAssets","type":"uint256"}],"name":"safeMint","outputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_shares","type":"uint256"},{"internalType":"address","name":"_receiver","type":"address"},{"internalType":"address","name":"_owner","type":"address"},{"internalType":"uint256","name":"_minAssets","type":"uint256"}],"name":"safeRedeem","outputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_assets","type":"uint256"},{"internalType":"address","name":"_receiver","type":"address"},{"internalType":"address","name":"_owner","type":"address"},{"internalType":"uint256","name":"_maxShares","type":"uint256"}],"name":"safeWithdraw","outputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newAdmin","type":"address"}],"name":"setAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"_newDelay","type":"uint8"}],"name":"setBlockDelay","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract Strategy","name":"_strategy","type":"address"},{"internalType":"uint256","name":"_newDebtRatio","type":"uint256"}],"name":"setDebtRatio","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_floatDebtRatio","type":"uint256"}],"name":"setFloatDebtRatio","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_newDuration","type":"uint256"}],"name":"setLockedProfitDuration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_newMaxDeposit","type":"uint256"}],"name":"setMaxDeposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract Strategy","name":"","type":"address"}],"name":"strategies","outputs":[{"internalType":"bool","name":"added","type":"bool"},{"internalType":"uint256","name":"debt","type":"uint256"},{"internalType":"uint256","name":"debtRatio","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract Strategy","name":"_strategy","type":"address"}],"name":"suspendStrategy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"_i","type":"uint8"},{"internalType":"uint8","name":"_j","type":"uint8"}],"name":"swapQueuePositions","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalAssets","outputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalDebt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalDebtRatio","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_from","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_assets","type":"uint256"},{"internalType":"address","name":"_receiver","type":"address"},{"internalType":"address","name":"_owner","type":"address"}],"name":"withdraw","outputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"stateMutability":"nonpayable","type":"function"}]

61010060405262015180600f556000196010553480156200001f57600080fd5b5060405162004eae38038062004eae8339810160408190526200004291620005f7565b84838383896001600160a01b03166306fdde036040518163ffffffff1660e01b815260040160006040518083038186803b1580156200008057600080fd5b505afa15801562000095573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052620000bf919081019062000745565b604051602001620000d19190620007e3565b6040516020818303038152906040528a6001600160a01b03166395d89b416040518163ffffffff1660e01b815260040160006040518083038186803b1580156200011a57600080fd5b505afa1580156200012f573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405262000159919081019062000745565b6040516020016200016b919062000827565b6040516020818303038152906040528b6001600160a01b031663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b158015620001b457600080fd5b505afa158015620001c9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620001ef91906200085e565b825162000204906000906020860190620004d8565b5081516200021a906001906020850190620004d8565b5060ff81166080524660a052620002306200037f565b60c052505060068054336001600160a01b0319918216179091556007805482166001600160a01b0387811691909117909155600880549092169085161790555060005b81518160ff1610156200034557600160096000848460ff16815181106200029e576200029e62000883565b60200260200101516001600160a01b03166001600160a01b0316815260200190815260200160002060006101000a81548160ff021916908315150217905550818160ff1681518110620002f557620002f562000883565b60200260200101516001600160a01b03167f5daa78f70ec2227622bb7db0a1d9e750860491853de15431e1e473d305381c2160405160405180910390a26200033d81620008af565b905062000273565b505050506200035a816200041b60201b60201c565b506001600160a01b03861660e052620003738462000449565b505050505050620009e8565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f6000604051620003b391906200090f565b6040805191829003822060208301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b600a8160ff161115620004415760405163d1056e4f60e01b815260040160405180910390fd5b60ff16600a55565b6000601454826015546200045e9190620009b3565b6200046a9190620009ce565b90506103e88111156200049757604051638a5c4d8360e01b81526004810182905260240160405180910390fd5b601482905560158190556040518281527fc370b12b9c3b502102003c0ea4f4313711f71410155879e860f23eb00f1272be9060200160405180910390a15050565b828054620004e690620008d2565b90600052602060002090601f0160209004810192826200050a576000855562000555565b82601f106200052557805160ff191683800117855562000555565b8280016001018555821562000555579182015b828111156200055557825182559160200191906001019062000538565b506200056392915062000567565b5090565b5b8082111562000563576000815560010162000568565b6001600160a01b03811681146200059457600080fd5b50565b805160ff81168114620005a957600080fd5b919050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b0381118282101715620005ef57620005ef620005ae565b604052919050565b60008060008060008060c087890312156200061157600080fd5b86516200061e816200057e565b955060206200062f88820162000597565b955060408801519450606088015162000648816200057e565b60808901519094506200065b816200057e565b60a08901519093506001600160401b03808211156200067957600080fd5b818a0191508a601f8301126200068e57600080fd5b815181811115620006a357620006a3620005ae565b8060051b9150620006b6848301620005c4565b818152918301840191848101908d841115620006d157600080fd5b938501935b83851015620006ff5784519250620006ee836200057e565b8282529385019390850190620006d6565b8096505050505050509295509295509295565b60005b838110156200072f57818101518382015260200162000715565b838111156200073f576000848401525b50505050565b6000602082840312156200075857600080fd5b81516001600160401b03808211156200077057600080fd5b818401915084601f8301126200078557600080fd5b8151818111156200079a576200079a620005ae565b620007af601f8201601f1916602001620005c4565b9150808252856020828501011115620007c757600080fd5b620007da81602084016020860162000712565b50949350505050565b6602ab730b3b4b4960cd1b8152600082516200080781600785016020870162000712565b68205661756c7420763360b81b6007939091019283015250601001919050565b607560f81b8152600082516200084581600185016020870162000712565b61763360f01b6001939091019283015250600301919050565b6000602082840312156200087157600080fd5b6200087c8262000597565b9392505050565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b600060ff821660ff811415620008c957620008c962000899565b60010192915050565b600181811c90821680620008e757607f821691505b602082108114156200090957634e487b7160e01b600052602260045260246000fd5b50919050565b600080835481600182811c9150808316806200092c57607f831692505b60208084108214156200094d57634e487b7160e01b86526022600452602486fd5b8180156200096457600181146200097657620009a5565b60ff19861689528489019650620009a5565b60008a81526020902060005b868110156200099d5781548b82015290850190830162000982565b505084890196505b509498975050505050505050565b60008219821115620009c957620009c962000899565b500190565b600082821015620009e357620009e362000899565b500390565b60805160a05160c05160e05161444d62000a616000396000818161058301528181610a1d01528181610ea301528181610f7601528181612d3d0152818161330c0152818161355b01528181613605015281816137d101526138750152600061139b0152600061136b01526000610542015261444d6000f3fe608060405234801561001057600080fd5b50600436106104575760003560e01c80638da5cb5b11610250578063c9411e2211610150578063e10d29ee116100c8578063ef8b30f711610097578063f3f2a8ff1161007c578063f3f2a8ff146109b1578063f851a440146109c4578063fc7b9c18146109d757600080fd5b8063ef8b30f71461098b578063f0e7d6c21461099e57600080fd5b8063e10d29ee14610947578063e22311721461095c578063e2f4030d14610965578063e438c4b71461097857600080fd5b8063d905777e1161011f578063d98f608811610104578063d98f608814610900578063dd62ed3e14610909578063e053ea311461093457600080fd5b8063d905777e146108c4578063d9380519146108ed57600080fd5b8063c9411e2214610878578063ce96cb771461088b578063cf1c316a1461089e578063d505accf146108b157600080fd5b8063b3d7f6b9116101e3578063bb371fdd116101b2578063c3535b5211610197578063c3535b5214610849578063c63d75b614610852578063c6e6f5921461086557600080fd5b8063bb371fdd14610823578063c12a8e5c1461083657600080fd5b8063b3d7f6b9146107c7578063b460af94146107da578063b9181611146107ed578063ba0876521461081057600080fd5b806395d89b411161021f57806395d89b41146107865780639f30380a1461078e578063a9059cbb146107a1578063aac90c91146107b457600080fd5b80638da5cb5b146107385780638ed955b91461074b57806394bf804d14610753578063952ca92c1461076657600080fd5b80633f4ba83a1161035b57806361c4a39c116102ee57806370a08231116102bd5780637b1e8395116102a25780637b1e8395146106fd5780637ecebe00146107105780638456cb591461073057600080fd5b806370a08231146106d557806379ba5097146106f557600080fd5b806361c4a39c146106935780636b56a5b4146106a65780636e553f65146106af578063704b6c02146106c257600080fd5b80634cdad5061161032a5780634cdad5061461065857806353a47bb71461066b5780635a7201dd1461067e5780635c975abb1461068657600080fd5b80633f4ba83a14610620578063402d267d1461062857806344b813961461063d578063485d7d941461064557600080fd5b80631ef4f201116103ee578063313ce567116103bd57806338d52e0f116103a257806338d52e0f1461057e57806339ebf823146105bd57806339ffd18f1461060d57600080fd5b8063313ce5671461053d5780633644e5151461057657600080fd5b80631ef4f201146104fb578063219125081461050e57806323b872dd146105215780632df9eab91461053457600080fd5b80630a28a4771161042a5780630a28a477146104c25780630e5c011e146104d557806311f240ac146104ea57806318160ddd146104f257600080fd5b806301e1d1141461045c57806306fdde031461047757806307a2d13a1461048c578063095ea7b31461049f575b600080fd5b6104646109e0565b6040519081526020015b60405180910390f35b61047f610aa6565b60405161046e9190613de6565b61046461049a366004613e59565b610b34565b6104b26104ad366004613e87565b610b61565b604051901515815260200161046e565b6104646104d0366004613e59565b610bce565b6104e86104e3366004613eb3565b610bee565b005b610464610caf565b61046460025481565b610464610509366004613ed0565b610ccb565b61046461051c366004613f05565b610dcf565b6104b261052f366004613f4b565b611268565b61046460155481565b6105647f000000000000000000000000000000000000000000000000000000000000000081565b60405160ff909116815260200161046e565b610464611367565b6105a57f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b03909116815260200161046e565b6105f06105cb366004613eb3565b60126020526000908152604090208054600182015460029092015460ff909116919083565b60408051931515845260208401929092529082015260600161046e565b6104e861061b366004613eb3565b6113bd565b6104e861147c565b610464610636366004613eb3565b5060105490565b610464611523565b6104e8610653366004613eb3565b61155b565b610464610666366004613e59565b611639565b6007546105a5906001600160a01b031681565b6104e8611650565b600c546104b29060ff1681565b6104e86106a1366004613e59565b61170e565b610464600f5481565b6104646106bd366004613f7b565b611776565b6104e86106d0366004613eb3565b611803565b6104646106e3366004613eb3565b60036020526000908152604090205481565b6104e86118af565b6104e861070b366004613e59565b611949565b61046461071e366004613eb3565b60056020526000908152604090205481565b6104e8611a44565b6006546105a5906001600160a01b031681565b6104e8611aef565b610464610761366004613f7b565b611ba5565b610464610774366004613eb3565b600b6020526000908152604090205481565b61047f611c32565b61046461079c366004613fab565b611c3f565b6104b26107af366004613e87565b611c93565b6104e86107c2366004614009565b611d84565b6104646107d5366004613e59565b611dd0565b6104646107e8366004614024565b611def565b6104b26107fb366004613eb3565b60096020526000908152604090205460ff1681565b61046461081e366004614024565b611e47565b6104e8610831366004613e59565b611e97565b610464610844366004614066565b611f67565b610464600e5481565b610464610860366004613eb3565b611fb2565b610464610873366004613e59565b611fbb565b6104e8610886366004613e87565b611fdb565b610464610899366004613eb3565b612289565b6104e86108ac366004613eb3565b6122ab565b6104e86108bf36600461408d565b61238d565b6104646108d2366004613eb3565b6001600160a01b031660009081526003602052604090205490565b6104e86108fb366004613eb3565b61266b565b610464600a5481565b6104646109173660046140fb565b600460209081526000928352604080842090915290825290205481565b6104e8610942366004613eb3565b6126cf565b61094f61278b565b60405161046e9190614129565b61046460145481565b610464610973366004614066565b6127ed565b6104e8610986366004613e87565b612838565b610464610999366004613e59565b6128db565b6104646109ac366004613fab565b6128e6565b6104e86109bf366004614176565b612932565b6008546105a5906001600160a01b031681565b61046460135481565b6013546040517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152600091906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a082319060240160206040518083038186803b158015610a5f57600080fd5b505afa158015610a73573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a9791906141a9565b610aa191906141f1565b905090565b60008054610ab390614209565b80601f0160208091040260200160405190810160405280929190818152602001828054610adf90614209565b8015610b2c5780601f10610b0157610100808354040283529160200191610b2c565b820191906000526020600020905b815481529060010190602001808311610b0f57829003601f168201915b505050505081565b6002546000908015610b5857610b53610b4b6109e0565b849083612abe565b610b5a565b825b9392505050565b3360008181526004602090815260408083206001600160a01b038716808552925280832085905551919290917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92590610bbc9086815260200190565b60405180910390a35060015b92915050565b6002546000908015610b5857610b5381610be6610caf565b859190612afa565b6006546001600160a01b03163314801590610c1457506008546001600160a01b03163314155b8015610c3057503360009081526009602052604090205460ff16155b15610c4d576040516282b42960e81b815260040160405180910390fd5b6001600160a01b03811660009081526012602052604090205460ff16610c9f576040517fb326b91e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610ca881612b3e565b5042600e55565b6000610cb9611523565b610cc16109e0565b610aa1919061425d565b6006546000906001600160a01b03163314801590610cf457506008546001600160a01b03163314155b8015610d1057503360009081526009602052604090205460ff16155b15610d2d576040516282b42960e81b815260040160405180910390fd5b6001600160a01b03841660009081526012602052604090205460ff16610d7f576040517fb326b91e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610d8a848430612bba565b50905081811015610b5a576040517f06b2f1c7000000000000000000000000000000000000000000000000000000008152600481018290526024015b60405180910390fd5b6006546000906001600160a01b03163314801590610df857506008546001600160a01b03163314155b15610e15576040516282b42960e81b815260040160405180910390fd5b6001600160a01b03841660009081526012602052604090205460ff16610e67576040517fb326b91e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610e72846000612d70565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a082319060240160206040518083038186803b158015610eed57600080fd5b505afa158015610f01573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f2591906141a9565b90508315610f3b57610f3685612b3e565b610f46565b610f46856000612e85565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015281907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a082319060240160206040518083038186803b158015610fc057600080fd5b505afa158015610fd4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ff891906141a9565b611002919061425d565b915082821015611041576040517f06b2f1c700000000000000000000000000000000000000000000000000000000815260048101839052602401610dc6565b6011546000906110539060019061425d565b67ffffffffffffffff81111561106b5761106b614274565b604051908082528060200260200182016040528015611094578160200160208202803683370190505b508051909150600090815b8160ff168160ff1610156111e557886001600160a01b031660118260ff16815481106110cd576110cd6142a3565b6000918252602090912001546001600160a01b031614156110ed57600192505b821561116b5760116111008260016142d2565b60ff1681548110611113576111136142a3565b9060005260206000200160009054906101000a90046001600160a01b0316848260ff1681518110611146576111466142a3565b60200260200101906001600160a01b031690816001600160a01b0316815250506111d5565b60118160ff1681548110611181576111816142a3565b9060005260206000200160009054906101000a90046001600160a01b0316848260ff16815181106111b4576111b46142a3565b60200260200101906001600160a01b031690816001600160a01b0316815250505b6111de816142f7565b905061109f565b506001600160a01b03881660009081526012602090815260408220805460ff19168155600181018390556002019190915583516112289160119190860190613d54565b506040516001600160a01b038916907f09a1db4b80c32706328728508c941a6b954f31eb5affd32f236c1fd405f8fea490600090a2505050509392505050565b600a546001600160a01b0384166000908152600b60205260408120549091859161129291906141f1565b4310156112cb576040517f71f13ee800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b038082166000908152600b6020526040808220439055600a54928716825290205485916112fe916141f1565b431015611337576040517f71f13ee800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b0381166000908152600b6020526040902043905561135d86868661307a565b9695505050505050565b60007f0000000000000000000000000000000000000000000000000000000000000000461461139857610aa161318a565b507f000000000000000000000000000000000000000000000000000000000000000090565b6006546001600160a01b031633148015906113e357506008546001600160a01b03163314155b80156113ff57503360009081526009602052604090205460ff16155b1561141c576040516282b42960e81b815260040160405180910390fd5b6001600160a01b03811660009081526012602052604090205460ff1661146e576040517fb326b91e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611479816000612d70565b50565b6006546001600160a01b031633148015906114a257506008546001600160a01b03163314155b80156114be57503360009081526009602052604090205460ff16155b156114db576040516282b42960e81b815260040160405180910390fd5b600c5460ff16611517576040517fc4d933da00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600c805460ff19169055565b600e54600f5460009190808201421061153f5760009250505090565b600d54611550904284900383612abe565b600d54039250505090565b6006546001600160a01b0316331480159061158157506008546001600160a01b03163314155b1561159e576040516282b42960e81b815260040160405180910390fd5b6001600160a01b03811660009081526009602052604090205460ff166115f0576040517ffbb7077700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b038116600081815260096020526040808220805460ff19169055517fea160e8ad4e620d61f1d6fc4a3eedd36336ed4c5fa149bbd9db0beab48a945c19190a250565b6002546000908015610b5857610b53610b4b610caf565b6006546001600160a01b0316331480159061167657506008546001600160a01b03163314155b801561169257503360009081526009602052604090205460ff16155b156116af576040516282b42960e81b815260040160405180910390fd5b60115460005b8160ff168160ff161015611706576116f660118260ff16815481106116dc576116dc6142a3565b60009182526020822001546001600160a01b031690612e85565b6116ff816142f7565b90506116b5565b505042600e55565b6006546001600160a01b0316331480159061173457506008546001600160a01b03163314155b801561175057503360009081526009602052604090205460ff16155b1561176d576040516282b42960e81b815260040160405180910390fd5b61147981613224565b600c5460009060ff16156117b6576040517f9e87fac800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6117bf836128db565b9050806117f8576040517ff456040300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610bc88382846132c3565b6006546001600160a01b0316331461182d576040516282b42960e81b815260040160405180910390fd5b6008546001600160a01b0382811691161415611875576040517f0cf4f0b300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600880547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0392909216919091179055565b6007546001600160a01b031633146118d9576040516282b42960e81b815260040160405180910390fd5b60065460405133916001600160a01b0316907fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c90600090a3600680547fffffffffffffffffffffffff00000000000000000000000000000000000000009081163317909155600780549091169055565b6006546001600160a01b0316331480159061196f57506008546001600160a01b03163314155b1561198c576040516282b42960e81b815260040160405180910390fd5b6203f4808111156119cc576040517f8a5c4d8300000000000000000000000000000000000000000000000000000000815260048101829052602401610dc6565b600f54811415611a08576040517fc4d933da00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600f8190556040518181527fd47528e12dd9dc51c605b7e50eb828460186f98893a068d958250c3428c71459906020015b60405180910390a150565b6006546001600160a01b03163314801590611a6a57506008546001600160a01b03163314155b8015611a8657503360009081526009602052604090205460ff16155b15611aa3576040516282b42960e81b815260040160405180910390fd5b600c5460ff1615611ae0576040517fc4d933da00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600c805460ff19166001179055565b6006546001600160a01b03163314801590611b1557506008546001600160a01b03163314155b8015611b3157503360009081526009602052604090205460ff16155b15611b4e576040516282b42960e81b815260040160405180910390fd5b60115460005b8160ff168160ff16101561170657611b9560118260ff1681548110611b7b57611b7b6142a3565b6000918252602090912001546001600160a01b0316612b3e565b611b9e816142f7565b9050611b54565b600c5460009060ff1615611be5576040517f9e87fac800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b82611c1c576040517ff456040300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611c2583611dd0565b9050610bc88184846132c3565b60018054610ab390614209565b6000611c4c858585611e47565b905081811015611c8b576040517f06b2f1c700000000000000000000000000000000000000000000000000000000815260048101829052602401610dc6565b949350505050565b600a54336000818152600b60205260408120549092611cb1916141f1565b431015611cea576040517f71f13ee800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b038082166000908152600b6020526040808220439055600a5492871682529020548591611d1d916141f1565b431015611d56576040517f71f13ee800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b0381166000908152600b60205260409020439055611d7b8585613389565b95945050505050565b6006546001600160a01b03163314801590611daa57506008546001600160a01b03163314155b15611dc7576040516282b42960e81b815260040160405180910390fd5b61147981613401565b6002546000908015610b5857610b53611de76109e0565b849083612afa565b600083611e28576040517ff456040300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611e3184610bce565b9050611e3f84828486613447565b509392505050565b6000611e5284611639565b905080611e8b576040517ff456040300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611c8b81858486613447565b6006546001600160a01b03163314801590611ebd57506008546001600160a01b03163314155b8015611ed957503360009081526009602052604090205460ff16155b15611ef6576040516282b42960e81b815260040160405180910390fd5b806010541415611f32576040517fc4d933da00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60108190556040518181527f99bada54b2a917867d043b855b7725d9ecfca8069e0f793345b940f1b4f9890e90602001611a39565b6000611f738484611776565b905081811015610b5a576040517f06b2f1c700000000000000000000000000000000000000000000000000000000815260048101829052602401610dc6565b6000610bc86010545b6002546000908015610b5857610b5381611fd36109e0565b859190612abe565b6006546001600160a01b03163314612005576040516282b42960e81b815260040160405180910390fd5b306001600160a01b0316826001600160a01b031663fbfa77cf6040518163ffffffff1660e01b815260040160206040518083038186803b15801561204857600080fd5b505afa15801561205c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120809190614317565b6001600160a01b0316146120c0576040517fbb46752d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b03821660009081526012602052604090205460ff1615612113576040517f102f8cfa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60115460141161214f576040517f7fec765800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b806015600082825461216191906141f1565b90915550506015546103e810156121aa576015546040517f8a5c4d83000000000000000000000000000000000000000000000000000000008152600401610dc691815260200190565b604080516060810182526001808252600060208084018281528486018781526001600160a01b038916808552601284528785209651875460ff1916901515178755915186860155516002909501949094556011805493840181559091527f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c6890910180547fffffffffffffffffffffffff00000000000000000000000000000000000000001683179055915183815290917f2f564a83158ad1831793ad3e69257b52f39ece5d49cb0d8746708ecb9ef964da910160405180910390a25050565b6001600160a01b038116600090815260036020526040812054610bc890610b34565b6006546001600160a01b031633148015906122d157506008546001600160a01b03163314155b156122ee576040516282b42960e81b815260040160405180910390fd5b6001600160a01b03811660009081526009602052604090205460ff1615612341576040517f0cf4f0b300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b038116600081815260096020526040808220805460ff19166001179055517f5daa78f70ec2227622bb7db0a1d9e750860491853de15431e1e473d305381c219190a250565b428410156123f7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f5045524d49545f444541444c494e455f455850495245440000000000000000006044820152606401610dc6565b60006001612403611367565b6001600160a01b038a811660008181526005602090815260409182902080546001810190915582517f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c98184015280840194909452938d166060840152608083018c905260a083019390935260c08083018b90528151808403909101815260e0830190915280519201919091207f190100000000000000000000000000000000000000000000000000000000000061010083015261010282019290925261012281019190915261014201604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181528282528051602091820120600084529083018083525260ff871690820152606081018590526080810184905260a0016020604051602081039080840390855afa158015612548573d6000803e3d6000fd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001519150506001600160a01b0381161580159061259c5750876001600160a01b0316816001600160a01b0316145b612602576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f494e56414c49445f5349474e45520000000000000000000000000000000000006044820152606401610dc6565b6001600160a01b0390811660009081526004602090815260408083208a8516808552908352928190208990555188815291928a16917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a350505050505050565b6006546001600160a01b03163314612695576040516282b42960e81b815260040160405180910390fd5b600780547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0392909216919091179055565b6006546001600160a01b031633148015906126f557506008546001600160a01b03163314155b801561271157503360009081526009602052604090205460ff16155b1561272e576040516282b42960e81b815260040160405180910390fd5b6001600160a01b03811660009081526012602052604090205460ff16612780576040517fb326b91e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610ca8816000612e85565b606060118054806020026020016040519081016040528092919081815260200182805480156127e357602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116127c5575b5050505050905090565b60006127f98484611ba5565b905081811115610b5a576040517f8a5c4d8300000000000000000000000000000000000000000000000000000000815260048101829052602401610dc6565b6006546001600160a01b0316331480159061285e57506008546001600160a01b03163314155b1561287b576040516282b42960e81b815260040160405180910390fd5b6001600160a01b03821660009081526012602052604090205460ff166128cd576040517fb326b91e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6128d78282612d70565b5050565b6000610bc882611fbb565b60006128f3858585611def565b905081811115611c8b576040517f8a5c4d8300000000000000000000000000000000000000000000000000000000815260048101829052602401610dc6565b6006546001600160a01b0316331480159061295857506008546001600160a01b03163314155b15612975576040516282b42960e81b815260040160405180910390fd5b600060118360ff168154811061298d5761298d6142a3565b6000918252602082200154601180546001600160a01b0390921693509060ff85169081106129bd576129bd6142a3565b600091825260209091200154601180546001600160a01b039092169250829160ff87169081106129ef576129ef6142a3565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b031602179055508160118460ff1681548110612a3457612a346142a3565b60009182526020918290200180547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b039384161790556040805160ff8089168252871692810192909252848316928416917fb96ff329cc84ebcfca281038ffe3c430e132c0893ce787e9be69ca4205e0778c910160405180910390a350505050565b6000827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0484118302158202612af357600080fd5b5091020490565b6000827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0484118302158202612b2f57600080fd5b50910281810615159190040190565b61147981826001600160a01b0316634641257d6040518163ffffffff1660e01b8152600401602060405180830381600087803b158015612b7d57600080fd5b505af1158015612b91573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612bb591906141a9565b612e85565b6000806000856001600160a01b0316632e1a7d4d866040518263ffffffff1660e01b8152600401612bed91815260200190565b606060405180830381600087803b158015612c0757600080fd5b505af1158015612c1b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c3f9190614334565b919450925090506000612c5283856141f1565b6001600160a01b038816600090815260126020526040812060010154919250828211612c7e5782612c80565b855b6001600160a01b038a16600090815260126020526040812060010180549293508392909190612cb090849061425d565b925050819055508060136000828254612cc9919061425d565b90915550506001600160a01b038716301415612d305760408051878152602081018790529081018590526001600160a01b038a16907fde5e3abbba77c313e4f5881ab0685bbbbb54f38b5cfbdd6230e88642a5df29f19060600160405180910390a2612d64565b612d646001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001688886136e1565b50505050935093915050565b6001600160a01b03821660009081526012602052604090206002015481811415612dc6576040517fc4d933da00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008183601554612dd791906141f1565b612de1919061425d565b90506103e8811115612e22576040517f8a5c4d8300000000000000000000000000000000000000000000000000000000815260048101829052602401610dc6565b6001600160a01b038416600081815260126020526040908190206002018590556015839055517f47ba83deb0ca4da236e7ea5395d5391db08f02030c1108e9ab874e458e50756e90612e779086815260200190565b60405180910390a250505050565b6000826001600160a01b03166301e1d1146040518163ffffffff1660e01b815260040160206040518083038186803b158015612ec057600080fd5b505afa158015612ed4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ef891906141a9565b6001600160a01b03841660009081526012602052604081206001018054908390559192508080612f26611523565b905083851115612f6a5783850392508260136000828254612f4791906141f1565b90915550869050612f5884836141f1565b612f6291906141f1565b600d55612f9c565b84841115612f9c57601380548686039081900390915591508086018210612f92576000612f98565b81868201035b600d555b6000601554600014612fe0576001600160a01b038816600090815260126020526040902060020154601554612fdb9190612fd46109e0565b9190612abe565b612fe3565b60005b9050858111156130055761300088612ffb888461425d565b6137a0565b613024565b80861115613024576130218861301b838961425d565b30612bba565b50505b60408051888152602081018690529081018490526001600160a01b038916907f36d8646df39e8831ab3926651692b6a0ea874e6cc807ea1c428fcf2ba32859f89060600160405180910390a25050505050505050565b6001600160a01b03831660009081526004602090815260408083203384529091528120547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81146130f4576130cf838261425d565b6001600160a01b03861660009081526004602090815260408083203384529091529020555b6001600160a01b0385166000908152600360205260408120805485929061311c90849061425d565b90915550506001600160a01b03808516600081815260036020526040908190208054870190555190918716907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef906131779087815260200190565b60405180910390a3506001949350505050565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60006040516131bc9190614362565b6040805191829003822060208301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b60006014548260155461323791906141f1565b613241919061425d565b90506103e8811115613282576040517f8a5c4d8300000000000000000000000000000000000000000000000000000000815260048101829052602401610dc6565b601482905560158190556040518281527fc370b12b9c3b502102003c0ea4f4313711f71410155879e860f23eb00f1272be9060200160405180910390a15050565b6010548311156132ff576040517f2493b27e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6133346001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016333086613a66565b61333e8183613b2c565b60408051848152602081018490526001600160a01b0383169133917fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d7910160405180910390a3505050565b336000908152600360205260408120805483919083906133aa90849061425d565b90915550506001600160a01b038316600081815260036020526040908190208054850190555133907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90610bbc9086815260200190565b600a8160ff16111561343f576040517fd1056e4f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60ff16600a55565b6000336001600160a01b038416146134d5576001600160a01b03831660009081526004602090815260408083203384529091529020547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81146134d3576134ae858261425d565b6001600160a01b03851660009081526004602090815260408083203384529091529020555b505b6134df8385613bef565b60408051868152602081018690526001600160a01b03808616929085169133917ffbde797d201c681b91056529119e0b02407c7bb96a4a2c75c01fc9667232c8db910160405180910390a46040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a082319060240160206040518083038186803b1580156135a557600080fd5b505afa1580156135b9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135dd91906141a9565b905080156136465760008187116135f457866135f6565b815b905061362c6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001685836136e1565b613636818861425d565b965061364281846141f1565b9250505b60115460005b8160ff168160ff1610156136d65787613664576136d6565b60008061369c60118460ff1681548110613680576136806142a3565b6000918252602090912001546001600160a01b03168b89612bba565b90925090506136ab81836141f1565b6136b5908b61425d565b99506136c182876141f1565b95505050806136cf906142f7565b905061364c565b505050949350505050565b60006040517fa9059cbb000000000000000000000000000000000000000000000000000000008152836004820152826024820152602060006044836000895af13d15601f3d116001600051141617169150508061379a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f5452414e534645525f4641494c454400000000000000000000000000000000006044820152606401610dc6565b50505050565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a082319060240160206040518083038186803b15801561381b57600080fd5b505afa15801561382f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061385391906141a9565b905060008183116138645782613866565b815b905061389c6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001685836136e1565b836001600160a01b031663e8b5e51f6040518163ffffffff1660e01b8152600401600060405180830381600087803b1580156138d757600080fd5b505af11580156138eb573d6000803e3d6000fd5b505050506001600160a01b03841660008181526012602090815260408083206001015481517f01e1d11400000000000000000000000000000000000000000000000000000000815291519094926301e1d1149260048082019391829003018186803b15801561395957600080fd5b505afa15801561396d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061399191906141a9565b9050600061399f838361425d565b905060008185116139b15760006139bb565b6139bb828661425d565b905060006139c9828761425d565b6001600160a01b038a166000908152601260205260408120600101805492935083929091906139f99084906141f1565b925050819055508060136000828254613a1291906141f1565b909155505060408051878152602081018490526001600160a01b038b16917f8fff79b574da9a93d7a5408c2fc00c4a9b2b462321c1659b2fbbb9ff3dd221ff910160405180910390a2505050505050505050565b60006040517f23b872dd0000000000000000000000000000000000000000000000000000000081528460048201528360248201528260448201526020600060648360008a5af13d15601f3d1160016000511416171691505080613b25576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f5452414e534645525f46524f4d5f4641494c45440000000000000000000000006044820152606401610dc6565b5050505050565b600a546001600160a01b0383166000908152600b60205260409020548391613b53916141f1565b431015613b8c576040517f71f13ee800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b038082166000908152600b602052604090204390558316613be0576040517ff456040300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b613bea8383613c74565b505050565b600a546001600160a01b0383166000908152600b60205260409020548391613c16916141f1565b431015613c4f576040517f71f13ee800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b0381166000908152600b60205260409020439055613bea8383613ce0565b8060026000828254613c8691906141f1565b90915550506001600160a01b0382166000818152600360209081526040808320805486019055518481527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91015b60405180910390a35050565b6001600160a01b03821660009081526003602052604081208054839290613d0890849061425d565b90915550506002805482900390556040518181526000906001600160a01b038416907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90602001613cd4565b828054828255906000526020600020908101928215613dc1579160200282015b82811115613dc157825182547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b03909116178255602090920191600190910190613d74565b50613dcd929150613dd1565b5090565b5b80821115613dcd5760008155600101613dd2565b600060208083528351808285015260005b81811015613e1357858101830151858201604001528201613df7565b81811115613e25576000604083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016929092016040019392505050565b600060208284031215613e6b57600080fd5b5035919050565b6001600160a01b038116811461147957600080fd5b60008060408385031215613e9a57600080fd5b8235613ea581613e72565b946020939093013593505050565b600060208284031215613ec557600080fd5b8135610b5a81613e72565b600080600060608486031215613ee557600080fd5b8335613ef081613e72565b95602085013595506040909401359392505050565b600080600060608486031215613f1a57600080fd5b8335613f2581613e72565b925060208401358015158114613f3a57600080fd5b929592945050506040919091013590565b600080600060608486031215613f6057600080fd5b8335613f6b81613e72565b92506020840135613f3a81613e72565b60008060408385031215613f8e57600080fd5b823591506020830135613fa081613e72565b809150509250929050565b60008060008060808587031215613fc157600080fd5b843593506020850135613fd381613e72565b92506040850135613fe381613e72565b9396929550929360600135925050565b803560ff8116811461400457600080fd5b919050565b60006020828403121561401b57600080fd5b610b5a82613ff3565b60008060006060848603121561403957600080fd5b83359250602084013561404b81613e72565b9150604084013561405b81613e72565b809150509250925092565b60008060006060848603121561407b57600080fd5b833592506020840135613f3a81613e72565b600080600080600080600060e0888a0312156140a857600080fd5b87356140b381613e72565b965060208801356140c381613e72565b955060408801359450606088013593506140df60808901613ff3565b925060a0880135915060c0880135905092959891949750929550565b6000806040838503121561410e57600080fd5b823561411981613e72565b91506020830135613fa081613e72565b6020808252825182820181905260009190848201906040850190845b8181101561416a5783516001600160a01b031683529284019291840191600101614145565b50909695505050505050565b6000806040838503121561418957600080fd5b61419283613ff3565b91506141a060208401613ff3565b90509250929050565b6000602082840312156141bb57600080fd5b5051919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60008219821115614204576142046141c2565b500190565b600181811c9082168061421d57607f821691505b60208210811415614257577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b60008282101561426f5761426f6141c2565b500390565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600060ff821660ff84168060ff038211156142ef576142ef6141c2565b019392505050565b600060ff821660ff81141561430e5761430e6141c2565b60010192915050565b60006020828403121561432957600080fd5b8151610b5a81613e72565b60008060006060848603121561434957600080fd5b8351925060208401519150604084015190509250925092565b600080835481600182811c91508083168061437e57607f831692505b60208084108214156143b7577f4e487b710000000000000000000000000000000000000000000000000000000086526022600452602486fd5b8180156143cb57600181146143dc57614409565b60ff19861689528489019650614409565b60008a81526020902060005b868110156144015781548b8201529085019083016143e8565b505084890196505b50949897505050505050505056fea2646970667358221220e62c02c21210028e62a2231b0810ddbc09003f03c01f06de7bbde4796ab845d264736f6c63430008090033000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc200000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000005000000000000000000000000e2ceda90aa1e43647ef306810a903b32c9a3aa94000000000000000000000000f4e2007bb865b78bc0eac0cc242e974efd49c06d00000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000a1f3a2c20a7e8e4470ffa52c01646e1ff4c759a0000000000000000000000001c595009b331fe85fd658aa0b6b7be95b6921021000000000000000000000000beca7b566fad28ccbe6dce59d42d37768ab3cd8b00000000000000000000000086d10751b18f3fe331c146546868a07224a8598b

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106104575760003560e01c80638da5cb5b11610250578063c9411e2211610150578063e10d29ee116100c8578063ef8b30f711610097578063f3f2a8ff1161007c578063f3f2a8ff146109b1578063f851a440146109c4578063fc7b9c18146109d757600080fd5b8063ef8b30f71461098b578063f0e7d6c21461099e57600080fd5b8063e10d29ee14610947578063e22311721461095c578063e2f4030d14610965578063e438c4b71461097857600080fd5b8063d905777e1161011f578063d98f608811610104578063d98f608814610900578063dd62ed3e14610909578063e053ea311461093457600080fd5b8063d905777e146108c4578063d9380519146108ed57600080fd5b8063c9411e2214610878578063ce96cb771461088b578063cf1c316a1461089e578063d505accf146108b157600080fd5b8063b3d7f6b9116101e3578063bb371fdd116101b2578063c3535b5211610197578063c3535b5214610849578063c63d75b614610852578063c6e6f5921461086557600080fd5b8063bb371fdd14610823578063c12a8e5c1461083657600080fd5b8063b3d7f6b9146107c7578063b460af94146107da578063b9181611146107ed578063ba0876521461081057600080fd5b806395d89b411161021f57806395d89b41146107865780639f30380a1461078e578063a9059cbb146107a1578063aac90c91146107b457600080fd5b80638da5cb5b146107385780638ed955b91461074b57806394bf804d14610753578063952ca92c1461076657600080fd5b80633f4ba83a1161035b57806361c4a39c116102ee57806370a08231116102bd5780637b1e8395116102a25780637b1e8395146106fd5780637ecebe00146107105780638456cb591461073057600080fd5b806370a08231146106d557806379ba5097146106f557600080fd5b806361c4a39c146106935780636b56a5b4146106a65780636e553f65146106af578063704b6c02146106c257600080fd5b80634cdad5061161032a5780634cdad5061461065857806353a47bb71461066b5780635a7201dd1461067e5780635c975abb1461068657600080fd5b80633f4ba83a14610620578063402d267d1461062857806344b813961461063d578063485d7d941461064557600080fd5b80631ef4f201116103ee578063313ce567116103bd57806338d52e0f116103a257806338d52e0f1461057e57806339ebf823146105bd57806339ffd18f1461060d57600080fd5b8063313ce5671461053d5780633644e5151461057657600080fd5b80631ef4f201146104fb578063219125081461050e57806323b872dd146105215780632df9eab91461053457600080fd5b80630a28a4771161042a5780630a28a477146104c25780630e5c011e146104d557806311f240ac146104ea57806318160ddd146104f257600080fd5b806301e1d1141461045c57806306fdde031461047757806307a2d13a1461048c578063095ea7b31461049f575b600080fd5b6104646109e0565b6040519081526020015b60405180910390f35b61047f610aa6565b60405161046e9190613de6565b61046461049a366004613e59565b610b34565b6104b26104ad366004613e87565b610b61565b604051901515815260200161046e565b6104646104d0366004613e59565b610bce565b6104e86104e3366004613eb3565b610bee565b005b610464610caf565b61046460025481565b610464610509366004613ed0565b610ccb565b61046461051c366004613f05565b610dcf565b6104b261052f366004613f4b565b611268565b61046460155481565b6105647f000000000000000000000000000000000000000000000000000000000000001281565b60405160ff909116815260200161046e565b610464611367565b6105a57f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc281565b6040516001600160a01b03909116815260200161046e565b6105f06105cb366004613eb3565b60126020526000908152604090208054600182015460029092015460ff909116919083565b60408051931515845260208401929092529082015260600161046e565b6104e861061b366004613eb3565b6113bd565b6104e861147c565b610464610636366004613eb3565b5060105490565b610464611523565b6104e8610653366004613eb3565b61155b565b610464610666366004613e59565b611639565b6007546105a5906001600160a01b031681565b6104e8611650565b600c546104b29060ff1681565b6104e86106a1366004613e59565b61170e565b610464600f5481565b6104646106bd366004613f7b565b611776565b6104e86106d0366004613eb3565b611803565b6104646106e3366004613eb3565b60036020526000908152604090205481565b6104e86118af565b6104e861070b366004613e59565b611949565b61046461071e366004613eb3565b60056020526000908152604090205481565b6104e8611a44565b6006546105a5906001600160a01b031681565b6104e8611aef565b610464610761366004613f7b565b611ba5565b610464610774366004613eb3565b600b6020526000908152604090205481565b61047f611c32565b61046461079c366004613fab565b611c3f565b6104b26107af366004613e87565b611c93565b6104e86107c2366004614009565b611d84565b6104646107d5366004613e59565b611dd0565b6104646107e8366004614024565b611def565b6104b26107fb366004613eb3565b60096020526000908152604090205460ff1681565b61046461081e366004614024565b611e47565b6104e8610831366004613e59565b611e97565b610464610844366004614066565b611f67565b610464600e5481565b610464610860366004613eb3565b611fb2565b610464610873366004613e59565b611fbb565b6104e8610886366004613e87565b611fdb565b610464610899366004613eb3565b612289565b6104e86108ac366004613eb3565b6122ab565b6104e86108bf36600461408d565b61238d565b6104646108d2366004613eb3565b6001600160a01b031660009081526003602052604090205490565b6104e86108fb366004613eb3565b61266b565b610464600a5481565b6104646109173660046140fb565b600460209081526000928352604080842090915290825290205481565b6104e8610942366004613eb3565b6126cf565b61094f61278b565b60405161046e9190614129565b61046460145481565b610464610973366004614066565b6127ed565b6104e8610986366004613e87565b612838565b610464610999366004613e59565b6128db565b6104646109ac366004613fab565b6128e6565b6104e86109bf366004614176565b612932565b6008546105a5906001600160a01b031681565b61046460135481565b6013546040517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152600091906001600160a01b037f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc216906370a082319060240160206040518083038186803b158015610a5f57600080fd5b505afa158015610a73573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a9791906141a9565b610aa191906141f1565b905090565b60008054610ab390614209565b80601f0160208091040260200160405190810160405280929190818152602001828054610adf90614209565b8015610b2c5780601f10610b0157610100808354040283529160200191610b2c565b820191906000526020600020905b815481529060010190602001808311610b0f57829003601f168201915b505050505081565b6002546000908015610b5857610b53610b4b6109e0565b849083612abe565b610b5a565b825b9392505050565b3360008181526004602090815260408083206001600160a01b038716808552925280832085905551919290917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92590610bbc9086815260200190565b60405180910390a35060015b92915050565b6002546000908015610b5857610b5381610be6610caf565b859190612afa565b6006546001600160a01b03163314801590610c1457506008546001600160a01b03163314155b8015610c3057503360009081526009602052604090205460ff16155b15610c4d576040516282b42960e81b815260040160405180910390fd5b6001600160a01b03811660009081526012602052604090205460ff16610c9f576040517fb326b91e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610ca881612b3e565b5042600e55565b6000610cb9611523565b610cc16109e0565b610aa1919061425d565b6006546000906001600160a01b03163314801590610cf457506008546001600160a01b03163314155b8015610d1057503360009081526009602052604090205460ff16155b15610d2d576040516282b42960e81b815260040160405180910390fd5b6001600160a01b03841660009081526012602052604090205460ff16610d7f576040517fb326b91e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610d8a848430612bba565b50905081811015610b5a576040517f06b2f1c7000000000000000000000000000000000000000000000000000000008152600481018290526024015b60405180910390fd5b6006546000906001600160a01b03163314801590610df857506008546001600160a01b03163314155b15610e15576040516282b42960e81b815260040160405180910390fd5b6001600160a01b03841660009081526012602052604090205460ff16610e67576040517fb326b91e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610e72846000612d70565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000907f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b0316906370a082319060240160206040518083038186803b158015610eed57600080fd5b505afa158015610f01573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f2591906141a9565b90508315610f3b57610f3685612b3e565b610f46565b610f46856000612e85565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015281907f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b0316906370a082319060240160206040518083038186803b158015610fc057600080fd5b505afa158015610fd4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ff891906141a9565b611002919061425d565b915082821015611041576040517f06b2f1c700000000000000000000000000000000000000000000000000000000815260048101839052602401610dc6565b6011546000906110539060019061425d565b67ffffffffffffffff81111561106b5761106b614274565b604051908082528060200260200182016040528015611094578160200160208202803683370190505b508051909150600090815b8160ff168160ff1610156111e557886001600160a01b031660118260ff16815481106110cd576110cd6142a3565b6000918252602090912001546001600160a01b031614156110ed57600192505b821561116b5760116111008260016142d2565b60ff1681548110611113576111136142a3565b9060005260206000200160009054906101000a90046001600160a01b0316848260ff1681518110611146576111466142a3565b60200260200101906001600160a01b031690816001600160a01b0316815250506111d5565b60118160ff1681548110611181576111816142a3565b9060005260206000200160009054906101000a90046001600160a01b0316848260ff16815181106111b4576111b46142a3565b60200260200101906001600160a01b031690816001600160a01b0316815250505b6111de816142f7565b905061109f565b506001600160a01b03881660009081526012602090815260408220805460ff19168155600181018390556002019190915583516112289160119190860190613d54565b506040516001600160a01b038916907f09a1db4b80c32706328728508c941a6b954f31eb5affd32f236c1fd405f8fea490600090a2505050509392505050565b600a546001600160a01b0384166000908152600b60205260408120549091859161129291906141f1565b4310156112cb576040517f71f13ee800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b038082166000908152600b6020526040808220439055600a54928716825290205485916112fe916141f1565b431015611337576040517f71f13ee800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b0381166000908152600b6020526040902043905561135d86868661307a565b9695505050505050565b60007f0000000000000000000000000000000000000000000000000000000000000001461461139857610aa161318a565b507f6cb1e97b7819fc4527af7097ebe27f99f5852f5662a02afc5f7bbaada8d9977490565b6006546001600160a01b031633148015906113e357506008546001600160a01b03163314155b80156113ff57503360009081526009602052604090205460ff16155b1561141c576040516282b42960e81b815260040160405180910390fd5b6001600160a01b03811660009081526012602052604090205460ff1661146e576040517fb326b91e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611479816000612d70565b50565b6006546001600160a01b031633148015906114a257506008546001600160a01b03163314155b80156114be57503360009081526009602052604090205460ff16155b156114db576040516282b42960e81b815260040160405180910390fd5b600c5460ff16611517576040517fc4d933da00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600c805460ff19169055565b600e54600f5460009190808201421061153f5760009250505090565b600d54611550904284900383612abe565b600d54039250505090565b6006546001600160a01b0316331480159061158157506008546001600160a01b03163314155b1561159e576040516282b42960e81b815260040160405180910390fd5b6001600160a01b03811660009081526009602052604090205460ff166115f0576040517ffbb7077700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b038116600081815260096020526040808220805460ff19169055517fea160e8ad4e620d61f1d6fc4a3eedd36336ed4c5fa149bbd9db0beab48a945c19190a250565b6002546000908015610b5857610b53610b4b610caf565b6006546001600160a01b0316331480159061167657506008546001600160a01b03163314155b801561169257503360009081526009602052604090205460ff16155b156116af576040516282b42960e81b815260040160405180910390fd5b60115460005b8160ff168160ff161015611706576116f660118260ff16815481106116dc576116dc6142a3565b60009182526020822001546001600160a01b031690612e85565b6116ff816142f7565b90506116b5565b505042600e55565b6006546001600160a01b0316331480159061173457506008546001600160a01b03163314155b801561175057503360009081526009602052604090205460ff16155b1561176d576040516282b42960e81b815260040160405180910390fd5b61147981613224565b600c5460009060ff16156117b6576040517f9e87fac800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6117bf836128db565b9050806117f8576040517ff456040300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610bc88382846132c3565b6006546001600160a01b0316331461182d576040516282b42960e81b815260040160405180910390fd5b6008546001600160a01b0382811691161415611875576040517f0cf4f0b300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600880547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0392909216919091179055565b6007546001600160a01b031633146118d9576040516282b42960e81b815260040160405180910390fd5b60065460405133916001600160a01b0316907fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c90600090a3600680547fffffffffffffffffffffffff00000000000000000000000000000000000000009081163317909155600780549091169055565b6006546001600160a01b0316331480159061196f57506008546001600160a01b03163314155b1561198c576040516282b42960e81b815260040160405180910390fd5b6203f4808111156119cc576040517f8a5c4d8300000000000000000000000000000000000000000000000000000000815260048101829052602401610dc6565b600f54811415611a08576040517fc4d933da00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600f8190556040518181527fd47528e12dd9dc51c605b7e50eb828460186f98893a068d958250c3428c71459906020015b60405180910390a150565b6006546001600160a01b03163314801590611a6a57506008546001600160a01b03163314155b8015611a8657503360009081526009602052604090205460ff16155b15611aa3576040516282b42960e81b815260040160405180910390fd5b600c5460ff1615611ae0576040517fc4d933da00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600c805460ff19166001179055565b6006546001600160a01b03163314801590611b1557506008546001600160a01b03163314155b8015611b3157503360009081526009602052604090205460ff16155b15611b4e576040516282b42960e81b815260040160405180910390fd5b60115460005b8160ff168160ff16101561170657611b9560118260ff1681548110611b7b57611b7b6142a3565b6000918252602090912001546001600160a01b0316612b3e565b611b9e816142f7565b9050611b54565b600c5460009060ff1615611be5576040517f9e87fac800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b82611c1c576040517ff456040300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611c2583611dd0565b9050610bc88184846132c3565b60018054610ab390614209565b6000611c4c858585611e47565b905081811015611c8b576040517f06b2f1c700000000000000000000000000000000000000000000000000000000815260048101829052602401610dc6565b949350505050565b600a54336000818152600b60205260408120549092611cb1916141f1565b431015611cea576040517f71f13ee800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b038082166000908152600b6020526040808220439055600a5492871682529020548591611d1d916141f1565b431015611d56576040517f71f13ee800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b0381166000908152600b60205260409020439055611d7b8585613389565b95945050505050565b6006546001600160a01b03163314801590611daa57506008546001600160a01b03163314155b15611dc7576040516282b42960e81b815260040160405180910390fd5b61147981613401565b6002546000908015610b5857610b53611de76109e0565b849083612afa565b600083611e28576040517ff456040300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611e3184610bce565b9050611e3f84828486613447565b509392505050565b6000611e5284611639565b905080611e8b576040517ff456040300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611c8b81858486613447565b6006546001600160a01b03163314801590611ebd57506008546001600160a01b03163314155b8015611ed957503360009081526009602052604090205460ff16155b15611ef6576040516282b42960e81b815260040160405180910390fd5b806010541415611f32576040517fc4d933da00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60108190556040518181527f99bada54b2a917867d043b855b7725d9ecfca8069e0f793345b940f1b4f9890e90602001611a39565b6000611f738484611776565b905081811015610b5a576040517f06b2f1c700000000000000000000000000000000000000000000000000000000815260048101829052602401610dc6565b6000610bc86010545b6002546000908015610b5857610b5381611fd36109e0565b859190612abe565b6006546001600160a01b03163314612005576040516282b42960e81b815260040160405180910390fd5b306001600160a01b0316826001600160a01b031663fbfa77cf6040518163ffffffff1660e01b815260040160206040518083038186803b15801561204857600080fd5b505afa15801561205c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120809190614317565b6001600160a01b0316146120c0576040517fbb46752d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b03821660009081526012602052604090205460ff1615612113576040517f102f8cfa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60115460141161214f576040517f7fec765800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b806015600082825461216191906141f1565b90915550506015546103e810156121aa576015546040517f8a5c4d83000000000000000000000000000000000000000000000000000000008152600401610dc691815260200190565b604080516060810182526001808252600060208084018281528486018781526001600160a01b038916808552601284528785209651875460ff1916901515178755915186860155516002909501949094556011805493840181559091527f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c6890910180547fffffffffffffffffffffffff00000000000000000000000000000000000000001683179055915183815290917f2f564a83158ad1831793ad3e69257b52f39ece5d49cb0d8746708ecb9ef964da910160405180910390a25050565b6001600160a01b038116600090815260036020526040812054610bc890610b34565b6006546001600160a01b031633148015906122d157506008546001600160a01b03163314155b156122ee576040516282b42960e81b815260040160405180910390fd5b6001600160a01b03811660009081526009602052604090205460ff1615612341576040517f0cf4f0b300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b038116600081815260096020526040808220805460ff19166001179055517f5daa78f70ec2227622bb7db0a1d9e750860491853de15431e1e473d305381c219190a250565b428410156123f7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f5045524d49545f444541444c494e455f455850495245440000000000000000006044820152606401610dc6565b60006001612403611367565b6001600160a01b038a811660008181526005602090815260409182902080546001810190915582517f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c98184015280840194909452938d166060840152608083018c905260a083019390935260c08083018b90528151808403909101815260e0830190915280519201919091207f190100000000000000000000000000000000000000000000000000000000000061010083015261010282019290925261012281019190915261014201604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181528282528051602091820120600084529083018083525260ff871690820152606081018590526080810184905260a0016020604051602081039080840390855afa158015612548573d6000803e3d6000fd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001519150506001600160a01b0381161580159061259c5750876001600160a01b0316816001600160a01b0316145b612602576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f494e56414c49445f5349474e45520000000000000000000000000000000000006044820152606401610dc6565b6001600160a01b0390811660009081526004602090815260408083208a8516808552908352928190208990555188815291928a16917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a350505050505050565b6006546001600160a01b03163314612695576040516282b42960e81b815260040160405180910390fd5b600780547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0392909216919091179055565b6006546001600160a01b031633148015906126f557506008546001600160a01b03163314155b801561271157503360009081526009602052604090205460ff16155b1561272e576040516282b42960e81b815260040160405180910390fd5b6001600160a01b03811660009081526012602052604090205460ff16612780576040517fb326b91e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610ca8816000612e85565b606060118054806020026020016040519081016040528092919081815260200182805480156127e357602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116127c5575b5050505050905090565b60006127f98484611ba5565b905081811115610b5a576040517f8a5c4d8300000000000000000000000000000000000000000000000000000000815260048101829052602401610dc6565b6006546001600160a01b0316331480159061285e57506008546001600160a01b03163314155b1561287b576040516282b42960e81b815260040160405180910390fd5b6001600160a01b03821660009081526012602052604090205460ff166128cd576040517fb326b91e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6128d78282612d70565b5050565b6000610bc882611fbb565b60006128f3858585611def565b905081811115611c8b576040517f8a5c4d8300000000000000000000000000000000000000000000000000000000815260048101829052602401610dc6565b6006546001600160a01b0316331480159061295857506008546001600160a01b03163314155b15612975576040516282b42960e81b815260040160405180910390fd5b600060118360ff168154811061298d5761298d6142a3565b6000918252602082200154601180546001600160a01b0390921693509060ff85169081106129bd576129bd6142a3565b600091825260209091200154601180546001600160a01b039092169250829160ff87169081106129ef576129ef6142a3565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b031602179055508160118460ff1681548110612a3457612a346142a3565b60009182526020918290200180547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b039384161790556040805160ff8089168252871692810192909252848316928416917fb96ff329cc84ebcfca281038ffe3c430e132c0893ce787e9be69ca4205e0778c910160405180910390a350505050565b6000827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0484118302158202612af357600080fd5b5091020490565b6000827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0484118302158202612b2f57600080fd5b50910281810615159190040190565b61147981826001600160a01b0316634641257d6040518163ffffffff1660e01b8152600401602060405180830381600087803b158015612b7d57600080fd5b505af1158015612b91573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612bb591906141a9565b612e85565b6000806000856001600160a01b0316632e1a7d4d866040518263ffffffff1660e01b8152600401612bed91815260200190565b606060405180830381600087803b158015612c0757600080fd5b505af1158015612c1b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c3f9190614334565b919450925090506000612c5283856141f1565b6001600160a01b038816600090815260126020526040812060010154919250828211612c7e5782612c80565b855b6001600160a01b038a16600090815260126020526040812060010180549293508392909190612cb090849061425d565b925050819055508060136000828254612cc9919061425d565b90915550506001600160a01b038716301415612d305760408051878152602081018790529081018590526001600160a01b038a16907fde5e3abbba77c313e4f5881ab0685bbbbb54f38b5cfbdd6230e88642a5df29f19060600160405180910390a2612d64565b612d646001600160a01b037f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc21688886136e1565b50505050935093915050565b6001600160a01b03821660009081526012602052604090206002015481811415612dc6576040517fc4d933da00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008183601554612dd791906141f1565b612de1919061425d565b90506103e8811115612e22576040517f8a5c4d8300000000000000000000000000000000000000000000000000000000815260048101829052602401610dc6565b6001600160a01b038416600081815260126020526040908190206002018590556015839055517f47ba83deb0ca4da236e7ea5395d5391db08f02030c1108e9ab874e458e50756e90612e779086815260200190565b60405180910390a250505050565b6000826001600160a01b03166301e1d1146040518163ffffffff1660e01b815260040160206040518083038186803b158015612ec057600080fd5b505afa158015612ed4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ef891906141a9565b6001600160a01b03841660009081526012602052604081206001018054908390559192508080612f26611523565b905083851115612f6a5783850392508260136000828254612f4791906141f1565b90915550869050612f5884836141f1565b612f6291906141f1565b600d55612f9c565b84841115612f9c57601380548686039081900390915591508086018210612f92576000612f98565b81868201035b600d555b6000601554600014612fe0576001600160a01b038816600090815260126020526040902060020154601554612fdb9190612fd46109e0565b9190612abe565b612fe3565b60005b9050858111156130055761300088612ffb888461425d565b6137a0565b613024565b80861115613024576130218861301b838961425d565b30612bba565b50505b60408051888152602081018690529081018490526001600160a01b038916907f36d8646df39e8831ab3926651692b6a0ea874e6cc807ea1c428fcf2ba32859f89060600160405180910390a25050505050505050565b6001600160a01b03831660009081526004602090815260408083203384529091528120547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81146130f4576130cf838261425d565b6001600160a01b03861660009081526004602090815260408083203384529091529020555b6001600160a01b0385166000908152600360205260408120805485929061311c90849061425d565b90915550506001600160a01b03808516600081815260036020526040908190208054870190555190918716907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef906131779087815260200190565b60405180910390a3506001949350505050565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60006040516131bc9190614362565b6040805191829003822060208301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b60006014548260155461323791906141f1565b613241919061425d565b90506103e8811115613282576040517f8a5c4d8300000000000000000000000000000000000000000000000000000000815260048101829052602401610dc6565b601482905560158190556040518281527fc370b12b9c3b502102003c0ea4f4313711f71410155879e860f23eb00f1272be9060200160405180910390a15050565b6010548311156132ff576040517f2493b27e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6133346001600160a01b037f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc216333086613a66565b61333e8183613b2c565b60408051848152602081018490526001600160a01b0383169133917fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d7910160405180910390a3505050565b336000908152600360205260408120805483919083906133aa90849061425d565b90915550506001600160a01b038316600081815260036020526040908190208054850190555133907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90610bbc9086815260200190565b600a8160ff16111561343f576040517fd1056e4f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60ff16600a55565b6000336001600160a01b038416146134d5576001600160a01b03831660009081526004602090815260408083203384529091529020547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81146134d3576134ae858261425d565b6001600160a01b03851660009081526004602090815260408083203384529091529020555b505b6134df8385613bef565b60408051868152602081018690526001600160a01b03808616929085169133917ffbde797d201c681b91056529119e0b02407c7bb96a4a2c75c01fc9667232c8db910160405180910390a46040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000907f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b0316906370a082319060240160206040518083038186803b1580156135a557600080fd5b505afa1580156135b9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135dd91906141a9565b905080156136465760008187116135f457866135f6565b815b905061362c6001600160a01b037f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc21685836136e1565b613636818861425d565b965061364281846141f1565b9250505b60115460005b8160ff168160ff1610156136d65787613664576136d6565b60008061369c60118460ff1681548110613680576136806142a3565b6000918252602090912001546001600160a01b03168b89612bba565b90925090506136ab81836141f1565b6136b5908b61425d565b99506136c182876141f1565b95505050806136cf906142f7565b905061364c565b505050949350505050565b60006040517fa9059cbb000000000000000000000000000000000000000000000000000000008152836004820152826024820152602060006044836000895af13d15601f3d116001600051141617169150508061379a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f5452414e534645525f4641494c454400000000000000000000000000000000006044820152606401610dc6565b50505050565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000907f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b0316906370a082319060240160206040518083038186803b15801561381b57600080fd5b505afa15801561382f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061385391906141a9565b905060008183116138645782613866565b815b905061389c6001600160a01b037f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc21685836136e1565b836001600160a01b031663e8b5e51f6040518163ffffffff1660e01b8152600401600060405180830381600087803b1580156138d757600080fd5b505af11580156138eb573d6000803e3d6000fd5b505050506001600160a01b03841660008181526012602090815260408083206001015481517f01e1d11400000000000000000000000000000000000000000000000000000000815291519094926301e1d1149260048082019391829003018186803b15801561395957600080fd5b505afa15801561396d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061399191906141a9565b9050600061399f838361425d565b905060008185116139b15760006139bb565b6139bb828661425d565b905060006139c9828761425d565b6001600160a01b038a166000908152601260205260408120600101805492935083929091906139f99084906141f1565b925050819055508060136000828254613a1291906141f1565b909155505060408051878152602081018490526001600160a01b038b16917f8fff79b574da9a93d7a5408c2fc00c4a9b2b462321c1659b2fbbb9ff3dd221ff910160405180910390a2505050505050505050565b60006040517f23b872dd0000000000000000000000000000000000000000000000000000000081528460048201528360248201528260448201526020600060648360008a5af13d15601f3d1160016000511416171691505080613b25576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f5452414e534645525f46524f4d5f4641494c45440000000000000000000000006044820152606401610dc6565b5050505050565b600a546001600160a01b0383166000908152600b60205260409020548391613b53916141f1565b431015613b8c576040517f71f13ee800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b038082166000908152600b602052604090204390558316613be0576040517ff456040300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b613bea8383613c74565b505050565b600a546001600160a01b0383166000908152600b60205260409020548391613c16916141f1565b431015613c4f576040517f71f13ee800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b0381166000908152600b60205260409020439055613bea8383613ce0565b8060026000828254613c8691906141f1565b90915550506001600160a01b0382166000818152600360209081526040808320805486019055518481527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91015b60405180910390a35050565b6001600160a01b03821660009081526003602052604081208054839290613d0890849061425d565b90915550506002805482900390556040518181526000906001600160a01b038416907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90602001613cd4565b828054828255906000526020600020908101928215613dc1579160200282015b82811115613dc157825182547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b03909116178255602090920191600190910190613d74565b50613dcd929150613dd1565b5090565b5b80821115613dcd5760008155600101613dd2565b600060208083528351808285015260005b81811015613e1357858101830151858201604001528201613df7565b81811115613e25576000604083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016929092016040019392505050565b600060208284031215613e6b57600080fd5b5035919050565b6001600160a01b038116811461147957600080fd5b60008060408385031215613e9a57600080fd5b8235613ea581613e72565b946020939093013593505050565b600060208284031215613ec557600080fd5b8135610b5a81613e72565b600080600060608486031215613ee557600080fd5b8335613ef081613e72565b95602085013595506040909401359392505050565b600080600060608486031215613f1a57600080fd5b8335613f2581613e72565b925060208401358015158114613f3a57600080fd5b929592945050506040919091013590565b600080600060608486031215613f6057600080fd5b8335613f6b81613e72565b92506020840135613f3a81613e72565b60008060408385031215613f8e57600080fd5b823591506020830135613fa081613e72565b809150509250929050565b60008060008060808587031215613fc157600080fd5b843593506020850135613fd381613e72565b92506040850135613fe381613e72565b9396929550929360600135925050565b803560ff8116811461400457600080fd5b919050565b60006020828403121561401b57600080fd5b610b5a82613ff3565b60008060006060848603121561403957600080fd5b83359250602084013561404b81613e72565b9150604084013561405b81613e72565b809150509250925092565b60008060006060848603121561407b57600080fd5b833592506020840135613f3a81613e72565b600080600080600080600060e0888a0312156140a857600080fd5b87356140b381613e72565b965060208801356140c381613e72565b955060408801359450606088013593506140df60808901613ff3565b925060a0880135915060c0880135905092959891949750929550565b6000806040838503121561410e57600080fd5b823561411981613e72565b91506020830135613fa081613e72565b6020808252825182820181905260009190848201906040850190845b8181101561416a5783516001600160a01b031683529284019291840191600101614145565b50909695505050505050565b6000806040838503121561418957600080fd5b61419283613ff3565b91506141a060208401613ff3565b90509250929050565b6000602082840312156141bb57600080fd5b5051919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60008219821115614204576142046141c2565b500190565b600181811c9082168061421d57607f821691505b60208210811415614257577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b60008282101561426f5761426f6141c2565b500390565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600060ff821660ff84168060ff038211156142ef576142ef6141c2565b019392505050565b600060ff821660ff81141561430e5761430e6141c2565b60010192915050565b60006020828403121561432957600080fd5b8151610b5a81613e72565b60008060006060848603121561434957600080fd5b8351925060208401519150604084015190509250925092565b600080835481600182811c91508083168061437e57607f831692505b60208084108214156143b7577f4e487b710000000000000000000000000000000000000000000000000000000086526022600452602486fd5b8180156143cb57600181146143dc57614409565b60ff19861689528489019650614409565b60008a81526020902060005b868110156144015781548b8201529085019083016143e8565b505084890196505b50949897505050505050505056fea2646970667358221220e62c02c21210028e62a2231b0810ddbc09003f03c01f06de7bbde4796ab845d264736f6c63430008090033

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

000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc200000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000005000000000000000000000000e2ceda90aa1e43647ef306810a903b32c9a3aa94000000000000000000000000f4e2007bb865b78bc0eac0cc242e974efd49c06d00000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000a1f3a2c20a7e8e4470ffa52c01646e1ff4c759a0000000000000000000000001c595009b331fe85fd658aa0b6b7be95b6921021000000000000000000000000beca7b566fad28ccbe6dce59d42d37768ab3cd8b00000000000000000000000086d10751b18f3fe331c146546868a07224a8598b

-----Decoded View---------------
Arg [0] : _asset (address): 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2
Arg [1] : _blockDelay (uint8): 5
Arg [2] : _floatDebtRatio (uint256): 5
Arg [3] : _nominatedOwner (address): 0xe2CEDA90aa1E43647EF306810a903b32c9a3Aa94
Arg [4] : _admin (address): 0xf4E2007bb865B78bc0EAc0cC242e974Efd49C06d
Arg [5] : _authorized (address[]): 0x0a1F3A2c20a7e8E4470fFA52c01646E1ff4c759A,0x1C595009B331fE85FD658aA0B6B7bE95B6921021,0xBeCA7b566FAd28cCbe6dcE59d42D37768Ab3CD8B,0x86d10751B18F3fE331C146546868a07224A8598B

-----Encoded View---------------
11 Constructor Arguments found :
Arg [0] : 000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2
Arg [1] : 0000000000000000000000000000000000000000000000000000000000000005
Arg [2] : 0000000000000000000000000000000000000000000000000000000000000005
Arg [3] : 000000000000000000000000e2ceda90aa1e43647ef306810a903b32c9a3aa94
Arg [4] : 000000000000000000000000f4e2007bb865b78bc0eac0cc242e974efd49c06d
Arg [5] : 00000000000000000000000000000000000000000000000000000000000000c0
Arg [6] : 0000000000000000000000000000000000000000000000000000000000000004
Arg [7] : 0000000000000000000000000a1f3a2c20a7e8e4470ffa52c01646e1ff4c759a
Arg [8] : 0000000000000000000000001c595009b331fe85fd658aa0b6b7be95b6921021
Arg [9] : 000000000000000000000000beca7b566fad28ccbe6dce59d42d37768ab3cd8b
Arg [10] : 00000000000000000000000086d10751b18f3fe331c146546868a07224a8598b


Loading...
Loading
Loading...
Loading
[ Download: CSV Export  ]
[ Download: CSV Export  ]

A token is a representation of an on-chain or off-chain asset. The token page shows information such as price, total supply, holders, transfers and social links. Learn more about this page in our Knowledge Base.