ETH Price: $2,310.03 (-5.73%)
 

Overview

ETH Balance

Scroll LogoScroll LogoScroll Logo0 ETH

ETH Value

$0.00

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To

There are no matching entries

1 Internal Transaction found.

Latest 1 internal transaction

Advanced mode:
Parent Transaction Hash Block From To
82275232024-08-09 19:40:44541 days ago1723232444  Contract Creation0 ETH
Cross-Chain Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
StrategyBuilder

Compiler Version
v0.8.20+commit.a1b79de6

Optimization Enabled:
Yes with 200 runs

Other Settings:
london EvmVersion
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.20;

import "./StrategyBlocks.sol";

enum Step {
    TakeFlashloan,
    RepayFlashloan,
    VaultDeposit,
    VaultWithdraw,
    Swap,
    PositionDeposit,
    PositionWithdraw,
    PositionBorrow,
    PositionRepay,
    PositionClose,
    PullFundsWithPermit,
    PullFundsWithPermit2,
    PullPosition,
    Trade,
    VaultDepositNative,
    VaultWithdrawNative,
    SwapFromVault,
    WrapNativeToken,
    UnwrapNativeToken,
    EmitEvent
}

type PositionN is uint256;

PositionN constant POSITION_ONE = PositionN.wrap(1);
PositionN constant POSITION_TWO = PositionN.wrap(2);

struct StepCall {
    Step step;
    bytes data;
}

struct StepResult {
    Step step;
    bytes data;
}

library StackLib {

    struct Stack {
        PositionId position1;
        PositionId position2;
        address repayTo;
    }

    function loadPositionId(Stack memory stack, PositionId positionId, PositionN n) internal pure returns (PositionId) {
        if (positionId.asUint() == 0) {
            positionId = PositionN.unwrap(n) == PositionN.unwrap(POSITION_ONE) ? stack.position1 : stack.position2;
        }
        return positionId;
    }

    function storePositionId(Stack memory stack, PositionId positionId, PositionN n) internal pure returns (Stack memory) {
        if (PositionN.unwrap(n) == PositionN.unwrap(POSITION_ONE)) stack.position1 = positionId;
        else stack.position2 = positionId;
        return stack;
    }

}

contract StrategyBuilder is StrategyBlocks {

    using ERC20Lib for *;
    using SafeERC20 for IERC20Permit;
    using StackLib for StackLib.Stack;

    error InvalidStep(Step step);

    event StragegyExecuted(address indexed user, bytes32 indexed action, PositionId position1, PositionId position2, bytes data);

    constructor(Timelock timelock, IMaestro _maestro, IERC721Permit2 _erc721Permit2, ContangoLens _lens)
        StrategyBlocks(timelock, _maestro, _erc721Permit2, _lens)
    { }

    // ======================== Public functions ========================

    function process(StepCall[] memory steps) external payable returns (StepResult[] memory results) {
        results = process(steps, msg.sender);
    }

    function process(StepCall[] memory steps, address returnPositionsTo) public payable returns (StepResult[] memory results) {
        StackLib.Stack memory stack;
        results = new StepResult[](steps.length);
        address user = msg.sender; // User MUST be msg.sender to enforce that permits can not be used by someone else's
        (, stack, results) = _actionProcessor(steps, 0, stack, user, results);
        _returnPositions(stack.position1, stack.position2, returnPositionsTo);
    }

    function continueActionProcessing(address, address repayTo, address, uint256, uint256, bytes calldata data)
        external
        validFlashloan(data)
        returns (bytes memory result)
    {
        (StepCall[] memory steps, uint256 offset, StackLib.Stack memory stack, address user, StepResult[] memory results) =
            abi.decode(data, (StepCall[], uint256, StackLib.Stack, address, StepResult[]));
        stack.repayTo = repayTo;
        (offset, stack, results) = _actionProcessor(steps, offset, stack, user, results);
        return abi.encode(offset, stack, results);
    }

    // ======================== Internal functions ========================

    function _onPositionReceived(address, address from, uint256, bytes calldata data) internal override {
        StepCall[] memory steps = abi.decode(data, (StepCall[]));
        StepResult[] memory results = new StepResult[](steps.length);
        StackLib.Stack memory stack;
        address user = from; // User MUST be the position owner to enforce that permits can not be used by someone else's
        (, stack, results) = _actionProcessor(steps, 0, stack, user, results);
        _returnPositions(stack.position1, stack.position2, user);
    }

    function _actionProcessor(
        StepCall[] memory steps,
        uint256 offset,
        StackLib.Stack memory stack,
        address user,
        StepResult[] memory results
    ) internal returns (uint256 offset_, StackLib.Stack memory stack_, StepResult[] memory results_) {
        results_ = results;
        stack_ = stack;
        for (offset_ = offset; offset_ < steps.length; offset_++) {
            StepCall memory step = steps[offset_];
            results[offset_].step = step.step;

            if (step.step == Step.VaultDeposit) {
                (IERC20 asset, uint256 amount) = abi.decode(step.data, (IERC20, uint256));
                results[offset_].data = abi.encode(_vaultDeposit(asset, amount));
            } else if (step.step == Step.VaultDepositNative) {
                results[offset_].data = abi.encode(_vaultDepositNative());
            } else if (step.step == Step.PullFundsWithPermit) {
                (address token, EIP2098Permit memory permit, uint256 amount, address to) =
                    abi.decode(step.data, (address, EIP2098Permit, uint256, address));
                results[offset_].data = abi.encode(_pullFundsWithPermit(token, permit, amount, user, to));
            } else if (step.step == Step.PullFundsWithPermit2) {
                (IERC20 token, EIP2098Permit memory permit, uint256 amount, address to) =
                    abi.decode(step.data, (IERC20, EIP2098Permit, uint256, address));
                results[offset_].data = abi.encode(_pullFundsWithPermit2(token, permit, amount, user, to));
            } else if (step.step == Step.VaultWithdraw) {
                (IERC20 asset, uint256 amount, address to) = abi.decode(step.data, (IERC20, uint256, address));
                results[offset_].data = abi.encode(_vaultWithdraw(asset, amount, to));
            } else if (step.step == Step.VaultWithdrawNative) {
                (uint256 amount, address payable to) = abi.decode(step.data, (uint256, address));
                results[offset_].data = abi.encode(_vaultWithdrawNative(amount, to));
            } else if (step.step == Step.WrapNativeToken) {
                results[offset_].data = abi.encode(_wrapNativeToken(abi.decode(step.data, (address))));
            } else if (step.step == Step.UnwrapNativeToken) {
                (uint256 amount, address payable to) = abi.decode(step.data, (uint256, address));
                results[offset_].data = abi.encode(_unwrapNativeToken(amount, to));
            } else if (step.step == Step.TakeFlashloan) {
                (IERC7399 flashLoanProvider, address asset, uint256 amount) = abi.decode(step.data, (IERC7399, address, uint256));
                (offset_, stack_, results_) = abi.decode(
                    _takeFlashloan(flashLoanProvider, asset, amount, steps, offset_ + 1, stack_, user, results_),
                    (uint256, StackLib.Stack, StepResult[])
                );
            } else if (step.step == Step.RepayFlashloan) {
                (IERC20 asset, uint256 amount) = abi.decode(step.data, (IERC20, uint256));
                results[offset_].data = abi.encode(_vaultWithdraw(asset, amount, stack_.repayTo));
            } else if (step.step == Step.PositionDeposit) {
                (PositionId positionId, PositionN n, uint256 amount) = abi.decode(step.data, (PositionId, PositionN, uint256));
                Trade memory trade;
                (positionId, trade) = _positionDeposit(stack_.loadPositionId(positionId, n), amount);
                stack_.storePositionId(positionId, n);
                results[offset_].data = abi.encode(positionId, trade);
            } else if (step.step == Step.PositionBorrow) {
                (PositionId positionId, PositionN n, uint256 amount) = abi.decode(step.data, (PositionId, PositionN, uint256));
                Trade memory trade;
                (positionId, trade) = _positionBorrow(stack_.loadPositionId(positionId, n), amount);
                stack_.storePositionId(positionId, n);
                results[offset_].data = abi.encode(positionId, trade);
            } else if (step.step == Step.PositionWithdraw) {
                (PositionId positionId, PositionN n, uint256 amount) = abi.decode(step.data, (PositionId, PositionN, uint256));
                Trade memory trade;
                (positionId, trade) = _positionWithdraw(stack_.loadPositionId(positionId, n), amount);
                stack_.storePositionId(positionId, n);
                results[offset_].data = abi.encode(positionId, trade);
            } else if (step.step == Step.PositionRepay) {
                (PositionId positionId, PositionN n, uint256 amount) = abi.decode(step.data, (PositionId, PositionN, uint256));
                Trade memory trade;
                (positionId, trade) = _positionRepay(stack_.loadPositionId(positionId, n), amount);
                stack_.storePositionId(positionId, n);
                results[offset_].data = abi.encode(positionId, trade);
            } else if (step.step == Step.PositionClose) {
                (PositionId positionId, PositionN n) = abi.decode(step.data, (PositionId, PositionN));
                Trade memory trade;
                (positionId, trade) = _positionClose(stack_.loadPositionId(positionId, n), user);
                stack_.storePositionId(positionId, n);
                results[offset_].data = abi.encode(positionId, trade);
            } else if (step.step == Step.Swap) {
                (SwapData memory swapData, IERC20 tokenToSell, IERC20 tokenToBuy, address to) =
                    abi.decode(step.data, (SwapData, IERC20, IERC20, address));
                results[offset_].data = abi.encode(_swap(user, swapData, tokenToSell, tokenToBuy, to));
            } else if (step.step == Step.SwapFromVault) {
                (SwapData memory swapData, IERC20 tokenToSell, IERC20 tokenToBuy) = abi.decode(step.data, (SwapData, IERC20, IERC20));
                results[offset_].data = abi.encode(_swapFromVault(user, swapData, tokenToSell, tokenToBuy));
            } else if (step.step == Step.Trade) {
                (PositionN n, TradeParams memory tp, ExecutionParams memory ep) =
                    abi.decode(step.data, (PositionN, TradeParams, ExecutionParams));
                (PositionId positionId, Trade memory trade) = _trade(tp, ep);
                results[offset_].data = abi.encode(positionId, trade);
                stack_.storePositionId(positionId, n);
            } else if (step.step == Step.PullPosition) {
                results[offset_].data = abi.encode(_pullPosition(abi.decode(step.data, (PositionPermit)), user));
            } else if (step.step == Step.EmitEvent) {
                (bytes32 action, bytes memory data) = abi.decode(step.data, (bytes32, bytes));
                emit StragegyExecuted(user, action, stack_.position1, stack_.position2, data);
            } else {
                revert InvalidStep(step.step);
            }
        }
    }

    function _takeFlashloan(
        IERC7399 flashLoanProvider,
        address asset,
        uint256 amount,
        StepCall[] memory steps,
        uint256 offset,
        StackLib.Stack memory stack,
        address user,
        StepResult[] memory results
    ) internal returns (bytes memory) {
        bytes memory data = abi.encode(steps, offset, stack, user, results);
        flashLoanHash = keccak256(data);
        return flashLoanProvider.flash(address(vault), asset, amount, data, this.continueActionProcessing);
    }

}

File 2 of 61 : StrategyBlocks.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.20;

import "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/utils/Address.sol";

import { IPermit2 } from "../dependencies/Uniswap.sol";
import "@contango/erc721Permit2/interfaces/IERC721Permit2.sol";
import "../moneymarkets/ContangoLens.sol";
import "../interfaces/IMaestro.sol";
import "../libraries/ERC20Lib.sol";
import "./PositionPermit.sol";

interface IStrategyBlocksEvents {

    event BeginStrategy(PositionId indexed positionId, address indexed owner);
    event EndStrategy(PositionId indexed positionId, address indexed owner);
    event SwapExecuted(address indexed trader, IERC20 tokenIn, uint256 amountIn, IERC20 tokenOut, uint256 amountOut);

}

abstract contract StrategyBlocks is IERC721Receiver, IStrategyBlocksEvents, AccessControl {

    using ERC20Lib for *;
    using SafeERC20 for IERC20Permit;
    using Address for address payable;

    error PositionLeftBehind();
    error InvalidCallback();
    error NotNativeToken();
    error NotPositionNFT();

    struct SwapResult {
        address trader;
        IERC20 tokenIn;
        uint256 amountIn;
        IERC20 tokenOut;
        uint256 amountOut;
    }

    uint256 public constant ALL = type(uint256).max;
    uint256 public constant BALANCE = type(uint256).max - 1;

    IContango public immutable contango;
    IVault public immutable vault;
    PositionNFT public immutable positionNFT;
    IERC721Permit2 public immutable erc721Permit2;
    ContangoLens public immutable lens;
    SimpleSpotExecutor public immutable spotExecutor;
    IPermit2 public immutable erc20Permit2;
    IWETH9 public immutable nativeToken;

    bytes32 internal flashLoanHash;

    constructor(Timelock timelock, IMaestro _maestro, IERC721Permit2 _erc721Permit2, ContangoLens _lens) {
        // Grant the admin role to the timelock by default
        _grantRole(DEFAULT_ADMIN_ROLE, Timelock.unwrap(timelock));

        contango = _maestro.contango();
        vault = _maestro.vault();
        positionNFT = contango.positionNFT();
        spotExecutor = _maestro.spotExecutor();
        erc721Permit2 = _erc721Permit2;
        lens = _lens;
        erc20Permit2 = _maestro.permit2();
        nativeToken = _maestro.nativeToken();
    }

    // ======================== Public functions ========================

    function onERC721Received(address operator, address from, uint256 tokenId, bytes calldata data) external override returns (bytes4) {
        if (msg.sender != address(positionNFT)) revert NotPositionNFT();

        // When's not a position creation
        if (operator != address(contango)) {
            emit BeginStrategy(fromUint(tokenId), from);
            _onPositionReceived(operator, from, tokenId, data);
            emit EndStrategy(fromUint(tokenId), from);
        }

        return this.onERC721Received.selector;
    }

    // ======================== Modifiers ========================

    modifier validFlashloan(bytes memory data) {
        if (keccak256(data) != flashLoanHash) revert InvalidCallback();
        _;
        delete flashLoanHash;
    }

    // ======================== Internal functions ========================

    function _onPositionReceived(address operator, address from, uint256 tokenId, bytes calldata data) internal virtual;

    function _vaultDeposit(IERC20 asset, uint256 amount) internal returns (uint256 actual) {
        if (amount == ALL) amount = asset.balanceOf(address(vault)) - vault.totalBalanceOf(asset);
        return vault.depositTo(asset, address(this), amount);
    }

    function _vaultDepositNative() internal returns (uint256 actual) {
        return vault.depositNative{ value: msg.value }(address(this));
    }

    function _vaultWithdraw(IERC20 asset, uint256 amount, address to) internal returns (uint256 actual) {
        if (amount == BALANCE) amount = vault.balanceOf(asset, address(this));
        return vault.withdraw(asset, address(this), amount, to);
    }

    function _vaultWithdrawNative(uint256 amount, address payable to) internal returns (uint256 actual) {
        if (amount == BALANCE) amount = vault.balanceOf(nativeToken, address(this));
        return vault.withdrawNative(address(this), amount, to);
    }

    function _positionDeposit(PositionId positionId, uint256 amount) internal returns (PositionId positionId_, Trade memory trade_) {
        if (amount == BALANCE) amount = vault.balanceOf(contango.instrument(positionId.getSymbol()).base, address(this));
        return _trade(_depositParams(positionId, amount));
    }

    function _positionBorrow(PositionId positionId, uint256 amount) internal returns (PositionId positionId_, Trade memory trade_) {
        return _trade(_borrowParams(positionId, amount));
    }

    function _positionWithdraw(PositionId positionId, uint256 amount) internal returns (PositionId positionId_, Trade memory trade_) {
        return _trade(_withdrawParams(positionId, amount));
    }

    function _positionRepay(PositionId positionId, uint256 amount) internal returns (PositionId positionId_, Trade memory trade_) {
        if (amount == ALL) amount = lens.balances(positionId).debt;
        if (amount == BALANCE) amount = vault.balanceOf(contango.instrument(positionId.getSymbol()).quote, address(this));
        return _trade(_repayParams(positionId, amount));
    }

    function _positionClose(PositionId positionId, address owner) internal returns (PositionId positionId_, Trade memory trade_) {
        (positionId_, trade_) = _trade(_closeParams(positionId));
        contango.donatePosition(positionId, owner);
    }

    function _trade(TradeParams memory params, ExecutionParams memory execution)
        internal
        returns (PositionId positionId_, Trade memory trade_)
    {
        return contango.trade(params, execution);
    }

    function _swapFromVault(address user, SwapData memory swapData, IERC20 tokenToSell, IERC20 tokenToBuy)
        internal
        returns (SwapResult memory result)
    {
        vault.withdraw(tokenToSell, address(this), swapData.amountIn, address(spotExecutor));
        result = _swap(user, swapData, tokenToSell, tokenToBuy, address(vault));
        vault.depositTo(tokenToBuy, address(this), result.amountOut);
    }

    function _swap(address user, SwapData memory swapData, IERC20 tokenToSell, IERC20 tokenToBuy, address to)
        internal
        returns (SwapResult memory)
    {
        uint256 amountOut = spotExecutor.executeSwap({
            tokenToSell: tokenToSell,
            tokenToBuy: tokenToBuy,
            amountIn: swapData.amountIn,
            minAmountOut: swapData.minAmountOut,
            spender: swapData.spender,
            router: swapData.router,
            swapBytes: swapData.swapBytes,
            to: to
        });

        emit SwapExecuted(user, tokenToSell, swapData.amountIn, tokenToBuy, amountOut);
        return SwapResult(user, tokenToSell, swapData.amountIn, tokenToBuy, amountOut);
    }

    function _wrapNativeToken(address to) internal returns (uint256) {
        uint256 amount = msg.value;
        nativeToken.deposit{ value: amount }();
        return nativeToken.transferOut(address(this), to, amount);
    }

    function _unwrapNativeToken(uint256 amount, address payable to) internal returns (uint256) {
        if (amount == BALANCE) amount = nativeToken.balanceOf(address(this));
        return nativeToken.transferOutNative(to, amount);
    }

    function _returnPositions(PositionId long, PositionId short, address owner) internal {
        _returnPosition(long, owner);
        _returnPosition(short, owner);
    }

    function _returnPosition(PositionId positionId, address owner) internal {
        if (positionNFT.exists(positionId)) {
            positionNFT.safeTransferFrom(address(this), owner, positionId.asUint());
            emit EndStrategy(positionId, owner);
        }
    }

    function _trade(TradeParams memory params) internal returns (PositionId positionId_, Trade memory trade_) {
        ExecutionParams memory noExecution;
        return _trade(params, noExecution);
    }

    function _borrowParams(PositionId positionId, uint256 amount) internal pure returns (TradeParams memory) {
        return TradeParams({ positionId: positionId, quantity: 0, limitPrice: 0, cashflowCcy: Currency.Quote, cashflow: -int256(amount) });
    }

    function _repayParams(PositionId positionId, uint256 amount) internal pure returns (TradeParams memory) {
        return TradeParams({ positionId: positionId, quantity: 0, limitPrice: 0, cashflowCcy: Currency.Quote, cashflow: int256(amount) });
    }

    function _withdrawParams(PositionId positionId, uint256 amount) internal pure returns (TradeParams memory) {
        int256 iAmount = -int256(amount);
        return TradeParams({ positionId: positionId, quantity: iAmount, limitPrice: 0, cashflowCcy: Currency.Base, cashflow: iAmount });
    }

    function _depositParams(PositionId positionId, uint256 amount) internal pure returns (TradeParams memory) {
        int256 iAmount = int256(amount);
        return TradeParams({ positionId: positionId, quantity: iAmount, limitPrice: 0, cashflowCcy: Currency.Base, cashflow: iAmount });
    }

    function _closeParams(PositionId positionId) internal pure returns (TradeParams memory) {
        return TradeParams({ positionId: positionId, quantity: type(int128).min, limitPrice: 0, cashflowCcy: Currency.Base, cashflow: -1 });
    }

    function _pullPosition(PositionPermit memory permit, address owner) internal returns (PositionId positionId) {
        positionId = permit.positionId;
        uint256 tokenId = positionId.asUint();
        erc721Permit2.permitTransferFrom({
            permit: IERC721Permit2.PermitTransferFrom({
                permitted: IERC721Permit2.TokenPermissions({ token: address(positionNFT), tokenId: tokenId }),
                nonce: uint256(keccak256(abi.encode(owner, positionNFT, positionId, permit.deadline))),
                deadline: permit.deadline
            }),
            transferDetails: IERC721Permit2.SignatureTransferDetails({ to: address(this), tokenId: tokenId }),
            owner: owner,
            signature: abi.encodePacked(permit.r, permit.vs)
        });
    }

    function _pullFundsWithPermit(address token, EIP2098Permit memory permit, uint256 amount, address owner, address to)
        internal
        returns (uint256)
    {
        // Inspired by https://github.com/Uniswap/permit2/blob/main/src/libraries/SignatureVerification.sol
        IERC20Permit(token).safePermit({
            owner: owner,
            spender: address(this),
            value: permit.amount,
            deadline: permit.deadline,
            r: permit.r,
            v: uint8(uint256(permit.vs >> 255)) + 27,
            s: permit.vs & bytes32(uint256(type(int256).max))
        });
        return IERC20(token).transferOut(owner, to, amount);
    }

    function _pullFundsWithPermit2(IERC20 token, EIP2098Permit memory permit, uint256 amount, address owner, address to)
        public
        returns (uint256)
    {
        erc20Permit2.permitTransferFrom({
            permit: IPermit2.PermitTransferFrom({
                permitted: IPermit2.TokenPermissions({ token: address(token), amount: permit.amount }),
                nonce: uint256(keccak256(abi.encode(owner, token, permit.amount, permit.deadline))),
                deadline: permit.deadline
            }),
            transferDetails: IPermit2.SignatureTransferDetails({ to: to, requestedAmount: amount }),
            owner: owner,
            signature: abi.encodePacked(permit.r, permit.vs)
        });
        return amount;
    }

    receive() external payable {
        if (msg.sender != address(nativeToken)) revert NotNativeToken();
    }

    function retrieve(IERC20 token, address to) external onlyRole(DEFAULT_ADMIN_ROLE) {
        token.transferBalance(to);
    }

    function retrieveNative(address payable to) external onlyRole(DEFAULT_ADMIN_ROLE) {
        to.sendValue(address(this).balance);
    }

    function retrieve(PositionId positionId, address to) external onlyRole(DEFAULT_ADMIN_ROLE) {
        positionNFT.safeTransferFrom(address(this), to, positionId.asUint());
    }

    function retrieveFromVault(IERC20 token, address to) external onlyRole(DEFAULT_ADMIN_ROLE) {
        _vaultWithdraw(token, BALANCE, to);
    }

}

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

pragma solidity ^0.8.0;

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

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

pragma solidity ^0.8.0;

import "./IAccessControl.sol";
import "../utils/Context.sol";
import "../utils/Strings.sol";
import "../utils/introspection/ERC165.sol";

/**
 * @dev Contract module that allows children to implement role-based access
 * control mechanisms. This is a lightweight version that doesn't allow enumerating role
 * members except through off-chain means by accessing the contract event logs. Some
 * applications may benefit from on-chain enumerability, for those cases see
 * {AccessControlEnumerable}.
 *
 * Roles are referred to by their `bytes32` identifier. These should be exposed
 * in the external API and be unique. The best way to achieve this is by
 * using `public constant` hash digests:
 *
 * ```solidity
 * bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
 * ```
 *
 * Roles can be used to represent a set of permissions. To restrict access to a
 * function call, use {hasRole}:
 *
 * ```solidity
 * function foo() public {
 *     require(hasRole(MY_ROLE, msg.sender));
 *     ...
 * }
 * ```
 *
 * Roles can be granted and revoked dynamically via the {grantRole} and
 * {revokeRole} functions. Each role has an associated admin role, and only
 * accounts that have a role's admin role can call {grantRole} and {revokeRole}.
 *
 * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
 * that only accounts with this role will be able to grant or revoke other
 * roles. More complex role relationships can be created by using
 * {_setRoleAdmin}.
 *
 * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
 * grant and revoke this role. Extra precautions should be taken to secure
 * accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules}
 * to enforce additional security measures for this role.
 */
