ETH Price: $3,441.75 (-0.02%)
Gas: 2 Gwei

Contract

0x6aBE5f87E3F4dC87301064F63CA5b244d119980d
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Token Holdings

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Harvest203655122024-07-23 0:00:2329 hrs ago1721692823IN
0x6aBE5f87...4d119980d
0 ETH0.002440363.87576234
Harvest203440242024-07-20 0:00:234 days ago1721433623IN
0x6aBE5f87...4d119980d
0 ETH0.003001734.95972742
Harvest203225222024-07-17 0:00:237 days ago1721174423IN
0x6aBE5f87...4d119980d
0 ETH0.004258976.79700837
Harvest203010222024-07-14 0:00:2310 days ago1720915223IN
0x6aBE5f87...4d119980d
0 ETH0.005365838.52171212
Harvest202795412024-07-11 0:00:2313 days ago1720656023IN
0x6aBE5f87...4d119980d
0 ETH0.002350613.73363323
Harvest202580752024-07-08 0:00:2316 days ago1720396823IN
0x6aBE5f87...4d119980d
0 ETH0.002058193.2687129
Harvest202365832024-07-05 0:00:2319 days ago1720137623IN
0x6aBE5f87...4d119980d
0 ETH0.005974159.48887825
Harvest202151142024-07-02 0:00:2322 days ago1719878423IN
0x6aBE5f87...4d119980d
0 ETH0.002215233.53574817
Harvest201936272024-06-29 0:00:2325 days ago1719619223IN
0x6aBE5f87...4d119980d
0 ETH0.002097323.27998324
Harvest201721402024-06-26 0:00:2328 days ago1719360023IN
0x6aBE5f87...4d119980d
0 ETH0.002428963.85775978
Harvest201506792024-06-23 0:00:3531 days ago1719100835IN
0x6aBE5f87...4d119980d
0 ETH0.001920883.0508936
Harvest201292262024-06-20 0:00:2334 days ago1718841623IN
0x6aBE5f87...4d119980d
0 ETH0.004243726.74039443
Harvest201077912024-06-17 0:00:2337 days ago1718582423IN
0x6aBE5f87...4d119980d
0 ETH0.003396555.39467722
Harvest200863292024-06-14 0:00:2340 days ago1718323223IN
0x6aBE5f87...4d119980d
0 ETH0.005595478.88764355
Harvest200648682024-06-11 0:00:2343 days ago1718064023IN
0x6aBE5f87...4d119980d
0 ETH0.006809710.8147954
Harvest200433902024-06-08 0:00:2346 days ago1717804823IN
0x6aBE5f87...4d119980d
0 ETH0.006191939.83395376
Harvest200219032024-06-05 0:00:2349 days ago1717545623IN
0x6aBE5f87...4d119980d
0 ETH0.004129146.55841143
Harvest200004172024-06-02 0:00:2352 days ago1717286423IN
0x6aBE5f87...4d119980d
0 ETH0.003982246.35537009
Harvest199789482024-05-30 0:00:2355 days ago1717027223IN
0x6aBE5f87...4d119980d
0 ETH0.005657778.98536259
Harvest199574912024-05-27 0:00:2358 days ago1716768023IN
0x6aBE5f87...4d119980d
0 ETH0.003271885.19679629
Harvest199360202024-05-24 0:00:2361 days ago1716508823IN
0x6aBE5f87...4d119980d
0 ETH0.005209988.27464796
Harvest199145662024-05-21 0:00:2364 days ago1716249623IN
0x6aBE5f87...4d119980d
0 ETH0.0110232317.59225029
Harvest198931242024-05-18 0:00:3567 days ago1715990435IN
0x6aBE5f87...4d119980d
0 ETH0.002899034.60459661
Harvest198716542024-05-15 0:00:2370 days ago1715731223IN
0x6aBE5f87...4d119980d
0 ETH0.003520165.61853802
Harvest198502312024-05-12 0:00:2373 days ago1715472023IN
0x6aBE5f87...4d119980d
0 ETH0.00322745.1258566
View all transactions

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
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


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

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

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