abstract contract AccessControl is Context, IAccessControl, ERC165 {
    struct RoleData {
        mapping(address => bool) members;
        bytes32 adminRole;
    }

    mapping(bytes32 => RoleData) private _roles;

    bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;

    /**
     * @dev Modifier that checks that an account has a specific role. Reverts
     * with a standardized message including the required role.
     *
     * The format of the revert reason is given by the following regular expression:
     *
     *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
     *
     * _Available since v4.1._
     */
    modifier onlyRole(bytes32 role) {
        _checkRole(role);
        _;
    }

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
    }

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) public view virtual override returns (bool) {
        return _roles[role].members[account];
    }

    /**
     * @dev Revert with a standard message if `_msgSender()` is missing `role`.
     * Overriding this function changes the behavior of the {onlyRole} modifier.
     *
     * Format of the revert message is described in {_checkRole}.
     *
     * _Available since v4.6._
     */
    function _checkRole(bytes32 role) internal view virtual {
        _checkRole(role, _msgSender());
    }

    /**
     * @dev Revert with a standard message if `account` is missing `role`.
     *
     * The format of the revert reason is given by the following regular expression:
     *
     *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
     */
    function _checkRole(bytes32 role, address account) internal view virtual {
        if (!hasRole(role, account)) {
            revert(
                string(
                    abi.encodePacked(
                        "AccessControl: account ",
                        Strings.toHexString(account),
                        " is missing role ",
                        Strings.toHexString(uint256(role), 32)
                    )
                )
            );
        }
    }

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) {
        return _roles[role].adminRole;
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     *
     * May emit a {RoleGranted} event.
     */
    function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
        _grantRole(role, account);
    }

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     *
     * May emit a {RoleRevoked} event.
     */
    function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
        _revokeRole(role, account);
    }

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been revoked `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `account`.
     *
     * May emit a {RoleRevoked} event.
     */
    function renounceRole(bytes32 role, address account) public virtual override {
        require(account == _msgSender(), "AccessControl: can only renounce roles for self");

        _revokeRole(role, account);
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event. Note that unlike {grantRole}, this function doesn't perform any
     * checks on the calling account.
     *
     * May emit a {RoleGranted} event.
     *
     * [WARNING]
     * ====
     * This function should only be called from the constructor when setting
     * up the initial roles for the system.
     *
     * Using this function in any other way is effectively circumventing the admin
     * system imposed by {AccessControl}.
     * ====
     *
     * NOTE: This function is deprecated in favor of {_grantRole}.
     */
    function _setupRole(bytes32 role, address account) internal virtual {
        _grantRole(role, account);
    }

    /**
     * @dev Sets `adminRole` as ``role``'s admin role.
     *
     * Emits a {RoleAdminChanged} event.
     */
    function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
        bytes32 previousAdminRole = getRoleAdmin(role);
        _roles[role].adminRole = adminRole;
        emit RoleAdminChanged(role, previousAdminRole, adminRole);
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * Internal function without access restriction.
     *
     * May emit a {RoleGranted} event.
     */
    function _grantRole(bytes32 role, address account) internal virtual {
        if (!hasRole(role, account)) {
            _roles[role].members[account] = true;
            emit RoleGranted(role, account, _msgSender());
        }
    }

    /**
     * @dev Revokes `role` from `account`.
     *
     * Internal function without access restriction.
     *
     * May emit a {RoleRevoked} event.
     */
    function _revokeRole(bytes32 role, address account) internal virtual {
        if (hasRole(role, account)) {
            _roles[role].members[account] = false;
            emit RoleRevoked(role, account, _msgSender());
        }
    }
}

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

pragma solidity ^0.8.1;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     *
     * Furthermore, `isContract` will also return true if the target contract within
     * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
     * which only has an effect at the end of a transaction.
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize/address.code.length, which returns 0
        // for contracts in construction, since the code is only stored at the end
        // of the constructor execution.

        return account.code.length > 0;
    }

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

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

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

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

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

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

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

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

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

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
     * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
     *
     * _Available since v4.8._
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        if (success) {
            if (returndata.length == 0) {
                // only check isContract if the call was successful and the return data is empty
                // otherwise we already know that it was a contract
                require(isContract(target), "Address: call to non-contract");
            }
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason or using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    function _revert(bytes memory returndata, string memory errorMessage) private pure {
        // Look for revert reason and bubble it up if present
        if (returndata.length > 0) {
            // The easiest way to bubble the revert reason is using memory via assembly
            /// @solidity memory-safe-assembly
            assembly {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert(errorMessage);
        }
    }
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.20;

interface IPermit2 {

    struct TokenPermissions {
        address token;
        uint256 amount;
    }

    struct PermitTransferFrom {
        TokenPermissions permitted;
        uint256 nonce;
        uint256 deadline;
    }

    struct SignatureTransferDetails {
        address to;
        uint256 requestedAmount;
    }

    function permitTransferFrom(
        PermitTransferFrom memory permit,
        SignatureTransferDetails calldata transferDetails,
        address owner,
        bytes calldata signature
    ) external;

    // solhint-disable-next-line func-name-mixedcase
    function DOMAIN_SEPARATOR() external view returns (bytes32);

}

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

// inspired by https://github.com/Uniswap/permit2/blob/main/src/interfaces/ISignatureTransfer.sol

/// @title IERC721Permit2
/// @notice Handles ERC721 token transfers through signature based actions
/// @dev Requires user's token approval on the Permit2 contract
interface IERC721Permit2 {
    /// @notice Thrown when validating an inputted signature that is stale
    /// @param signatureDeadline The timestamp at which a signature is no longer valid
    error SignatureExpired(uint256 signatureDeadline);

    /// @notice Thrown when validating that the inputted nonce has not been used
    error InvalidNonce();

    /// @notice Thrown when the requested tokenId for a transfer is not the permissioned tokenId
    /// @param tokenId The invalid, requested tokenId
    error InvalidTokenId(uint256 tokenId);

    /// @notice Thrown when the number of tokens permissioned to a spender does not match the number of tokens being transferred
    /// @dev If the spender does not need to transfer the number of tokens permitted, the spender can request amount 0 to be transferred
    error LengthMismatch();

    /// @notice Emits an event when the owner successfully invalidates an unordered nonce.
    event UnorderedNonceInvalidation(address indexed owner, uint256 word, uint256 mask);

    /// @notice The token and amount details for a transfer signed in the permit transfer signature
    struct TokenPermissions {
        // ERC721 token address
        address token;
        // the tokenId to be transferred
        uint256 tokenId;
    }

    /// @notice The signed permit message for a single token transfer
    struct PermitTransferFrom {
        TokenPermissions permitted;
        // a unique value for every token owner's signature to prevent signature replays
        uint256 nonce;
        // deadline on the permit signature
        uint256 deadline;
    }

    /// @notice Specifies the recipient address and amount for batched transfers.
    /// @dev Recipient and tokenId correspond to the index of the signed token permissions array.
    /// @dev Reverts if the requested tokenId is not the permissioned tokenId
    struct SignatureTransferDetails {
        // recipient address
        address to;
        // spender requested tokenId
        uint256 tokenId;
    }

    /// @notice Used to reconstruct the signed permit message for multiple token transfers
    /// @dev Do not need to pass in spender address as it is required that it is msg.sender
    /// @dev Note that a user still signs over a spender address
    struct PermitBatchTransferFrom {
        // the tokens and corresponding amounts permitted for a transfer
        TokenPermissions[] permitted;
        // a unique value for every token owner's signature to prevent signature replays
        uint256 nonce;
        // deadline on the permit signature
        uint256 deadline;
    }

    /**
     * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
     */
    // solhint-disable-next-line func-name-mixedcase
    function DOMAIN_SEPARATOR() external view returns (bytes32);

    /// @notice A map from token owner address and a caller specified word index to a bitmap. Used to set bits in the bitmap to prevent against signature replay protection
    /// @dev Uses unordered nonces so that permit messages do not need to be spent in a certain order
    /// @dev The mapping is indexed first by the token owner, then by an index specified in the nonce
    /// @dev It returns a uint256 bitmap
    /// @dev The index, or wordPosition is capped at type(uint248).max
    function nonceBitmap(address, uint256) external view returns (uint256);

    /// @notice Transfers a token using a signed permit message
    /// @dev Reverts if the requested amount is greater than the permitted signed amount
    /// @param permit The permit data signed over by the owner
    /// @param owner The owner of the tokens to transfer
    /// @param transferDetails The spender's requested transfer details for the permitted token
    /// @param signature The signature to verify
    function permitTransferFrom(
        PermitTransferFrom memory permit,
        SignatureTransferDetails calldata transferDetails,
        address owner,
        bytes calldata signature
    ) external;

    /// @notice Transfers multiple tokens using a signed permit message
    /// @param permit The permit data signed over by the owner
    /// @param owner The owner of the tokens to transfer
    /// @param transferDetails Specifies the recipient and requested amount for the token transfer
    /// @param signature The signature to verify
    function permitTransferFrom(
        PermitBatchTransferFrom memory permit,
        SignatureTransferDetails[] calldata transferDetails,
        address owner,
        bytes calldata signature
    ) external;

    /// @notice Invalidates the bits specified in mask for the bitmap at the word position
    /// @dev The wordPos is maxed at type(uint248).max
    /// @param wordPos A number to index the nonceBitmap at
    /// @param mask A bitmap masked against msg.sender's current bitmap at the word position
    function invalidateUnorderedNonces(uint256 wordPos, uint256 mask) external;
}

File 8 of 61 : ContangoLens.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.20;

import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";

import "../interfaces/IContango.sol";
import "../interfaces/IContangoOracle.sol";
import "./interfaces/IFlashBorrowProvider.sol";
import "./interfaces/IMoneyMarketView.sol";

contract ContangoLens is AccessControlUpgradeable, UUPSUpgradeable, IContangoOracle {

    event MoneyMarketViewRegistered(MoneyMarketId indexed mm, IMoneyMarketView indexed moneyMarketView);

    error CallFailed(address target, bytes4 selector);
    error InvalidMoneyMarket(MoneyMarketId mm);

    struct BorrowingLending {
        uint256 borrowing;
        uint256 lending;
    }

    struct TokenMetadata {
        string name;
        string symbol;
        uint8 decimals;
        uint256 unit;
    }

    struct MetaData {
        Instrument instrument;
        Balances balances;
        Balances balancesUSD;
        Prices prices;
        Prices pricesUSD;
        uint256 ltv;
        uint256 liquidationThreshold;
        BorrowingLending rates;
        BorrowingLending liquidity;
        Reward[] borrowingRewards;
        Reward[] lendingRewards;
        bytes irmRaw;
        AvailableActions[] availableActions;
        Limits limits;
        uint256 fee;
        bool supportsFlashBorrow;
        TokenMetadata baseToken;
        TokenMetadata quoteToken;
    }

    IContango public immutable contango;
    PositionNFT public immutable positionNFT;
    mapping(MoneyMarketId mmId => IMoneyMarketView mmv) public moneyMarketViews;

    constructor(IContango _contango) {
        contango = _contango;
        positionNFT = _contango.positionNFT();
    }

    function initialize(Timelock timelock) public initializer {
        __AccessControl_init_unchained();
        __UUPSUpgradeable_init_unchained();
        _grantRole(DEFAULT_ADMIN_ROLE, Timelock.unwrap(timelock));
    }

    function setMoneyMarketView(IMoneyMarketView immv) public onlyRole(DEFAULT_ADMIN_ROLE) {
        MoneyMarketId mm = immv.moneyMarketId();
        moneyMarketViews[mm] = immv;
        emit MoneyMarketViewRegistered(mm, immv);
    }

    function moneyMarketId(PositionId positionId) public view returns (MoneyMarketId) {
        return moneyMarketView(positionId).moneyMarketId();
    }

    function moneyMarketId(MoneyMarketId mmId) public view returns (MoneyMarketId) {
        return moneyMarketView(mmId).moneyMarketId();
    }

    function moneyMarketName(PositionId positionId) public view returns (string memory) {
        return moneyMarketView(positionId).moneyMarketName();
    }

    function moneyMarketName(MoneyMarketId mmId) public view returns (string memory) {
        return moneyMarketView(mmId).moneyMarketName();
    }

    function balances(PositionId positionId) public returns (Balances memory balances_) {
        return positionNFT.exists(positionId) ? moneyMarketView(positionId).balances(positionId) : balances_;
    }

    function prices(PositionId positionId) public view returns (Prices memory prices_) {
        return moneyMarketView(positionId).prices(positionId);
    }

    function balancesUSD(PositionId positionId) public returns (Balances memory balancesUSD_) {
        return positionNFT.exists(positionId) ? moneyMarketView(positionId).balancesUSD(positionId) : balancesUSD_;
    }

    function priceInNativeToken(PositionId positionId, IERC20 asset) public view returns (uint256 price_) {
        return moneyMarketView(positionId).priceInNativeToken(asset);
    }

    function priceInNativeToken(MoneyMarketId mmId, IERC20 asset) public view returns (uint256 price_) {
        return moneyMarketView(mmId).priceInNativeToken(asset);
    }

    function priceInUSD(PositionId positionId, IERC20 asset) public view returns (uint256 price_) {
        return moneyMarketView(positionId).priceInUSD(asset);
    }

    function priceInUSD(MoneyMarketId mmId, IERC20 asset) public view returns (uint256 price_) {
        return moneyMarketView(mmId).priceInUSD(asset);
    }

    function baseQuoteRate(PositionId positionId) public view returns (uint256) {
        return moneyMarketView(positionId).baseQuoteRate(positionId);
    }

    function thresholds(PositionId positionId) public view returns (uint256 ltv, uint256 liquidationThreshold) {
        return moneyMarketView(positionId).thresholds(positionId);
    }

    function liquidity(PositionId positionId) public view returns (uint256 borrowing, uint256 lending) {
        return moneyMarketView(positionId).liquidity(positionId);
    }

    function rates(PositionId positionId) public view returns (uint256 borrowing, uint256 lending) {
        return moneyMarketView(positionId).rates(positionId);
    }

    function irmRaw(PositionId positionId) external view returns (bytes memory data) {
        return moneyMarketView(positionId).irmRaw(positionId);
    }

    function rewards(PositionId positionId) public returns (Reward[] memory borrowing, Reward[] memory lending) {
        return moneyMarketView(positionId).rewards(positionId);
    }

    function availableActions(PositionId positionId) external returns (AvailableActions[] memory available) {
        return moneyMarketView(positionId).availableActions(positionId);
    }

    function limits(PositionId positionId) external view returns (Limits memory limits_) {
        return moneyMarketView(positionId).limits(positionId);
    }

    function moneyMarketView(PositionId positionId) public view returns (IMoneyMarketView moneyMarketView_) {
        return moneyMarketView(positionId.getMoneyMarket());
    }

    function moneyMarketView(MoneyMarketId mmId) public view returns (IMoneyMarketView moneyMarketView_) {
        moneyMarketView_ = moneyMarketViews[mmId];
        if (address(moneyMarketView_) == address(0)) revert InvalidMoneyMarket(mmId);
    }

    function leverage(PositionId positionId) public returns (uint256 leverage_) {
        if (!positionNFT.exists(positionId)) return 0;

        Instrument memory instrument = contango.instrument(positionId.getSymbol());

        Balances memory _balances = balances(positionId);
        Prices memory _prices = prices(positionId);
        uint256 collateralValue = _balances.collateral * _prices.collateral / instrument.baseUnit;
        uint256 debtValue = _balances.debt * _prices.debt / instrument.quoteUnit;

        leverage_ = collateralValue * WAD / (collateralValue - debtValue);
    }

    function netRate(PositionId positionId) public returns (int256 netRate_) {
        if (!positionNFT.exists(positionId)) return 0;

        (uint256 borrowing, uint256 lending) = rates(positionId);
        netRate_ = int256(lending) - int256(borrowing * WAD / leverage(positionId));
    }

    function _authorizeUpgrade(address newImplementation) internal override onlyRole(DEFAULT_ADMIN_ROLE) { }

    function metaData(PositionId positionId) external returns (MetaData memory metaData_) {
        Instrument memory instrument = contango.instrument(positionId.getSymbol());
        IMoneyMarketView mmv = moneyMarketView(positionId);
        metaData_.instrument = instrument;
        if (positionNFT.exists(positionId)) {
            metaData_.balances = mmv.balances(positionId);
            metaData_.balancesUSD = mmv.balancesUSD(positionId);
        }
        metaData_.prices = mmv.prices(positionId);
        metaData_.pricesUSD.collateral = mmv.priceInUSD(instrument.base);
        metaData_.pricesUSD.debt = mmv.priceInUSD(instrument.quote);
        metaData_.pricesUSD.unit = WAD;
        (metaData_.ltv, metaData_.liquidationThreshold) = mmv.thresholds(positionId);
        (metaData_.rates.borrowing, metaData_.rates.lending) = mmv.rates(positionId);
        (metaData_.liquidity.borrowing, metaData_.liquidity.lending) = mmv.liquidity(positionId);
        (metaData_.borrowingRewards, metaData_.lendingRewards) = mmv.rewards(positionId);
        metaData_.irmRaw = mmv.irmRaw(positionId);
        metaData_.availableActions = mmv.availableActions(positionId);
        metaData_.limits = mmv.limits(positionId);
        metaData_.fee = contango.feeManager().feeModel().calculateFee(address(0), positionId, WAD);
        metaData_.supportsFlashBorrow =
            contango.positionFactory().moneyMarket(positionId.getMoneyMarket()).supportsInterface(type(IFlashBorrowProvider).interfaceId);
        metaData_.baseToken = _tokenMetadata(instrument.base);
        metaData_.quoteToken = _tokenMetadata(instrument.quote);
    }

    function _tokenMetadata(IERC20 token) internal view returns (TokenMetadata memory tokenMetadata_) {
        tokenMetadata_.name = _tryString(token, token.name);
        tokenMetadata_.symbol = _tryString(token, token.symbol);
        tokenMetadata_.decimals = token.decimals();
        tokenMetadata_.unit = 10 ** tokenMetadata_.decimals;
    }

    function _tryString(IERC20 token, function () external view returns (string memory) f) private view returns (string memory) {
        (bool success, bytes memory result) = address(token).staticcall(abi.encodeWithSelector(f.selector));
        if (!success) revert CallFailed(address(token), f.selector);
        return result.length > 32 ? abi.decode(result, (string)) : bytes32ToString(abi.decode(result, (bytes32)));
    }

    function bytes32ToString(bytes32 _bytes32) internal pure returns (string memory) {
        uint8 i = 0;
        while (i < 32 && _bytes32[i] != 0) i++;
        bytes memory bytesArray = new bytes(i);
        for (i = 0; i < 32 && _bytes32[i] != 0; i++) {
            bytesArray[i] = _bytes32[i];
        }
        return string(bytesArray);
    }

}

File 9 of 61 : IMaestro.sol
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import { IERC20Metadata as IERC20 } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol";

import "../libraries/DataTypes.sol";
import "../interfaces/IOrderManager.sol";
import "../utils/SimpleSpotExecutor.sol";
import { IPermit2 } from "../dependencies/Uniswap.sol";

struct LinkedOrderParams {
    uint128 limitPrice; // in quote currency
    uint128 tolerance; // 0.003e4 = 0.3%
    Currency cashflowCcy;
    uint32 deadline;
    OrderType orderType;
}

struct EIP2098Permit {
    uint256 amount;
    uint256 deadline;
    bytes32 r;
    bytes32 vs;
}

struct SwapData {
    address router;
    address spender;
    uint256 amountIn;
    uint256 minAmountOut;
    bytes swapBytes;
}

interface IMaestro is IContangoErrors, IOrderManagerErrors, IVaultErrors {

    error InvalidCashflow();
    error InsufficientPermitAmount(uint256 required, uint256 actual);
    error MismatchingPositionId(OrderId orderId1, OrderId orderId2);
    error NotNativeToken(IERC20 token);

    function contango() external view returns (IContango);
    function orderManager() external view returns (IOrderManager);
    function vault() external view returns (IVault);
    function positionNFT() external view returns (PositionNFT);
    function nativeToken() external view returns (IWETH9);
    function spotExecutor() external view returns (SimpleSpotExecutor);
    function permit2() external view returns (IPermit2);

    // =================== Funding primitives ===================

    function deposit(IERC20 token, uint256 amount) external payable returns (uint256);

    function depositNative() external payable returns (uint256);

    function depositWithPermit(IERC20Permit token, EIP2098Permit calldata permit, uint256 amount) external payable returns (uint256);

    function depositWithPermit2(IERC20 token, EIP2098Permit calldata permit, uint256 amount) external payable returns (uint256);

    function withdraw(IERC20 token, uint256 amount, address to) external payable returns (uint256);

    function withdrawNative(uint256 amount, address to) external payable returns (uint256);

    // =================== Trading actions ===================

    function trade(TradeParams calldata tradeParams, ExecutionParams calldata execParams)
        external
        payable
        returns (PositionId, Trade memory);

    function depositAndTrade(TradeParams calldata tradeParams, ExecutionParams calldata execParams)
        external
        payable
        returns (PositionId, Trade memory);

    function depositAndTradeWithPermit(TradeParams calldata tradeParams, ExecutionParams calldata execParams, EIP2098Permit calldata permit)
        external
        payable
        returns (PositionId, Trade memory);

    function tradeAndWithdraw(TradeParams calldata tradeParams, ExecutionParams calldata execParams, address to)
        external
        payable
        returns (PositionId positionId, Trade memory trade_, uint256 amount);

    function tradeAndWithdrawNative(TradeParams calldata tradeParams, ExecutionParams calldata execParams, address to)
        external
        payable
        returns (PositionId positionId, Trade memory trade_, uint256 amount);

    function swapAndDeposit(IERC20 tokenToSell, IERC20 tokenToDeposit, SwapData calldata swapData) external payable returns (uint256);

    function swapAndDepositNative(IERC20 tokenToDeposit, SwapData calldata swapData) external payable returns (uint256);

    function swapAndDepositWithPermit(IERC20 tokenToSell, IERC20 tokenToDeposit, SwapData calldata swapData, EIP2098Permit calldata permit)
        external
        payable
        returns (uint256);

    function swapAndDepositWithPermit2(IERC20 tokenToSell, IERC20 tokenToDeposit, SwapData calldata swapData, EIP2098Permit calldata permit)
        external
        payable
        returns (uint256);

    function tradeAndLinkedOrder(
        TradeParams calldata tradeParams,
        ExecutionParams calldata execParams,
        LinkedOrderParams memory linkedOrderParams
    ) external payable returns (PositionId positionId, Trade memory trade_, OrderId linkedOrderId);

    function tradeAndLinkedOrders(
        TradeParams calldata tradeParams,
        ExecutionParams calldata execParams,
        LinkedOrderParams memory linkedOrderParams1,
        LinkedOrderParams memory linkedOrderParams2
    ) external payable returns (PositionId positionId, Trade memory trade_, OrderId linkedOrderId1, OrderId linkedOrderId2);

    function depositTradeAndLinkedOrder(
        TradeParams calldata tradeParams,
        ExecutionParams calldata execParams,
        LinkedOrderParams memory linkedOrderParams
    ) external payable returns (PositionId positionId, Trade memory trade_, OrderId linkedOrderId);

    function depositTradeAndLinkedOrderWithPermit(
        TradeParams calldata tradeParams,
        ExecutionParams calldata execParams,
        LinkedOrderParams memory linkedOrderParams,
        EIP2098Permit calldata permit
    ) external payable returns (PositionId positionId, Trade memory trade_, OrderId linkedOrderId);

    function depositTradeAndLinkedOrders(
        TradeParams calldata tradeParams,
        ExecutionParams calldata execParams,
        LinkedOrderParams memory linkedOrderParams1,
        LinkedOrderParams memory linkedOrderParams2
    ) external payable returns (PositionId positionId, Trade memory trade_, OrderId linkedOrderId1, OrderId linkedOrderId2);

    function depositTradeAndLinkedOrdersWithPermit(
        TradeParams calldata tradeParams,
        ExecutionParams calldata execParams,
        LinkedOrderParams memory linkedOrderParams1,
        LinkedOrderParams memory linkedOrderParams2,
        EIP2098Permit calldata permit
    ) external payable returns (PositionId positionId, Trade memory trade_, OrderId linkedOrderId1, OrderId linkedOrderId2);

    function place(OrderParams memory params) external payable returns (OrderId orderId);

    function placeLinkedOrder(PositionId positionId, LinkedOrderParams memory params) external payable returns (OrderId orderId);

    function placeLinkedOrders(
        PositionId positionId,
        LinkedOrderParams memory linkedOrderParams1,
        LinkedOrderParams memory linkedOrderParams2
    ) external payable returns (OrderId linkedOrderId1, OrderId linkedOrderId2);

    function depositAndPlace(OrderParams memory params) external payable returns (OrderId orderId);

    function depositAndPlaceWithPermit(OrderParams memory params, EIP2098Permit calldata permit)
        external
        payable
        returns (OrderId orderId);

    function cancel(OrderId orderId) external payable;

    function cancel(OrderId orderId1, OrderId orderId2) external payable;

    function cancelReplaceLinkedOrder(OrderId cancelOrderId, LinkedOrderParams memory newLinkedOrderParams)
        external
        payable
        returns (OrderId newLinkedOrderId);

    function cancelReplaceLinkedOrders(
        OrderId cancelOrderId1,
        OrderId cancelOrderId2,
        LinkedOrderParams memory newLinkedOrderParams1,
        LinkedOrderParams memory newLinkedOrderParams2
    ) external payable returns (OrderId newLinkedOrderId1, OrderId newLinkedOrderId2);

    function cancelAndWithdraw(OrderId orderId, address to) external payable returns (uint256);

    function cancelAndWithdrawNative(OrderId orderId, address to) external payable returns (uint256);

}

//SPDX-License-Identifier: MIT
pragma solidity 0.8.20;

import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/utils/Address.sol";
import "@openzeppelin/contracts/utils/math/SafeCast.sol";
import "../dependencies/IWETH9.sol";

library ERC20Lib {

    using Address for address payable;
    using SafeERC20 for IERC20;
    using SafeCast for *;

    error ZeroPayer();
    error ZeroDestination();

    function transferOutNative(IWETH9 token, address payable to, uint256 amount) internal returns (uint256 amountTransferred) {
        if (to == address(0)) revert ZeroDestination();
        if (amount == 0) return amount;

        token.withdraw(amount);
        to.sendValue(amount);

        return amount;
    }

    function transferOut(IERC20 token, address payer, address to, uint256 amount) internal returns (uint256 amountTransferred) {
        if (payer == address(0)) revert ZeroPayer();
        if (to == address(0)) revert ZeroDestination();
        if (payer == to || amount == 0) return amount;

        return _transferOut(token, payer, to, amount);
    }

    function _transferOut(IERC20 token, address payer, address to, uint256 amount) internal returns (uint256 amountTransferred) {
        payer == address(this) ? token.safeTransfer(to, amount) : token.safeTransferFrom(payer, to, amount);
        return amount;
    }

    function transferBalance(IERC20 token, address to) internal returns (uint256 balance) {
        balance = myBalance(token);
        if (balance > 0) transferOut(token, address(this), to, balance);
    }

    function myBalance(IERC20 token) internal view returns (uint256) {
        return token.balanceOf(address(this));
    }

    function myBalanceI(IERC20 token) internal view returns (int256) {
        return myBalance(token).toInt256();
    }

    function approveIfNecessary(IERC20 asset, address spender) internal {
        if (asset.allowance(address(this), spender) == 0) asset.forceApprove(spender, type(uint256).max);
    }

    function unit(IERC20 token) internal view returns (uint256) {
        return 10 ** token.decimals();
    }

    function infiniteApproval(IERC20 token, address addr) internal {
        token.forceApprove(addr, type(uint256).max);
    }

}

File 11 of 61 : PositionPermit.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.20;

import "../libraries/DataTypes.sol";

struct PositionPermit {
    PositionId positionId;
    uint256 deadline;
    bytes32 r;
    bytes32 vs;
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)

pragma solidity ^0.8.0;

/**
 * @dev External interface of AccessControl declared to support ERC165 detection.
 */
interface IAccessControl {
    /**
     * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
     *
     * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
     * {RoleAdminChanged} not being emitted signaling this.
     *
     * _Available since v3.1._
     */
    event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);

    /**
     * @dev Emitted when `account` is granted `role`.
     *
     * `sender` is the account that originated the contract call, an admin role
     * bearer except when using {AccessControl-_setupRole}.
     */
    event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Emitted when `account` is revoked `role`.
     *
     * `sender` is the account that originated the contract call:
     *   - if using `revokeRole`, it is the admin role bearer
     *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
     */
    event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) external view returns (bool);

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {AccessControl-_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) external view returns (bytes32);

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function grantRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function revokeRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been granted `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `account`.
     */
    function renounceRole(bytes32 role, address account) external;
}

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

pragma solidity ^0.8.0;

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

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

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

pragma solidity ^0.8.0;

import "./math/Math.sol";
import "./math/SignedMath.sol";

/**
 * @dev String operations.
 */
library Strings {
    bytes16 private constant _SYMBOLS = "0123456789abcdef";
    uint8 private constant _ADDRESS_LENGTH = 20;

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        unchecked {
            uint256 length = Math.log10(value) + 1;
            string memory buffer = new string(length);
            uint256 ptr;
            /// @solidity memory-safe-assembly
            assembly {
                ptr := add(buffer, add(32, length))
            }
            while (true) {
                ptr--;
                /// @solidity memory-safe-assembly
                assembly {
                    mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
                }
                value /= 10;
                if (value == 0) break;
            }
            return buffer;
        }
    }

    /**
     * @dev Converts a `int256` to its ASCII `string` decimal representation.
     */
    function toString(int256 value) internal pure returns (string memory) {
        return string(abi.encodePacked(value < 0 ? "-" : "", toString(SignedMath.abs(value))));
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        unchecked {
            return toHexString(value, Math.log256(value) + 1);
        }
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
     */
    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
        bytes memory buffer = new bytes(2 * length + 2);
        buffer[0] = "0";
        buffer[1] = "x";
        for (uint256 i = 2 * length + 1; i > 1; --i) {
            buffer[i] = _SYMBOLS[value & 0xf];
            value >>= 4;
        }
        require(value == 0, "Strings: hex length insufficient");
        return string(buffer);
    }

    /**
     * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
     */
    function toHexString(address addr) internal pure returns (string memory) {
        return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
    }

    /**
     * @dev Returns true if the two strings are equal.
     */
    function equal(string memory a, string memory b) internal pure returns (bool) {
        return keccak256(bytes(a)) == keccak256(bytes(b));
    }
}

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

pragma solidity ^0.8.0;

import "./IERC165.sol";

/**
 * @dev Implementation of the {IERC165} interface.
 *
 * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
 * for the additional interface id that will be supported. For example:
 *
 * ```solidity
 * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
 *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
 * }
 * ```
 *
 * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
 */
abstract contract ERC165 is IERC165 {
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IERC165).interfaceId;
    }
}

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

pragma solidity ^0.8.0;

import "../../interfaces/draft-IERC1822Upgradeable.sol";
import "../ERC1967/ERC1967UpgradeUpgradeable.sol";
import "./Initializable.sol";

/**
 * @dev An upgradeability mechanism designed for UUPS proxies. The functions included here can perform an upgrade of an
 * {ERC1967Proxy}, when this contract is set as the implementation behind such a proxy.
 *
 * A security mechanism ensures that an upgrade does not turn off upgradeability accidentally, although this risk is
 * reinstated if the upgrade retains upgradeability but removes the security mechanism, e.g. by replacing
 * `UUPSUpgradeable` with a custom implementation of upgrades.
 *
 * The {_authorizeUpgrade} function must be overridden to include access restriction to the upgrade mechanism.
 *
 * _Available since v4.1._
 */
abstract contract UUPSUpgradeable is Initializable, IERC1822ProxiableUpgradeable, ERC1967UpgradeUpgradeable {
    function __UUPSUpgradeable_init() internal onlyInitializing {
    }

    function __UUPSUpgradeable_init_unchained() internal onlyInitializing {
    }
    /// @custom:oz-upgrades-unsafe-allow state-variable-immutable state-variable-assignment
    address private immutable __self = address(this);

    /**
     * @dev Check that the execution is being performed through a delegatecall call and that the execution context is
     * a proxy contract with an implementation (as defined in ERC1967) pointing to self. This should only be the case
     * for UUPS and transparent proxies that are using the current contract as their implementation. Execution of a
     * function through ERC1167 minimal proxies (clones) would not normally pass this test, but is not guaranteed to
     * fail.
     */
    modifier onlyProxy() {
        require(address(this) != __self, "Function must be called through delegatecall");
        require(_getImplementation() == __self, "Function must be called through active proxy");
        _;
    }

    /**
     * @dev Check that the execution is not being performed through a delegate call. This allows a function to be
     * callable on the implementing contract but not through proxies.
     */
    modifier notDelegated() {
        require(address(this) == __self, "UUPSUpgradeable: must not be called through delegatecall");
        _;
    }

    /**
     * @dev Implementation of the ERC1822 {proxiableUUID} function. This returns the storage slot used by the
     * implementation. It is used to validate the implementation's compatibility when performing an upgrade.
     *
     * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
     * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
     * function revert if invoked through a proxy. This is guaranteed by the `notDelegated` modifier.
     */
    function proxiableUUID() external view virtual override notDelegated returns (bytes32) {
        return _IMPLEMENTATION_SLOT;
    }

    /**
     * @dev Upgrade the implementation of the proxy to `newImplementation`.
     *
     * Calls {_authorizeUpgrade}.
     *
     * Emits an {Upgraded} event.
     *
     * @custom:oz-upgrades-unsafe-allow-reachable delegatecall
     */
    function upgradeTo(address newImplementation) public virtual onlyProxy {
        _authorizeUpgrade(newImplementation);
        _upgradeToAndCallUUPS(newImplementation, new bytes(0), false);
    }

    /**
     * @dev Upgrade the implementation of the proxy to `newImplementation`, and subsequently execute the function call
     * encoded in `data`.
     *
     * Calls {_authorizeUpgrade}.
     *
     * Emits an {Upgraded} event.
     *
     * @custom:oz-upgrades-unsafe-allow-reachable delegatecall
     */
    function upgradeToAndCall(address newImplementation, bytes memory data) public payable virtual onlyProxy {
        _authorizeUpgrade(newImplementation);
        _upgradeToAndCallUUPS(newImplementation, data, true);
    }

    /**
     * @dev Function that should revert when `msg.sender` is not authorized to upgrade the contract. Called by
     * {upgradeTo} and {upgradeToAndCall}.
     *
     * Normally, this function will use an xref:access.adoc[access control] modifier such as {Ownable-onlyOwner}.
     *
     * ```solidity
     * function _authorizeUpgrade(address) internal override onlyOwner {}
     * ```
     */
    function _authorizeUpgrade(address newImplementation) internal virtual;

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[50] private __gap;
}

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

pragma solidity ^0.8.0;

import "./IAccessControlUpgradeable.sol";
import "../utils/ContextUpgradeable.sol";
import "../utils/StringsUpgradeable.sol";
import "../utils/introspection/ERC165Upgradeable.sol";
import "../proxy/utils/Initializable.sol";

/**
 * @dev Contract module that allows children to implement role-based access
 * control mechanisms. This is a lightweight version that doesn't allow enumerating role
 * members except through off-chain means by accessing the contract event logs. Some
 * applications may benefit from on-chain enumerability, for those cases see
 * {AccessControlEnumerable}.
 *
 * Roles are referred to by their `bytes32` identifier. These should be exposed
 * in the external API and be unique. The best way to achieve this is by
 * using `public constant` hash digests:
 *
 * ```solidity
 * bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
 * ```
 *
 * Roles can be used to represent a set of permissions. To restrict access to a
 * function call, use {hasRole}:
 *
 * ```solidity
 * function foo() public {
 *     require(hasRole(MY_ROLE, msg.sender));
 *     ...
 * }
 * ```
 *
 * Roles can be granted and revoked dynamically via the {grantRole} and
 * {revokeRole} functions. Each role has an associated admin role, and only
 * accounts that have a role's admin role can call {grantRole} and {revokeRole}.
 *
 * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
 * that only accounts with this role will be able to grant or revoke other
 * roles. More complex role relationships can be created by using
 * {_setRoleAdmin}.
 *
 * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
 * grant and revoke this role. Extra precautions should be taken to secure
 * accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules}
 * to enforce additional security measures for this role.
 */
abstract contract AccessControlUpgradeable is Initializable, ContextUpgradeable, IAccessControlUpgradeable, ERC165Upgradeable {
    function __AccessControl_init() internal onlyInitializing {
    }

    function __AccessControl_init_unchained() internal onlyInitializing {
    }
    struct RoleData {
        mapping(address => bool) members;
        bytes32 adminRole;
    }

    mapping(bytes32 => RoleData) private _roles;

    bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;

    /**
     * @dev Modifier that checks that an account has a specific role. Reverts
     * with a standardized message including the required role.
     *
     * The format of the revert reason is given by the following regular expression:
     *
     *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
     *
     * _Available since v4.1._
     */
    modifier onlyRole(bytes32 role) {
        _checkRole(role);
        _;
    }

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IAccessControlUpgradeable).interfaceId || super.supportsInterface(interfaceId);
    }

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) public view virtual override returns (bool) {
        return _roles[role].members[account];
    }

    /**
     * @dev Revert with a standard message if `_msgSender()` is missing `role`.
     * Overriding this function changes the behavior of the {onlyRole} modifier.
     *
     * Format of the revert message is described in {_checkRole}.
     *
     * _Available since v4.6._
     */
    function _checkRole(bytes32 role) internal view virtual {
        _checkRole(role, _msgSender());
    }

    /**
     * @dev Revert with a standard message if `account` is missing `role`.
     *
     * The format of the revert reason is given by the following regular expression:
     *
     *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
     */
    function _checkRole(bytes32 role, address account) internal view virtual {
        if (!hasRole(role, account)) {
            revert(
                string(
                    abi.encodePacked(
                        "AccessControl: account ",
                        StringsUpgradeable.toHexString(account),
                        " is missing role ",
                        StringsUpgradeable.toHexString(uint256(role), 32)
                    )
                )
            );
        }
    }

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) {
        return _roles[role].adminRole;
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     *
     * May emit a {RoleGranted} event.
     */
    function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
        _grantRole(role, account);
    }

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     *
     * May emit a {RoleRevoked} event.
     */
    function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
        _revokeRole(role, account);
    }

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been revoked `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `account`.
     *
     * May emit a {RoleRevoked} event.
     */
    function renounceRole(bytes32 role, address account) public virtual override {
        require(account == _msgSender(), "AccessControl: can only renounce roles for self");

        _revokeRole(role, account);
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event. Note that unlike {grantRole}, this function doesn't perform any
     * checks on the calling account.
     *
     * May emit a {RoleGranted} event.
     *
     * [WARNING]
     * ====
     * This function should only be called from the constructor when setting
     * up the initial roles for the system.
     *
     * Using this function in any other way is effectively circumventing the admin
     * system imposed by {AccessControl}.
     * ====
     *
     * NOTE: This function is deprecated in favor of {_grantRole}.
     */
    function _setupRole(bytes32 role, address account) internal virtual {
        _grantRole(role, account);
    }

    /**
     * @dev Sets `adminRole` as ``role``'s admin role.
     *
     * Emits a {RoleAdminChanged} event.
     */
    function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
        bytes32 previousAdminRole = getRoleAdmin(role);
        _roles[role].adminRole = adminRole;
        emit RoleAdminChanged(role, previousAdminRole, adminRole);
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * Internal function without access restriction.
     *
     * May emit a {RoleGranted} event.
     */
    function _grantRole(bytes32 role, address account) internal virtual {
        if (!hasRole(role, account)) {
            _roles[role].members[account] = true;
            emit RoleGranted(role, account, _msgSender());
        }
    }

    /**
     * @dev Revokes `role` from `account`.
     *
     * Internal function without access restriction.
     *
     * May emit a {RoleRevoked} event.
     */
    function _revokeRole(bytes32 role, address account) internal virtual {
        if (hasRole(role, account)) {
            _roles[role].members[account] = false;
            emit RoleRevoked(role, account, _msgSender());
        }
    }

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[49] private __gap;
}

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

import "erc7399/IERC7399.sol";

import "../core/PositionNFT.sol";
import "../interfaces/IContango.sol";
import "../interfaces/IFeeManager.sol";
import "../interfaces/IVault.sol";
import "../libraries/DataTypes.sol";
import "../moneymarkets/interfaces/IUnderlyingPositionFactory.sol";

struct SwapInfo {
    Currency inputCcy;
    int256 input;
    int256 output;
    uint256 price; // in quote currency
}

struct Trade {
    int256 quantity;
    SwapInfo swap;
    Currency cashflowCcy;
    int256 cashflow; // negative when removing from position, positive otherwise
    uint256 fee;
    Currency feeCcy;
    uint256 forwardPrice;
}

struct TradeParams {
    PositionId positionId; // existing position or a new one when coded with number 0 (see ../libraries/DataTypes.sol and test/Encoder.sol)
    int256 quantity;
    uint256 limitPrice; // in quote currency
    Currency cashflowCcy;
    int256 cashflow;
}

struct ExecutionParams {
    address spender;
    address router;
    uint256 swapAmount;
    bytes swapBytes;
    IERC7399 flashLoanProvider;
}

struct Instrument {
    IERC20 base;
    uint256 baseUnit; // e.g. WETH: 1e18
    IERC20 quote;
    uint256 quoteUnit; // e.g. USDC: 1e6
    bool closingOnly;
}

interface IContangoEvents {

    event PositionUpserted(
        PositionId indexed positionId,
        address indexed owner,
        address indexed tradedBy,
        Currency cashflowCcy,
        int256 cashflow,
        int256 quantityDelta,
        uint256 price,
        uint256 fee,
        Currency feeCcy
    );

    event ClosingOnlySet(Symbol indexed symbol, bool closingOnly);
    event InstrumentCreated(Symbol indexed symbol, IERC20 base, IERC20 quote);
    event MoneyMarketRegistered(MoneyMarketId indexed id, IMoneyMarket moneyMarket);
    event PositionDonated(PositionId indexed positionId, address indexed from, address indexed to);
    event RewardsClaimed(PositionId indexed positionId, address indexed to);

}

interface IContangoErrors {

    error CashflowCcyRequired(); // 0x2bed762a
    error ClosingOnly(); // 0x1dacbd6f
    error InsufficientBaseOnOpen(uint256 expected, int256 actual); // 0x49cb41d9
    error InsufficientBaseCashflow(int256 expected, int256 actual); // 0x0ef42287
    error InstrumentAlreadyExists(Symbol symbol); // 0x6170624c
    error InvalidInstrument(Symbol symbol); // 0x2d5bccd2
    error NotFlashBorrowProvider(address msgSender); // 0x50459441
    error OnlyFullClosureAllowedAfterExpiry(); // 0x62a73c9a
    error PriceAboveLimit(uint256 limit, uint256 actual); // 0x6120c45f
    error PriceBelowLimit(uint256 limit, uint256 actual); // 0x756cfc28
    error UnexpectedCallback(); // 0xdab1e993
    error InvalidCashflowCcy(); // 0x2c6ff311
    error UnexpectedTrade(); // 0xf1a9b64c

}

interface IContango is IContangoEvents, IContangoErrors {

    function trade(TradeParams calldata tradeParams, ExecutionParams calldata execParams)
        external
        payable
        returns (PositionId positionId, Trade memory trade);

    function tradeOnBehalfOf(TradeParams calldata tradeParams, ExecutionParams calldata execParams, address onBehalfOf)
        external
        payable
        returns (PositionId positionId, Trade memory trade);

    function claimRewards(PositionId positionId, address to) external;

    function donatePosition(PositionId positionId, address to) external;

    // ======== View ========

    function positionFactory() external view returns (IUnderlyingPositionFactory);
    function instrument(Symbol symbol) external view returns (Instrument memory);
    function positionNFT() external view returns (PositionNFT);
    function vault() external view returns (IVault);
    function feeManager() external view returns (IFeeManager);

    // ======== Admin ========

    function createInstrument(Symbol symbol, IERC20 base, IERC20 quote) external;
    function setClosingOnly(Symbol symbol, bool closingOnly) external;
    function pause() external;
    function unpause() external;

    // ======== Callbacks ========

    function completeClose(address initiator, address repayTo, address asset, uint256 amount, uint256 fee, bytes calldata params)
        external
        returns (bytes memory result);

    function completeOpenFromFlashLoan(
        address initiator,
        address repayTo,
        address asset,
        uint256 amount,
        uint256 fee,
        bytes calldata params
    ) external returns (bytes memory result);

    function completeOpenFromFlashBorrow(IERC20 asset, uint256 amountOwed, bytes calldata params) external returns (bytes memory result);

}

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

import { IERC20Metadata as IERC20 } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import "../libraries/DataTypes.sol";

interface IContangoOracle {

    function priceInNativeToken(PositionId positionId, IERC20 asset) external view returns (uint256 price_);

    function priceInNativeToken(MoneyMarketId mmId, IERC20 asset) external view returns (uint256 price_);

    function priceInUSD(PositionId positionId, IERC20 asset) external view returns (uint256 price_);

    function priceInUSD(MoneyMarketId mmId, IERC20 asset) external view returns (uint256 price_);

    function baseQuoteRate(PositionId positionId) external view returns (uint256);

}

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

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

interface IFlashBorrowProvider {

    error InvalidSenderOrInitiator();

    /// @dev Requests a flash borrow.
    /// @param asset The address of the asset to flash-borrow
    /// @param amount The amount to flash-borrow
    /// @param params Bytes parameters to be passed to the callback
    /// @param callback The callback function to be called after the flash loan
    /// @return result The result of the callback
    function flashBorrow(
        IERC20 asset,
        uint256 amount,
        bytes calldata params,
        /// @dev callback
        /// @param asset Borrowed asset
        /// @param amountOwed The amount to be paid for the flash loan borrowed
        /// @param params The params forwarded to the callback
        /// @return result ABI encoded result of the callback
        function(IERC20, uint256, bytes memory) external returns (bytes memory) callback
    ) external returns (bytes memory result);

}

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

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

import "../../libraries/DataTypes.sol";

struct Balances {
    uint256 collateral;
    uint256 debt;
}

struct Prices {
    uint256 collateral;
    uint256 debt;
    uint256 unit;
}

struct TokenData {
    IERC20 token;
    string name;
    string symbol;
    uint8 decimals;
    uint256 unit;
}

struct Reward {
    TokenData token;
    uint256 rate;
    uint256 claimable;
    uint256 usdPrice;
}

enum AvailableActions {
    Lend,
    Withdraw,
    Borrow,
    Repay
}

struct Limits {
    uint256 minBorrowing;
    uint256 maxBorrowing;
    uint256 minBorrowingForRewards;
    uint256 minLending;
    uint256 maxLending;
    uint256 minLendingForRewards;
}

interface IMoneyMarketView {

    error UnsupportedAsset(IERC20 asset);

    function moneyMarketId() external view returns (MoneyMarketId);

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

    function balances(PositionId positionId) external returns (Balances memory balances_);

    function balancesUSD(PositionId positionId) external returns (Balances memory balances_);

    function prices(PositionId positionId) external view returns (Prices memory prices_);

    function baseQuoteRate(PositionId positionId) external view returns (uint256);

    function priceInNativeToken(IERC20 asset) external view returns (uint256 price_);

    function priceInUSD(IERC20 asset) external view returns (uint256 price_);

    function thresholds(PositionId positionId) external view returns (uint256 ltv, uint256 liquidationThreshold);

    function liquidity(PositionId positionId) external view returns (uint256 borrowing, uint256 lending);

    function rates(PositionId positionId) external view returns (uint256 borrowing, uint256 lending);

    function irmRaw(PositionId positionId) external view returns (bytes memory data);

    function rewards(PositionId positionId) external returns (Reward[] memory borrowing, Reward[] memory lending);

    function availableActions(PositionId positionId) external returns (AvailableActions[] memory available);

    function limits(PositionId positionId) external view returns (Limits memory limits_);

}

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

pragma solidity ^0.8.0;

import "../IERC20.sol";

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

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

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

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

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
 *
 * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
 * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
 * need to send a transaction, and thus is not required to hold Ether at all.
 */
interface IERC20Permit {
    /**
     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
     * given ``owner``'s signed approval.
     *
     * IMPORTANT: The same issues {IERC20-approve} has related to transaction
     * ordering also apply here.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `deadline` must be a timestamp in the future.
     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
     * over the EIP712-formatted function arguments.
     * - the signature must use ``owner``'s current nonce (see {nonces}).
     *
     * For more information on the signature format, see the
     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
     * section].
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    /**
     * @dev Returns the current nonce for `owner`. This value must be
     * included whenever a signature is generated for {permit}.
     *
     * Every successful call to {permit} increases ``owner``'s nonce by one. This
     * prevents a signature from being used multiple times.
     */
    function nonces(address owner) external view returns (uint256);

    /**
     * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
     */
    // solhint-disable-next-line func-name-mixedcase
    function DOMAIN_SEPARATOR() external view returns (bytes32);
}

File 24 of 61 : DataTypes.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.20;

import "./extensions/PositionIdExt.sol";

uint256 constant WAD = 1e18;
uint256 constant RAY = 1e27;
uint256 constant PERCENTAGE_UNIT = 1e4;
uint256 constant ONE_HUNDRED_PERCENT = 1e4;

enum Currency {
    None,
    Base,
    Quote
}

type Symbol is bytes16;

type Payload is bytes5;

type PositionId is bytes32;

using {
    decode,
    getSymbol,
    getNumber,
    getMoneyMarket,
    getExpiry,
    isPerp,
    isExpired,
    withNumber,
    getFlags,
    getPayload,
    asUint
} for PositionId global;

type OrderId is bytes32;

type MoneyMarketId is uint8;

type Timelock is address;

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

import "../libraries/DataTypes.sol";
import "./IContango.sol";
import "./IContangoOracle.sol";

enum OrderType {
    Limit,
    TakeProfit,
    StopLoss
}

struct OrderParams {
    PositionId positionId;
    int128 quantity;
    uint128 limitPrice; // in quote currency
    uint256 tolerance; // 0.003e4 = 0.3%
    int128 cashflow;
    Currency cashflowCcy;
    uint32 deadline;
    OrderType orderType;
}

struct Order {
    address owner;
    PositionId positionId;
    int256 quantity;
    uint256 limitPrice;
    uint256 tolerance;
    int256 cashflow;
    Currency cashflowCcy;
    uint256 deadline;
    OrderType orderType;
}

interface IOrderManagerEvents {

    event OrderPlaced(
        OrderId indexed orderId,
        PositionId indexed positionId,
        address indexed owner,
        int256 quantity,
        uint256 limitPrice,
        uint256 tolerance,
        int256 cashflow,
        Currency cashflowCcy,
        uint256 deadline,
        OrderType orderType,
        address placedBy
    );

    event OrderCancelled(OrderId indexed orderId);
    event OrderExecuted(OrderId indexed orderId, PositionId indexed positionId, uint256 keeperReward);

    event GasMultiplierSet(uint256 gasMultiplier);
    event GasTipSet(uint256 gasTip);
    event OracleSet(IContangoOracle oracle);

}

interface IOrderManagerErrors {

    error AboveMaxGasMultiplier(uint64 gasMultiplier); // 0x64d79f87
    error BelowMinGasMultiplier(uint64 gasMultiplier); // 0x7f732d9b
    error InvalidDeadline(uint256 deadline, uint256 blockTimestamp); // 0x8848019e
    error InvalidOrderType(OrderType orderType); // 0xf2bc1bb6
    error InvalidPrice(uint256 forwardPrice, uint256 limitPrice); // 0xaf608abb
    error InvalidQuantity(); // 0x524f409b
    error InvalidTolerance(uint256 tolerance); // 0x7ca28bcf
    error OrderDoesNotExist(OrderId orderId); // 0xbd8da02b
    error OrderAlreadyExists(OrderId orderId); // 0x086371d3
    error OrderExpired(OrderId orderId, uint256 deadline, uint256 blockTimestamp); // 0xc8105aba
    error OrderInvalidated(OrderId orderId); // 0xd10aebae
    error PositionDoesNotExist(PositionId positionId); // 0x80cc2277

}

interface IOrderManager is IOrderManagerEvents, IOrderManagerErrors, IContangoErrors {

    function placeOnBehalfOf(OrderParams calldata params, address onBehalfOf) external returns (OrderId orderId);

    function place(OrderParams calldata params) external returns (OrderId orderId);

    function cancel(OrderId orderId) external;

    function execute(OrderId orderId, ExecutionParams calldata execParams)
        external
        payable
        returns (PositionId positionId_, Trade memory trade_, uint256 keeperReward_);

    // ======== View ========

    function orders(OrderId orderId) external view returns (Order memory order);
    function hasOrder(OrderId orderId) external view returns (bool);
    function nativeToken() external view returns (IWETH9);
    function positionNFT() external view returns (PositionNFT);

    // ======== Admin ========

    function setGasMultiplier(uint64 gasMultiplier) external;
    function setGasTip(uint64 gasTip) external;
    function setOracle(IContangoOracle _oracle) external;

}

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

import "../libraries/ERC20Lib.sol";

interface SimpleSpotExecutorEvents {

    event SwapExecuted(IERC20 indexed tokenToSell, IERC20 indexed tokenToBuy, uint256 amountIn, uint256 amountOut);

}

interface SimpleSpotExecutorErrors {

    error InsufficientAmountOut(uint256 minExpected, uint256 actual);

}

contract SimpleSpotExecutor is SimpleSpotExecutorEvents, SimpleSpotExecutorErrors {

    function executeSwap(
        IERC20 tokenToSell,
        IERC20 tokenToBuy,
        address spender,
        uint256 amountIn,
        uint256 minAmountOut,
        address router,
        bytes calldata swapBytes,
        address to
    ) external returns (uint256 output) {
        SafeERC20.forceApprove(tokenToSell, spender, amountIn);
        Address.functionCall(router, swapBytes);

        output = ERC20Lib.transferBalance(tokenToBuy, to);
        if (output < minAmountOut) revert InsufficientAmountOut(minAmountOut, output);

        emit SwapExecuted(tokenToSell, tokenToBuy, amountIn, output);
    }

}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.3) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.0;

import "../IERC20.sol";
import "../extensions/IERC20Permit.sol";
import "../../../utils/Address.sol";

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

    /**
     * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeTransfer(IERC20 token, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
    }

    /**
     * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
     * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
     */
    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
    }

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

    /**
     * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 oldAllowance = token.allowance(address(this), spender);
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));
    }

    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        unchecked {
            uint256 oldAllowance = token.allowance(address(this), spender);
            require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
        }
    }

    /**
     * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
     * to be set to zero before setting it to a non-zero value, such as USDT.
     */
    function forceApprove(IERC20 token, address spender, uint256 value) internal {
        bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);

        if (!_callOptionalReturnBool(token, approvalCall)) {
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));
            _callOptionalReturn(token, approvalCall);
        }
    }

    /**
     * @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.
     * Revert on invalid signature.
     */
    function safePermit(
        IERC20Permit token,
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal {
        uint256 nonceBefore = token.nonces(owner);
        token.permit(owner, spender, value, deadline, v, r, s);
        uint256 nonceAfter = token.nonces(owner);
        require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
    }

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

        bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
        require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
        // and not revert is the subcall reverts.

        (bool success, bytes memory returndata) = address(token).call(data);
        return
            success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token));
    }
}

File 28 of 61 : SafeCast.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SafeCast.sol)
// This file was procedurally generated from scripts/generate/templates/SafeCast.js.

pragma solidity ^0.8.0;

/**
 * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow
 * checks.
 *
 * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
 * easily result in undesired exploitation or bugs, since developers usually
 * assume that overflows raise errors. `SafeCast` restores this intuition by
 * reverting the transaction when such an operation overflows.
 *
 * Using this library instead of the unchecked operations eliminates an entire
 * class of bugs, so it's recommended to use it always.
 *
 * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing
 * all math on `uint256` and `int256` and then downcasting.
 */
library SafeCast {
    /**
     * @dev Returns the downcasted uint248 from uint256, reverting on
     * overflow (when the input is greater than largest uint248).
     *
     * Counterpart to Solidity's `uint248` operator.
     *
     * Requirements:
     *
     * - input must fit into 248 bits
     *
     * _Available since v4.7._
     */
    function toUint248(uint256 value) internal pure returns (uint248) {
        require(value <= type(uint248).max, "SafeCast: value doesn't fit in 248 bits");
        return uint248(value);
    }

    /**
     * @dev Returns the downcasted uint240 from uint256, reverting on
     * overflow (when the input is greater than largest uint240).
     *
     * Counterpart to Solidity's `uint240` operator.
     *
     * Requirements:
     *
     * - input must fit into 240 bits
     *
     * _Available since v4.7._
     */
    function toUint240(uint256 value) internal pure returns (uint240) {
        require(value <= type(uint240).max, "SafeCast: value doesn't fit in 240 bits");
        return uint240(value);
    }

    /**
     * @dev Returns the downcasted uint232 from uint256, reverting on
     * overflow (when the input is greater than largest uint232).
     *
     * Counterpart to Solidity's `uint232` operator.
     *
     * Requirements:
     *
     * - input must fit into 232 bits
     *
     * _Available since v4.7._
     */
    function toUint232(uint256 value) internal pure returns (uint232) {
        require(value <= type(uint232).max, "SafeCast: value doesn't fit in 232 bits");
        return uint232(value);
    }

    /**
     * @dev Returns the downcasted uint224 from uint256, reverting on
     * overflow (when the input is greater than largest uint224).
     *
     * Counterpart to Solidity's `uint224` operator.
     *
     * Requirements:
     *
     * - input must fit into 224 bits
     *
     * _Available since v4.2._
     */
    function toUint224(uint256 value) internal pure returns (uint224) {
        require(value <= type(uint224).max, "SafeCast: value doesn't fit in 224 bits");
        return uint224(value);
    }

    /**
     * @dev Returns the downcasted uint216 from uint256, reverting on
     * overflow (when the input is greater than largest uint216).
     *
     * Counterpart to Solidity's `uint216` operator.
     *
     * Requirements:
     *
     * - input must fit into 216 bits
     *
     * _Available since v4.7._
     */
    function toUint216(uint256 value) internal pure returns (uint216) {
        require(value <= type(uint216).max, "SafeCast: value doesn't fit in 216 bits");
        return uint216(value);
    }

    /**
     * @dev Returns the downcasted uint208 from uint256, reverting on
     * overflow (when the input is greater than largest uint208).
     *
     * Counterpart to Solidity's `uint208` operator.
     *
     * Requirements:
     *
     * - input must fit into 208 bits
     *
     * _Available since v4.7._
     */
    function toUint208(uint256 value) internal pure returns (uint208) {
        require(value <= type(uint208).max, "SafeCast: value doesn't fit in 208 bits");
        return uint208(value);
    }

    /**
     * @dev Returns the downcasted uint200 from uint256, reverting on
     * overflow (when the input is greater than largest uint200).
     *
     * Counterpart to Solidity's `uint200` operator.
     *
     * Requirements:
     *
     * - input must fit into 200 bits
     *
     * _Available since v4.7._
     */
    function toUint200(uint256 value) internal pure returns (uint200) {
        require(value <= type(uint200).max, "SafeCast: value doesn't fit in 200 bits");
        return uint200(value);
    }

    /**
     * @dev Returns the downcasted uint192 from uint256, reverting on
     * overflow (when the input is greater than largest uint192).
     *
     * Counterpart to Solidity's `uint192` operator.
     *
     * Requirements:
     *
     * - input must fit into 192 bits
     *
     * _Available since v4.7._
     */
    function toUint192(uint256 value) internal pure returns (uint192) {
        require(value <= type(uint192).max, "SafeCast: value doesn't fit in 192 bits");
        return uint192(value);
    }

    /**
     * @dev Returns the downcasted uint184 from uint256, reverting on
     * overflow (when the input is greater than largest uint184).
     *
     * Counterpart to Solidity's `uint184` operator.
     *
     * Requirements:
     *
     * - input must fit into 184 bits
     *
     * _Available since v4.7._
     */
    function toUint184(uint256 value) internal pure returns (uint184) {
        require(value <= type(uint184).max, "SafeCast: value doesn't fit in 184 bits");
        return uint184(value);
    }

    /**
     * @dev Returns the downcasted uint176 from uint256, reverting on
     * overflow (when the input is greater than largest uint176).
     *
     * Counterpart to Solidity's `uint176` operator.
     *
     * Requirements:
     *
     * - input must fit into 176 bits
     *
     * _Available since v4.7._
     */
    function toUint176(uint256 value) internal pure returns (uint176) {
        require(value <= type(uint176).max, "SafeCast: value doesn't fit in 176 bits");
        return uint176(value);
    }

    /**
     * @dev Returns the downcasted uint168 from uint256, reverting on
     * overflow (when the input is greater than largest uint168).
     *
     * Counterpart to Solidity's `uint168` operator.
     *
     * Requirements:
     *
     * - input must fit into 168 bits
     *
     * _Available since v4.7._
     */
    function toUint168(uint256 value) internal pure returns (uint168) {
        require(value <= type(uint168).max, "SafeCast: value doesn't fit in 168 bits");
        return uint168(value);
    }

    /**
     * @dev Returns the downcasted uint160 from uint256, reverting on
     * overflow (when the input is greater than largest uint160).
     *
     * Counterpart to Solidity's `uint160` operator.
     *
     * Requirements:
     *
     * - input must fit into 160 bits
     *
     * _Available since v4.7._
     */
    function toUint160(uint256 value) internal pure returns (uint160) {
        require(value <= type(uint160).max, "SafeCast: value doesn't fit in 160 bits");
        return uint160(value);
    }

    /**
     * @dev Returns the downcasted uint152 from uint256, reverting on
     * overflow (when the input is greater than largest uint152).
     *
     * Counterpart to Solidity's `uint152` operator.
     *
     * Requirements:
     *
     * - input must fit into 152 bits
     *
     * _Available since v4.7._
     */
    function toUint152(uint256 value) internal pure returns (uint152) {
        require(value <= type(uint152).max, "SafeCast: value doesn't fit in 152 bits");
        return uint152(value);
    }

    /**
     * @dev Returns the downcasted uint144 from uint256, reverting on
     * overflow (when the input is greater than largest uint144).
     *
     * Counterpart to Solidity's `uint144` operator.
     *
     * Requirements:
     *
     * - input must fit into 144 bits
     *
     * _Available since v4.7._
     */
    function toUint144(uint256 value) internal pure returns (uint144) {
        require(value <= type(uint144).max, "SafeCast: value doesn't fit in 144 bits");
        return uint144(value);
    }

    /**
     * @dev Returns the downcasted uint136 from uint256, reverting on
     * overflow (when the input is greater than largest uint136).
     *
     * Counterpart to Solidity's `uint136` operator.
     *
     * Requirements:
     *
     * - input must fit into 136 bits
     *
     * _Available since v4.7._
     */
    function toUint136(uint256 value) internal pure returns (uint136) {
        require(value <= type(uint136).max, "SafeCast: value doesn't fit in 136 bits");
        return uint136(value);
    }

    /**
     * @dev Returns the downcasted uint128 from uint256, reverting on
     * overflow (when the input is greater than largest uint128).
     *
     * Counterpart to Solidity's `uint128` operator.
     *
     * Requirements:
     *
     * - input must fit into 128 bits
     *
     * _Available since v2.5._
     */
    function toUint128(uint256 value) internal pure returns (uint128) {
        require(value <= type(uint128).max, "SafeCast: value doesn't fit in 128 bits");
        return uint128(value);
    }

    /**
     * @dev Returns the downcasted uint120 from uint256, reverting on
     * overflow (when the input is greater than largest uint120).
     *
     * Counterpart to Solidity's `uint120` operator.
     *
     * Requirements:
     *
     * - input must fit into 120 bits
     *
     * _Available since v4.7._
     */
    function toUint120(uint256 value) internal pure returns (uint120) {
        require(value <= type(uint120).max, "SafeCast: value doesn't fit in 120 bits");
        return uint120(value);
    }

    /**
     * @dev Returns the downcasted uint112 from uint256, reverting on
     * overflow (when the input is greater than largest uint112).
     *
     * Counterpart to Solidity's `uint112` operator.
     *
     * Requirements:
     *
     * - input must fit into 112 bits
     *
     * _Available since v4.7._
     */
    function toUint112(uint256 value) internal pure returns (uint112) {
        require(value <= type(uint112).max, "SafeCast: value doesn't fit in 112 bits");
        return uint112(value);
    }

    /**
     * @dev Returns the downcasted uint104 from uint256, reverting on
     * overflow (when the input is greater than largest uint104).
     *
     * Counterpart to Solidity's `uint104` operator.
     *
     * Requirements:
     *
     * - input must fit into 104 bits
     *
     * _Available since v4.7._
     */
    function toUint104(uint256 value) internal pure returns (uint104) {
        require(value <= type(uint104).max, "SafeCast: value doesn't fit in 104 bits");
        return uint104(value);
    }

    /**
     * @dev Returns the downcasted uint96 from uint256, reverting on
     * overflow (when the input is greater than largest uint96).
     *
     * Counterpart to Solidity's `uint96` operator.
     *
     * Requirements:
     *
     * - input must fit into 96 bits
     *
     * _Available since v4.2._
     */
    function toUint96(uint256 value) internal pure returns (uint96) {
        require(value <= type(uint96).max, "SafeCast: value doesn't fit in 96 bits");
        return uint96(value);
    }

    /**
     * @dev Returns the downcasted uint88 from uint256, reverting on
     * overflow (when the input is greater than largest uint88).
     *
     * Counterpart to Solidity's `uint88` operator.
     *
     * Requirements:
     *
     * - input must fit into 88 bits
     *
     * _Available since v4.7._
     */
    function toUint88(uint256 value) internal pure returns (uint88) {
        require(value <= type(uint88).max, "SafeCast: value doesn't fit in 88 bits");
        return uint88(value);
    }

    /**
     * @dev Returns the downcasted uint80 from uint256, reverting on
     * overflow (when the input is greater than largest uint80).
     *
     * Counterpart to Solidity's `uint80` operator.
     *
     * Requirements:
     *
     * - input must fit into 80 bits
     *
     * _Available since v4.7._
     */
    function toUint80(uint256 value) internal pure returns (uint80) {
        require(value <= type(uint80).max, "SafeCast: value doesn't fit in 80 bits");
        return uint80(value);
    }

    /**
     * @dev Returns the downcasted uint72 from uint256, reverting on
     * overflow (when the input is greater than largest uint72).
     *
     * Counterpart to Solidity's `uint72` operator.
     *
     * Requirements:
     *
     * - input must fit into 72 bits
     *
     * _Available since v4.7._
     */
    function toUint72(uint256 value) internal pure returns (uint72) {
        require(value <= type(uint72).max, "SafeCast: value doesn't fit in 72 bits");
        return uint72(value);
    }

    /**
     * @dev Returns the downcasted uint64 from uint256, reverting on
     * overflow (when the input is greater than largest uint64).
     *
     * Counterpart to Solidity's `uint64` operator.
     *
     * Requirements:
     *
     * - input must fit into 64 bits
     *
     * _Available since v2.5._
     */
    function toUint64(uint256 value) internal pure returns (uint64) {
        require(value <= type(uint64).max, "SafeCast: value doesn't fit in 64 bits");
        return uint64(value);
    }

    /**
     * @dev Returns the downcasted uint56 from uint256, reverting on
     * overflow (when the input is greater than largest uint56).
     *
     * Counterpart to Solidity's `uint56` operator.
     *
     * Requirements:
     *
     * - input must fit into 56 bits
     *
     * _Available since v4.7._
     */
    function toUint56(uint256 value) internal pure returns (uint56) {
        require(value <= type(uint56).max, "SafeCast: value doesn't fit in 56 bits");
        return uint56(value);
    }

    /**
     * @dev Returns the downcasted uint48 from uint256, reverting on
     * overflow (when the input is greater than largest uint48).
     *
     * Counterpart to Solidity's `uint48` operator.
     *
     * Requirements:
     *
     * - input must fit into 48 bits
     *
     * _Available since v4.7._
     */
    function toUint48(uint256 value) internal pure returns (uint48) {
        require(value <= type(uint48).max, "SafeCast: value doesn't fit in 48 bits");
        return uint48(value);
    }

    /**
     * @dev Returns the downcasted uint40 from uint256, reverting on
     * overflow (when the input is greater than largest uint40).
     *
     * Counterpart to Solidity's `uint40` operator.
     *
     * Requirements:
     *
     * - input must fit into 40 bits
     *
     * _Available since v4.7._
     */
    function toUint40(uint256 value) internal pure returns (uint40) {
        require(value <= type(uint40).max, "SafeCast: value doesn't fit in 40 bits");
        return uint40(value);
    }

    /**
     * @dev Returns the downcasted uint32 from uint256, reverting on
     * overflow (when the input is greater than largest uint32).
     *
     * Counterpart to Solidity's `uint32` operator.
     *
     * Requirements:
     *
     * - input must fit into 32 bits
     *
     * _Available since v2.5._
     */
    function toUint32(uint256 value) internal pure returns (uint32) {
        require(value <= type(uint32).max, "SafeCast: value doesn't fit in 32 bits");
        return uint32(value);
    }

    /**
     * @dev Returns the downcasted uint24 from uint256, reverting on
     * overflow (when the input is greater than largest uint24).
     *
     * Counterpart to Solidity's `uint24` operator.
     *
     * Requirements:
     *
     * - input must fit into 24 bits
     *
     * _Available since v4.7._
     */
    function toUint24(uint256 value) internal pure returns (uint24) {
        require(value <= type(uint24).max, "SafeCast: value doesn't fit in 24 bits");
        return uint24(value);
    }

    /**
     * @dev Returns the downcasted uint16 from uint256, reverting on
     * overflow (when the input is greater than largest uint16).
     *
     * Counterpart to Solidity's `uint16` operator.
     *
     * Requirements:
     *
     * - input must fit into 16 bits
     *
     * _Available since v2.5._
     */
    function toUint16(uint256 value) internal pure returns (uint16) {
        require(value <= type(uint16).max, "SafeCast: value doesn't fit in 16 bits");
        return uint16(value);
    }

    /**
     * @dev Returns the downcasted uint8 from uint256, reverting on
     * overflow (when the input is greater than largest uint8).
     *
     * Counterpart to Solidity's `uint8` operator.
     *
     * Requirements:
     *
     * - input must fit into 8 bits
     *
     * _Available since v2.5._
     */
    function toUint8(uint256 value) internal pure returns (uint8) {
        require(value <= type(uint8).max, "SafeCast: value doesn't fit in 8 bits");
        return uint8(value);
    }

    /**
     * @dev Converts a signed int256 into an unsigned uint256.
     *
     * Requirements:
     *
     * - input must be greater than or equal to 0.
     *
     * _Available since v3.0._
     */
    function toUint256(int256 value) internal pure returns (uint256) {
        require(value >= 0, "SafeCast: value must be positive");
        return uint256(value);
    }

    /**
     * @dev Returns the downcasted int248 from int256, reverting on
     * overflow (when the input is less than smallest int248 or
     * greater than largest int248).
     *
     * Counterpart to Solidity's `int248` operator.
     *
     * Requirements:
     *
     * - input must fit into 248 bits
     *
     * _Available since v4.7._
     */
    function toInt248(int256 value) internal pure returns (int248 downcasted) {
        downcasted = int248(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 248 bits");
    }

    /**
     * @dev Returns the downcasted int240 from int256, reverting on
     * overflow (when the input is less than smallest int240 or
     * greater than largest int240).
     *
     * Counterpart to Solidity's `int240` operator.
     *
     * Requirements:
     *
     * - input must fit into 240 bits
     *
     * _Available since v4.7._
     */
    function toInt240(int256 value) internal pure returns (int240 downcasted) {
        downcasted = int240(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 240 bits");
    }

    /**
     * @dev Returns the downcasted int232 from int256, reverting on
     * overflow (when the input is less than smallest int232 or
     * greater than largest int232).
     *
     * Counterpart to Solidity's `int232` operator.
     *
     * Requirements:
     *
     * - input must fit into 232 bits
     *
     * _Available since v4.7._
     */
    function toInt232(int256 value) internal pure returns (int232 downcasted) {
        downcasted = int232(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 232 bits");
    }

    /**
     * @dev Returns the downcasted int224 from int256, reverting on
     * overflow (when the input is less than smallest int224 or
     * greater than largest int224).
     *
     * Counterpart to Solidity's `int224` operator.
     *
     * Requirements:
     *
     * - input must fit into 224 bits
     *
     * _Available since v4.7._
     */
    function toInt224(int256 value) internal pure returns (int224 downcasted) {
        downcasted = int224(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 224 bits");
    }

    /**
     * @dev Returns the downcasted int216 from int256, reverting on
     * overflow (when the input is less than smallest int216 or
     * greater than largest int216).
     *
     * Counterpart to Solidity's `int216` operator.
     *
     * Requirements:
     *
     * - input must fit into 216 bits
     *
     * _Available since v4.7._
     */
    function toInt216(int256 value) internal pure returns (int216 downcasted) {
        downcasted = int216(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 216 bits");
    }

    /**
     * @dev Returns the downcasted int208 from int256, reverting on
     * overflow (when the input is less than smallest int208 or
     * greater than largest int208).
     *
     * Counterpart to Solidity's `int208` operator.
     *
     * Requirements:
     *
     * - input must fit into 208 bits
     *
     * _Available since v4.7._
     */
    function toInt208(int256 value) internal pure returns (int208 downcasted) {
        downcasted = int208(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 208 bits");
    }

    /**
     * @dev Returns the downcasted int200 from int256, reverting on
     * overflow (when the input is less than smallest int200 or
     * greater than largest int200).
     *
     * Counterpart to Solidity's `int200` operator.
     *
     * Requirements:
     *
     * - input must fit into 200 bits
     *
     * _Available since v4.7._
     */
    function toInt200(int256 value) internal pure returns (int200 downcasted) {
        downcasted = int200(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 200 bits");
    }

    /**
     * @dev Returns the downcasted int192 from int256, reverting on
     * overflow (when the input is less than smallest int192 or
     * greater than largest int192).
     *
     * Counterpart to Solidity's `int192` operator.
     *
     * Requirements:
     *
     * - input must fit into 192 bits
     *
     * _Available since v4.7._
     */
    function toInt192(int256 value) internal pure returns (int192 downcasted) {
        downcasted = int192(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 192 bits");
    }

    /**
     * @dev Returns the downcasted int184 from int256, reverting on
     * overflow (when the input is less than smallest int184 or
     * greater than largest int184).
     *
     * Counterpart to Solidity's `int184` operator.
     *
     * Requirements:
     *
     * - input must fit into 184 bits
     *
     * _Available since v4.7._
     */
    function toInt184(int256 value) internal pure returns (int184 downcasted) {
        downcasted = int184(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 184 bits");
    }

    /**
     * @dev Returns the downcasted int176 from int256, reverting on
     * overflow (when the input is less than smallest int176 or
     * greater than largest int176).
     *
     * Counterpart to Solidity's `int176` operator.
     *
     * Requirements:
     *
     * - input must fit into 176 bits
     *
     * _Available since v4.7._
     */
    function toInt176(int256 value) internal pure returns (int176 downcasted) {
        downcasted = int176(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 176 bits");
    }

    /**
     * @dev Returns the downcasted int168 from int256, reverting on
     * overflow (when the input is less than smallest int168 or
     * greater than largest int168).
     *
     * Counterpart to Solidity's `int168` operator.
     *
     * Requirements:
     *
     * - input must fit into 168 bits
     *
     * _Available since v4.7._
     */
    function toInt168(int256 value) internal pure returns (int168 downcasted) {
        downcasted = int168(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 168 bits");
    }

    /**
     * @dev Returns the downcasted int160 from int256, reverting on
     * overflow (when the input is less than smallest int160 or
     * greater than largest int160).
     *
     * Counterpart to Solidity's `int160` operator.
     *
     * Requirements:
     *
     * - input must fit into 160 bits
     *
     * _Available since v4.7._
     */
    function toInt160(int256 value) internal pure returns (int160 downcasted) {
        downcasted = int160(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 160 bits");
    }

    /**
     * @dev Returns the downcasted int152 from int256, reverting on
     * overflow (when the input is less than smallest int152 or
     * greater than largest int152).
     *
     * Counterpart to Solidity's `int152` operator.
     *
     * Requirements:
     *
     * - input must fit into 152 bits
     *
     * _Available since v4.7._
     */
    function toInt152(int256 value) internal pure returns (int152 downcasted) {
        downcasted = int152(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 152 bits");
    }

    /**
     * @dev Returns the downcasted int144 from int256, reverting on
     * overflow (when the input is less than smallest int144 or
     * greater than largest int144).
     *
     * Counterpart to Solidity's `int144` operator.
     *
     * Requirements:
     *
     * - input must fit into 144 bits
     *
     * _Available since v4.7._
     */
    function toInt144(int256 value) internal pure returns (int144 downcasted) {
        downcasted = int144(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 144 bits");
    }

    /**
     * @dev Returns the downcasted int136 from int256, reverting on
     * overflow (when the input is less than smallest int136 or
     * greater than largest int136).
     *
     * Counterpart to Solidity's `int136` operator.
     *
     * Requirements:
     *
     * - input must fit into 136 bits
     *
     * _Available since v4.7._
     */
    function toInt136(int256 value) internal pure returns (int136 downcasted) {
        downcasted = int136(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 136 bits");
    }

    /**
     * @dev Returns the downcasted int128 from int256, reverting on
     * overflow (when the input is less than smallest int128 or
     * greater than largest int128).
     *
     * Counterpart to Solidity's `int128` operator.
     *
     * Requirements:
     *
     * - input must fit into 128 bits
     *
     * _Available since v3.1._
     */
    function toInt128(int256 value) internal pure returns (int128 downcasted) {
        downcasted = int128(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 128 bits");
    }

    /**
     * @dev Returns the downcasted int120 from int256, reverting on
     * overflow (when the input is less than smallest int120 or
     * greater than largest int120).
     *
     * Counterpart to Solidity's `int120` operator.
     *
     * Requirements:
     *
     * - input must fit into 120 bits
     *
     * _Available since v4.7._
     */
    function toInt120(int256 value) internal pure returns (int120 downcasted) {
        downcasted = int120(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 120 bits");
    }

    /**
     * @dev Returns the downcasted int112 from int256, reverting on
     * overflow (when the input is less than smallest int112 or
     * greater than largest int112).
     *
     * Counterpart to Solidity's `int112` operator.
     *
     * Requirements:
     *
     * - input must fit into 112 bits
     *
     * _Available since v4.7._
     */
    function toInt112(int256 value) internal pure returns (int112 downcasted) {
        downcasted = int112(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 112 bits");
    }

    /**
     * @dev Returns the downcasted int104 from int256, reverting on
     * overflow (when the input is less than smallest int104 or
     * greater than largest int104).
     *
     * Counterpart to Solidity's `int104` operator.
     *
     * Requirements:
     *
     * - input must fit into 104 bits
     *
     * _Available since v4.7._
     */
    function toInt104(int256 value) internal pure returns (int104 downcasted) {
        downcasted = int104(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 104 bits");
    }

    /**
     * @dev Returns the downcasted int96 from int256, reverting on
     * overflow (when the input is less than smallest int96 or
     * greater than largest int96).
     *
     * Counterpart to Solidity's `int96` operator.
     *
     * Requirements:
     *
     * - input must fit into 96 bits
     *
     * _Available since v4.7._
     */
    function toInt96(int256 value) internal pure returns (int96 downcasted) {
        downcasted = int96(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 96 bits");
    }

    /**
     * @dev Returns the downcasted int88 from int256, reverting on
     * overflow (when the input is less than smallest int88 or
     * greater than largest int88).
     *
     * Counterpart to Solidity's `int88` operator.
     *
     * Requirements:
     *
     * - input must fit into 88 bits
     *
     * _Available since v4.7._
     */
    function toInt88(int256 value) internal pure returns (int88 downcasted) {
        downcasted = int88(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 88 bits");
    }

    /**
     * @dev Returns the downcasted int80 from int256, reverting on
     * overflow (when the input is less than smallest int80 or
     * greater than largest int80).
     *
     * Counterpart to Solidity's `int80` operator.
     *
     * Requirements:
     *
     * - input must fit into 80 bits
     *
     * _Available since v4.7._
     */
    function toInt80(int256 value) internal pure returns (int80 downcasted) {
        downcasted = int80(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 80 bits");
    }

    /**
     * @dev Returns the downcasted int72 from int256, reverting on
     * overflow (when the input is less than smallest int72 or
     * greater than largest int72).
     *
     * Counterpart to Solidity's `int72` operator.
     *
     * Requirements:
     *
     * - input must fit into 72 bits
     *
     * _Available since v4.7._
     */
    function toInt72(int256 value) internal pure returns (int72 downcasted) {
        downcasted = int72(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 72 bits");
    }

    /**
     * @dev Returns the downcasted int64 from int256, reverting on
     * overflow (when the input is less than smallest int64 or
     * greater than largest int64).
     *
     * Counterpart to Solidity's `int64` operator.
     *
     * Requirements:
     *
     * - input must fit into 64 bits
     *
     * _Available since v3.1._
     */
    function toInt64(int256 value) internal pure returns (int64 downcasted) {
        downcasted = int64(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 64 bits");
    }

    /**
     * @dev Returns the downcasted int56 from int256, reverting on
     * overflow (when the input is less than smallest int56 or
     * greater than largest int56).
     *
     * Counterpart to Solidity's `int56` operator.
     *
     * Requirements:
     *
     * - input must fit into 56 bits
     *
     * _Available since v4.7._
     */
    function toInt56(int256 value) internal pure returns (int56 downcasted) {
        downcasted = int56(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 56 bits");
    }

    /**
     * @dev Returns the downcasted int48 from int256, reverting on
     * overflow (when the input is less than smallest int48 or
     * greater than largest int48).
     *
     * Counterpart to Solidity's `int48` operator.
     *
     * Requirements:
     *
     * - input must fit into 48 bits
     *
     * _Available since v4.7._
     */
    function toInt48(int256 value) internal pure returns (int48 downcasted) {
        downcasted = int48(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 48 bits");
    }

    /**
     * @dev Returns the downcasted int40 from int256, reverting on
     * overflow (when the input is less than smallest int40 or
     * greater than largest int40).
     *
     * Counterpart to Solidity's `int40` operator.
     *
     * Requirements:
     *
     * - input must fit into 40 bits
     *
     * _Available since v4.7._
     */
    function toInt40(int256 value) internal pure returns (int40 downcasted) {
        downcasted = int40(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 40 bits");
    }

    /**
     * @dev Returns the downcasted int32 from int256, reverting on
     * overflow (when the input is less than smallest int32 or
     * greater than largest int32).
     *
     * Counterpart to Solidity's `int32` operator.
     *
     * Requirements:
     *
     * - input must fit into 32 bits
     *
     * _Available since v3.1._
     */
    function toInt32(int256 value) internal pure returns (int32 downcasted) {
        downcasted = int32(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 32 bits");
    }

    /**
     * @dev Returns the downcasted int24 from int256, reverting on
     * overflow (when the input is less than smallest int24 or
     * greater than largest int24).
     *
     * Counterpart to Solidity's `int24` operator.
     *
     * Requirements:
     *
     * - input must fit into 24 bits
     *
     * _Available since v4.7._
     */
    function toInt24(int256 value) internal pure returns (int24 downcasted) {
        downcasted = int24(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 24 bits");
    }

    /**
     * @dev Returns the downcasted int16 from int256, reverting on
     * overflow (when the input is less than smallest int16 or
     * greater than largest int16).
     *
     * Counterpart to Solidity's `int16` operator.
     *
     * Requirements:
     *
     * - input must fit into 16 bits
     *
     * _Available since v3.1._
     */
    function toInt16(int256 value) internal pure returns (int16 downcasted) {
        downcasted = int16(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 16 bits");
    }

    /**
     * @dev Returns the downcasted int8 from int256, reverting on
     * overflow (when the input is less than smallest int8 or
     * greater than largest int8).
     *
     * Counterpart to Solidity's `int8` operator.
     *
     * Requirements:
     *
     * - input must fit into 8 bits
     *
     * _Available since v3.1._
     */
    function toInt8(int256 value) internal pure returns (int8 downcasted) {
        downcasted = int8(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 8 bits");
    }

    /**
     * @dev Converts an unsigned uint256 into a signed int256.
     *
     * Requirements:
     *
     * - input must be less than or equal to maxInt256.
     *
     * _Available since v3.0._
     */
    function toInt256(uint256 value) internal pure returns (int256) {
        // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
        require(value <= uint256(type(int256).max), "SafeCast: value doesn't fit in an int256");
        return int256(value);
    }
}

// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;

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

interface IWETH9 is IERC20 {

    function deposit() external payable;

    function withdraw(uint256 wad) external;

}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol)

pragma solidity ^0.8.0;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    enum Rounding {
        Down, // Toward negative infinity
        Up, // Toward infinity
        Zero // Toward zero
    }

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

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

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

    /**
     * @dev Returns the ceiling of the division of two numbers.
     *
     * This differs from standard division with `/` in that it rounds up instead
     * of rounding down.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b - 1) / b can overflow on addition, so we distribute.
        return a == 0 ? 0 : (a - 1) / b + 1;
    }

    /**
     * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
     * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
     * with further edits by Uniswap Labs also under MIT license.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
        unchecked {
            // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
            // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
            // variables such that product = prod1 * 2^256 + prod0.
            uint256 prod0; // Least significant 256 bits of the product
            uint256 prod1; // Most significant 256 bits of the product
            assembly {
                let mm := mulmod(x, y, not(0))
                prod0 := mul(x, y)
                prod1 := sub(sub(mm, prod0), lt(mm, prod0))
            }

            // Handle non-overflow cases, 256 by 256 division.
            if (prod1 == 0) {
                // Solidity will revert if denominator == 0, unlike the div opcode on its own.
                // The surrounding unchecked block does not change this fact.
                // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
                return prod0 / denominator;
            }

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            require(denominator > prod1, "Math: mulDiv overflow");

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

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

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

            // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
            // See https://cs.stackexchange.com/q/138556/92363.

            // Does not overflow because the denominator cannot be zero at this stage in the function.
            uint256 twos = denominator & (~denominator + 1);
            assembly {
                // Divide denominator by twos.
                denominator := div(denominator, twos)

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

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

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

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

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

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

    /**
     * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
        uint256 result = mulDiv(x, y, denominator);
        if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
            result += 1;
        }
        return result;
    }

    /**
     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
     *
     * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
     */
    function sqrt(uint256 a) internal pure returns (uint256) {
        if (a == 0) {
            return 0;
        }

        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
        //
        // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
        // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
        //
        // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
        // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
        // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
        //
        // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
        uint256 result = 1 << (log2(a) >> 1);

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

    /**
     * @notice Calculates sqrt(a), following the selected rounding direction.
     */
    function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = sqrt(a);
            return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 2, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 128;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 64;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 32;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 16;
            }
            if (value >> 8 > 0) {
                value >>= 8;
                result += 8;
            }
            if (value >> 4 > 0) {
                value >>= 4;
                result += 4;
            }
            if (value >> 2 > 0) {
                value >>= 2;
                result += 2;
            }
            if (value >> 1 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log2(value);
            return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 10, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >= 10 ** 64) {
                value /= 10 ** 64;
                result += 64;
            }
            if (value >= 10 ** 32) {
                value /= 10 ** 32;
                result += 32;
            }
            if (value >= 10 ** 16) {
                value /= 10 ** 16;
                result += 16;
            }
            if (value >= 10 ** 8) {
                value /= 10 ** 8;
                result += 8;
            }
            if (value >= 10 ** 4) {
                value /= 10 ** 4;
                result += 4;
            }
            if (value >= 10 ** 2) {
                value /= 10 ** 2;
                result += 2;
            }
            if (value >= 10 ** 1) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log10(value);
            return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 256, rounded down, of a positive value.
     * Returns 0 if given 0.
     *
     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
     */
    function log256(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 16;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 8;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 4;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 2;
            }
            if (value >> 8 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 256, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log256(value);
            return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol)

pragma solidity ^0.8.0;

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

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

    /**
     * @dev Returns the average of two signed numbers without overflow.
     * The result is rounded towards zero.
     */
    function average(int256 a, int256 b) internal pure returns (int256) {
        // Formula from the book "Hacker's Delight"
        int256 x = (a & b) + ((a ^ b) >> 1);
        return x + (int256(uint256(x) >> 255) & (a ^ b));
    }

    /**
     * @dev Returns the absolute unsigned value of a signed value.
     */
    function abs(int256 n) internal pure returns (uint256) {
        unchecked {
            // must be unchecked in order to support `n = type(int256).min`
            return uint256(n >= 0 ? n : -n);
        }
    }
}

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

pragma solidity ^0.8.0;

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

File 33 of 61 : draft-IERC1822Upgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (interfaces/draft-IERC1822.sol)

pragma solidity ^0.8.0;

/**
 * @dev ERC1822: Universal Upgradeable Proxy Standard (UUPS) documents a method for upgradeability through a simplified
 * proxy whose upgrades are fully controlled by the current implementation.
 */
interface IERC1822ProxiableUpgradeable {
    /**
     * @dev Returns the storage slot that the proxiable contract assumes is being used to store the implementation
     * address.
     *
     * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
     * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
     * function revert if invoked through a proxy.
     */
    function proxiableUUID() external view returns (bytes32);
}

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

pragma solidity ^0.8.2;

import "../beacon/IBeaconUpgradeable.sol";
import "../../interfaces/IERC1967Upgradeable.sol";
import "../../interfaces/draft-IERC1822Upgradeable.sol";
import "../../utils/AddressUpgradeable.sol";
import "../../utils/StorageSlotUpgradeable.sol";
import "../utils/Initializable.sol";

/**
 * @dev This abstract contract provides getters and event emitting update functions for
 * https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots.
 *
 * _Available since v4.1._
 */
abstract contract ERC1967UpgradeUpgradeable is Initializable, IERC1967Upgradeable {
    function __ERC1967Upgrade_init() internal onlyInitializing {
    }

    function __ERC1967Upgrade_init_unchained() internal onlyInitializing {
    }
    // This is the keccak-256 hash of "eip1967.proxy.rollback" subtracted by 1
    bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143;

    /**
     * @dev Storage slot with the address of the current implementation.
     * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
     * validated in the constructor.
     */
    bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;

    /**
     * @dev Returns the current implementation address.
     */
    function _getImplementation() internal view returns (address) {
        return StorageSlotUpgradeable.getAddressSlot(_IMPLEMENTATION_SLOT).value;
    }

    /**
     * @dev Stores a new address in the EIP1967 implementation slot.
     */
    function _setImplementation(address newImplementation) private {
        require(AddressUpgradeable.isContract(newImplementation), "ERC1967: new implementation is not a contract");
        StorageSlotUpgradeable.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
    }

    /**
     * @dev Perform implementation upgrade
     *
     * Emits an {Upgraded} event.
     */
    function _upgradeTo(address newImplementation) internal {
        _setImplementation(newImplementation);
        emit Upgraded(newImplementation);
    }

    /**
     * @dev Perform implementation upgrade with additional setup call.
     *
     * Emits an {Upgraded} event.
     */
    function _upgradeToAndCall(address newImplementation, bytes memory data, bool forceCall) internal {
        _upgradeTo(newImplementation);
        if (data.length > 0 || forceCall) {
            AddressUpgradeable.functionDelegateCall(newImplementation, data);
        }
    }

    /**
     * @dev Perform implementation upgrade with security checks for UUPS proxies, and additional setup call.
     *
     * Emits an {Upgraded} event.
     */
    function _upgradeToAndCallUUPS(address newImplementation, bytes memory data, bool forceCall) internal {
        // Upgrades from old implementations will perform a rollback test. This test requires the new
        // implementation to upgrade back to the old, non-ERC1822 compliant, implementation. Removing
        // this special case will break upgrade paths from old UUPS implementation to new ones.
        if (StorageSlotUpgradeable.getBooleanSlot(_ROLLBACK_SLOT).value) {
            _setImplementation(newImplementation);
        } else {
            try IERC1822ProxiableUpgradeable(newImplementation).proxiableUUID() returns (bytes32 slot) {
                require(slot == _IMPLEMENTATION_SLOT, "ERC1967Upgrade: unsupported proxiableUUID");
            } catch {
                revert("ERC1967Upgrade: new implementation is not UUPS");
            }
            _upgradeToAndCall(newImplementation, data, forceCall);
        }
    }

    /**
     * @dev Storage slot with the admin of the contract.
     * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is
     * validated in the constructor.
     */
    bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;

    /**
     * @dev Returns the current admin.
     */
    function _getAdmin() internal view returns (address) {
        return StorageSlotUpgradeable.getAddressSlot(_ADMIN_SLOT).value;
    }

    /**
     * @dev Stores a new address in the EIP1967 admin slot.
     */
    function _setAdmin(address newAdmin) private {
        require(newAdmin != address(0), "ERC1967: new admin is the zero address");
        StorageSlotUpgradeable.getAddressSlot(_ADMIN_SLOT).value = newAdmin;
    }

    /**
     * @dev Changes the admin of the proxy.
     *
     * Emits an {AdminChanged} event.
     */
    function _changeAdmin(address newAdmin) internal {
        emit AdminChanged(_getAdmin(), newAdmin);
        _setAdmin(newAdmin);
    }

    /**
     * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.
     * This is bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)) and is validated in the constructor.
     */
    bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;

    /**
     * @dev Returns the current beacon.
     */
    function _getBeacon() internal view returns (address) {
        return StorageSlotUpgradeable.getAddressSlot(_BEACON_SLOT).value;
    }

    /**
     * @dev Stores a new beacon in the EIP1967 beacon slot.
     */
    function _setBeacon(address newBeacon) private {
        require(AddressUpgradeable.isContract(newBeacon), "ERC1967: new beacon is not a contract");
        require(
            AddressUpgradeable.isContract(IBeaconUpgradeable(newBeacon).implementation()),
            "ERC1967: beacon implementation is not a contract"
        );
        StorageSlotUpgradeable.getAddressSlot(_BEACON_SLOT).value = newBeacon;
    }

    /**
     * @dev Perform beacon upgrade with additional setup call. Note: This upgrades the address of the beacon, it does
     * not upgrade the implementation contained in the beacon (see {UpgradeableBeacon-_setImplementation} for that).
     *
     * Emits a {BeaconUpgraded} event.
     */
    function _upgradeBeaconToAndCall(address newBeacon, bytes memory data, bool forceCall) internal {
        _setBeacon(newBeacon);
        emit BeaconUpgraded(newBeacon);
        if (data.length > 0 || forceCall) {
            AddressUpgradeable.functionDelegateCall(IBeaconUpgradeable(newBeacon).implementation(), data);
        }
    }

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[50] private __gap;
}

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

pragma solidity ^0.8.2;

import "../../utils/AddressUpgradeable.sol";

/**
 * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
 * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
 * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
 * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
 *
 * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
 * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
 * case an upgrade adds a module that needs to be initialized.
 *
 * For example:
 *
 * [.hljs-theme-light.nopadding]
 * ```solidity
 * contract MyToken is ERC20Upgradeable {
 *     function initialize() initializer public {
 *         __ERC20_init("MyToken", "MTK");
 *     }
 * }
 *
 * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
 *     function initializeV2() reinitializer(2) public {
 *         __ERC20Permit_init("MyToken");
 *     }
 * }
 * ```
 *
 * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
 * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
 *
 * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
 * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
 *
 * [CAUTION]
 * ====
 * Avoid leaving a contract uninitialized.
 *
 * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
 * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
 * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
 *
 * [.hljs-theme-light.nopadding]
 * ```
 * /// @custom:oz-upgrades-unsafe-allow constructor
 * constructor() {
 *     _disableInitializers();
 * }
 * ```
 * ====
 */
abstract contract Initializable {
    /**
     * @dev Indicates that the contract has been initialized.
     * @custom:oz-retyped-from bool
     */
    uint8 private _initialized;

    /**
     * @dev Indicates that the contract is in the process of being initialized.
     */
    bool private _initializing;

    /**
     * @dev Triggered when the contract has been initialized or reinitialized.
     */
    event Initialized(uint8 version);

    /**
     * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
     * `onlyInitializing` functions can be used to initialize parent contracts.
     *
     * Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a
     * constructor.
     *
     * Emits an {Initialized} event.
     */
    modifier initializer() {
        bool isTopLevelCall = !_initializing;
        require(
            (isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1),
            "Initializable: contract is already initialized"
        );
        _initialized = 1;
        if (isTopLevelCall) {
            _initializing = true;
        }
        _;
        if (isTopLevelCall) {
            _initializing = false;
            emit Initialized(1);
        }
    }

    /**
     * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
     * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
     * used to initialize parent contracts.
     *
     * A reinitializer may be used after the original initialization step. This is essential to configure modules that
     * are added through upgrades and that require initialization.
     *
     * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
     * cannot be nested. If one is invoked in the context of another, execution will revert.
     *
     * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
     * a contract, executing them in the right order is up to the developer or operator.
     *
     * WARNING: setting the version to 255 will prevent any future reinitialization.
     *
     * Emits an {Initialized} event.
     */
    modifier reinitializer(uint8 version) {
        require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
        _initialized = version;
        _initializing = true;
        _;
        _initializing = false;
        emit Initialized(version);
    }

    /**
     * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
     * {initializer} and {reinitializer} modifiers, directly or indirectly.
     */
    modifier onlyInitializing() {
        require(_initializing, "Initializable: contract is not initializing");
        _;
    }

    /**
     * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
     * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
     * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
     * through proxies.
     *
     * Emits an {Initialized} event the first time it is successfully executed.
     */
    function _disableInitializers() internal virtual {
        require(!_initializing, "Initializable: contract is initializing");
        if (_initialized != type(uint8).max) {
            _initialized = type(uint8).max;
            emit Initialized(type(uint8).max);
        }
    }

    /**
     * @dev Returns the highest version that has been initialized. See {reinitializer}.
     */
    function _getInitializedVersion() internal view returns (uint8) {
        return _initialized;
    }

    /**
     * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
     */
    function _isInitializing() internal view returns (bool) {
        return _initializing;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)

pragma solidity ^0.8.0;

/**
 * @dev External interface of AccessControl declared to support ERC165 detection.
 */
interface IAccessControlUpgradeable {
    /**
     * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
     *
     * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
     * {RoleAdminChanged} not being emitted signaling this.
     *
     * _Available since v3.1._
     */
    event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);

    /**
     * @dev Emitted when `account` is granted `role`.
     *
     * `sender` is the account that originated the contract call, an admin role
     * bearer except when using {AccessControl-_setupRole}.
     */
    event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Emitted when `account` is revoked `role`.
     *
     * `sender` is the account that originated the contract call:
     *   - if using `revokeRole`, it is the admin role bearer
     *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
     */
    event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) external view returns (bool);

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {AccessControl-_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) external view returns (bytes32);

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function grantRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function revokeRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been granted `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `account`.
     */
    function renounceRole(bytes32 role, address account) external;
}

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

pragma solidity ^0.8.0;
import "../proxy/utils/Initializable.sol";

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

    function __Context_init_unchained() internal onlyInitializing {
    }
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

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

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[50] private __gap;
}

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

pragma solidity ^0.8.0;

import "./math/MathUpgradeable.sol";
import "./math/SignedMathUpgradeable.sol";

/**
 * @dev String operations.
 */
library StringsUpgradeable {
    bytes16 private constant _SYMBOLS = "0123456789abcdef";
    uint8 private constant _ADDRESS_LENGTH = 20;

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        unchecked {
            uint256 length = MathUpgradeable.log10(value) + 1;
            string memory buffer = new string(length);
            uint256 ptr;
            /// @solidity memory-safe-assembly
            assembly {
                ptr := add(buffer, add(32, length))
            }
            while (true) {
                ptr--;
                /// @solidity memory-safe-assembly
                assembly {
                    mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
                }
                value /= 10;
                if (value == 0) break;
            }
            return buffer;
        }
    }

    /**
     * @dev Converts a `int256` to its ASCII `string` decimal representation.
     */
    function toString(int256 value) internal pure returns (string memory) {
        return string(abi.encodePacked(value < 0 ? "-" : "", toString(SignedMathUpgradeable.abs(value))));
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        unchecked {
            return toHexString(value, MathUpgradeable.log256(value) + 1);
        }
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
     */
    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
        bytes memory buffer = new bytes(2 * length + 2);
        buffer[0] = "0";
        buffer[1] = "x";
        for (uint256 i = 2 * length + 1; i > 1; --i) {
            buffer[i] = _SYMBOLS[value & 0xf];
            value >>= 4;
        }
        require(value == 0, "Strings: hex length insufficient");
        return string(buffer);
    }

    /**
     * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
     */
    function toHexString(address addr) internal pure returns (string memory) {
        return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
    }

    /**
     * @dev Returns true if the two strings are equal.
     */
    function equal(string memory a, string memory b) internal pure returns (bool) {
        return keccak256(bytes(a)) == keccak256(bytes(b));
    }
}

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

pragma solidity ^0.8.0;

import "./IERC165Upgradeable.sol";
import "../../proxy/utils/Initializable.sol";

/**
 * @dev Implementation of the {IERC165} interface.
 *
 * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
 * for the additional interface id that will be supported. For example:
 *
 * ```solidity
 * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
 *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
 * }
 * ```
 *
 * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
 */
abstract contract ERC165Upgradeable is Initializable, IERC165Upgradeable {
    function __ERC165_init() internal onlyInitializing {
    }

    function __ERC165_init_unchained() internal onlyInitializing {
    }
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IERC165Upgradeable).interfaceId;
    }

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[50] private __gap;
}

// SPDX-License-Identifier: CC0
pragma solidity >=0.6.4;

/// @dev Specification for flash lenders compatible with ERC-7399
interface IERC7399 {
    /// @dev The amount of currency available to be lent.
    /// @param asset The loan currency.
    /// @return The amount of `asset` that can be borrowed.
    function maxFlashLoan(address asset) external view returns (uint256);

    /// @dev The fee to be charged for a given loan.
    /// @param asset The loan currency.
    /// @param amount The amount of assets lent.
    /// @return The amount of `asset` to be charged for the loan, on top of the returned principal.
    function flashFee(address asset, uint256 amount) external view returns (uint256);

    /// @dev Initiate a flash loan.
    /// @param loanReceiver The address receiving the flash loan
    /// @param asset The asset to be loaned
    /// @param amount The amount to loaned
    /// @param data The ABI encoded user data
    /// @param callback The address and signature of the callback function
    /// @return result ABI encoded result of the callback
    function flash(
        address loanReceiver,
        address asset,
        uint256 amount,
        bytes calldata data,
        /// @dev callback. This is a combination of the callback receiver address, and the signature of callback
        /// function. It is encoded packed as 20 bytes + 4 bytes.
        /// @dev the return of the callback function is not encoded in the parameter, but must be `returns (bytes
        /// memory)` for compliance with the standard.
        /// @param initiator The address that called this function
        /// @param paymentReceiver The address that needs to receive the amount plus fee at the end of the callback
        /// @param asset The asset to be loaned
        /// @param amount The amount to loaned
        /// @param fee The fee to be paid
        /// @param data The ABI encoded data to be passed to the callback
        /// @return result ABI encoded result of the callback
        function(address, address, address, uint256, uint256, bytes memory) external returns (bytes memory) callback
    ) external returns (bytes memory);
}

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

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/access/AccessControl.sol";
import "../libraries/DataTypes.sol";
import "../libraries/Errors.sol";
import "../libraries/Roles.sol";

/// @title PositionNFT
/// @notice An ERC721 NFT that represents ownership of each position created through the protocol
/// @dev Instances can only be minted by other contango contracts
contract PositionNFT is ERC721, AccessControl {

    event ContangoContractSet(address indexed contractAddr, bool indexed enabled);

    uint256 public counter = 1;
    mapping(address contractAddr => bool enabled) public contangoContracts;

    constructor(Timelock timelock) ERC721("Contango Position", "CTGP") {
        // Grant the admin role to the timelock by default
        _grantRole(DEFAULT_ADMIN_ROLE, Timelock.unwrap(timelock));
    }

    /// @notice creates a new position in the protocol by minting a new NFT instance
    /// @param positionId positionId of the new position without the number component set
    /// @param to The would be owner of the newly minted position
    /// @return positionId_ The newly created positionId
    function mint(PositionId positionId, address to) external onlyRole(MINTER_ROLE) returns (PositionId positionId_) {
        positionId_ = positionId.withNumber(counter++);
        _safeMint(to, uint256(PositionId.unwrap(positionId_)));
    }

    /// @notice closes a position in the protocol by burning the NFT instance
    /// @param positionId positionId of the closed position
    function burn(PositionId positionId) external onlyRole(MINTER_ROLE) {
        _burn(uint256(PositionId.unwrap(positionId)));
    }

    function positionOwner(PositionId positionId) public view returns (address) {
        return ownerOf(uint256(PositionId.unwrap(positionId)));
    }

    function exists(PositionId positionId) external view returns (bool) {
        return _exists(uint256(PositionId.unwrap(positionId)));
    }

    function setContangoContract(address contractAddr, bool enabled) external onlyRole(DEFAULT_ADMIN_ROLE) {
        contangoContracts[contractAddr] = enabled;
        emit ContangoContractSet(contractAddr, enabled);
    }

    function isApprovedForAll(address owner, address operator) public view override returns (bool) {
        return owner == operator || contangoContracts[operator] || super.isApprovedForAll(owner, operator);
    }

    /// @inheritdoc ERC721
    function supportsInterface(bytes4 interfaceId) public view virtual override(AccessControl, ERC721) returns (bool) {
        return super.supportsInterface(interfaceId) || AccessControl.supportsInterface(interfaceId);
    }

}

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

import "../libraries/DataTypes.sol";
import "./IFeeModel.sol";
import "./IReferralManager.sol";

interface IFeeManagerEvents {

    event FeePaid(
        PositionId indexed positionId,
        address indexed trader,
        address indexed referrer,
        uint256 referrerAmount,
        uint256 traderRebate,
        uint256 protocolFee,
        Currency feeCcy
    );

}

interface IFeeManager is IFeeManagerEvents {

    function feeModel() external view returns (IFeeModel);
    function referralManager() external view returns (IReferralManager);

    /// @notice Applies fees for a given trade. Assumes necessary funds are approved by msg.sender
    /// @param trader The trade trader
    /// @param positionId The trade position id
    /// @param quantity The trade quantity
    /// @return fee Amount of fees paid
    /// @return feeCcy Currency of fee paid
    function applyFee(address trader, PositionId positionId, uint256 quantity) external returns (uint256 fee, Currency feeCcy);

}

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

import { IERC20Metadata as IERC20 } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import "../dependencies/IWETH9.sol";

interface IVaultErrors {

    error ZeroAmount();
    error UnsupportedToken(IERC20 token);
    error NotEnoughBalance(IERC20 token, uint256 balance, uint256 requested);

}

interface IVault is IVaultErrors {

    function nativeToken() external view returns (IWETH9);

    function isTokenSupported(IERC20 token) external view returns (bool);

    function setTokenSupport(IERC20 token, bool isSupported) external;

    function balanceOf(IERC20 token, address owner) external view returns (uint256);

    function totalBalanceOf(IERC20 token) external view returns (uint256);

    function deposit(IERC20 token, address account, uint256 amount) external returns (uint256);

    function depositTo(IERC20 token, address account, uint256 amount) external returns (uint256);

    function depositNative(address account) external payable returns (uint256);

    function withdraw(IERC20 token, address account, uint256 amount, address to) external returns (uint256);

    function withdrawNative(address account, uint256 amount, address to) external returns (uint256);

}

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

import "./IMoneyMarket.sol";

interface IUnderlyingPositionFactoryEvents {

    event UnderlyingPositionCreated(address indexed account, PositionId indexed positionId);
    event MoneyMarketRegistered(MoneyMarketId indexed mm, IMoneyMarket indexed moneyMarket);

}

interface IUnderlyingPositionFactory is IUnderlyingPositionFactoryEvents {

    function registerMoneyMarket(IMoneyMarket imm) external;

    function createUnderlyingPosition(PositionId) external returns (IMoneyMarket);

    /// @return plain IMoneyMarket implementation without any position context
    function moneyMarket(MoneyMarketId) external view returns (IMoneyMarket);

    /// @return position context loaded IMoneyMarket
    function moneyMarket(PositionId) external view returns (IMoneyMarket);

}

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

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

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

import "../DataTypes.sol";

error InvalidUInt48(uint256 n);
error InvalidUInt32(uint256 n);
error InvalidExpiry();
error InvalidPositionId();

//                                 { 5B: Payload  }
//  16B   -      1B      -   4B   -  1B   -  4B   -  6B
// symbol - money market - expiry - flags - empty - number

function decode(PositionId positionId) pure returns (Symbol symbol, MoneyMarketId mm, uint32 expiry, uint256 number) {
    bytes32 raw = PositionId.unwrap(positionId);
    symbol = Symbol.wrap(bytes16(raw));
    mm = MoneyMarketId.wrap(uint8(uint256(raw >> 120)));
    expiry = (uint32(uint256(raw >> 88)));
    number = uint48(uint256(raw));
}

function getSymbol(PositionId positionId) pure returns (Symbol) {
    return Symbol.wrap(bytes16(PositionId.unwrap(positionId)));
}

function getNumber(PositionId positionId) pure returns (uint256) {
    return uint48(uint256(PositionId.unwrap(positionId)));
}

function getMoneyMarket(PositionId positionId) pure returns (MoneyMarketId) {
    return MoneyMarketId.wrap(uint8(uint256(PositionId.unwrap(positionId) >> 120)));
}

function getExpiry(PositionId positionId) pure returns (uint32) {
    return (uint32(uint256(PositionId.unwrap(positionId) >> 88)));
}

function isPerp(PositionId positionId) pure returns (bool) {
    return getExpiry(positionId) == type(uint32).max;
}

function isExpired(PositionId positionId) view returns (bool) {
    return block.timestamp >= getExpiry(positionId);
}

function withNumber(PositionId positionId, uint256 number) pure returns (PositionId) {
    if (uint48(number) != number) revert InvalidUInt48(number);
    if (getNumber(positionId) != 0) revert InvalidPositionId();
    return PositionId.wrap(bytes32(uint256(PositionId.unwrap(positionId)) + number));
}

function getFlags(PositionId positionId) pure returns (bytes1) {
    return bytes1(PositionId.unwrap(positionId) << 168);
}

function getPayload(PositionId positionId) pure returns (Payload) {
    return Payload.wrap(bytes5(PositionId.unwrap(positionId) << 168));
}

function asUint(PositionId positionId) pure returns (uint256) {
    return uint256(PositionId.unwrap(positionId));
}

function fromUint(uint256 n) pure returns (PositionId) {
    return PositionId.wrap(bytes32(n));
}

File 47 of 61 : IBeaconUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (proxy/beacon/IBeacon.sol)

pragma solidity ^0.8.0;

/**
 * @dev This is the interface that {BeaconProxy} expects of its beacon.
 */
interface IBeaconUpgradeable {
    /**
     * @dev Must return an address that can be used as a delegate call target.
     *
     * {BeaconProxy} will check that this address is a contract.
     */
    function implementation() external view returns (address);
}

File 48 of 61 : IERC1967Upgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (interfaces/IERC1967.sol)

pragma solidity ^0.8.0;

/**
 * @dev ERC-1967: Proxy Storage Slots. This interface contains the events defined in the ERC.
 *
 * _Available since v4.8.3._
 */
interface IERC1967Upgradeable {
    /**
     * @dev Emitted when the implementation is upgraded.
     */
    event Upgraded(address indexed implementation);

    /**
     * @dev Emitted when the admin account has changed.
     */
    event AdminChanged(address previousAdmin, address newAdmin);

    /**
     * @dev Emitted when the beacon is changed.
     */
    event BeaconUpgraded(address indexed beacon);
}

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

pragma solidity ^0.8.1;

/**
 * @dev Collection of functions related to the address type
 */
library AddressUpgradeable {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     *
     * Furthermore, `isContract` will also return true if the target contract within
     * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
     * which only has an effect at the end of a transaction.
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize/address.code.length, which returns 0
        // for contracts in construction, since the code is only stored at the end
        // of the constructor execution.

        return account.code.length > 0;
    }

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

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

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

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

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

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

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

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

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

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
     * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
     *
     * _Available since v4.8._
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        if (success) {
            if (returndata.length == 0) {
                // only check isContract if the call was successful and the return data is empty
                // otherwise we already know that it was a contract
                require(isContract(target), "Address: call to non-contract");
            }
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason or using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    function _revert(bytes memory returndata, string memory errorMessage) private pure {
        // Look for revert reason and bubble it up if present
        if (returndata.length > 0) {
            // The easiest way to bubble the revert reason is using memory via assembly
            /// @solidity memory-safe-assembly
            assembly {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert(errorMessage);
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/StorageSlot.sol)
// This file was procedurally generated from scripts/generate/templates/StorageSlot.js.

pragma solidity ^0.8.0;

/**
 * @dev Library for reading and writing primitive types to specific storage slots.
 *
 * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
 * This library helps with reading and writing to such slots without the need for inline assembly.
 *
 * The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
 *
 * Example usage to set ERC1967 implementation slot:
 * ```solidity
 * contract ERC1967 {
 *     bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
 *
 *     function _getImplementation() internal view returns (address) {
 *         return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
 *     }
 *
 *     function _setImplementation(address newImplementation) internal {
 *         require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
 *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
 *     }
 * }
 * ```
 *
 * _Available since v4.1 for `address`, `bool`, `bytes32`, `uint256`._
 * _Available since v4.9 for `string`, `bytes`._
 */
library StorageSlotUpgradeable {
    struct AddressSlot {
        address value;
    }

    struct BooleanSlot {
        bool value;
    }

    struct Bytes32Slot {
        bytes32 value;
    }

    struct Uint256Slot {
        uint256 value;
    }

    struct StringSlot {
        string value;
    }

    struct BytesSlot {
        bytes value;
    }

    /**
     * @dev Returns an `AddressSlot` with member `value` located at `slot`.
     */
    function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `BooleanSlot` with member `value` located at `slot`.
     */
    function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
     */
    function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `Uint256Slot` with member `value` located at `slot`.
     */
    function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `StringSlot` with member `value` located at `slot`.
     */
    function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `StringSlot` representation of the string storage pointer `store`.
     */
    function getStringSlot(string storage store) internal pure returns (StringSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := store.slot
        }
    }

    /**
     * @dev Returns an `BytesSlot` with member `value` located at `slot`.
     */
    function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.
     */
    function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := store.slot
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol)

pragma solidity ^0.8.0;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library MathUpgradeable {
    enum Rounding {
        Down, // Toward negative infinity
        Up, // Toward infinity
        Zero // Toward zero
    }

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

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

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

    /**
     * @dev Returns the ceiling of the division of two numbers.
     *
     * This differs from standard division with `/` in that it rounds up instead
     * of rounding down.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b - 1) / b can overflow on addition, so we distribute.
        return a == 0 ? 0 : (a - 1) / b + 1;
    }

    /**
     * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
     * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
     * with further edits by Uniswap Labs also under MIT license.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
        unchecked {
            // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
            // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
            // variables such that product = prod1 * 2^256 + prod0.
            uint256 prod0; // Least significant 256 bits of the product
            uint256 prod1; // Most significant 256 bits of the product
            assembly {
                let mm := mulmod(x, y, not(0))
                prod0 := mul(x, y)
                prod1 := sub(sub(mm, prod0), lt(mm, prod0))
            }

            // Handle non-overflow cases, 256 by 256 division.
            if (prod1 == 0) {
                // Solidity will revert if denominator == 0, unlike the div opcode on its own.
                // The surrounding unchecked block does not change this fact.
                // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
                return prod0 / denominator;
            }

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            require(denominator > prod1, "Math: mulDiv overflow");

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

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

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

            // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
            // See https://cs.stackexchange.com/q/138556/92363.

            // Does not overflow because the denominator cannot be zero at this stage in the function.
            uint256 twos = denominator & (~denominator + 1);
            assembly {
                // Divide denominator by twos.
                denominator := div(denominator, twos)

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

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

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

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

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

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

    /**
     * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
        uint256 result = mulDiv(x, y, denominator);
        if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
            result += 1;
        }
        return result;
    }

    /**
     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
     *
     * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
     */
    function sqrt(uint256 a) internal pure returns (uint256) {
        if (a == 0) {
            return 0;
        }

        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
        //
        // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
        // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
        //
        // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
        // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
        // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
        //
        // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
        uint256 result = 1 << (log2(a) >> 1);

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

    /**
     * @notice Calculates sqrt(a), following the selected rounding direction.
     */
    function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = sqrt(a);
            return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 2, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 128;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 64;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 32;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 16;
            }
            if (value >> 8 > 0) {
                value >>= 8;
                result += 8;
            }
            if (value >> 4 > 0) {
                value >>= 4;
                result += 4;
            }
            if (value >> 2 > 0) {
                value >>= 2;
                result += 2;
            }
            if (value >> 1 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log2(value);
            return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 10, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >= 10 ** 64) {
                value /= 10 ** 64;
                result += 64;
            }
            if (value >= 10 ** 32) {
                value /= 10 ** 32;
                result += 32;
            }
            if (value >= 10 ** 16) {
                value /= 10 ** 16;
                result += 16;
            }
            if (value >= 10 ** 8) {
                value /= 10 ** 8;
                result += 8;
            }
            if (value >= 10 ** 4) {
                value /= 10 ** 4;
                result += 4;
            }
            if (value >= 10 ** 2) {
                value /= 10 ** 2;
                result += 2;
            }
            if (value >= 10 ** 1) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log10(value);
            return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 256, rounded down, of a positive value.
     * Returns 0 if given 0.
     *
     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
     */
    function log256(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 16;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 8;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 4;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 2;
            }
            if (value >> 8 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 256, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log256(value);
            return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol)

pragma solidity ^0.8.0;

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

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

    /**
     * @dev Returns the average of two signed numbers without overflow.
     * The result is rounded towards zero.
     */
    function average(int256 a, int256 b) internal pure returns (int256) {
        // Formula from the book "Hacker's Delight"
        int256 x = (a & b) + ((a ^ b) >> 1);
        return x + (int256(uint256(x) >> 255) & (a ^ b));
    }

    /**
     * @dev Returns the absolute unsigned value of a signed value.
     */
    function abs(int256 n) internal pure returns (uint256) {
        unchecked {
            // must be unchecked in order to support `n = type(int256).min`
            return uint256(n >= 0 ? n : -n);
        }
    }
}

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

pragma solidity ^0.8.0;

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

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

pragma solidity ^0.8.0;

import "./IERC721.sol";
import "./IERC721Receiver.sol";
import "./extensions/IERC721Metadata.sol";
import "../../utils/Address.sol";
import "../../utils/Context.sol";
import "../../utils/Strings.sol";
import "../../utils/introspection/ERC165.sol";

/**
 * @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including
 * the Metadata extension, but not including the Enumerable extension, which is available separately as
 * {ERC721Enumerable}.
 */
contract ERC721 is Context, ERC165, IERC721, IERC721Metadata {
    using Address for address;
    using Strings for uint256;

    // Token name
    string private _name;

    // Token symbol
    string private _symbol;

    // Mapping from token ID to owner address
    mapping(uint256 => address) private _owners;

    // Mapping owner address to token count
    mapping(address => uint256) private _balances;

    // Mapping from token ID to approved address
    mapping(uint256 => address) private _tokenApprovals;

    // Mapping from owner to operator approvals
    mapping(address => mapping(address => bool)) private _operatorApprovals;

    /**
     * @dev Initializes the contract by setting a `name` and a `symbol` to the token collection.
     */
    constructor(string memory name_, string memory symbol_) {
        _name = name_;
        _symbol = symbol_;
    }

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
        return
            interfaceId == type(IERC721).interfaceId ||
            interfaceId == type(IERC721Metadata).interfaceId ||
            super.supportsInterface(interfaceId);
    }

    /**
     * @dev See {IERC721-balanceOf}.
     */
    function balanceOf(address owner) public view virtual override returns (uint256) {
        require(owner != address(0), "ERC721: address zero is not a valid owner");
        return _balances[owner];
    }

    /**
     * @dev See {IERC721-ownerOf}.
     */
    function ownerOf(uint256 tokenId) public view virtual override returns (address) {
        address owner = _ownerOf(tokenId);
        require(owner != address(0), "ERC721: invalid token ID");
        return owner;
    }

    /**
     * @dev See {IERC721Metadata-name}.
     */
    function name() public view virtual override returns (string memory) {
        return _name;
    }

    /**
     * @dev See {IERC721Metadata-symbol}.
     */
    function symbol() public view virtual override returns (string memory) {
        return _symbol;
    }

    /**
     * @dev See {IERC721Metadata-tokenURI}.
     */
    function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
        _requireMinted(tokenId);

        string memory baseURI = _baseURI();
        return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, tokenId.toString())) : "";
    }

    /**
     * @dev Base URI for computing {tokenURI}. If set, the resulting URI for each
     * token will be the concatenation of the `baseURI` and the `tokenId`. Empty
     * by default, can be overridden in child contracts.
     */
    function _baseURI() internal view virtual returns (string memory) {
        return "";
    }

    /**
     * @dev See {IERC721-approve}.
     */
    function approve(address to, uint256 tokenId) public virtual override {
        address owner = ERC721.ownerOf(tokenId);
        require(to != owner, "ERC721: approval to current owner");

        require(
            _msgSender() == owner || isApprovedForAll(owner, _msgSender()),
            "ERC721: approve caller is not token owner or approved for all"
        );

        _approve(to, tokenId);
    }

    /**
     * @dev See {IERC721-getApproved}.
     */
    function getApproved(uint256 tokenId) public view virtual override returns (address) {
        _requireMinted(tokenId);

        return _tokenApprovals[tokenId];
    }

    /**
     * @dev See {IERC721-setApprovalForAll}.
     */
    function setApprovalForAll(address operator, bool approved) public virtual override {
        _setApprovalForAll(_msgSender(), operator, approved);
    }

    /**
     * @dev See {IERC721-isApprovedForAll}.
     */
    function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) {
        return _operatorApprovals[owner][operator];
    }

    /**
     * @dev See {IERC721-transferFrom}.
     */
    function transferFrom(address from, address to, uint256 tokenId) public virtual override {
        //solhint-disable-next-line max-line-length
        require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: caller is not token owner or approved");

        _transfer(from, to, tokenId);
    }

    /**
     * @dev See {IERC721-safeTransferFrom}.
     */
    function safeTransferFrom(address from, address to, uint256 tokenId) public virtual override {
        safeTransferFrom(from, to, tokenId, "");
    }

    /**
     * @dev See {IERC721-safeTransferFrom}.
     */
    function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public virtual override {
        require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: caller is not token owner or approved");
        _safeTransfer(from, to, tokenId, data);
    }

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
     * are aware of the ERC721 protocol to prevent tokens from being forever locked.
     *
     * `data` is additional data, it has no specified format and it is sent in call to `to`.
     *
     * This internal function is equivalent to {safeTransferFrom}, and can be used to e.g.
     * implement alternative mechanisms to perform token transfer, such as signature-based.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function _safeTransfer(address from, address to, uint256 tokenId, bytes memory data) internal virtual {
        _transfer(from, to, tokenId);
        require(_checkOnERC721Received(from, to, tokenId, data), "ERC721: transfer to non ERC721Receiver implementer");
    }

    /**
     * @dev Returns the owner of the `tokenId`. Does NOT revert if token doesn't exist
     */
    function _ownerOf(uint256 tokenId) internal view virtual returns (address) {
        return _owners[tokenId];
    }

    /**
     * @dev Returns whether `tokenId` exists.
     *
     * Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}.
     *
     * Tokens start existing when they are minted (`_mint`),
     * and stop existing when they are burned (`_burn`).
     */
    function _exists(uint256 tokenId) internal view virtual returns (bool) {
        return _ownerOf(tokenId) != address(0);
    }

    /**
     * @dev Returns whether `spender` is allowed to manage `tokenId`.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function _isApprovedOrOwner(address spender, uint256 tokenId) internal view virtual returns (bool) {
        address owner = ERC721.ownerOf(tokenId);
        return (spender == owner || isApprovedForAll(owner, spender) || getApproved(tokenId) == spender);
    }

    /**
     * @dev Safely mints `tokenId` and transfers it to `to`.
     *
     * Requirements:
     *
     * - `tokenId` must not exist.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function _safeMint(address to, uint256 tokenId) internal virtual {
        _safeMint(to, tokenId, "");
    }

    /**
     * @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is
     * forwarded in {IERC721Receiver-onERC721Received} to contract recipients.
     */
    function _safeMint(address to, uint256 tokenId, bytes memory data) internal virtual {
        _mint(to, tokenId);
        require(
            _checkOnERC721Received(address(0), to, tokenId, data),
            "ERC721: transfer to non ERC721Receiver implementer"
        );
    }

    /**
     * @dev Mints `tokenId` and transfers it to `to`.
     *
     * WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible
     *
     * Requirements:
     *
     * - `tokenId` must not exist.
     * - `to` cannot be the zero address.
     *
     * Emits a {Transfer} event.
     */
    function _mint(address to, uint256 tokenId) internal virtual {
        require(to != address(0), "ERC721: mint to the zero address");
        require(!_exists(tokenId), "ERC721: token already minted");

        _beforeTokenTransfer(address(0), to, tokenId, 1);

        // Check that tokenId was not minted by `_beforeTokenTransfer` hook
        require(!_exists(tokenId), "ERC721: token already minted");

        unchecked {
            // Will not overflow unless all 2**256 token ids are minted to the same owner.
            // Given that tokens are minted one by one, it is impossible in practice that
            // this ever happens. Might change if we allow batch minting.
            // The ERC fails to describe this case.
            _balances[to] += 1;
        }

        _owners[tokenId] = to;

        emit Transfer(address(0), to, tokenId);

        _afterTokenTransfer(address(0), to, tokenId, 1);
    }

    /**
     * @dev Destroys `tokenId`.
     * The approval is cleared when the token is burned.
     * This is an internal function that does not check if the sender is authorized to operate on the token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     *
     * Emits a {Transfer} event.
     */
    function _burn(uint256 tokenId) internal virtual {
        address owner = ERC721.ownerOf(tokenId);

        _beforeTokenTransfer(owner, address(0), tokenId, 1);

        // Update ownership in case tokenId was transferred by `_beforeTokenTransfer` hook
        owner = ERC721.ownerOf(tokenId);

        // Clear approvals
        delete _tokenApprovals[tokenId];

        unchecked {
            // Cannot overflow, as that would require more tokens to be burned/transferred
            // out than the owner initially received through minting and transferring in.
            _balances[owner] -= 1;
        }
        delete _owners[tokenId];

        emit Transfer(owner, address(0), tokenId);

        _afterTokenTransfer(owner, address(0), tokenId, 1);
    }

    /**
     * @dev Transfers `tokenId` from `from` to `to`.
     *  As opposed to {transferFrom}, this imposes no restrictions on msg.sender.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     *
     * Emits a {Transfer} event.
     */
    function _transfer(address from, address to, uint256 tokenId) internal virtual {
        require(ERC721.ownerOf(tokenId) == from, "ERC721: transfer from incorrect owner");
        require(to != address(0), "ERC721: transfer to the zero address");

        _beforeTokenTransfer(from, to, tokenId, 1);

        // Check that tokenId was not transferred by `_beforeTokenTransfer` hook
        require(ERC721.ownerOf(tokenId) == from, "ERC721: transfer from incorrect owner");

        // Clear approvals from the previous owner
        delete _tokenApprovals[tokenId];

        unchecked {
            // `_balances[from]` cannot overflow for the same reason as described in `_burn`:
            // `from`'s balance is the number of token held, which is at least one before the current
            // transfer.
            // `_balances[to]` could overflow in the conditions described in `_mint`. That would require
            // all 2**256 token ids to be minted, which in practice is impossible.
            _balances[from] -= 1;
            _balances[to] += 1;
        }
        _owners[tokenId] = to;

        emit Transfer(from, to, tokenId);

        _afterTokenTransfer(from, to, tokenId, 1);
    }

    /**
     * @dev Approve `to` to operate on `tokenId`
     *
     * Emits an {Approval} event.
     */
    function _approve(address to, uint256 tokenId) internal virtual {
        _tokenApprovals[tokenId] = to;
        emit Approval(ERC721.ownerOf(tokenId), to, tokenId);
    }

    /**
     * @dev Approve `operator` to operate on all of `owner` tokens
     *
     * Emits an {ApprovalForAll} event.
     */
    function _setApprovalForAll(address owner, address operator, bool approved) internal virtual {
        require(owner != operator, "ERC721: approve to caller");
        _operatorApprovals[owner][operator] = approved;
        emit ApprovalForAll(owner, operator, approved);
    }

    /**
     * @dev Reverts if the `tokenId` has not been minted yet.
     */
    function _requireMinted(uint256 tokenId) internal view virtual {
        require(_exists(tokenId), "ERC721: invalid token ID");
    }

    /**
     * @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target address.
     * The call is not executed if the target address is not a contract.
     *
     * @param from address representing the previous owner of the given token ID
     * @param to target address that will receive the tokens
     * @param tokenId uint256 ID of the token to be transferred
     * @param data bytes optional data to send along with the call
     * @return bool whether the call correctly returned the expected magic value
     */
    function _checkOnERC721Received(
        address from,
        address to,
        uint256 tokenId,
        bytes memory data
    ) private returns (bool) {
        if (to.isContract()) {
            try IERC721Receiver(to).onERC721Received(_msgSender(), from, tokenId, data) returns (bytes4 retval) {
                return retval == IERC721Receiver.onERC721Received.selector;
            } catch (bytes memory reason) {
                if (reason.length == 0) {
                    revert("ERC721: transfer to non ERC721Receiver implementer");
                } else {
                    /// @solidity memory-safe-assembly
                    assembly {
                        revert(add(32, reason), mload(reason))
                    }
                }
            }
        } else {
            return true;
        }
    }

    /**
     * @dev Hook that is called before any token transfer. This includes minting and burning. If {ERC721Consecutive} is
     * used, the hook may be called as part of a consecutive (batch) mint, as indicated by `batchSize` greater than 1.
     *
     * Calling conditions:
     *
     * - When `from` and `to` are both non-zero, ``from``'s tokens will be transferred to `to`.
     * - When `from` is zero, the tokens will be minted for `to`.
     * - When `to` is zero, ``from``'s tokens will be burned.
     * - `from` and `to` are never both zero.
     * - `batchSize` is non-zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _beforeTokenTransfer(address from, address to, uint256 firstTokenId, uint256 batchSize) internal virtual {}

    /**
     * @dev Hook that is called after any token transfer. This includes minting and burning. If {ERC721Consecutive} is
     * used, the hook may be called as part of a consecutive (batch) mint, as indicated by `batchSize` greater than 1.
     *
     * Calling conditions:
     *
     * - When `from` and `to` are both non-zero, ``from``'s tokens were transferred to `to`.
     * - When `from` is zero, the tokens were minted for `to`.
     * - When `to` is zero, ``from``'s tokens were burned.
     * - `from` and `to` are never both zero.
     * - `batchSize` is non-zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _afterTokenTransfer(address from, address to, uint256 firstTokenId, uint256 batchSize) internal virtual {}

    /**
     * @dev Unsafe write access to the balances, used by extensions that "mint" tokens using an {ownerOf} override.
     *
     * WARNING: Anyone calling this MUST ensure that the balances remain consistent with the ownership. The invariant
     * being that for any address `a` the value returned by `balanceOf(a)` must be equal to the number of tokens such
     * that `ownerOf(tokenId)` is `a`.
     */
    // solhint-disable-next-line func-name-mixedcase
    function __unsafe_increaseBalance(address account, uint256 amount) internal {
        _balances[account] += amount;
    }
}

File 55 of 61 : Errors.sol
//SPDX-License-Identifier: MIT
pragma solidity 0.8.20;

error SenderIsNotNativeToken(address msgSender, address nativeToken);
error Unauthorised(address msgSender);
error UnsupportedOperation();

File 56 of 61 : Roles.sol
//SPDX-License-Identifier: MIT
pragma solidity 0.8.20;

bytes32 constant EMERGENCY_BREAK_ROLE = keccak256("EMERGENCY_BREAK");
bytes32 constant OPERATOR_ROLE = keccak256("OPERATOR");
bytes32 constant CONTANGO_ROLE = keccak256("CONTANGO");
bytes32 constant BOT_ROLE = keccak256("BOT");
bytes32 constant MINTER_ROLE = keccak256("MINTER");
bytes32 constant MODIFIER_ROLE = keccak256("MODIFIER");

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

import "../libraries/DataTypes.sol";

interface IFeeModel {

    /// @notice Calculates fees for a given trade
    /// @param trader The trade trader
    /// @param positionId The trade position id
    /// @param quantity The trade quantity
    /// @return calculatedFee The calculated fee of the trade cost
    function calculateFee(address trader, PositionId positionId, uint256 quantity) external view returns (uint256 calculatedFee);

}

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

import "../libraries/DataTypes.sol";

struct FeeDistribution {
    uint256 protocol;
    uint256 referrer;
    uint256 trader;
    address referrerAddress;
}

interface IReferralManagerEvents {

    event RewardsAndRebatesSet(uint256 referrerReward, uint256 traderRebate);
    event ReferralCodeRegistered(address indexed referrer, bytes32 referralCode);
    event TraderReferred(address indexed trader, address indexed referrer, bytes32 indexed referralCode);

}

interface IReferralManager is IReferralManagerEvents {

    error ReferralCodeInvalid(bytes32 code); // 0x66f70111
    error ReferralCodeAlreadySet(bytes32 code); // 0xb7f957d0
    error ReferralCodeUnavailable(bytes32 code); // 0x244fbb46
    error ReferralCodeNotRegistered(bytes32 code); // 0x7b85b5e5
    error CannotSelfRefer(); // 0xd1affa92
    error RewardsConfigCannotExceedMax(); // 0xd483d656

    // @notice Sets the rewards and rebates for referrals
    // @param referrerReward The percentage of fees to reward the referrer
    // @param traderRebate The percentage of fees to rebate the trader
    // @dev The sum of referrerReward and traderRebate cannot exceed 100%
    // @dev The precision of the rewards and rebates is 1e4 == 100%
    function setRewardsAndRebates(uint256 referrerReward, uint256 traderRebate) external;

    // @notice Checks if a referral code is available
    // @param code The referral code to check
    function isCodeAvailable(bytes32 code) external view returns (bool);

    // @notice Registers a referral code for the caller
    // @param code The referral code to register
    function registerReferralCode(bytes32 code) external;

    // @notice Sets the referrer for the caller
    // @param code The referral code to use
    function setTraderReferralByCode(bytes32 code) external;

    // @notice Sets the referrer for a given address
    // @param code The referral code to use
    // @param trader The trader to set the referrer for
    function setTraderReferralByCodeForAddress(bytes32 code, address trader) external;

    // @notice Determines the distribution for a given amount to the protocol, referrer, and trader
    // @param trader The trader used to lookup the referrer
    // @param amount The amount to distribute
    function calculateRewardDistribution(address trader, uint256 amount) external view returns (FeeDistribution memory);

}

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

import { IERC20Metadata as IERC20 } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import "@openzeppelin/contracts/utils/introspection/IERC165.sol";

import { PositionId, MoneyMarketId } from "../../libraries/DataTypes.sol";

interface IMoneyMarket is IERC165 {

    error InvalidMoneyMarketId();
    error RewardsNotImplemented();

    /// @dev indicates whether the money market requires an underlying account to be created
    /// if true, the money market must be cloned to create an underlying position
    /// otherwise the money market can be used directly as it know how to isolate positions
    function NEEDS_ACCOUNT() external view returns (bool);

    function moneyMarketId() external view returns (MoneyMarketId);

    function initialise(PositionId positionId, IERC20 collateralAsset, IERC20 debtAsset) external;

    function lend(PositionId positionId, IERC20 asset, uint256 amount) external returns (uint256 actualAmount);

    function withdraw(PositionId positionId, IERC20 asset, uint256 amount, address to) external returns (uint256 actualAmount);

    function borrow(PositionId positionId, IERC20 asset, uint256 amount, address to) external returns (uint256 actualAmount);

    function repay(PositionId positionId, IERC20 asset, uint256 amount) external returns (uint256 actualAmount);

    function claimRewards(PositionId positionId, IERC20 collateralAsset, IERC20 debtAsset, address to) external;

    function collateralBalance(PositionId positionId, IERC20 asset) external returns (uint256 balance);

}

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

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

pragma solidity ^0.8.0;

import "../IERC721.sol";

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

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

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

Settings
{
  "remappings": [
    "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
    "@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
    "forge-std/=lib/forge-std/src/",
    "forge-gas-snapshot/=lib/forge-gas-snapshot/src/",
    "erc7399/=lib/erc7399/src/",
    "erc7399-wrappers/=lib/erc7399-wrappers/src/",
    "@prb/math/=lib/prb-math/",
    "@contango/erc721Permit2/=lib/erc721-permit2/src/",
    "@prb/test/=lib/prb-math/lib/prb-test/src/",
    "ds-test/=lib/prb-math/lib/forge-std/lib/ds-test/src/",
    "erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/",
    "erc721-permit2/=lib/erc721-permit2/",
    "openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts/",
    "openzeppelin/=lib/openzeppelin-contracts-upgradeable/contracts/",
    "prb-math/=lib/prb-math/src/",
    "prb-test/=lib/prb-math/lib/prb-test/src/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "none",
    "appendCBOR": false
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "london",
  "viaIR": false,
  "libraries": {}
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"Timelock","name":"timelock","type":"address"},{"internalType":"contract IMaestro","name":"_maestro","type":"address"},{"internalType":"contract IERC721Permit2","name":"_erc721Permit2","type":"address"},{"internalType":"contract ContangoLens","name":"_lens","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"InvalidCallback","type":"error"},{"inputs":[{"internalType":"enum Step","name":"step","type":"uint8"}],"name":"InvalidStep","type":"error"},{"inputs":[],"name":"NotNativeToken","type":"error"},{"inputs":[],"name":"NotPositionNFT","type":"error"},{"inputs":[],"name":"PositionLeftBehind","type":"error"},{"inputs":[],"name":"ZeroDestination","type":"error"},{"inputs":[],"name":"ZeroPayer","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"PositionId","name":"positionId","type":"bytes32"},{"indexed":true,"internalType":"address","name":"owner","type":"address"}],"name":"BeginStrategy","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"PositionId","name":"positionId","type":"bytes32"},{"indexed":true,"internalType":"address","name":"owner","type":"address"}],"name":"EndStrategy","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"bytes32","name":"action","type":"bytes32"},{"indexed":false,"internalType":"PositionId","name":"position1","type":"bytes32"},{"indexed":false,"internalType":"PositionId","name":"position2","type":"bytes32"},{"indexed":false,"internalType":"bytes","name":"data","type":"bytes"}],"name":"StragegyExecuted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"trader","type":"address"},{"indexed":false,"internalType":"contract IERC20Metadata","name":"tokenIn","type":"address"},{"indexed":false,"internalType":"uint256","name":"amountIn","type":"uint256"},{"indexed":false,"internalType":"contract IERC20Metadata","name":"tokenOut","type":"address"},{"indexed":false,"internalType":"uint256","name":"amountOut","type":"uint256"}],"name":"SwapExecuted","type":"event"},{"inputs":[],"name":"ALL","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"BALANCE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20Metadata","name":"token","type":"address"},{"components":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"vs","type":"bytes32"}],"internalType":"struct EIP2098Permit","name":"permit","type":"tuple"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"to","type":"address"}],"name":"_pullFundsWithPermit2","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"contango","outputs":[{"internalType":"contract IContango","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"repayTo","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"continueActionProcessing","outputs":[{"internalType":"bytes","name":"result","type":"bytes"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"erc20Permit2","outputs":[{"internalType":"contract IPermit2","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"erc721Permit2","outputs":[{"internalType":"contract IERC721Permit2","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lens","outputs":[{"internalType":"contract ContangoLens","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nativeToken","outputs":[{"internalType":"contract IWETH9","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"address","name":"from","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"onERC721Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"positionNFT","outputs":[{"internalType":"contract PositionNFT","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"enum Step","name":"step","type":"uint8"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct StepCall[]","name":"steps","type":"tuple[]"},{"internalType":"address","name":"returnPositionsTo","type":"address"}],"name":"process","outputs":[{"components":[{"internalType":"enum Step","name":"step","type":"uint8"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct StepResult[]","name":"results","type":"tuple[]"}],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"enum Step","name":"step","type":"uint8"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct StepCall[]","name":"steps","type":"tuple[]"}],"name":"process","outputs":[{"components":[{"internalType":"enum Step","name":"step","type":"uint8"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct StepResult[]","name":"results","type":"tuple[]"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"PositionId","name":"positionId","type":"bytes32"},{"internalType":"address","name":"to","type":"address"}],"name":"retrieve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20Metadata","name":"token","type":"address"},{"internalType":"address","name":"to","type":"address"}],"name":"retrieve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20Metadata","name":"token","type":"address"},{"internalType":"address","name":"to","type":"address"}],"name":"retrieveFromVault","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address payable","name":"to","type":"address"}],"name":"retrieveNative","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"spotExecutor","outputs":[{"internalType":"contract SimpleSpotExecutor","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"vault","outputs":[{"internalType":"contract IVault","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]

6101806040523480156200001257600080fd5b50604051620050b5380380620050b58339810160408190526200003591620003fe565b838383836200004660008562000344565b826001600160a01b0316630a2959756040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000085573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620000ab919062000466565b6001600160a01b03166080816001600160a01b031681525050826001600160a01b031663fbfa77cf6040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000103573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000129919062000466565b6001600160a01b031660a0816001600160a01b0316815250506080516001600160a01b03166323228ecb6040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000183573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620001a9919062000466565b6001600160a01b031660c0816001600160a01b031681525050826001600160a01b031663a5efcaf36040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000201573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000227919062000466565b6001600160a01b039081166101205282811660e05281811661010052604080516312261ee760e01b81529051918516916312261ee7916004808201926020929091908290030181865afa15801562000283573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620002a9919062000466565b6001600160a01b0316610140816001600160a01b031681525050826001600160a01b031663e1758bd86040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000302573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000328919062000466565b6001600160a01b031661016052506200048d9650505050505050565b6000828152602081815260408083206001600160a01b038516845290915290205460ff16620003e1576000828152602081815260408083206001600160a01b03851684529091529020805460ff19166001179055620003a03390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45b5050565b6001600160a01b0381168114620003fb57600080fd5b50565b600080600080608085870312156200041557600080fd5b84516200042281620003e5565b60208601519094506200043581620003e5565b60408601519093506200044881620003e5565b60608601519092506200045b81620003e5565b939692955090935050565b6000602082840312156200047957600080fd5b81516200048681620003e5565b9392505050565b60805160a05160c05160e05161010051610120516101405161016051614acd620005e8600039600081816101950152818161054801528181611ccb01528181611dca01528181611e4801528181611e9e0152611f2101526000818161044b01526109000152600081816104d40152818161251c01526126c101526000818161026d01526122670152600081816102fd01526129940152600081816103310152818161064201528181610a6e015281816128db01528181612a1c0152612aa30152600081816105dc0152818161172a015281816117cc01528181611a0501528181611a8701528181611b1e01528181611bba01528181611cf901528181611d9301528181611fa301528181612077015281816122f7015281816126e90152818161276201526127a501526000818161022101528181610682015281816120a6015281816123260152818161248801526128490152614acd6000f3fe6080604052600436106101855760003560e01c806344402059116100d1578063c1400da01161008a578063f06c2dfc11610064578063f06c2dfc1461056a578063f533c1b61461058a578063f88bc8961461059d578063fbfa77cf146105ca57600080fd5b8063c1400da0146104f6578063d547741f14610516578063e1758bd81461053657600080fd5b8063444020591461041957806373c325691461043957806391d148541461046d5780639e687b6c1461048d578063a217fddf146104ad578063a5efcaf3146104c257600080fd5b806323228ecb1161013e578063332c884d11610118578063332c884d146103a357806336568abe146103c35780633adee5b0146103e3578063435f0d411461040357600080fd5b806323228ecb1461031f578063248a9ca3146103535780632f2ff15d1461038357600080fd5b806301ffc9a7146101da5780630a2959751461020f578063112666b71461025b578063150b7a021461028f57806315e9977e146102c85780631a17a004146102eb57600080fd5b366101d557336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146101d35760405163be5abbed60e01b815260040160405180910390fd5b005b600080fd5b3480156101e657600080fd5b506101fa6101f5366004613585565b6105fe565b60405190151581526020015b60405180910390f35b34801561021b57600080fd5b506102437f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b039091168152602001610206565b34801561026757600080fd5b506102437f000000000000000000000000000000000000000000000000000000000000000081565b34801561029b57600080fd5b506102af6102aa366004613615565b610635565b6040516001600160e01b03199091168152602001610206565b3480156102d457600080fd5b506102dd610741565b604051908152602001610206565b3480156102f757600080fd5b506102437f000000000000000000000000000000000000000000000000000000000000000081565b34801561032b57600080fd5b506102437f000000000000000000000000000000000000000000000000000000000000000081565b34801561035f57600080fd5b506102dd61036e366004613687565b60009081526020819052604090206001015490565b34801561038f57600080fd5b506101d361039e3660046136a0565b610751565b6103b66103b136600461393c565b61077b565b6040516102069190613a77565b3480156103cf57600080fd5b506101d36103de3660046136a0565b61082a565b3480156103ef57600080fd5b506102dd6103fe366004613a8a565b6108ad565b34801561040f57600080fd5b506102dd60001981565b34801561042557600080fd5b506101d3610434366004613b28565b610a03565b34801561044557600080fd5b506102437f000000000000000000000000000000000000000000000000000000000000000081565b34801561047957600080fd5b506101fa6104883660046136a0565b610a2b565b34801561049957600080fd5b506101d36104a83660046136a0565b610a54565b3480156104b957600080fd5b506102dd600081565b3480156104ce57600080fd5b506102437f000000000000000000000000000000000000000000000000000000000000000081565b34801561050257600080fd5b506101d3610511366004613b56565b610ae6565b34801561052257600080fd5b506101d36105313660046136a0565b610b04565b34801561054257600080fd5b506102437f000000000000000000000000000000000000000000000000000000000000000081565b34801561057657600080fd5b506101d3610585366004613b28565b610b29565b6103b6610598366004613b73565b610b47565b3480156105a957600080fd5b506105bd6105b8366004613ba7565b610b53565b6040516102069190613c35565b3480156105d657600080fd5b506102437f000000000000000000000000000000000000000000000000000000000000000081565b60006001600160e01b03198216637965db0b60e01b148061062f57506301ffc9a760e01b6001600160e01b03198316145b92915050565b6000336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146106805760405163020e57df60e41b815260040160405180910390fd5b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316866001600160a01b03161461072f576001600160a01b038516846040517fe1971091f8cd4abc65419e3b56c5a5b1542ce392786599db6e069e80b8f269cc90600090a36106fa8686868686610c40565b6001600160a01b038516846040517fe95aa63b1883254a4494c9e600f7509ec1a5a78dc2f6aef8214b6038d3c4583490600090a35b50630a85bd0160e11b95945050505050565b61074e6001600019613c5e565b81565b60008281526020819052604090206001015461076c81610d09565b6107768383610d16565b505050565b60408051606081810183526000808352602083018190529282019290925283516001600160401b038111156107b2576107b26136d0565b6040519080825280602002602001820160405280156107f857816020015b6040805180820190915260008152606060208201528152602001906001900390816107d05790505b5091503361080a856000848487610d9a565b81516020830151919650919450610822925086611677565b505092915050565b6001600160a01b038116331461089f5760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201526e103937b632b9903337b91039b2b63360891b60648201526084015b60405180910390fd5b6108a9828261168b565b5050565b6040805160a080820183526001600160a01b0388811660608085018281528a516080808801919091529086528a516020808d015189518c881681840152998a0195909552928801528601919091526000947f0000000000000000000000000000000000000000000000000000000000000000909216936330f28b7a93909291830191016040516020818303038152906040528051906020012060001c815260200188602001518152506040518060400160405280866001600160a01b03168152602001888152508689604001518a60600151604051602001610999929190918252602082015260400190565b6040516020818303038152906040526040518563ffffffff1660e01b81526004016109c79493929190613ca7565b600060405180830381600087803b1580156109e157600080fd5b505af11580156109f5573d6000803e3d6000fd5b509598975050505050505050565b6000610a0e81610d09565b610a2583610a1f6001600019613c5e565b846116f0565b50505050565b6000918252602082815260408084206001600160a01b0393909316845291905290205460ff1690565b6000610a5f81610d09565b604051632142170760e11b81527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906342842e0e90610aaf90309086908890600401613cf2565b600060405180830381600087803b158015610ac957600080fd5b505af1158015610add573d6000803e3d6000fd5b50505050505050565b6000610af181610d09565b6108a96001600160a01b03831647611843565b600082815260208190526040902060010154610b1f81610d09565b610776838361168b565b6000610b3481610d09565b610a256001600160a01b0384168361195c565b606061062f823361077b565b606082828080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152505060015483516020850120149150610bb590505760405163f7a632f560e01b815260040160405180910390fd5b600080808080610bc7888a018a613d16565b945094509450945094508c83604001906001600160a01b031690816001600160a01b031681525050610bfc8585858585610d9a565b6040519296509094509150610c1990859085908490602001613dde565b60405160208183030381529060405296505050505050506000600155979650505050505050565b6000610c4e82840184613b73565b9050600081516001600160401b03811115610c6b57610c6b6136d0565b604051908082528060200260200182016040528015610cb157816020015b604080518082019091526000815260606020820152815260200190600190039081610c895790505b509050610cd7604080516060810182526000808252602082018190529181019190915290565b86610ce6846000848487610d9a565b81516020830151919650919450610cfe925083611677565b505050505050505050565b610d138133611982565b50565b610d208282610a2b565b6108a9576000828152602081815260408083206001600160a01b03851684529091529020805460ff19166001179055610d563390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b604080516060810182526000808252602082018190529101528383825b875183101561166c576000888481518110610dd457610dd4613e21565b602002602001015190508060000151858581518110610df557610df5613e21565b6020026020010151600001906013811115610e1257610e12613982565b90816013811115610e2557610e25613982565b905250600281516013811115610e3d57610e3d613982565b03610eb4576000808260200151806020019051810190610e5d9190613e37565b91509150610e6b82826119db565b604051602001610e7d91815260200190565b604051602081830303815290604052878781518110610e9e57610e9e613e21565b6020026020010151602001819052505050611659565b600e81516013811115610ec957610ec9613982565b03610f1d57610ed6611b9a565b604051602001610ee891815260200190565b604051602081830303815290604052858581518110610f0957610f09613e21565b602002602001015160200181905250611659565b600a81516013811115610f3257610f32613982565b03610fb5576000806000808460200151806020019051810190610f559190613ea9565b9350935093509350610f6a8484848d85611c2e565b604051602001610f7c91815260200190565b604051602081830303815290604052898981518110610f9d57610f9d613e21565b60200260200101516020018190525050505050611659565b600b81516013811115610fca57610fca613982565b03611002576000806000808460200151806020019051810190610fed9190613ea9565b9350935093509350610f6a8484848d856108ad565b60038151601381111561101757611017613982565b0361109457600080600083602001518060200190518101906110399190613efb565b92509250925061104a8383836116f0565b60405160200161105c91815260200190565b60405160208183030381529060405288888151811061107d5761107d613e21565b602002602001015160200181905250505050611659565b600f815160138111156110a9576110a9613982565b036110d75760008082602001518060200190518101906110c99190613f3e565b91509150610e6b8282611c9f565b6011815160138111156110ec576110ec613982565b0361111157610ed6816020015180602001905181019061110c9190613f63565b611dc2565b60128151601381111561112657611126613982565b036111545760008082602001518060200190518101906111469190613f3e565b91509150610e6b8282611e74565b60008151601381111561116957611169613982565b036111cf576000806000836020015180602001905181019061118b9190613f80565b919450925090506111ac8383838f6111a48c6001613fc3565b8b8f8c611f48565b8060200190518101906111bf919061401b565b9198509650945061165992505050565b6001815160138111156111e4576111e4613982565b036112175760008082602001518060200190518101906112049190613e37565b91509150610e6b828287604001516116f0565b60058151601381111561122c5761122c613982565b03611294576000806000836020015180602001905181019061124e919061416c565b92509250925061125c6134e6565b61127061126a88868661202e565b83612058565b90945090506112808785856121d3565b508381604051602001610f7c9291906141aa565b6007815160138111156112a9576112a9613982565b036112ed57600080600083602001518060200190518101906112cb919061416c565b9250925092506112d96134e6565b6112706112e788868661202e565b83612210565b60068151601381111561130257611302613982565b036113465760008060008360200151806020019051810190611324919061416c565b9250925092506113326134e6565b61127061134088868661202e565b83612227565b60088151601381111561135b5761135b613982565b0361139f576000806000836020015180602001905181019061137d919061416c565b92509250925061138b6134e6565b61127061139988868661202e565b8361223e565b6009815160138111156113b4576113b4613982565b036114185760008082602001518060200190518101906113d4919061423d565b915091506113e06134e6565b6113f46113ee87858561202e565b8a612446565b90935090506114048684846121d3565b50828160405160200161105c9291906141aa565b60048151601381111561142d5761142d613982565b0361147557600080600080846020015180602001905181019061145091906142e4565b93509350935093506114658a858585856124ed565b604051602001610f7c919061434f565b60108151601381111561148a5761148a613982565b036114ce57600080600083602001518060200190518101906114ac9190614394565b9250925092506114be89848484612664565b60405160200161105c919061434f565b600d815160138111156114e3576114e3613982565b0361157457600080600083602001518060200190518101906115059190614486565b9250925092506000806115188484612828565b91509150818160405160200161152f9291906141aa565b6040516020818303038152906040528a8a8151811061155057611550613e21565b60209081029190910181015101526115698883876121d3565b505050505050611659565b600c8151601381111561158957611589613982565b036115af57610ed681602001518060200190518101906115a99190614523565b876128c4565b6013815160138111156115c4576115c4613982565b0361163d5760008082602001518060200190518101906115e49190614569565b9150915081886001600160a01b03167ff875488a7062029dc23176e0ab3cafdbe4071d23a093e7dc7d5d9f0208ff7228876000015188602001518560405161162e939291906145af565b60405180910390a35050611659565b805160405163991fb71160e01b815261089691906004016145ce565b5082611664816145dc565b935050610db7565b955095509592505050565b6116818382612a06565b6107768282612a06565b6116958282610a2b565b156108a9576000828152602081815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b60006116ff6001600019613c5e565b830361179857604051633de222bb60e21b81526001600160a01b0385811660048301523060248301527f0000000000000000000000000000000000000000000000000000000000000000169063f7888aec90604401602060405180830381865afa158015611771573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061179591906145f5565b92505b604051630748bfbb60e11b81526001600160a01b0385811660048301523060248301526044820185905283811660648301527f00000000000000000000000000000000000000000000000000000000000000001690630e917f76906084016020604051808303816000875af1158015611815573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061183991906145f5565b90505b9392505050565b804710156118935760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a20696e73756666696369656e742062616c616e63650000006044820152606401610896565b6000826001600160a01b03168260405160006040518083038185875af1925050503d80600081146118e0576040519150601f19603f3d011682016040523d82523d6000602084013e6118e5565b606091505b50509050806107765760405162461bcd60e51b815260206004820152603a60248201527f416464726573733a20756e61626c6520746f2073656e642076616c75652c207260448201527f6563697069656e74206d617920686176652072657665727465640000000000006064820152608401610896565b600061196783612b50565b9050801561062f5761197b83308484612bbb565b5092915050565b61198c8282610a2b565b6108a95761199981612c4c565b6119a4836020612c5e565b6040516020016119b592919061460e565b60408051601f198184030181529082905262461bcd60e51b825261089691600401613c35565b60006000198203611b0757604051632587701560e11b81526001600160a01b0384811660048301527f00000000000000000000000000000000000000000000000000000000000000001690634b0ee02a90602401602060405180830381865afa158015611a4c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a7091906145f5565b6040516370a0823160e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811660048301528516906370a0823190602401602060405180830381865afa158015611ad6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611afa91906145f5565b611b049190613c5e565b91505b604051633c84c56760e21b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063f213159c90611b5790869030908790600401613cf2565b6020604051808303816000875af1158015611b76573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061183c91906145f5565b6040516333bb7f9160e01b81523060048201526000906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906333bb7f9190349060240160206040518083038185885af1158015611c04573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190611c2991906145f5565b905090565b6000611c8083308760000151886020015160ff8a60600151901c60001c601b611c579190614683565b60408b015160608c01516001600160a01b038e16969594939291906001600160ff1b0316612df9565b611c956001600160a01b038716848487612bbb565b9695505050505050565b6000611cae6001600019613c5e565b8303611d6757604051633de222bb60e21b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811660048301523060248301527f0000000000000000000000000000000000000000000000000000000000000000169063f7888aec90604401602060405180830381865afa158015611d40573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d6491906145f5565b92505b6040516341974a4960e11b8152306004820152602481018490526001600160a01b0383811660448301527f0000000000000000000000000000000000000000000000000000000000000000169063832e949290606401611b57565b6000803490507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d0e30db0826040518263ffffffff1660e01b81526004016000604051808303818588803b158015611e2357600080fd5b505af1158015611e37573d6000803e3d6000fd5b5061183c9350506001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001691503090508584612bbb565b6000611e836001600019613c5e565b8303611f14576040516370a0823160e01b81523060048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa158015611eed573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f1191906145f5565b92505b61183c6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168385612fcf565b606060008686868686604051602001611f6595949392919061469c565b60408051601f19818403018152908290528051602082012060015563040a08f160e41b825291506001600160a01b038b16906340a08f1090611fd9907f0000000000000000000000000000000000000000000000000000000000000000908d908d908790309063f88bc8969060040161474e565b6000604051808303816000875af1158015611ff8573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261202091908101906147a2565b9a9950505050505050505050565b600082600003612050576001821461204a57836020015161204d565b83515b92505b509092915050565b60006120626134e6565b61206f6001600019613c5e565b83036121b5577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663f7888aec7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663c4dac5d56120da8890565b6040516001600160e01b031960e084901b1681526001600160801b0319909116600482015260240160a060405180830381865afa15801561211f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061214391906147e6565b5160405160e083901b6001600160e01b03191681526001600160a01b039091166004820152306024820152604401602060405180830381865afa15801561218e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121b291906145f5565b92505b6121c76121c28585613075565b6130b3565b915091505b9250929050565b60408051606081018252600080825260208201819052918101919091526001820361220057828452612208565b602084018390525b509192915050565b600061221a6134e6565b6121c76121c285856130fb565b60006122316134e6565b6121c76121c2858561314b565b60006122486134e6565b60001983036122e257604051638909aa3f60e01b8152600481018590527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690638909aa3f9060240160408051808303816000875af11580156122b7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122db919061484c565b6020015192505b6122ef6001600019613c5e565b8303612439577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663f7888aec7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663c4dac5d561235a8890565b6040516001600160e01b031960e084901b1681526001600160801b0319909116600482015260240160a060405180830381865afa15801561239f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123c391906147e6565b60409081015190516001600160e01b031960e084901b1681526001600160a01b039091166004820152306024820152604401602060405180830381865afa158015612412573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061243691906145f5565b92505b6121c76121c28585613191565b60006124506134e6565b61245c6121c2856131d9565b60405163016a468560e21b8152600481018790526001600160a01b0386811660248301529294509092507f0000000000000000000000000000000000000000000000000000000000000000909116906305a91a1490604401600060405180830381600087803b1580156124ce57600080fd5b505af11580156124e2573d6000803e3d6000fd5b505050509250929050565b6040805160a08101825260008082526020820181905291810182905260608101829052608081019190915260007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316634fd4b191868689602001518a604001518b606001518c600001518d608001518b6040518963ffffffff1660e01b815260040161258898979695949392919061487e565b6020604051808303816000875af11580156125a7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125cb91906145f5565b60408781015181516001600160a01b0389811682526020820192909252878216818401526060810184905291519293508916917fdde2f3711ab09cdddcfee16ca03e54d21fb8cf3fa647b9797913c950d38ad6939181900360800190a26040805160a0810182526001600160a01b0398891681529588166020870152958601519585019590955250509092166060830152608082015290565b6040805160a0810182526000808252602082018190529181018290526060810182905260808101919091526040848101519051630748bfbb60e11b81526001600160a01b03858116600483015230602483015260448201929092527f0000000000000000000000000000000000000000000000000000000000000000821660648201527f000000000000000000000000000000000000000000000000000000000000000090911690630e917f76906084016020604051808303816000875af1158015612734573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061275891906145f5565b50612786858585857f00000000000000000000000000000000000000000000000000000000000000006124ed565b6080810151604051633c84c56760e21b81529192506001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169163f213159c916127dc9186913091600401613cf2565b6020604051808303816000875af11580156127fb573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061281f91906145f5565b50949350505050565b60006128326134e6565b60405163219c20ff60e21b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063867083fc9061288090879087906004016148de565b610160604051808303816000875af11580156128a0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121c79190614979565b81516040805160a080820183526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116606080850182815260808087018990529086526020808b01805189518c881681850152808b01969096528585018b90528584015288518086039093018352939095018752805190850120848601529051848601528451808601865230815280840187905285890151828a0151875195860191909152848701528551808503870181529184019586905263187945bd60e11b90955285947f0000000000000000000000000000000000000000000000000000000000000000909216936330f28b7a936129cd9391928991606401613ca7565b600060405180830381600087803b1580156129e757600080fd5b505af11580156129fb573d6000803e3d6000fd5b505050505092915050565b604051630e29a66960e21b8152600481018390527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906338a699a490602401602060405180830381865afa158015612a6b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a8f9190614a4c565b156108a957604051632142170760e11b81527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906342842e0e90612ae490309085908790600401613cf2565b600060405180830381600087803b158015612afe57600080fd5b505af1158015612b12573d6000803e3d6000fd5b50506040516001600160a01b03841692508491507fe95aa63b1883254a4494c9e600f7509ec1a5a78dc2f6aef8214b6038d3c4583490600090a35050565b6040516370a0823160e01b81523060048201526000906001600160a01b038316906370a0823190602401602060405180830381865afa158015612b97573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061062f91906145f5565b60006001600160a01b038416612be45760405163a26bef6960e01b815260040160405180910390fd5b6001600160a01b038316612c0b57604051637d0f1ea160e01b815260040160405180910390fd5b826001600160a01b0316846001600160a01b03161480612c29575081155b15612c35575080612c44565b612c418585858561321e565b90505b949350505050565b606061062f6001600160a01b03831660145b60606000612c6d836002614a67565b612c78906002613fc3565b6001600160401b03811115612c8f57612c8f6136d0565b6040519080825280601f01601f191660200182016040528015612cb9576020820181803683370190505b509050600360fc1b81600081518110612cd457612cd4613e21565b60200101906001600160f81b031916908160001a905350600f60fb1b81600181518110612d0357612d03613e21565b60200101906001600160f81b031916908160001a9053506000612d27846002614a67565b612d32906001613fc3565b90505b6001811115612daa576f181899199a1a9b1b9c1cb0b131b232b360811b85600f1660108110612d6657612d66613e21565b1a60f81b828281518110612d7c57612d7c613e21565b60200101906001600160f81b031916908160001a90535060049490941c93612da381614a7e565b9050612d35565b50831561183c5760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152606401610896565b604051623f675f60e91b81526001600160a01b038881166004830152600091908a1690637ecebe0090602401602060405180830381865afa158015612e42573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e6691906145f5565b60405163d505accf60e01b81526001600160a01b038a811660048301528981166024830152604482018990526064820188905260ff8716608483015260a4820186905260c48201859052919250908a169063d505accf9060e401600060405180830381600087803b158015612eda57600080fd5b505af1158015612eee573d6000803e3d6000fd5b5050604051623f675f60e91b81526001600160a01b038b81166004830152600093508c169150637ecebe0090602401602060405180830381865afa158015612f3a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f5e91906145f5565b9050612f6b826001613fc3565b8114612fc35760405162461bcd60e51b815260206004820152602160248201527f5361666545524332303a207065726d697420646964206e6f74207375636365656044820152601960fa1b6064820152608401610896565b50505050505050505050565b60006001600160a01b038316612ff857604051637d0f1ea160e01b815260040160405180910390fd5b8160000361300757508061183c565b604051632e1a7d4d60e01b8152600481018390526001600160a01b03851690632e1a7d4d90602401600060405180830381600087803b15801561304957600080fd5b505af115801561305d573d6000803e3d6000fd5b5061197b925050506001600160a01b03841683611843565b61307d61355e565b6040805160a0810182528481526020810184905260009181019190915282906060810160015b8152602001919091529392505050565b60006130bd6134e6565b6040805160a08101825260008082526020820181905291810182905260608082015260808101919091526130f18482612828565b9250925050915091565b61310361355e565b6040518060a00160405280848152602001600081526020016000815260200160028081111561313457613134613982565b815260200161314284614a95565b90529392505050565b61315361355e565b600061315e83614a95565b90506040518060a0016040528085815260200182815260200160008152602001600160028111156130a3576130a3613982565b61319961355e565b6040518060a0016040528084815260200160008152602001600081526020016002808111156131ca576131ca613982565b81526020019290925250919050565b6131e161355e565b506040805160a0810182529182526f7fffffffffffffffffffffffffffffff19602083015260009082015260016060820152600019608082015290565b60006001600160a01b038416301461324a576132456001600160a01b038616858585613266565b61325e565b61325e6001600160a01b03861684846132be565b509392505050565b610a25846323b872dd60e01b85858560405160240161328793929190613cf2565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b0319909316929092179091526132ee565b6040516001600160a01b03831660248201526044810182905261077690849063a9059cbb60e01b90606401613287565b6000613343826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166133c39092919063ffffffff16565b90508051600014806133645750808060200190518101906133649190614a4c565b6107765760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610896565b6060611839848460008585600080866001600160a01b031685876040516133ea9190614ab1565b60006040518083038185875af1925050503d8060008114613427576040519150601f19603f3d011682016040523d82523d6000602084013e61342c565b606091505b509150915061343d87838387613448565b979650505050505050565b606083156134b75782516000036134b0576001600160a01b0385163b6134b05760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610896565b5081612c44565b612c4483838151156134cc5781518083602001fd5b8060405162461bcd60e51b81526004016108969190613c35565b6040518060e0016040528060008152602001613524604080516080810190915280600081526020016000815260200160008152602001600081525090565b81526020016000815260200160008152602001600081526020016000600281111561355157613551613982565b8152602001600081525090565b6040805160a081018252600080825260208201819052918101829052906060820190613551565b60006020828403121561359757600080fd5b81356001600160e01b03198116811461183c57600080fd5b6001600160a01b0381168114610d1357600080fd5b80356135cf816135af565b919050565b60008083601f8401126135e657600080fd5b5081356001600160401b038111156135fd57600080fd5b6020830191508360208285010111156121cc57600080fd5b60008060008060006080868803121561362d57600080fd5b8535613638816135af565b94506020860135613648816135af565b93506040860135925060608601356001600160401b0381111561366a57600080fd5b613676888289016135d4565b969995985093965092949392505050565b60006020828403121561369957600080fd5b5035919050565b600080604083850312156136b357600080fd5b8235915060208301356136c5816135af565b809150509250929050565b634e487b7160e01b600052604160045260246000fd5b604080519081016001600160401b0381118282101715613708576137086136d0565b60405290565b604051608081016001600160401b0381118282101715613708576137086136d0565b604051606081016001600160401b0381118282101715613708576137086136d0565b60405160a081016001600160401b0381118282101715613708576137086136d0565b60405160e081016001600160401b0381118282101715613708576137086136d0565b604051601f8201601f191681016001600160401b03811182821017156137be576137be6136d0565b604052919050565b60006001600160401b038211156137df576137df6136d0565b5060051b60200190565b60148110610d1357600080fd5b60006001600160401b0382111561380f5761380f6136d0565b50601f01601f191660200190565b600082601f83011261382e57600080fd5b8135602061384361383e836137c6565b613796565b82815260059290921b8401810191818101908684111561386257600080fd5b8286015b848110156139315780356001600160401b03808211156138865760008081fd5b908801906040828b03601f19018113156138a05760008081fd5b6138a86136e6565b878401356138b5816137e9565b815283820135838111156138c95760008081fd5b8085019450508b603f8501126138e157600092508283fd5b8784013592506138f361383e846137f6565b8381528c838587010111156139085760008081fd5b838386018a83013760009381018901939093528088019290925250845250918301918301613866565b509695505050505050565b6000806040838503121561394f57600080fd5b82356001600160401b0381111561396557600080fd5b6139718582860161381d565b92505060208301356136c5816135af565b634e487b7160e01b600052602160045260246000fd5b601481106139a8576139a8613982565b9052565b60005b838110156139c75781810151838201526020016139af565b50506000910152565b600081518084526139e88160208601602086016139ac565b601f01601f19169290920160200192915050565b613a07828251613998565b6000602082015160406020850152612c4460408501826139d0565b600081518084526020808501808196508360051b8101915082860160005b85811015613a6a578284038952613a588483516139fc565b98850198935090840190600101613a40565b5091979650505050505050565b60208152600061183c6020830184613a22565b6000806000806000858703610100811215613aa457600080fd5b8635613aaf816135af565b95506080601f1982011215613ac357600080fd5b50613acc61370e565b602087013581526040870135602082015260608701356040820152608087013560608201528094505060a0860135925060c0860135613b0a816135af565b915060e0860135613b1a816135af565b809150509295509295909350565b60008060408385031215613b3b57600080fd5b8235613b46816135af565b915060208301356136c5816135af565b600060208284031215613b6857600080fd5b813561183c816135af565b600060208284031215613b8557600080fd5b81356001600160401b03811115613b9b57600080fd5b612c448482850161381d565b600080600080600080600060c0888a031215613bc257600080fd5b8735613bcd816135af565b96506020880135613bdd816135af565b95506040880135613bed816135af565b9450606088013593506080880135925060a08801356001600160401b03811115613c1657600080fd5b613c228a828b016135d4565b989b979a50959850939692959293505050565b60208152600061183c60208301846139d0565b634e487b7160e01b600052601160045260246000fd5b8181038181111561062f5761062f613c48565b613c8f82825180516001600160a01b03168252602090810151910152565b60208101516040830152604081015160608301525050565b6000610100613cb68388613c71565b85516001600160a01b03166080840152602086015160a08401526001600160a01b03851660c084015260e0830181905261343d818401856139d0565b6001600160a01b039384168152919092166020820152604081019190915260600190565b600080600080600085870360e0811215613d2f57600080fd5b86356001600160401b0380821115613d4657600080fd5b613d528a838b0161381d565b9750602089013596506060603f1984011215613d6d57600080fd5b613d75613730565b9250604089013583526060890135602084015260808901359150613d98826135af565b816040840152829550613dad60a08a016135c4565b945060c0890135925080831115613dc357600080fd5b5050613dd18882890161381d565b9150509295509295909350565b838152613e0b602082018480518252602080820151908301526040908101516001600160a01b0316910152565b60a060808201526000612c4160a0830184613a22565b634e487b7160e01b600052603260045260246000fd5b60008060408385031215613e4a57600080fd5b8251613e55816135af565b6020939093015192949293505050565b600060808284031215613e7757600080fd5b613e7f61370e565b90508151815260208201516020820152604082015160408201526060820151606082015292915050565b60008060008060e08587031215613ebf57600080fd5b8451613eca816135af565b9350613ed98660208701613e65565b925060a0850151915060c0850151613ef0816135af565b939692955090935050565b600080600060608486031215613f1057600080fd5b8351613f1b816135af565b602085015160408601519194509250613f33816135af565b809150509250925092565b60008060408385031215613f5157600080fd5b8251915060208301516136c5816135af565b600060208284031215613f7557600080fd5b815161183c816135af565b600080600060608486031215613f9557600080fd5b8351613fa0816135af565b6020850151909350613fb1816135af565b80925050604084015190509250925092565b8082018082111561062f5761062f613c48565b600082601f830112613fe757600080fd5b8151613ff561383e826137f6565b81815284602083860101111561400a57600080fd5b612c448260208301602087016139ac565b600080600060a0848603121561403057600080fd5b83519250601f1960608587038201121561404957600080fd5b614051613730565b60208601518152604086015160208201526060860151614070816135af565b604082015260808601519093506001600160401b038082111561409257600080fd5b818701915087601f8301126140a657600080fd5b81516140b461383e826137c6565b8082825260208201915060208360051b86010192508a8311156140d657600080fd5b602085015b8381101561415b578051858111156140f257600080fd5b86016040818e038901121561410657600080fd5b61410e6136e6565b602082015161411c816137e9565b815260408201518781111561413057600080fd5b61413f8f602083860101613fd6565b60208301525080855250506020830192506020810190506140db565b508096505050505050509250925092565b60008060006060848603121561418157600080fd5b8351925060208401519150604084015190509250925092565b600381106139a8576139a8613982565b6000610160820190508382528251602083015260208301516141d060408401825161419a565b6020810151606084015260408101516080840152606081015160a084015250604083015161420160c084018261419a565b50606083015160e0830152608083015161010083015260a083015161422a61012084018261419a565b5060c08301516101408301529392505050565b6000806040838503121561425057600080fd5b505080516020909101519092909150565b600060a0828403121561427357600080fd5b61427b613752565b90508151614288816135af565b81526020820151614298816135af565b80602083015250604082015160408201526060820151606082015260808201516001600160401b038111156142cc57600080fd5b6142d884828501613fd6565b60808301525092915050565b600080600080608085870312156142fa57600080fd5b84516001600160401b0381111561431057600080fd5b61431c87828801614261565b945050602085015161432d816135af565b604086015190935061433e816135af565b6060860151909250613ef0816135af565b81516001600160a01b03908116825260208084015182169083015260408084015190830152606080840151909116908201526080918201519181019190915260a00190565b6000806000606084860312156143a957600080fd5b83516001600160401b038111156143bf57600080fd5b6143cb86828701614261565b93505060208401516143dc816135af565b6040850151909250613f33816135af565b8051600381106135cf57600080fd5b600060a0828403121561440e57600080fd5b614416613752565b90508151614423816135af565b81526020820151614433816135af565b60208201526040828101519082015260608201516001600160401b0381111561445b57600080fd5b61446784828501613fd6565b606083015250608082015161447b816135af565b608082015292915050565b600080600083850360e081121561449c57600080fd5b8451935060a0601f19820112156144b257600080fd5b506144bb613752565b6020850151815260408501516020820152606085015160408201526144e2608086016143ed565b606082015260a0850151608082015260c08501519092506001600160401b0381111561450d57600080fd5b614519868287016143fc565b9150509250925092565b60006080828403121561453557600080fd5b61453d61370e565b825181526020830151602082015260408301516040820152606083015160608201528091505092915050565b6000806040838503121561457c57600080fd5b8251915060208301516001600160401b0381111561459957600080fd5b6145a585828601613fd6565b9150509250929050565b838152826020820152606060408201526000612c4160608301846139d0565b6020810161062f8284613998565b6000600182016145ee576145ee613c48565b5060010190565b60006020828403121561460757600080fd5b5051919050565b7f416363657373436f6e74726f6c3a206163636f756e74200000000000000000008152600083516146468160178501602088016139ac565b7001034b99036b4b9b9b4b733903937b6329607d1b60179184019182015283516146778160288401602088016139ac565b01602801949350505050565b60ff818116838216019081111561062f5761062f613c48565b60e08082528651908201819052600090610100600582901b8401810191908401906020808b01855b838110156146f25760ff198887030185526146e08683516139fc565b955093820193908201906001016146c4565b5050850189905250508551604080850191909152602087015160608501528601516001600160a01b031660808401526001600160a01b03851660a084015282810360c08401526147428185613a22565b98975050505050505050565b6001600160a01b038781168252861660208201526040810185905260a060608201819052600090614781908301866139d0565b905063ffffffff83168460201b1760401b6080830152979650505050505050565b6000602082840312156147b457600080fd5b81516001600160401b038111156147ca57600080fd5b612c4484828501613fd6565b805180151581146135cf57600080fd5b600060a082840312156147f857600080fd5b614800613752565b825161480b816135af565b8152602083810151908201526040830151614825816135af565b604082015260608381015190820152614840608084016147d6565b60808201529392505050565b60006040828403121561485e57600080fd5b6148666136e6565b82518152602083015160208201528091505092915050565b600061010060018060a01b03808c168452808b166020850152808a16604085015288606085015287608085015280871660a08501528160c08501526148c5828501876139d0565b925080851660e085015250509998505050505050505050565b8251815260208301516020820152604083015160408201526000606084015161490a606084018261419a565b506080840151608083015260c060a083015260018060a01b038084511660c08401528060208501511660e08401526040840151610100840152606084015160a061012085015261495e6101608501826139d0565b90508160808601511661014085015280925050509392505050565b60008082840361016081121561498e57600080fd5b8351925061014080601f19830112156149a657600080fd5b6149ae613774565b602086015181526080603f19840112156149c757600080fd5b6149cf61370e565b92506149dd604087016143ed565b8352606086015160208401526080860151604084015260a08601516060840152826020820152614a0f60c087016143ed565b604082015260e086015160608201526101008601516080820152614a3661012087016143ed565b60a082015294015160c085015250909391925050565b600060208284031215614a5e57600080fd5b61183c826147d6565b808202811582820484141761062f5761062f613c48565b600081614a8d57614a8d613c48565b506000190190565b6000600160ff1b8201614aaa57614aaa613c48565b5060000390565b60008251614ac38184602087016139ac565b919091019291505056000000000000000000000000c0939a4ed0129bc5162f6f693935b3f72a46a90d000000000000000000000000a6a147946facac9e0b99824870b36088764f969f000000000000000000000000e7c3eadc74a090334106cd584b074e5bc220f80e000000000000000000000000e03835dfae2644f37049c1fef13e8ced6b1bb72a

Deployed Bytecode

0x6080604052600436106101855760003560e01c806344402059116100d1578063c1400da01161008a578063f06c2dfc11610064578063f06c2dfc1461056a578063f533c1b61461058a578063f88bc8961461059d578063fbfa77cf146105ca57600080fd5b8063c1400da0146104f6578063d547741f14610516578063e1758bd81461053657600080fd5b8063444020591461041957806373c325691461043957806391d148541461046d5780639e687b6c1461048d578063a217fddf146104ad578063a5efcaf3146104c257600080fd5b806323228ecb1161013e578063332c884d11610118578063332c884d146103a357806336568abe146103c35780633adee5b0146103e3578063435f0d411461040357600080fd5b806323228ecb1461031f578063248a9ca3146103535780632f2ff15d1461038357600080fd5b806301ffc9a7146101da5780630a2959751461020f578063112666b71461025b578063150b7a021461028f57806315e9977e146102c85780631a17a004146102eb57600080fd5b366101d557336001600160a01b037f000000000000000000000000530000000000000000000000000000000000000416146101d35760405163be5abbed60e01b815260040160405180910390fd5b005b600080fd5b3480156101e657600080fd5b506101fa6101f5366004613585565b6105fe565b60405190151581526020015b60405180910390f35b34801561021b57600080fd5b506102437f0000000000000000000000006cae28b3d09d8f8fc74ccd496ac986fc84c0c24e81565b6040516001600160a01b039091168152602001610206565b34801561026757600080fd5b506102437f000000000000000000000000e03835dfae2644f37049c1fef13e8ced6b1bb72a81565b34801561029b57600080fd5b506102af6102aa366004613615565b610635565b6040516001600160e01b03199091168152602001610206565b3480156102d457600080fd5b506102dd610741565b604051908152602001610206565b3480156102f757600080fd5b506102437f000000000000000000000000e7c3eadc74a090334106cd584b074e5bc220f80e81565b34801561032b57600080fd5b506102437f000000000000000000000000c2462f03920d47fc5b9e2c5f0ba5d2ded058fd7881565b34801561035f57600080fd5b506102dd61036e366004613687565b60009081526020819052604090206001015490565b34801561038f57600080fd5b506101d361039e3660046136a0565b610751565b6103b66103b136600461393c565b61077b565b6040516102069190613a77565b3480156103cf57600080fd5b506101d36103de3660046136a0565b61082a565b3480156103ef57600080fd5b506102dd6103fe366004613a8a565b6108ad565b34801561040f57600080fd5b506102dd60001981565b34801561042557600080fd5b506101d3610434366004613b28565b610a03565b34801561044557600080fd5b506102437f000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba381565b34801561047957600080fd5b506101fa6104883660046136a0565b610a2b565b34801561049957600080fd5b506101d36104a83660046136a0565b610a54565b3480156104b957600080fd5b506102dd600081565b3480156104ce57600080fd5b506102437f00000000000000000000000094c8c29c9c7ee1b0d38557b1eec44f608a4dede681565b34801561050257600080fd5b506101d3610511366004613b56565b610ae6565b34801561052257600080fd5b506101d36105313660046136a0565b610b04565b34801561054257600080fd5b506102437f000000000000000000000000530000000000000000000000000000000000000481565b34801561057657600080fd5b506101d3610585366004613b28565b610b29565b6103b6610598366004613b73565b610b47565b3480156105a957600080fd5b506105bd6105b8366004613ba7565b610b53565b6040516102069190613c35565b3480156105d657600080fd5b506102437f0000000000000000000000003f37c7d8e61c000085aac0515775b06a3412f36b81565b60006001600160e01b03198216637965db0b60e01b148061062f57506301ffc9a760e01b6001600160e01b03198316145b92915050565b6000336001600160a01b037f000000000000000000000000c2462f03920d47fc5b9e2c5f0ba5d2ded058fd7816146106805760405163020e57df60e41b815260040160405180910390fd5b7f0000000000000000000000006cae28b3d09d8f8fc74ccd496ac986fc84c0c24e6001600160a01b0316866001600160a01b03161461072f576001600160a01b038516846040517fe1971091f8cd4abc65419e3b56c5a5b1542ce392786599db6e069e80b8f269cc90600090a36106fa8686868686610c40565b6001600160a01b038516846040517fe95aa63b1883254a4494c9e600f7509ec1a5a78dc2f6aef8214b6038d3c4583490600090a35b50630a85bd0160e11b95945050505050565b61074e6001600019613c5e565b81565b60008281526020819052604090206001015461076c81610d09565b6107768383610d16565b505050565b60408051606081810183526000808352602083018190529282019290925283516001600160401b038111156107b2576107b26136d0565b6040519080825280602002602001820160405280156107f857816020015b6040805180820190915260008152606060208201528152602001906001900390816107d05790505b5091503361080a856000848487610d9a565b81516020830151919650919450610822925086611677565b505092915050565b6001600160a01b038116331461089f5760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201526e103937b632b9903337b91039b2b63360891b60648201526084015b60405180910390fd5b6108a9828261168b565b5050565b6040805160a080820183526001600160a01b0388811660608085018281528a516080808801919091529086528a516020808d015189518c881681840152998a0195909552928801528601919091526000947f000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba3909216936330f28b7a93909291830191016040516020818303038152906040528051906020012060001c815260200188602001518152506040518060400160405280866001600160a01b03168152602001888152508689604001518a60600151604051602001610999929190918252602082015260400190565b6040516020818303038152906040526040518563ffffffff1660e01b81526004016109c79493929190613ca7565b600060405180830381600087803b1580156109e157600080fd5b505af11580156109f5573d6000803e3d6000fd5b509598975050505050505050565b6000610a0e81610d09565b610a2583610a1f6001600019613c5e565b846116f0565b50505050565b6000918252602082815260408084206001600160a01b0393909316845291905290205460ff1690565b6000610a5f81610d09565b604051632142170760e11b81527f000000000000000000000000c2462f03920d47fc5b9e2c5f0ba5d2ded058fd786001600160a01b0316906342842e0e90610aaf90309086908890600401613cf2565b600060405180830381600087803b158015610ac957600080fd5b505af1158015610add573d6000803e3d6000fd5b50505050505050565b6000610af181610d09565b6108a96001600160a01b03831647611843565b600082815260208190526040902060010154610b1f81610d09565b610776838361168b565b6000610b3481610d09565b610a256001600160a01b0384168361195c565b606061062f823361077b565b606082828080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152505060015483516020850120149150610bb590505760405163f7a632f560e01b815260040160405180910390fd5b600080808080610bc7888a018a613d16565b945094509450945094508c83604001906001600160a01b031690816001600160a01b031681525050610bfc8585858585610d9a565b6040519296509094509150610c1990859085908490602001613dde565b60405160208183030381529060405296505050505050506000600155979650505050505050565b6000610c4e82840184613b73565b9050600081516001600160401b03811115610c6b57610c6b6136d0565b604051908082528060200260200182016040528015610cb157816020015b604080518082019091526000815260606020820152815260200190600190039081610c895790505b509050610cd7604080516060810182526000808252602082018190529181019190915290565b86610ce6846000848487610d9a565b81516020830151919650919450610cfe925083611677565b505050505050505050565b610d138133611982565b50565b610d208282610a2b565b6108a9576000828152602081815260408083206001600160a01b03851684529091529020805460ff19166001179055610d563390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b604080516060810182526000808252602082018190529101528383825b875183101561166c576000888481518110610dd457610dd4613e21565b602002602001015190508060000151858581518110610df557610df5613e21565b6020026020010151600001906013811115610e1257610e12613982565b90816013811115610e2557610e25613982565b905250600281516013811115610e3d57610e3d613982565b03610eb4576000808260200151806020019051810190610e5d9190613e37565b91509150610e6b82826119db565b604051602001610e7d91815260200190565b604051602081830303815290604052878781518110610e9e57610e9e613e21565b6020026020010151602001819052505050611659565b600e81516013811115610ec957610ec9613982565b03610f1d57610ed6611b9a565b604051602001610ee891815260200190565b604051602081830303815290604052858581518110610f0957610f09613e21565b602002602001015160200181905250611659565b600a81516013811115610f3257610f32613982565b03610fb5576000806000808460200151806020019051810190610f559190613ea9565b9350935093509350610f6a8484848d85611c2e565b604051602001610f7c91815260200190565b604051602081830303815290604052898981518110610f9d57610f9d613e21565b60200260200101516020018190525050505050611659565b600b81516013811115610fca57610fca613982565b03611002576000806000808460200151806020019051810190610fed9190613ea9565b9350935093509350610f6a8484848d856108ad565b60038151601381111561101757611017613982565b0361109457600080600083602001518060200190518101906110399190613efb565b92509250925061104a8383836116f0565b60405160200161105c91815260200190565b60405160208183030381529060405288888151811061107d5761107d613e21565b602002602001015160200181905250505050611659565b600f815160138111156110a9576110a9613982565b036110d75760008082602001518060200190518101906110c99190613f3e565b91509150610e6b8282611c9f565b6011815160138111156110ec576110ec613982565b0361111157610ed6816020015180602001905181019061110c9190613f63565b611dc2565b60128151601381111561112657611126613982565b036111545760008082602001518060200190518101906111469190613f3e565b91509150610e6b8282611e74565b60008151601381111561116957611169613982565b036111cf576000806000836020015180602001905181019061118b9190613f80565b919450925090506111ac8383838f6111a48c6001613fc3565b8b8f8c611f48565b8060200190518101906111bf919061401b565b9198509650945061165992505050565b6001815160138111156111e4576111e4613982565b036112175760008082602001518060200190518101906112049190613e37565b91509150610e6b828287604001516116f0565b60058151601381111561122c5761122c613982565b03611294576000806000836020015180602001905181019061124e919061416c565b92509250925061125c6134e6565b61127061126a88868661202e565b83612058565b90945090506112808785856121d3565b508381604051602001610f7c9291906141aa565b6007815160138111156112a9576112a9613982565b036112ed57600080600083602001518060200190518101906112cb919061416c565b9250925092506112d96134e6565b6112706112e788868661202e565b83612210565b60068151601381111561130257611302613982565b036113465760008060008360200151806020019051810190611324919061416c565b9250925092506113326134e6565b61127061134088868661202e565b83612227565b60088151601381111561135b5761135b613982565b0361139f576000806000836020015180602001905181019061137d919061416c565b92509250925061138b6134e6565b61127061139988868661202e565b8361223e565b6009815160138111156113b4576113b4613982565b036114185760008082602001518060200190518101906113d4919061423d565b915091506113e06134e6565b6113f46113ee87858561202e565b8a612446565b90935090506114048684846121d3565b50828160405160200161105c9291906141aa565b60048151601381111561142d5761142d613982565b0361147557600080600080846020015180602001905181019061145091906142e4565b93509350935093506114658a858585856124ed565b604051602001610f7c919061434f565b60108151601381111561148a5761148a613982565b036114ce57600080600083602001518060200190518101906114ac9190614394565b9250925092506114be89848484612664565b60405160200161105c919061434f565b600d815160138111156114e3576114e3613982565b0361157457600080600083602001518060200190518101906115059190614486565b9250925092506000806115188484612828565b91509150818160405160200161152f9291906141aa565b6040516020818303038152906040528a8a8151811061155057611550613e21565b60209081029190910181015101526115698883876121d3565b505050505050611659565b600c8151601381111561158957611589613982565b036115af57610ed681602001518060200190518101906115a99190614523565b876128c4565b6013815160138111156115c4576115c4613982565b0361163d5760008082602001518060200190518101906115e49190614569565b9150915081886001600160a01b03167ff875488a7062029dc23176e0ab3cafdbe4071d23a093e7dc7d5d9f0208ff7228876000015188602001518560405161162e939291906145af565b60405180910390a35050611659565b805160405163991fb71160e01b815261089691906004016145ce565b5082611664816145dc565b935050610db7565b955095509592505050565b6116818382612a06565b6107768282612a06565b6116958282610a2b565b156108a9576000828152602081815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b60006116ff6001600019613c5e565b830361179857604051633de222bb60e21b81526001600160a01b0385811660048301523060248301527f0000000000000000000000003f37c7d8e61c000085aac0515775b06a3412f36b169063f7888aec90604401602060405180830381865afa158015611771573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061179591906145f5565b92505b604051630748bfbb60e11b81526001600160a01b0385811660048301523060248301526044820185905283811660648301527f0000000000000000000000003f37c7d8e61c000085aac0515775b06a3412f36b1690630e917f76906084016020604051808303816000875af1158015611815573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061183991906145f5565b90505b9392505050565b804710156118935760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a20696e73756666696369656e742062616c616e63650000006044820152606401610896565b6000826001600160a01b03168260405160006040518083038185875af1925050503d80600081146118e0576040519150601f19603f3d011682016040523d82523d6000602084013e6118e5565b606091505b50509050806107765760405162461bcd60e51b815260206004820152603a60248201527f416464726573733a20756e61626c6520746f2073656e642076616c75652c207260448201527f6563697069656e74206d617920686176652072657665727465640000000000006064820152608401610896565b600061196783612b50565b9050801561062f5761197b83308484612bbb565b5092915050565b61198c8282610a2b565b6108a95761199981612c4c565b6119a4836020612c5e565b6040516020016119b592919061460e565b60408051601f198184030181529082905262461bcd60e51b825261089691600401613c35565b60006000198203611b0757604051632587701560e11b81526001600160a01b0384811660048301527f0000000000000000000000003f37c7d8e61c000085aac0515775b06a3412f36b1690634b0ee02a90602401602060405180830381865afa158015611a4c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a7091906145f5565b6040516370a0823160e01b81526001600160a01b037f0000000000000000000000003f37c7d8e61c000085aac0515775b06a3412f36b811660048301528516906370a0823190602401602060405180830381865afa158015611ad6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611afa91906145f5565b611b049190613c5e565b91505b604051633c84c56760e21b81526001600160a01b037f0000000000000000000000003f37c7d8e61c000085aac0515775b06a3412f36b169063f213159c90611b5790869030908790600401613cf2565b6020604051808303816000875af1158015611b76573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061183c91906145f5565b6040516333bb7f9160e01b81523060048201526000906001600160a01b037f0000000000000000000000003f37c7d8e61c000085aac0515775b06a3412f36b16906333bb7f9190349060240160206040518083038185885af1158015611c04573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190611c2991906145f5565b905090565b6000611c8083308760000151886020015160ff8a60600151901c60001c601b611c579190614683565b60408b015160608c01516001600160a01b038e16969594939291906001600160ff1b0316612df9565b611c956001600160a01b038716848487612bbb565b9695505050505050565b6000611cae6001600019613c5e565b8303611d6757604051633de222bb60e21b81526001600160a01b037f0000000000000000000000005300000000000000000000000000000000000004811660048301523060248301527f0000000000000000000000003f37c7d8e61c000085aac0515775b06a3412f36b169063f7888aec90604401602060405180830381865afa158015611d40573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d6491906145f5565b92505b6040516341974a4960e11b8152306004820152602481018490526001600160a01b0383811660448301527f0000000000000000000000003f37c7d8e61c000085aac0515775b06a3412f36b169063832e949290606401611b57565b6000803490507f00000000000000000000000053000000000000000000000000000000000000046001600160a01b031663d0e30db0826040518263ffffffff1660e01b81526004016000604051808303818588803b158015611e2357600080fd5b505af1158015611e37573d6000803e3d6000fd5b5061183c9350506001600160a01b037f00000000000000000000000053000000000000000000000000000000000000041691503090508584612bbb565b6000611e836001600019613c5e565b8303611f14576040516370a0823160e01b81523060048201527f00000000000000000000000053000000000000000000000000000000000000046001600160a01b0316906370a0823190602401602060405180830381865afa158015611eed573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f1191906145f5565b92505b61183c6001600160a01b037f0000000000000000000000005300000000000000000000000000000000000004168385612fcf565b606060008686868686604051602001611f6595949392919061469c565b60408051601f19818403018152908290528051602082012060015563040a08f160e41b825291506001600160a01b038b16906340a08f1090611fd9907f0000000000000000000000003f37c7d8e61c000085aac0515775b06a3412f36b908d908d908790309063f88bc8969060040161474e565b6000604051808303816000875af1158015611ff8573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261202091908101906147a2565b9a9950505050505050505050565b600082600003612050576001821461204a57836020015161204d565b83515b92505b509092915050565b60006120626134e6565b61206f6001600019613c5e565b83036121b5577f0000000000000000000000003f37c7d8e61c000085aac0515775b06a3412f36b6001600160a01b031663f7888aec7f0000000000000000000000006cae28b3d09d8f8fc74ccd496ac986fc84c0c24e6001600160a01b031663c4dac5d56120da8890565b6040516001600160e01b031960e084901b1681526001600160801b0319909116600482015260240160a060405180830381865afa15801561211f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061214391906147e6565b5160405160e083901b6001600160e01b03191681526001600160a01b039091166004820152306024820152604401602060405180830381865afa15801561218e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121b291906145f5565b92505b6121c76121c28585613075565b6130b3565b915091505b9250929050565b60408051606081018252600080825260208201819052918101919091526001820361220057828452612208565b602084018390525b509192915050565b600061221a6134e6565b6121c76121c285856130fb565b60006122316134e6565b6121c76121c2858561314b565b60006122486134e6565b60001983036122e257604051638909aa3f60e01b8152600481018590527f000000000000000000000000e03835dfae2644f37049c1fef13e8ced6b1bb72a6001600160a01b031690638909aa3f9060240160408051808303816000875af11580156122b7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122db919061484c565b6020015192505b6122ef6001600019613c5e565b8303612439577f0000000000000000000000003f37c7d8e61c000085aac0515775b06a3412f36b6001600160a01b031663f7888aec7f0000000000000000000000006cae28b3d09d8f8fc74ccd496ac986fc84c0c24e6001600160a01b031663c4dac5d561235a8890565b6040516001600160e01b031960e084901b1681526001600160801b0319909116600482015260240160a060405180830381865afa15801561239f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123c391906147e6565b60409081015190516001600160e01b031960e084901b1681526001600160a01b039091166004820152306024820152604401602060405180830381865afa158015612412573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061243691906145f5565b92505b6121c76121c28585613191565b60006124506134e6565b61245c6121c2856131d9565b60405163016a468560e21b8152600481018790526001600160a01b0386811660248301529294509092507f0000000000000000000000006cae28b3d09d8f8fc74ccd496ac986fc84c0c24e909116906305a91a1490604401600060405180830381600087803b1580156124ce57600080fd5b505af11580156124e2573d6000803e3d6000fd5b505050509250929050565b6040805160a08101825260008082526020820181905291810182905260608101829052608081019190915260007f00000000000000000000000094c8c29c9c7ee1b0d38557b1eec44f608a4dede66001600160a01b0316634fd4b191868689602001518a604001518b606001518c600001518d608001518b6040518963ffffffff1660e01b815260040161258898979695949392919061487e565b6020604051808303816000875af11580156125a7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125cb91906145f5565b60408781015181516001600160a01b0389811682526020820192909252878216818401526060810184905291519293508916917fdde2f3711ab09cdddcfee16ca03e54d21fb8cf3fa647b9797913c950d38ad6939181900360800190a26040805160a0810182526001600160a01b0398891681529588166020870152958601519585019590955250509092166060830152608082015290565b6040805160a0810182526000808252602082018190529181018290526060810182905260808101919091526040848101519051630748bfbb60e11b81526001600160a01b03858116600483015230602483015260448201929092527f00000000000000000000000094c8c29c9c7ee1b0d38557b1eec44f608a4dede6821660648201527f0000000000000000000000003f37c7d8e61c000085aac0515775b06a3412f36b90911690630e917f76906084016020604051808303816000875af1158015612734573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061275891906145f5565b50612786858585857f0000000000000000000000003f37c7d8e61c000085aac0515775b06a3412f36b6124ed565b6080810151604051633c84c56760e21b81529192506001600160a01b037f0000000000000000000000003f37c7d8e61c000085aac0515775b06a3412f36b169163f213159c916127dc9186913091600401613cf2565b6020604051808303816000875af11580156127fb573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061281f91906145f5565b50949350505050565b60006128326134e6565b60405163219c20ff60e21b81526001600160a01b037f0000000000000000000000006cae28b3d09d8f8fc74ccd496ac986fc84c0c24e169063867083fc9061288090879087906004016148de565b610160604051808303816000875af11580156128a0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121c79190614979565b81516040805160a080820183526001600160a01b037f000000000000000000000000c2462f03920d47fc5b9e2c5f0ba5d2ded058fd788116606080850182815260808087018990529086526020808b01805189518c881681850152808b01969096528585018b90528584015288518086039093018352939095018752805190850120848601529051848601528451808601865230815280840187905285890151828a0151875195860191909152848701528551808503870181529184019586905263187945bd60e11b90955285947f000000000000000000000000e7c3eadc74a090334106cd584b074e5bc220f80e909216936330f28b7a936129cd9391928991606401613ca7565b600060405180830381600087803b1580156129e757600080fd5b505af11580156129fb573d6000803e3d6000fd5b505050505092915050565b604051630e29a66960e21b8152600481018390527f000000000000000000000000c2462f03920d47fc5b9e2c5f0ba5d2ded058fd786001600160a01b0316906338a699a490602401602060405180830381865afa158015612a6b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a8f9190614a4c565b156108a957604051632142170760e11b81527f000000000000000000000000c2462f03920d47fc5b9e2c5f0ba5d2ded058fd786001600160a01b0316906342842e0e90612ae490309085908790600401613cf2565b600060405180830381600087803b158015612afe57600080fd5b505af1158015612b12573d6000803e3d6000fd5b50506040516001600160a01b03841692508491507fe95aa63b1883254a4494c9e600f7509ec1a5a78dc2f6aef8214b6038d3c4583490600090a35050565b6040516370a0823160e01b81523060048201526000906001600160a01b038316906370a0823190602401602060405180830381865afa158015612b97573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061062f91906145f5565b60006001600160a01b038416612be45760405163a26bef6960e01b815260040160405180910390fd5b6001600160a01b038316612c0b57604051637d0f1ea160e01b815260040160405180910390fd5b826001600160a01b0316846001600160a01b03161480612c29575081155b15612c35575080612c44565b612c418585858561321e565b90505b949350505050565b606061062f6001600160a01b03831660145b60606000612c6d836002614a67565b612c78906002613fc3565b6001600160401b03811115612c8f57612c8f6136d0565b6040519080825280601f01601f191660200182016040528015612cb9576020820181803683370190505b509050600360fc1b81600081518110612cd457612cd4613e21565b60200101906001600160f81b031916908160001a905350600f60fb1b81600181518110612d0357612d03613e21565b60200101906001600160f81b031916908160001a9053506000612d27846002614a67565b612d32906001613fc3565b90505b6001811115612daa576f181899199a1a9b1b9c1cb0b131b232b360811b85600f1660108110612d6657612d66613e21565b1a60f81b828281518110612d7c57612d7c613e21565b60200101906001600160f81b031916908160001a90535060049490941c93612da381614a7e565b9050612d35565b50831561183c5760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152606401610896565b604051623f675f60e91b81526001600160a01b038881166004830152600091908a1690637ecebe0090602401602060405180830381865afa158015612e42573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e6691906145f5565b60405163d505accf60e01b81526001600160a01b038a811660048301528981166024830152604482018990526064820188905260ff8716608483015260a4820186905260c48201859052919250908a169063d505accf9060e401600060405180830381600087803b158015612eda57600080fd5b505af1158015612eee573d6000803e3d6000fd5b5050604051623f675f60e91b81526001600160a01b038b81166004830152600093508c169150637ecebe0090602401602060405180830381865afa158015612f3a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f5e91906145f5565b9050612f6b826001613fc3565b8114612fc35760405162461bcd60e51b815260206004820152602160248201527f5361666545524332303a207065726d697420646964206e6f74207375636365656044820152601960fa1b6064820152608401610896565b50505050505050505050565b60006001600160a01b038316612ff857604051637d0f1ea160e01b815260040160405180910390fd5b8160000361300757508061183c565b604051632e1a7d4d60e01b8152600481018390526001600160a01b03851690632e1a7d4d90602401600060405180830381600087803b15801561304957600080fd5b505af115801561305d573d6000803e3d6000fd5b5061197b925050506001600160a01b03841683611843565b61307d61355e565b6040805160a0810182528481526020810184905260009181019190915282906060810160015b8152602001919091529392505050565b60006130bd6134e6565b6040805160a08101825260008082526020820181905291810182905260608082015260808101919091526130f18482612828565b9250925050915091565b61310361355e565b6040518060a00160405280848152602001600081526020016000815260200160028081111561313457613134613982565b815260200161314284614a95565b90529392505050565b61315361355e565b600061315e83614a95565b90506040518060a0016040528085815260200182815260200160008152602001600160028111156130a3576130a3613982565b61319961355e565b6040518060a0016040528084815260200160008152602001600081526020016002808111156131ca576131ca613982565b81526020019290925250919050565b6131e161355e565b506040805160a0810182529182526f7fffffffffffffffffffffffffffffff19602083015260009082015260016060820152600019608082015290565b60006001600160a01b038416301461324a576132456001600160a01b038616858585613266565b61325e565b61325e6001600160a01b03861684846132be565b509392505050565b610a25846323b872dd60e01b85858560405160240161328793929190613cf2565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b0319909316929092179091526132ee565b6040516001600160a01b03831660248201526044810182905261077690849063a9059cbb60e01b90606401613287565b6000613343826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166133c39092919063ffffffff16565b90508051600014806133645750808060200190518101906133649190614a4c565b6107765760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610896565b6060611839848460008585600080866001600160a01b031685876040516133ea9190614ab1565b60006040518083038185875af1925050503d8060008114613427576040519150601f19603f3d011682016040523d82523d6000602084013e61342c565b606091505b509150915061343d87838387613448565b979650505050505050565b606083156134b75782516000036134b0576001600160a01b0385163b6134b05760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610896565b5081612c44565b612c4483838151156134cc5781518083602001fd5b8060405162461bcd60e51b81526004016108969190613c35565b6040518060e0016040528060008152602001613524604080516080810190915280600081526020016000815260200160008152602001600081525090565b81526020016000815260200160008152602001600081526020016000600281111561355157613551613982565b8152602001600081525090565b6040805160a081018252600080825260208201819052918101829052906060820190613551565b60006020828403121561359757600080fd5b81356001600160e01b03198116811461183c57600080fd5b6001600160a01b0381168114610d1357600080fd5b80356135cf816135af565b919050565b60008083601f8401126135e657600080fd5b5081356001600160401b038111156135fd57600080fd5b6020830191508360208285010111156121cc57600080fd5b60008060008060006080868803121561362d57600080fd5b8535613638816135af565b94506020860135613648816135af565b93506040860135925060608601356001600160401b0381111561366a57600080fd5b613676888289016135d4565b969995985093965092949392505050565b60006020828403121561369957600080fd5b5035919050565b600080604083850312156136b357600080fd5b8235915060208301356136c5816135af565b809150509250929050565b634e487b7160e01b600052604160045260246000fd5b604080519081016001600160401b0381118282101715613708576137086136d0565b60405290565b604051608081016001600160401b0381118282101715613708576137086136d0565b604051606081016001600160401b0381118282101715613708576137086136d0565b60405160a081016001600160401b0381118282101715613708576137086136d0565b60405160e081016001600160401b0381118282101715613708576137086136d0565b604051601f8201601f191681016001600160401b03811182821017156137be576137be6136d0565b604052919050565b60006001600160401b038211156137df576137df6136d0565b5060051b60200190565b60148110610d1357600080fd5b60006001600160401b0382111561380f5761380f6136d0565b50601f01601f191660200190565b600082601f83011261382e57600080fd5b8135602061384361383e836137c6565b613796565b82815260059290921b8401810191818101908684111561386257600080fd5b8286015b848110156139315780356001600160401b03808211156138865760008081fd5b908801906040828b03601f19018113156138a05760008081fd5b6138a86136e6565b878401356138b5816137e9565b815283820135838111156138c95760008081fd5b8085019450508b603f8501126138e157600092508283fd5b8784013592506138f361383e846137f6565b8381528c838587010111156139085760008081fd5b838386018a83013760009381018901939093528088019290925250845250918301918301613866565b509695505050505050565b6000806040838503121561394f57600080fd5b82356001600160401b0381111561396557600080fd5b6139718582860161381d565b92505060208301356136c5816135af565b634e487b7160e01b600052602160045260246000fd5b601481106139a8576139a8613982565b9052565b60005b838110156139c75781810151838201526020016139af565b50506000910152565b600081518084526139e88160208601602086016139ac565b601f01601f19169290920160200192915050565b613a07828251613998565b6000602082015160406020850152612c4460408501826139d0565b600081518084526020808501808196508360051b8101915082860160005b85811015613a6a578284038952613a588483516139fc565b98850198935090840190600101613a40565b5091979650505050505050565b60208152600061183c6020830184613a22565b6000806000806000858703610100811215613aa457600080fd5b8635613aaf816135af565b95506080601f1982011215613ac357600080fd5b50613acc61370e565b602087013581526040870135602082015260608701356040820152608087013560608201528094505060a0860135925060c0860135613b0a816135af565b915060e0860135613b1a816135af565b809150509295509295909350565b60008060408385031215613b3b57600080fd5b8235613b46816135af565b915060208301356136c5816135af565b600060208284031215613b6857600080fd5b813561183c816135af565b600060208284031215613b8557600080fd5b81356001600160401b03811115613b9b57600080fd5b612c448482850161381d565b600080600080600080600060c0888a031215613bc257600080fd5b8735613bcd816135af565b96506020880135613bdd816135af565b95506040880135613bed816135af565b9450606088013593506080880135925060a08801356001600160401b03811115613c1657600080fd5b613c228a828b016135d4565b989b979a50959850939692959293505050565b60208152600061183c60208301846139d0565b634e487b7160e01b600052601160045260246000fd5b8181038181111561062f5761062f613c48565b613c8f82825180516001600160a01b03168252602090810151910152565b60208101516040830152604081015160608301525050565b6000610100613cb68388613c71565b85516001600160a01b03166080840152602086015160a08401526001600160a01b03851660c084015260e0830181905261343d818401856139d0565b6001600160a01b039384168152919092166020820152604081019190915260600190565b600080600080600085870360e0811215613d2f57600080fd5b86356001600160401b0380821115613d4657600080fd5b613d528a838b0161381d565b9750602089013596506060603f1984011215613d6d57600080fd5b613d75613730565b9250604089013583526060890135602084015260808901359150613d98826135af565b816040840152829550613dad60a08a016135c4565b945060c0890135925080831115613dc357600080fd5b5050613dd18882890161381d565b9150509295509295909350565b838152613e0b602082018480518252602080820151908301526040908101516001600160a01b0316910152565b60a060808201526000612c4160a0830184613a22565b634e487b7160e01b600052603260045260246000fd5b60008060408385031215613e4a57600080fd5b8251613e55816135af565b6020939093015192949293505050565b600060808284031215613e7757600080fd5b613e7f61370e565b90508151815260208201516020820152604082015160408201526060820151606082015292915050565b60008060008060e08587031215613ebf57600080fd5b8451613eca816135af565b9350613ed98660208701613e65565b925060a0850151915060c0850151613ef0816135af565b939692955090935050565b600080600060608486031215613f1057600080fd5b8351613f1b816135af565b602085015160408601519194509250613f33816135af565b809150509250925092565b60008060408385031215613f5157600080fd5b8251915060208301516136c5816135af565b600060208284031215613f7557600080fd5b815161183c816135af565b600080600060608486031215613f9557600080fd5b8351613fa0816135af565b6020850151909350613fb1816135af565b80925050604084015190509250925092565b8082018082111561062f5761062f613c48565b600082601f830112613fe757600080fd5b8151613ff561383e826137f6565b81815284602083860101111561400a57600080fd5b612c448260208301602087016139ac565b600080600060a0848603121561403057600080fd5b83519250601f1960608587038201121561404957600080fd5b614051613730565b60208601518152604086015160208201526060860151614070816135af565b604082015260808601519093506001600160401b038082111561409257600080fd5b818701915087601f8301126140a657600080fd5b81516140b461383e826137c6565b8082825260208201915060208360051b86010192508a8311156140d657600080fd5b602085015b8381101561415b578051858111156140f257600080fd5b86016040818e038901121561410657600080fd5b61410e6136e6565b602082015161411c816137e9565b815260408201518781111561413057600080fd5b61413f8f602083860101613fd6565b60208301525080855250506020830192506020810190506140db565b508096505050505050509250925092565b60008060006060848603121561418157600080fd5b8351925060208401519150604084015190509250925092565b600381106139a8576139a8613982565b6000610160820190508382528251602083015260208301516141d060408401825161419a565b6020810151606084015260408101516080840152606081015160a084015250604083015161420160c084018261419a565b50606083015160e0830152608083015161010083015260a083015161422a61012084018261419a565b5060c08301516101408301529392505050565b6000806040838503121561425057600080fd5b505080516020909101519092909150565b600060a0828403121561427357600080fd5b61427b613752565b90508151614288816135af565b81526020820151614298816135af565b80602083015250604082015160408201526060820151606082015260808201516001600160401b038111156142cc57600080fd5b6142d884828501613fd6565b60808301525092915050565b600080600080608085870312156142fa57600080fd5b84516001600160401b0381111561431057600080fd5b61431c87828801614261565b945050602085015161432d816135af565b604086015190935061433e816135af565b6060860151909250613ef0816135af565b81516001600160a01b03908116825260208084015182169083015260408084015190830152606080840151909116908201526080918201519181019190915260a00190565b6000806000606084860312156143a957600080fd5b83516001600160401b038111156143bf57600080fd5b6143cb86828701614261565b93505060208401516143dc816135af565b6040850151909250613f33816135af565b8051600381106135cf57600080fd5b600060a0828403121561440e57600080fd5b614416613752565b90508151614423816135af565b81526020820151614433816135af565b60208201526040828101519082015260608201516001600160401b0381111561445b57600080fd5b61446784828501613fd6565b606083015250608082015161447b816135af565b608082015292915050565b600080600083850360e081121561449c57600080fd5b8451935060a0601f19820112156144b257600080fd5b506144bb613752565b6020850151815260408501516020820152606085015160408201526144e2608086016143ed565b606082015260a0850151608082015260c08501519092506001600160401b0381111561450d57600080fd5b614519868287016143fc565b9150509250925092565b60006080828403121561453557600080fd5b61453d61370e565b825181526020830151602082015260408301516040820152606083015160608201528091505092915050565b6000806040838503121561457c57600080fd5b8251915060208301516001600160401b0381111561459957600080fd5b6145a585828601613fd6565b9150509250929050565b838152826020820152606060408201526000612c4160608301846139d0565b6020810161062f8284613998565b6000600182016145ee576145ee613c48565b5060010190565b60006020828403121561460757600080fd5b5051919050565b7f416363657373436f6e74726f6c3a206163636f756e74200000000000000000008152600083516146468160178501602088016139ac565b7001034b99036b4b9b9b4b733903937b6329607d1b60179184019182015283516146778160288401602088016139ac565b01602801949350505050565b60ff818116838216019081111561062f5761062f613c48565b60e08082528651908201819052600090610100600582901b8401810191908401906020808b01855b838110156146f25760ff198887030185526146e08683516139fc565b955093820193908201906001016146c4565b5050850189905250508551604080850191909152602087015160608501528601516001600160a01b031660808401526001600160a01b03851660a084015282810360c08401526147428185613a22565b98975050505050505050565b6001600160a01b038781168252861660208201526040810185905260a060608201819052600090614781908301866139d0565b905063ffffffff83168460201b1760401b6080830152979650505050505050565b6000602082840312156147b457600080fd5b81516001600160401b038111156147ca57600080fd5b612c4484828501613fd6565b805180151581146135cf57600080fd5b600060a082840312156147f857600080fd5b614800613752565b825161480b816135af565b8152602083810151908201526040830151614825816135af565b604082015260608381015190820152614840608084016147d6565b60808201529392505050565b60006040828403121561485e57600080fd5b6148666136e6565b82518152602083015160208201528091505092915050565b600061010060018060a01b03808c168452808b166020850152808a16604085015288606085015287608085015280871660a08501528160c08501526148c5828501876139d0565b925080851660e085015250509998505050505050505050565b8251815260208301516020820152604083015160408201526000606084015161490a606084018261419a565b506080840151608083015260c060a083015260018060a01b038084511660c08401528060208501511660e08401526040840151610100840152606084015160a061012085015261495e6101608501826139d0565b90508160808601511661014085015280925050509392505050565b60008082840361016081121561498e57600080fd5b8351925061014080601f19830112156149a657600080fd5b6149ae613774565b602086015181526080603f19840112156149c757600080fd5b6149cf61370e565b92506149dd604087016143ed565b8352606086015160208401526080860151604084015260a08601516060840152826020820152614a0f60c087016143ed565b604082015260e086015160608201526101008601516080820152614a3661012087016143ed565b60a082015294015160c085015250909391925050565b600060208284031215614a5e57600080fd5b61183c826147d6565b808202811582820484141761062f5761062f613c48565b600081614a8d57614a8d613c48565b506000190190565b6000600160ff1b8201614aaa57614aaa613c48565b5060000390565b60008251614ac38184602087016139ac565b919091019291505056

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

000000000000000000000000c0939a4ed0129bc5162f6f693935b3f72a46a90d000000000000000000000000a6a147946facac9e0b99824870b36088764f969f000000000000000000000000e7c3eadc74a090334106cd584b074e5bc220f80e000000000000000000000000e03835dfae2644f37049c1fef13e8ced6b1bb72a

-----Decoded View---------------
Arg [0] : timelock (address): 0xc0939a4Ed0129bc5162F6f693935B3F72a46a90D
Arg [1] : _maestro (address): 0xa6a147946FACAc9E0B99824870B36088764f969F
Arg [2] : _erc721Permit2 (address): 0xE7c3eADC74a090334106cd584B074E5BC220F80e
Arg [3] : _lens (address): 0xe03835Dfae2644F37049c1feF13E8ceD6b1Bb72a

-----Encoded View---------------
4 Constructor Arguments found :
Arg [0] : 000000000000000000000000c0939a4ed0129bc5162f6f693935b3f72a46a90d
Arg [1] : 000000000000000000000000a6a147946facac9e0b99824870b36088764f969f
Arg [2] : 000000000000000000000000e7c3eadc74a090334106cd584b074e5bc220f80e
Arg [3] : 000000000000000000000000e03835dfae2644f37049c1fef13e8ced6b1bb72a


Block Transaction Gas Used Reward
view all blocks sequenced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
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.