ETH Price: $2,068.51 (+11.29%)
Gas: 0.12 Gwei
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Withdraw241638732026-01-04 21:05:2351 days ago1767560723IN
0xE442B5A7...c4f7caa72
0 ETH0.000138492.02920913
Withdraw240972522025-12-26 13:58:1161 days ago1766757491IN
0xE442B5A7...c4f7caa72
0 ETH0.000012510.16655613
Repay240912582025-12-25 17:53:2362 days ago1766685203IN
0xE442B5A7...c4f7caa72
0 ETH0.000218092.02727961
Withdraw240673962025-12-22 9:56:5965 days ago1766397419IN
0xE442B5A7...c4f7caa72
0 ETH0.000200812.02553167
Withdraw240207302025-12-15 21:36:5971 days ago1765834619IN
0xE442B5A7...c4f7caa72
0 ETH0.000152572.0418873
Take Quote239392172025-12-04 10:51:1183 days ago1764845471IN
0xE442B5A7...c4f7caa72
0 ETH0.000086320.12878766
Withdraw239392142025-12-04 10:50:3583 days ago1764845435IN
0xE442B5A7...c4f7caa72
0 ETH0.00008791.02985933
Borrow239210672025-12-01 20:38:3585 days ago1764621515IN
0xE442B5A7...c4f7caa72
0 ETH0.00036852.03265036
Withdraw238897652025-11-27 11:31:1190 days ago1764243071IN
0xE442B5A7...c4f7caa72
0 ETH0.000140972.06593932
Take Quote238857812025-11-26 22:06:2390 days ago1764194783IN
0xE442B5A7...c4f7caa72
0 ETH0.000779911.2028853
Take Swap Quote238857602025-11-26 22:02:1190 days ago1764194531IN
0xE442B5A7...c4f7caa72
0 ETH0.000075590.55462778
Withdraw238857592025-11-26 22:01:5990 days ago1764194519IN
0xE442B5A7...c4f7caa72
0 ETH0.000037940.5559102
Withdraw238857572025-11-26 22:01:2390 days ago1764194483IN
0xE442B5A7...c4f7caa72
0 ETH0.000071911.05367893
Repay238675842025-11-24 8:38:4793 days ago1763973527IN
0xE442B5A7...c4f7caa72
0 ETH0.000223722.07943379
Withdraw238451062025-11-21 4:48:4796 days ago1763700527IN
0xE442B5A7...c4f7caa72
0 ETH0.000075081.29586184
Take Quote238412722025-11-20 15:54:4797 days ago1763654087IN
0xE442B5A7...c4f7caa72
0 ETH0.000950781.46645099
Take Quote238342862025-11-19 16:23:4798 days ago1763569427IN
0xE442B5A7...c4f7caa72
0 ETH0.003279864.89344946
Take Quote238341442025-11-19 15:54:3598 days ago1763567675IN
0xE442B5A7...c4f7caa72
0 ETH0.002923954.83983449
Withdraw238335932025-11-19 14:02:5998 days ago1763560979IN
0xE442B5A7...c4f7caa72
0 ETH0.0000761.31181325
Withdraw238335922025-11-19 14:02:4798 days ago1763560967IN
0xE442B5A7...c4f7caa72
0 ETH0.000075911.31026818
Withdraw238335862025-11-19 14:01:3598 days ago1763560895IN
0xE442B5A7...c4f7caa72
0 ETH0.000053160.77897
Take Quote238335812025-11-19 14:00:3598 days ago1763560835IN
0xE442B5A7...c4f7caa72
0 ETH0.00050190.7798968
Withdraw238335782025-11-19 13:59:5998 days ago1763560799IN
0xE442B5A7...c4f7caa72
0 ETH0.0000530.77666819
Withdraw237836622025-11-12 14:12:23105 days ago1762956743IN
0xE442B5A7...c4f7caa72
0 ETH0.000127562.2121613
Take Quote237830042025-11-12 11:59:23105 days ago1762948763IN
0xE442B5A7...c4f7caa72
0 ETH0.001120491.78739636
View all transactions

Latest 25 internal transactions (View All)

Advanced mode:
Parent Transaction Hash Method Block
From
To
0x3d602d80242270222026-01-13 16:37:3543 days ago1768322255
0xE442B5A7...c4f7caa72
 Contract Creation0 ETH
0x3d602d80239916802025-12-11 19:59:5976 days ago1765483199
0xE442B5A7...c4f7caa72
 Contract Creation0 ETH
0x3d602d80239392172025-12-04 10:51:1183 days ago1764845471
0xE442B5A7...c4f7caa72
 Contract Creation0 ETH
0x3d602d80238914172025-11-27 17:04:5990 days ago1764263099
0xE442B5A7...c4f7caa72
 Contract Creation0 ETH
0x3d602d80238857812025-11-26 22:06:2390 days ago1764194783
0xE442B5A7...c4f7caa72
 Contract Creation0 ETH
0x3d602d80238412722025-11-20 15:54:4797 days ago1763654087
0xE442B5A7...c4f7caa72
 Contract Creation0 ETH
0x3d602d80238342862025-11-19 16:23:4798 days ago1763569427
0xE442B5A7...c4f7caa72
 Contract Creation0 ETH
0x3d602d80238341442025-11-19 15:54:3598 days ago1763567675
0xE442B5A7...c4f7caa72
 Contract Creation0 ETH
0x3d602d80238335812025-11-19 14:00:3598 days ago1763560835
0xE442B5A7...c4f7caa72
 Contract Creation0 ETH
0x3d602d80237830042025-11-12 11:59:23105 days ago1762948763
0xE442B5A7...c4f7caa72
 Contract Creation0 ETH
0x3d602d80237830022025-11-12 11:58:59105 days ago1762948739
0xE442B5A7...c4f7caa72
 Contract Creation0 ETH
0x3d602d80237693762025-11-10 14:11:59107 days ago1762783919
0xE442B5A7...c4f7caa72
 Contract Creation0 ETH
0x3d602d80237563482025-11-08 18:31:35109 days ago1762626695
0xE442B5A7...c4f7caa72
 Contract Creation0 ETH
0x3d602d80237397932025-11-06 10:57:35111 days ago1762426655
0xE442B5A7...c4f7caa72
 Contract Creation0 ETH
0x3d602d80236692722025-10-27 14:07:23121 days ago1761574043
0xE442B5A7...c4f7caa72
 Contract Creation0 ETH
0x3d602d80236338012025-10-22 14:57:47126 days ago1761145067
0xE442B5A7...c4f7caa72
 Contract Creation0 ETH
0x3d602d80236261122025-10-21 13:03:47127 days ago1761051827
0xE442B5A7...c4f7caa72
 Contract Creation0 ETH
0x3d602d80236260922025-10-21 12:59:47127 days ago1761051587
0xE442B5A7...c4f7caa72
 Contract Creation0 ETH
0x3d602d80235701992025-10-13 17:11:59135 days ago1760375519
0xE442B5A7...c4f7caa72
 Contract Creation0 ETH
0x3d602d80235689942025-10-13 13:09:23135 days ago1760360963
0xE442B5A7...c4f7caa72
 Contract Creation0 ETH
0x3d602d80235458702025-10-10 7:33:47138 days ago1760081627
0xE442B5A7...c4f7caa72
 Contract Creation0 ETH
0x3d602d80235457992025-10-10 7:19:35138 days ago1760080775
0xE442B5A7...c4f7caa72
 Contract Creation0 ETH
0x3d602d80235208382025-10-06 19:33:35142 days ago1759779215
0xE442B5A7...c4f7caa72
 Contract Creation0 ETH
0x3d602d80235129672025-10-05 17:09:23143 days ago1759684163
0xE442B5A7...c4f7caa72
 Contract Creation0 ETH
0x3d602d80235127742025-10-05 16:30:47143 days ago1759681847
0xE442B5A7...c4f7caa72
 Contract Creation0 ETH
View All Internal Transactions
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
Router

Compiler Version
v0.8.24+commit.e11b9ed9

Optimization Enabled:
Yes with 1000 runs

Other Settings:
paris EvmVersion
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {Clones} from "@openzeppelin/contracts/proxy/Clones.sol";
import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import {MessageHashUtils} from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol";
import {DataTypes} from "./DataTypes.sol";
import {Errors} from "./errors/Errors.sol";
import {IEscrow} from "./interfaces/IEscrow.sol";
import {IFeeHandler} from "./interfaces/IFeeHandler.sol";
import {IRouter} from "./interfaces/IRouter.sol";

contract Router is Ownable, IRouter {
    using SafeERC20 for IERC20Metadata;

    uint64 internal constant BASE = 1 ether;
    uint96 internal constant MAX_MATCH_FEE = 0.2 ether;
    uint96 internal constant MAX_EXERCISE_FEE = 0.005 ether;

    // @dev: EIP-1271 with bytes4(keccak256("isValidSignature(bytes32,bytes)")
    bytes4 internal constant EIP1271_SIG_AND_MAGIC_VALUE = 0x1626ba7e;

    address public immutable escrowImpl;
    address public feeHandler;
    uint256 public numEscrows;

    mapping(address => bool) public isEscrow;
    mapping(bytes32 => bool) public isQuoteUsed;
    mapping(bytes32 => bool) public isSwapQuoteUsed;
    mapping(address => bool) public quotesPaused;

    address[] internal _escrows;

    constructor(address initOwner, address _escrowImpl) Ownable(initOwner) {
        if (_escrowImpl == address(0)) {
            revert Errors.InvalidAddress();
        }
        escrowImpl = _escrowImpl;
    }

    function createAuction(
        address escrowOwner,
        DataTypes.AuctionInitialization calldata auctionInitialization,
        address distPartner
    ) external {
        (address escrow, uint256 oTokenIndex) = _createEscrow();
        uint96 exerciseFee = getExerciseFee();
        IEscrow(escrow).initializeAuction(
            address(this),
            escrowOwner,
            exerciseFee,
            auctionInitialization,
            oTokenIndex,
            distPartner
        );
        IERC20Metadata(auctionInitialization.underlyingToken).safeTransferFrom(
            msg.sender,
            escrow,
            auctionInitialization.notional
        );
        emit CreateAuction(
            escrowOwner,
            escrow,
            auctionInitialization,
            exerciseFee,
            distPartner
        );
    }

    function withdrawFromEscrowAndCreateAuction(
        address oldEscrow,
        address escrowOwner,
        DataTypes.AuctionInitialization calldata auctionInitialization,
        address distPartner
    ) external {
        if (!isEscrow[oldEscrow]) {
            revert Errors.NotAnEscrow();
        }
        if (msg.sender != IEscrow(oldEscrow).owner()) {
            revert Errors.InvalidSender();
        }
        (address newEscrow, uint256 oTokenIndex) = _createEscrow();
        IEscrow(newEscrow).initializeAuction(
            address(this),
            escrowOwner,
            getExerciseFee(),
            auctionInitialization,
            oTokenIndex,
            distPartner
        );

        uint256 oldEscrowBal = IERC20Metadata(
            auctionInitialization.underlyingToken
        ).balanceOf(oldEscrow);

        // @dev: if new notional gte old escrow balance
        // then we can rollover funds
        address withdrawTo = auctionInitialization.notional >= oldEscrowBal
            ? newEscrow
            : msg.sender;
        uint256 netTransferAmountNeeded = auctionInitialization.notional >=
            oldEscrowBal
            ? auctionInitialization.notional - oldEscrowBal
            : auctionInitialization.notional;

        IEscrow(oldEscrow).handleWithdraw(
            withdrawTo,
            auctionInitialization.underlyingToken,
            oldEscrowBal
        );

        // @dev: iff new notional equal old escrow balance
        // then no transfer needed
        if (netTransferAmountNeeded > 0) {
            IERC20Metadata(auctionInitialization.underlyingToken)
                .safeTransferFrom(
                    msg.sender,
                    newEscrow,
                    netTransferAmountNeeded
                );
        }

        emit WithdrawFromEscrowAndCreateAuction(
            escrowOwner,
            oldEscrow,
            newEscrow,
            auctionInitialization
        );
    }

    function withdraw(
        address escrow,
        address to,
        address token,
        uint256 amount
    ) external {
        if (!isEscrow[escrow]) {
            revert Errors.NotAnEscrow();
        }
        if (msg.sender != IEscrow(escrow).owner()) {
            revert Errors.InvalidSender();
        }
        IEscrow(escrow).handleWithdraw(to, token, amount);
        emit Withdraw(msg.sender, escrow, to, token, amount);
    }

    function bidOnAuction(
        address escrow,
        address optionReceiver,
        uint256 relBid,
        uint256 _refSpot,
        bytes[] memory _oracleData
    )
        external
        returns (DataTypes.BidPreview memory preview, address distPartner)
    {
        if (!isEscrow[escrow]) {
            revert Errors.NotAnEscrow();
        }
        (preview, distPartner) = IEscrow(escrow).handleAuctionBid(
            relBid,
            optionReceiver,
            _refSpot,
            _oracleData
        );
        IERC20Metadata(preview.premiumToken).safeTransferFrom(
            msg.sender,
            IEscrow(escrow).owner(),
            preview.premium -
                preview.matchFeeDistPartner -
                preview.matchFeeProtocol
        );
        if (preview.matchFeeDistPartner > 0) {
            IERC20Metadata(preview.premiumToken).safeTransferFrom(
                msg.sender,
                distPartner,
                preview.matchFeeDistPartner
            );
        }
        if (preview.matchFeeProtocol > 0) {
            address _feeHandler = feeHandler;
            IERC20Metadata(preview.premiumToken).safeTransferFrom(
                msg.sender,
                _feeHandler,
                preview.matchFeeProtocol
            );
            IFeeHandler(_feeHandler).provisionFees(
                preview.premiumToken,
                preview.matchFeeProtocol
            );
        }

        emit BidOnAuction(
            msg.sender,
            escrow,
            optionReceiver,
            preview,
            distPartner
        );
    }

    function exercise(
        address escrow,
        address underlyingReceiver,
        uint256 underlyingAmount,
        bool payInSettlementToken,
        bytes[] memory oracleData
    ) external {
        if (!isEscrow[escrow]) {
            revert Errors.NotAnEscrow();
        }
        (
            address settlementToken,
            uint256 settlementAmount,
            uint256 exerciseFeeAmount
        ) = IEscrow(escrow).handleExercise(
                msg.sender,
                underlyingReceiver,
                underlyingAmount,
                payInSettlementToken,
                oracleData
            );
        if (payInSettlementToken) {
            IERC20Metadata(settlementToken).safeTransferFrom(
                msg.sender,
                IEscrow(escrow).owner(),
                settlementAmount
            );
        }
        address _feeHandler = feeHandler;
        if (_feeHandler != address(0) && exerciseFeeAmount > 0) {
            IERC20Metadata(settlementToken).safeTransferFrom(
                msg.sender,
                feeHandler,
                exerciseFeeAmount
            );
            IFeeHandler(_feeHandler).provisionFees(
                settlementToken,
                exerciseFeeAmount
            );
        }
        emit Exercise(
            msg.sender,
            escrow,
            underlyingReceiver,
            underlyingAmount,
            exerciseFeeAmount
        );
    }

    function borrow(
        address escrow,
        address underlyingReceiver,
        uint128 borrowUnderlyingAmount
    ) external {
        if (!isEscrow[escrow]) {
            revert Errors.NotAnEscrow();
        }
        (
            address settlementToken,
            uint256 collateralAmount,
            uint256 collateralFeeAmount
        ) = IEscrow(escrow).handleBorrow(
                msg.sender,
                underlyingReceiver,
                borrowUnderlyingAmount
            );
        IERC20Metadata(settlementToken).safeTransferFrom(
            msg.sender,
            escrow,
            collateralAmount
        );
        address _feeHandler = feeHandler;
        if (_feeHandler != address(0) && collateralFeeAmount > 0) {
            IERC20Metadata(settlementToken).safeTransferFrom(
                msg.sender,
                feeHandler,
                collateralFeeAmount
            );
            IFeeHandler(_feeHandler).provisionFees(
                settlementToken,
                collateralFeeAmount
            );
        }
        emit Borrow(
            msg.sender,
            escrow,
            underlyingReceiver,
            borrowUnderlyingAmount,
            collateralAmount,
            collateralFeeAmount
        );
    }

    function repay(
        address escrow,
        address collateralReceiver,
        uint128 repayUnderlyingAmount
    ) external {
        if (!isEscrow[escrow]) {
            revert Errors.NotAnEscrow();
        }
        (address underlyingToken, uint256 unlockedCollateralAmount) = IEscrow(
            escrow
        ).handleRepay(msg.sender, collateralReceiver, repayUnderlyingAmount);
        IERC20Metadata(underlyingToken).safeTransferFrom(
            msg.sender,
            escrow,
            repayUnderlyingAmount
        );
        emit Repay(
            escrow,
            escrow,
            collateralReceiver,
            repayUnderlyingAmount,
            unlockedCollateralAmount
        );
    }

    function takeQuote(
        address escrowOwner,
        DataTypes.RFQInitialization calldata rfqInitialization,
        address distPartner
    ) external {
        DataTypes.TakeQuotePreview memory preview = previewTakeQuote(
            rfqInitialization,
            distPartner
        );

        if (preview.status != DataTypes.RFQStatus.Success) {
            revert Errors.InvalidTakeQuote();
        }

        isQuoteUsed[preview.msgHash] = true;

        (address escrow, uint256 oTokenIndex) = _createEscrow();
        uint96 exerciseFee = getExerciseFee();
        IEscrow(escrow).initializeRFQMatch(
            address(this),
            escrowOwner,
            preview.quoter,
            exerciseFee,
            rfqInitialization,
            oTokenIndex
        );

        IERC20Metadata(rfqInitialization.optionInfo.underlyingToken)
            .safeTransferFrom(
                msg.sender,
                escrow,
                rfqInitialization.optionInfo.notional
            );
        IERC20Metadata(preview.premiumToken).safeTransferFrom(
            preview.quoter,
            msg.sender,
            rfqInitialization.rfqQuote.premium -
                preview.matchFeeDistPartner -
                preview.matchFeeProtocol
        );
        if (preview.matchFeeDistPartner > 0) {
            IERC20Metadata(preview.premiumToken).safeTransferFrom(
                preview.quoter,
                distPartner,
                preview.matchFeeDistPartner
            );
        }
        if (preview.matchFeeProtocol > 0) {
            address _feeHandler = feeHandler;
            IERC20Metadata(preview.premiumToken).safeTransferFrom(
                preview.quoter,
                _feeHandler,
                preview.matchFeeProtocol
            );
            IFeeHandler(_feeHandler).provisionFees(
                preview.premiumToken,
                preview.matchFeeProtocol
            );
        }
        emit TakeQuote(
            msg.sender,
            escrowOwner,
            escrow,
            rfqInitialization,
            preview,
            exerciseFee,
            distPartner
        );
    }

    function takeSwapQuote(
        address to,
        DataTypes.SwapQuote calldata swapQuote
    ) external {
        if (block.timestamp > swapQuote.validUntil) {
            revert Errors.SwapQuoteExpired();
        }

        bytes32 msgHash = keccak256(
            abi.encode(
                block.chainid,
                swapQuote.takerGiveToken,
                swapQuote.takerGiveAmount,
                swapQuote.makerGiveToken,
                swapQuote.makerGiveAmount,
                swapQuote.validUntil
            )
        );

        address maker;
        if (swapQuote.eip1271Maker == address(0)) {
            maker = ECDSA.recover(
                MessageHashUtils.toEthSignedMessageHash(msgHash),
                swapQuote.signature
            );
        } else {
            bool isValid = _checkEIP1271Signature(
                swapQuote.eip1271Maker,
                msgHash,
                swapQuote.signature
            );
            if (!isValid) {
                revert Errors.InvalidEIP1271Signature();
            }
            maker = swapQuote.eip1271Maker;
        }

        if (quotesPaused[maker]) {
            revert Errors.SwapQuotePaused();
        }
        if (isSwapQuoteUsed[msgHash]) {
            revert Errors.SwapQuoteAlreadyUsed();
        }
        isSwapQuoteUsed[msgHash] = true;
        IERC20Metadata(swapQuote.takerGiveToken).safeTransferFrom(
            msg.sender,
            maker,
            swapQuote.takerGiveAmount
        );
        IERC20Metadata(swapQuote.makerGiveToken).safeTransferFrom(
            maker,
            to,
            swapQuote.makerGiveAmount
        );
        emit TakeSwapQuote(msg.sender, to, maker, swapQuote);
    }

    function togglePauseQuotes() external {
        bool isPaused = quotesPaused[msg.sender];
        quotesPaused[msg.sender] = !isPaused;
        emit PauseQuotes(msg.sender, !isPaused);
    }

    function mintOption(
        address optionReceiver,
        address escrowOwner,
        DataTypes.OptionInfo calldata optionInfo,
        DataTypes.OptionNaming calldata optionNaming,
        address distPartner
    ) external {
        if (optionInfo.underlyingToken == optionInfo.settlementToken) {
            revert Errors.InvalidTokenPair();
        }
        if (optionInfo.notional == 0) {
            revert Errors.InvalidNotional();
        }
        if (block.timestamp > optionInfo.expiry) {
            revert Errors.InvalidExpiry();
        }
        if (optionInfo.expiry < optionInfo.earliestExercise + 1 days) {
            revert Errors.InvalidEarliestExercise();
        }
        if (optionInfo.advancedSettings.borrowCap > BASE) {
            revert Errors.InvalidBorrowCap();
        }
        (address escrow, ) = _createEscrow();
        (uint256 mintFeeProtocol, uint256 mintFeeDistPartner) = getMintFees(
            distPartner,
            optionInfo.notional
        );
        address mintOptionTokensTo = (mintFeeProtocol > 0 ||
            mintFeeDistPartner > 0)
            ? address(this)
            : optionReceiver;
        IEscrow(escrow).initializeMintOption(
            address(this),
            escrowOwner,
            mintOptionTokensTo,
            getExerciseFee(),
            optionInfo,
            optionNaming
        );
        IERC20Metadata(optionInfo.underlyingToken).safeTransferFrom(
            msg.sender,
            escrow,
            optionInfo.notional
        );
        if (mintOptionTokensTo == address(this)) {
            IERC20Metadata(escrow).safeTransfer(
                optionReceiver,
                optionInfo.notional - mintFeeProtocol - mintFeeDistPartner
            );
            if (mintFeeDistPartner > 0) {
                IERC20Metadata(escrow).safeTransfer(
                    distPartner,
                    mintFeeDistPartner
                );
            }
            if (mintFeeProtocol > 0) {
                IERC20Metadata(escrow).safeTransfer(
                    feeHandler,
                    mintFeeProtocol
                );
                IFeeHandler(feeHandler).provisionFees(escrow, mintFeeProtocol);
            }
        }
        emit MintOption(
            msg.sender,
            optionReceiver,
            escrowOwner,
            optionInfo,
            mintFeeProtocol,
            mintFeeDistPartner,
            distPartner
        );
    }

    function setFeeHandler(address newFeeHandler) external onlyOwner {
        address oldFeeHandler = feeHandler;
        if (oldFeeHandler == newFeeHandler) {
            revert Errors.FeeHandlerAlreadySet();
        }
        feeHandler = newFeeHandler;
        emit NewFeeHandler(oldFeeHandler, newFeeHandler);
    }

    function emitTransferEvent(
        address from,
        address to,
        uint256 value
    ) external {
        address escrow = msg.sender;
        if (!isEscrow[escrow]) {
            revert Errors.NotAnEscrow();
        }
        emit Transfer(escrow, from, to, value);
    }

    function emitTransferOwnershipEvent(
        address oldOwner,
        address newOwner
    ) external {
        address escrow = msg.sender;
        if (!isEscrow[escrow]) {
            revert Errors.NotAnEscrow();
        }
        emit TransferOwnership(escrow, oldOwner, newOwner);
    }

    function getExerciseFee() public view returns (uint96 exerciseFee) {
        address _feeHandler = feeHandler;
        if (_feeHandler == address(0)) {
            return 0;
        }
        exerciseFee = IFeeHandler(_feeHandler).exerciseFee();
        exerciseFee = exerciseFee > MAX_EXERCISE_FEE
            ? MAX_EXERCISE_FEE
            : exerciseFee;
    }

    function getMatchFees(
        address distPartner,
        uint128 optionPremium,
        DataTypes.OptionInfo calldata optionInfo
    )
        public
        view
        returns (uint128 matchFeeProtocol, uint128 matchFeeDistPartner)
    {
        address _feeHandler = feeHandler;
        if (_feeHandler != address(0)) {
            (uint256 matchFee, uint256 matchFeeDistPartnerShare) = IFeeHandler(
                _feeHandler
            ).getMatchFeeInfo(distPartner, optionPremium, optionInfo);

            uint256 cappedMatchFee = matchFee > MAX_MATCH_FEE
                ? MAX_MATCH_FEE
                : matchFee;
            uint256 cappedMatchFeeDistPartnerShare = matchFeeDistPartnerShare >
                BASE
                ? BASE
                : matchFeeDistPartnerShare;
            uint256 totalMatchFee = (optionPremium * cappedMatchFee) / BASE;
            matchFeeDistPartner = SafeCast.toUint128(
                (totalMatchFee * cappedMatchFeeDistPartnerShare) / BASE
            );
            matchFeeProtocol = SafeCast.toUint128(
                totalMatchFee - matchFeeDistPartner
            );
        }
    }

    function getMintFees(
        address distPartner,
        uint128 notional
    )
        public
        view
        returns (uint256 mintFeeProtocol, uint256 mintFeeDistPartner)
    {
        address _feeHandler = feeHandler;
        if (_feeHandler != address(0)) {
            (uint256 mintFee, uint256 mintFeeDistPartnerShare) = IFeeHandler(
                _feeHandler
            ).getMintFeeInfo(distPartner);

            // @dev: use same cap as for match fee
            uint256 cappedMintFee = mintFee > MAX_MATCH_FEE
                ? MAX_MATCH_FEE
                : mintFee;
            uint256 cappedMintFeeDistPartnerShare = mintFeeDistPartnerShare >
                BASE
                ? BASE
                : mintFeeDistPartnerShare;
            uint256 totalMintFee = (notional * cappedMintFee) / BASE;
            mintFeeDistPartner =
                (totalMintFee * cappedMintFeeDistPartnerShare) /
                BASE;
            mintFeeProtocol = totalMintFee - mintFeeDistPartner;
        }
    }

    function previewTakeQuote(
        DataTypes.RFQInitialization calldata rfqInitialization,
        address distPartner
    ) public view returns (DataTypes.TakeQuotePreview memory) {
        bytes32 msgHash = keccak256(
            abi.encode(
                block.chainid,
                rfqInitialization.optionInfo,
                rfqInitialization.rfqQuote.premium,
                rfqInitialization.rfqQuote.validUntil
            )
        );

        address quoter;
        if (rfqInitialization.rfqQuote.eip1271Maker == address(0)) {
            quoter = ECDSA.recover(
                MessageHashUtils.toEthSignedMessageHash(msgHash),
                rfqInitialization.rfqQuote.signature
            );
        } else {
            bool isValid = _checkEIP1271Signature(
                rfqInitialization.rfqQuote.eip1271Maker,
                msgHash,
                rfqInitialization.rfqQuote.signature
            );
            if (!isValid) {
                return
                    _createTakeQuotePreview(
                        DataTypes.RFQStatus.InvalidEIP1271Signature,
                        msgHash,
                        quoter
                    );
            }
            quoter = rfqInitialization.rfqQuote.eip1271Maker;
        }

        if (
            rfqInitialization.optionInfo.underlyingToken ==
            rfqInitialization.optionInfo.settlementToken ||
            rfqInitialization.optionInfo.notional == 0 ||
            rfqInitialization.optionInfo.strike == 0 ||
            rfqInitialization.optionInfo.expiry <
            rfqInitialization.optionInfo.earliestExercise + 1 days ||
            rfqInitialization.optionInfo.advancedSettings.borrowCap > BASE
        ) {
            return
                _createTakeQuotePreview(
                    DataTypes.RFQStatus.InvalidQuote,
                    msgHash,
                    quoter
                );
        }

        if (
            block.timestamp > rfqInitialization.rfqQuote.validUntil ||
            block.timestamp > rfqInitialization.optionInfo.expiry
        ) {
            return
                _createTakeQuotePreview(
                    DataTypes.RFQStatus.Expired,
                    msgHash,
                    quoter
                );
        }
        if (isQuoteUsed[msgHash]) {
            return
                _createTakeQuotePreview(
                    DataTypes.RFQStatus.AlreadyExecuted,
                    msgHash,
                    quoter
                );
        }

        if (quotesPaused[quoter]) {
            return
                _createTakeQuotePreview(
                    DataTypes.RFQStatus.QuotesPaused,
                    msgHash,
                    quoter
                );
        }
        (uint128 matchFeeProtocol, uint128 matchFeeDistPartner) = getMatchFees(
            distPartner,
            rfqInitialization.rfqQuote.premium,
            rfqInitialization.optionInfo
        );
        return
            DataTypes.TakeQuotePreview({
                status: DataTypes.RFQStatus.Success,
                msgHash: msgHash,
                quoter: quoter,
                premium: rfqInitialization.rfqQuote.premium,
                premiumToken: rfqInitialization
                    .optionInfo
                    .advancedSettings
                    .premiumTokenIsUnderlying
                    ? rfqInitialization.optionInfo.underlyingToken
                    : rfqInitialization.optionInfo.settlementToken,
                matchFeeProtocol: matchFeeProtocol,
                matchFeeDistPartner: matchFeeDistPartner
            });
    }

    function getEscrows(
        uint256 from,
        uint256 numElements
    ) external view returns (address[] memory _escrowArray) {
        uint256 length = _escrows.length;
        if (numElements == 0 || from + numElements > length) {
            revert Errors.InvalidGetEscrowsQuery();
        }
        _escrowArray = new address[](numElements);
        for (uint256 i = 0; i < numElements; ++i) {
            _escrowArray[i] = _escrows[from + i];
        }
    }

    function _createEscrow()
        internal
        returns (address escrow, uint256 oTokenIndex)
    {
        oTokenIndex = numEscrows + 1;
        escrow = Clones.cloneDeterministic(
            escrowImpl,
            keccak256(abi.encode(oTokenIndex))
        );
        numEscrows = oTokenIndex;
        isEscrow[escrow] = true;
        _escrows.push(escrow);
    }

    function _createTakeQuotePreview(
        DataTypes.RFQStatus status,
        bytes32 msgHash,
        address quoter
    ) internal pure returns (DataTypes.TakeQuotePreview memory) {
        return
            DataTypes.TakeQuotePreview({
                status: status,
                msgHash: msgHash,
                quoter: quoter,
                premium: 0,
                premiumToken: address(0),
                matchFeeProtocol: 0,
                matchFeeDistPartner: 0
            });
    }

    function _checkEIP1271Signature(
        address erc1271Wallet,
        bytes32 msgHash,
        bytes calldata signature
    ) internal view returns (bool isValid) {
        // @dev: legacy EIP1271 wallets using bytes4(keccak256("isValidSignature(bytes,bytes)")
        // are not supported
        (bool success, bytes memory returnData) = erc1271Wallet.staticcall(
            abi.encodeWithSelector(
                EIP1271_SIG_AND_MAGIC_VALUE,
                msgHash,
                signature
            )
        );
        if (success && returnData.length == 32) {
            bytes4 result = abi.decode(returnData, (bytes4));
            return result == EIP1271_SIG_AND_MAGIC_VALUE;
        }
        return false;
    }
}

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

pragma solidity ^0.8.20;

import {Context} from "../utils/Context.sol";

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * The initial owner is set to the address provided by the deployer. This can
 * later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
    address private _owner;

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

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

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

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

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        if (owner() != _msgSender()) {
            revert OwnableUnauthorizedAccount(_msgSender());
        }
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby disabling any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        if (newOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}

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

pragma solidity ^0.8.20;

/**
 * @dev https://eips.ethereum.org/EIPS/eip-1167[EIP 1167] is a standard for
 * deploying minimal proxy contracts, also known as "clones".
 *
 * > To simply and cheaply clone contract functionality in an immutable way, this standard specifies
 * > a minimal bytecode implementation that delegates all calls to a known, fixed address.
 *
 * The library includes functions to deploy a proxy using either `create` (traditional deployment) or `create2`
 * (salted deterministic deployment). It also includes functions to predict the addresses of clones deployed using the
 * deterministic method.
 */
library Clones {
    /**
     * @dev A clone instance deployment failed.
     */
    error ERC1167FailedCreateClone();

    /**
     * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
     *
     * This function uses the create opcode, which should never revert.
     */
    function clone(address implementation) internal returns (address instance) {
        /// @solidity memory-safe-assembly
        assembly {
            // Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes
            // of the `implementation` address with the bytecode before the address.
            mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000))
            // Packs the remaining 17 bytes of `implementation` with the bytecode after the address.
            mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3))
            instance := create(0, 0x09, 0x37)
        }
        if (instance == address(0)) {
            revert ERC1167FailedCreateClone();
        }
    }

    /**
     * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
     *
     * This function uses the create2 opcode and a `salt` to deterministically deploy
     * the clone. Using the same `implementation` and `salt` multiple time will revert, since
     * the clones cannot be deployed twice at the same address.
     */
    function cloneDeterministic(address implementation, bytes32 salt) internal returns (address instance) {
        /// @solidity memory-safe-assembly
        assembly {
            // Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes
            // of the `implementation` address with the bytecode before the address.
            mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000))
            // Packs the remaining 17 bytes of `implementation` with the bytecode after the address.
            mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3))
            instance := create2(0, 0x09, 0x37, salt)
        }
        if (instance == address(0)) {
            revert ERC1167FailedCreateClone();
        }
    }

    /**
     * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
     */
    function predictDeterministicAddress(
        address implementation,
        bytes32 salt,
        address deployer
    ) internal pure returns (address predicted) {
        /// @solidity memory-safe-assembly
        assembly {
            let ptr := mload(0x40)
            mstore(add(ptr, 0x38), deployer)
            mstore(add(ptr, 0x24), 0x5af43d82803e903d91602b57fd5bf3ff)
            mstore(add(ptr, 0x14), implementation)
            mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73)
            mstore(add(ptr, 0x58), salt)
            mstore(add(ptr, 0x78), keccak256(add(ptr, 0x0c), 0x37))
            predicted := keccak256(add(ptr, 0x43), 0x55)
        }
    }

    /**
     * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
     */
    function predictDeterministicAddress(
        address implementation,
        bytes32 salt
    ) internal view returns (address predicted) {
        return predictDeterministicAddress(implementation, salt, address(this));
    }
}

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

pragma solidity ^0.8.20;

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

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

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

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

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

pragma solidity ^0.8.20;

/**
 * @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.
 *
 * ==== Security Considerations
 *
 * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
 * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
 * considered as an intention to spend the allowance in any specific way. The second is that because permits have
 * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
 * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
 * generally recommended is:
 *
 * ```solidity
 * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
 *     try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
 *     doThing(..., value);
 * }
 *
 * function doThing(..., uint256 value) public {
 *     token.safeTransferFrom(msg.sender, address(this), value);
 *     ...
 * }
 * ```
 *
 * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
 * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
 * {SafeERC20-safeTransferFrom}).
 *
 * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
 * contracts should have entry points that don't rely on permit.
 */
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].
     *
     * CAUTION: See Security Considerations above.
     */
    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);
}

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

pragma solidity ^0.8.20;

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

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

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

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

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

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

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

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

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

pragma solidity ^0.8.20;

import {IERC20} from "../IERC20.sol";
import {IERC20Permit} from "../extensions/IERC20Permit.sol";
import {Address} from "../../../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 An operation with an ERC20 token failed.
     */
    error SafeERC20FailedOperation(address token);

    /**
     * @dev Indicates a failed `decreaseAllowance` request.
     */
    error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);

    /**
     * @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.encodeCall(token.transfer, (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.encodeCall(token.transferFrom, (from, to, 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);
        forceApprove(token, spender, oldAllowance + value);
    }

    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
     * value, non-reverting calls are assumed to be successful.
     */
    function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
        unchecked {
            uint256 currentAllowance = token.allowance(address(this), spender);
            if (currentAllowance < requestedDecrease) {
                revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
            }
            forceApprove(token, spender, currentAllowance - requestedDecrease);
        }
    }

    /**
     * @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.encodeCall(token.approve, (spender, value));

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

    /**
     * @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);
        if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @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(token).code.length > 0;
    }
}

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

pragma solidity ^0.8.20;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev The ETH balance of the account is not enough to perform the operation.
     */
    error AddressInsufficientBalance(address account);

    /**
     * @dev There's no code at `target` (it is not a contract).
     */
    error AddressEmptyCode(address target);

    /**
     * @dev A call to an address target failed. The target may have reverted.
     */
    error FailedInnerCall();

    /**
     * @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.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        if (address(this).balance < amount) {
            revert AddressInsufficientBalance(address(this));
        }

        (bool success, ) = recipient.call{value: amount}("");
        if (!success) {
            revert FailedInnerCall();
        }
    }

    /**
     * @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 or custom error, it is bubbled
     * up by this function (like regular Solidity function calls). However, if
     * the call reverted with no returned reason, this function reverts with a
     * {FailedInnerCall} error.
     *
     * 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.
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0);
    }

    /**
     * @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`.
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
        if (address(this).balance < value) {
            revert AddressInsufficientBalance(address(this));
        }
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

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

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

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
     * was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an
     * unsuccessful call.
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata
    ) internal view returns (bytes memory) {
        if (!success) {
            _revert(returndata);
        } else {
            // only check if target is a contract if the call was successful and the return data is empty
            // otherwise we already know that it was a contract
            if (returndata.length == 0 && target.code.length == 0) {
                revert AddressEmptyCode(target);
            }
            return returndata;
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
     * revert reason or with a default {FailedInnerCall} error.
     */
    function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
        if (!success) {
            _revert(returndata);
        } else {
            return returndata;
        }
    }

    /**
     * @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.
     */
    function _revert(bytes memory returndata) 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 FailedInnerCall();
        }
    }
}

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

pragma solidity ^0.8.20;

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

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

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

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

pragma solidity ^0.8.20;

/**
 * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
 *
 * These functions can be used to verify that a message was signed by the holder
 * of the private keys of a given address.
 */
library ECDSA {
    enum RecoverError {
        NoError,
        InvalidSignature,
        InvalidSignatureLength,
        InvalidSignatureS
    }

    /**
     * @dev The signature derives the `address(0)`.
     */
    error ECDSAInvalidSignature();

    /**
     * @dev The signature has an invalid length.
     */
    error ECDSAInvalidSignatureLength(uint256 length);

    /**
     * @dev The signature has an S value that is in the upper half order.
     */
    error ECDSAInvalidSignatureS(bytes32 s);

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with `signature` or an error. This will not
     * return address(0) without also returning an error description. Errors are documented using an enum (error type)
     * and a bytes32 providing additional information about the error.
     *
     * If no error is returned, then the address can be used for verification purposes.
     *
     * The `ecrecover` EVM precompile allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it.
     *
     * Documentation for signature generation:
     * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
     * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
     */
    function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError, bytes32) {
        if (signature.length == 65) {
            bytes32 r;
            bytes32 s;
            uint8 v;
            // ecrecover takes the signature parameters, and the only way to get them
            // currently is to use assembly.
            /// @solidity memory-safe-assembly
            assembly {
                r := mload(add(signature, 0x20))
                s := mload(add(signature, 0x40))
                v := byte(0, mload(add(signature, 0x60)))
            }
            return tryRecover(hash, v, r, s);
        } else {
            return (address(0), RecoverError.InvalidSignatureLength, bytes32(signature.length));
        }
    }

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature`. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM precompile allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it.
     */
    function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
        (address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, signature);
        _throwError(error, errorArg);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
     *
     * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
     */
    function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError, bytes32) {
        unchecked {
            bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
            // We do not check for an overflow here since the shift operation results in 0 or 1.
            uint8 v = uint8((uint256(vs) >> 255) + 27);
            return tryRecover(hash, v, r, s);
        }
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
     */
    function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) {
        (address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, r, vs);
        _throwError(error, errorArg);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `v`,
     * `r` and `s` signature fields separately.
     */
    function tryRecover(
        bytes32 hash,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal pure returns (address, RecoverError, bytes32) {
        // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
        // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
        // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
        // signatures from current libraries generate a unique signature with an s-value in the lower half order.
        //
        // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
        // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
        // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
        // these malleable signatures as well.
        if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
            return (address(0), RecoverError.InvalidSignatureS, s);
        }

        // If the signature is valid (and not malleable), return the signer address
        address signer = ecrecover(hash, v, r, s);
        if (signer == address(0)) {
            return (address(0), RecoverError.InvalidSignature, bytes32(0));
        }

        return (signer, RecoverError.NoError, bytes32(0));
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `v`,
     * `r` and `s` signature fields separately.
     */
    function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
        (address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, v, r, s);
        _throwError(error, errorArg);
        return recovered;
    }

    /**
     * @dev Optionally reverts with the corresponding custom error according to the `error` argument provided.
     */
    function _throwError(RecoverError error, bytes32 errorArg) private pure {
        if (error == RecoverError.NoError) {
            return; // no error: do nothing
        } else if (error == RecoverError.InvalidSignature) {
            revert ECDSAInvalidSignature();
        } else if (error == RecoverError.InvalidSignatureLength) {
            revert ECDSAInvalidSignatureLength(uint256(errorArg));
        } else if (error == RecoverError.InvalidSignatureS) {
            revert ECDSAInvalidSignatureS(errorArg);
        }
    }
}

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

pragma solidity ^0.8.20;

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

/**
 * @dev Signature message hash utilities for producing digests to be consumed by {ECDSA} recovery or signing.
 *
 * The library provides methods for generating a hash of a message that conforms to the
 * https://eips.ethereum.org/EIPS/eip-191[EIP 191] and https://eips.ethereum.org/EIPS/eip-712[EIP 712]
 * specifications.
 */
library MessageHashUtils {
    /**
     * @dev Returns the keccak256 digest of an EIP-191 signed data with version
     * `0x45` (`personal_sign` messages).
     *
     * The digest is calculated by prefixing a bytes32 `messageHash` with
     * `"\x19Ethereum Signed Message:\n32"` and hashing the result. It corresponds with the
     * hash signed when using the https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] JSON-RPC method.
     *
     * NOTE: The `messageHash` parameter is intended to be the result of hashing a raw message with
     * keccak256, although any bytes32 value can be safely used because the final digest will
     * be re-hashed.
     *
     * See {ECDSA-recover}.
     */
    function toEthSignedMessageHash(bytes32 messageHash) internal pure returns (bytes32 digest) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, "\x19Ethereum Signed Message:\n32") // 32 is the bytes-length of messageHash
            mstore(0x1c, messageHash) // 0x1c (28) is the length of the prefix
            digest := keccak256(0x00, 0x3c) // 0x3c is the length of the prefix (0x1c) + messageHash (0x20)
        }
    }

    /**
     * @dev Returns the keccak256 digest of an EIP-191 signed data with version
     * `0x45` (`personal_sign` messages).
     *
     * The digest is calculated by prefixing an arbitrary `message` with
     * `"\x19Ethereum Signed Message:\n" + len(message)` and hashing the result. It corresponds with the
     * hash signed when using the https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] JSON-RPC method.
     *
     * See {ECDSA-recover}.
     */
    function toEthSignedMessageHash(bytes memory message) internal pure returns (bytes32) {
        return
            keccak256(bytes.concat("\x19Ethereum Signed Message:\n", bytes(Strings.toString(message.length)), message));
    }

    /**
     * @dev Returns the keccak256 digest of an EIP-191 signed data with version
     * `0x00` (data with intended validator).
     *
     * The digest is calculated by prefixing an arbitrary `data` with `"\x19\x00"` and the intended
     * `validator` address. Then hashing the result.
     *
     * See {ECDSA-recover}.
     */
    function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked(hex"19_00", validator, data));
    }

    /**
     * @dev Returns the keccak256 digest of an EIP-712 typed data (EIP-191 version `0x01`).
     *
     * The digest is calculated from a `domainSeparator` and a `structHash`, by prefixing them with
     * `\x19\x01` and hashing the result. It corresponds to the hash signed by the
     * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`] JSON-RPC method as part of EIP-712.
     *
     * See {ECDSA-recover}.
     */
    function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 digest) {
        /// @solidity memory-safe-assembly
        assembly {
            let ptr := mload(0x40)
            mstore(ptr, hex"19_01")
            mstore(add(ptr, 0x02), domainSeparator)
            mstore(add(ptr, 0x22), structHash)
            digest := keccak256(ptr, 0x42)
        }
    }
}

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

pragma solidity ^0.8.20;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    /**
     * @dev Muldiv operation overflow.
     */
    error MathOverflowedMulDiv();

    enum Rounding {
        Floor, // Toward negative infinity
        Ceil, // Toward positive infinity
        Trunc, // Toward zero
        Expand // Away from zero
    }

    /**
     * @dev Returns the addition of two unsigned integers, with an overflow flag.
     */
    function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            uint256 c = a + b;
            if (c < a) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, with an overflow flag.
     */
    function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b > a) return (false, 0);
            return (true, a - b);
        }
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
     */
    function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
            // benefit is lost if 'b' is also tested.
            // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
            if (a == 0) return (true, 0);
            uint256 c = a * b;
            if (c / a != b) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the division of two unsigned integers, with a division by zero flag.
     */
    function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a / b);
        }
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
     */
    function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a % b);
        }
    }

    /**
     * @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 towards infinity instead
     * of rounding towards zero.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        if (b == 0) {
            // Guarantee the same behavior as in a regular Solidity division.
            return a / b;
        }

        // (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 = x * y; // Least significant 256 bits of the product
            uint256 prod1; // Most significant 256 bits of the product
            assembly {
                let mm := mulmod(x, y, not(0))
                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.
            if (denominator <= prod1) {
                revert MathOverflowedMulDiv();
            }

            ///////////////////////////////////////////////
            // 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.

            uint256 twos = denominator & (0 - denominator);
            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 (unsignedRoundsUp(rounding) && 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
     * towards zero.
     *
     * 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 + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 2 of a positive value rounded towards zero.
     * 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 + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 10 of a positive value rounded towards zero.
     * 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 + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 256 of a positive value rounded towards zero.
     * 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 + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0);
        }
    }

    /**
     * @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
     */
    function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
        return uint8(rounding) % 2 == 1;
    }
}

File 13 of 20 : SafeCast.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/SafeCast.sol)
// This file was procedurally generated from scripts/generate/templates/SafeCast.js.

pragma solidity ^0.8.20;

/**
 * @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.
 */
library SafeCast {
    /**
     * @dev Value doesn't fit in an uint of `bits` size.
     */
    error SafeCastOverflowedUintDowncast(uint8 bits, uint256 value);

    /**
     * @dev An int value doesn't fit in an uint of `bits` size.
     */
    error SafeCastOverflowedIntToUint(int256 value);

    /**
     * @dev Value doesn't fit in an int of `bits` size.
     */
    error SafeCastOverflowedIntDowncast(uint8 bits, int256 value);

    /**
     * @dev An uint value doesn't fit in an int of `bits` size.
     */
    error SafeCastOverflowedUintToInt(uint256 value);

    /**
     * @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
     */
    function toUint248(uint256 value) internal pure returns (uint248) {
        if (value > type(uint248).max) {
            revert SafeCastOverflowedUintDowncast(248, value);
        }
        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
     */
    function toUint240(uint256 value) internal pure returns (uint240) {
        if (value > type(uint240).max) {
            revert SafeCastOverflowedUintDowncast(240, value);
        }
        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
     */
    function toUint232(uint256 value) internal pure returns (uint232) {
        if (value > type(uint232).max) {
            revert SafeCastOverflowedUintDowncast(232, value);
        }
        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
     */
    function toUint224(uint256 value) internal pure returns (uint224) {
        if (value > type(uint224).max) {
            revert SafeCastOverflowedUintDowncast(224, value);
        }
        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
     */
    function toUint216(uint256 value) internal pure returns (uint216) {
        if (value > type(uint216).max) {
            revert SafeCastOverflowedUintDowncast(216, value);
        }
        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
     */
    function toUint208(uint256 value) internal pure returns (uint208) {
        if (value > type(uint208).max) {
            revert SafeCastOverflowedUintDowncast(208, value);
        }
        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
     */
    function toUint200(uint256 value) internal pure returns (uint200) {
        if (value > type(uint200).max) {
            revert SafeCastOverflowedUintDowncast(200, value);
        }
        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
     */
    function toUint192(uint256 value) internal pure returns (uint192) {
        if (value > type(uint192).max) {
            revert SafeCastOverflowedUintDowncast(192, value);
        }
        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
     */
    function toUint184(uint256 value) internal pure returns (uint184) {
        if (value > type(uint184).max) {
            revert SafeCastOverflowedUintDowncast(184, value);
        }
        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
     */
    function toUint176(uint256 value) internal pure returns (uint176) {
        if (value > type(uint176).max) {
            revert SafeCastOverflowedUintDowncast(176, value);
        }
        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
     */
    function toUint168(uint256 value) internal pure returns (uint168) {
        if (value > type(uint168).max) {
            revert SafeCastOverflowedUintDowncast(168, value);
        }
        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
     */
    function toUint160(uint256 value) internal pure returns (uint160) {
        if (value > type(uint160).max) {
            revert SafeCastOverflowedUintDowncast(160, value);
        }
        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
     */
    function toUint152(uint256 value) internal pure returns (uint152) {
        if (value > type(uint152).max) {
            revert SafeCastOverflowedUintDowncast(152, value);
        }
        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
     */
    function toUint144(uint256 value) internal pure returns (uint144) {
        if (value > type(uint144).max) {
            revert SafeCastOverflowedUintDowncast(144, value);
        }
        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
     */
    function toUint136(uint256 value) internal pure returns (uint136) {
        if (value > type(uint136).max) {
            revert SafeCastOverflowedUintDowncast(136, value);
        }
        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
     */
    function toUint128(uint256 value) internal pure returns (uint128) {
        if (value > type(uint128).max) {
            revert SafeCastOverflowedUintDowncast(128, value);
        }
        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
     */
    function toUint120(uint256 value) internal pure returns (uint120) {
        if (value > type(uint120).max) {
            revert SafeCastOverflowedUintDowncast(120, value);
        }
        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
     */
    function toUint112(uint256 value) internal pure returns (uint112) {
        if (value > type(uint112).max) {
            revert SafeCastOverflowedUintDowncast(112, value);
        }
        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
     */
    function toUint104(uint256 value) internal pure returns (uint104) {
        if (value > type(uint104).max) {
            revert SafeCastOverflowedUintDowncast(104, value);
        }
        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
     */
    function toUint96(uint256 value) internal pure returns (uint96) {
        if (value > type(uint96).max) {
            revert SafeCastOverflowedUintDowncast(96, value);
        }
        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
     */
    function toUint88(uint256 value) internal pure returns (uint88) {
        if (value > type(uint88).max) {
            revert SafeCastOverflowedUintDowncast(88, value);
        }
        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
     */
    function toUint80(uint256 value) internal pure returns (uint80) {
        if (value > type(uint80).max) {
            revert SafeCastOverflowedUintDowncast(80, value);
        }
        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
     */
    function toUint72(uint256 value) internal pure returns (uint72) {
        if (value > type(uint72).max) {
            revert SafeCastOverflowedUintDowncast(72, value);
        }
        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
     */
    function toUint64(uint256 value) internal pure returns (uint64) {
        if (value > type(uint64).max) {
            revert SafeCastOverflowedUintDowncast(64, value);
        }
        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
     */
    function toUint56(uint256 value) internal pure returns (uint56) {
        if (value > type(uint56).max) {
            revert SafeCastOverflowedUintDowncast(56, value);
        }
        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
     */
    function toUint48(uint256 value) internal pure returns (uint48) {
        if (value > type(uint48).max) {
            revert SafeCastOverflowedUintDowncast(48, value);
        }
        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
     */
    function toUint40(uint256 value) internal pure returns (uint40) {
        if (value > type(uint40).max) {
            revert SafeCastOverflowedUintDowncast(40, value);
        }
        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
     */
    function toUint32(uint256 value) internal pure returns (uint32) {
        if (value > type(uint32).max) {
            revert SafeCastOverflowedUintDowncast(32, value);
        }
        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
     */
    function toUint24(uint256 value) internal pure returns (uint24) {
        if (value > type(uint24).max) {
            revert SafeCastOverflowedUintDowncast(24, value);
        }
        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
     */
    function toUint16(uint256 value) internal pure returns (uint16) {
        if (value > type(uint16).max) {
            revert SafeCastOverflowedUintDowncast(16, value);
        }
        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
     */
    function toUint8(uint256 value) internal pure returns (uint8) {
        if (value > type(uint8).max) {
            revert SafeCastOverflowedUintDowncast(8, value);
        }
        return uint8(value);
    }

    /**
     * @dev Converts a signed int256 into an unsigned uint256.
     *
     * Requirements:
     *
     * - input must be greater than or equal to 0.
     */
    function toUint256(int256 value) internal pure returns (uint256) {
        if (value < 0) {
            revert SafeCastOverflowedIntToUint(value);
        }
        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
     */
    function toInt248(int256 value) internal pure returns (int248 downcasted) {
        downcasted = int248(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(248, value);
        }
    }

    /**
     * @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
     */
    function toInt240(int256 value) internal pure returns (int240 downcasted) {
        downcasted = int240(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(240, value);
        }
    }

    /**
     * @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
     */
    function toInt232(int256 value) internal pure returns (int232 downcasted) {
        downcasted = int232(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(232, value);
        }
    }

    /**
     * @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
     */
    function toInt224(int256 value) internal pure returns (int224 downcasted) {
        downcasted = int224(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(224, value);
        }
    }

    /**
     * @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
     */
    function toInt216(int256 value) internal pure returns (int216 downcasted) {
        downcasted = int216(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(216, value);
        }
    }

    /**
     * @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
     */
    function toInt208(int256 value) internal pure returns (int208 downcasted) {
        downcasted = int208(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(208, value);
        }
    }

    /**
     * @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
     */
    function toInt200(int256 value) internal pure returns (int200 downcasted) {
        downcasted = int200(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(200, value);
        }
    }

    /**
     * @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
     */
    function toInt192(int256 value) internal pure returns (int192 downcasted) {
        downcasted = int192(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(192, value);
        }
    }

    /**
     * @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
     */
    function toInt184(int256 value) internal pure returns (int184 downcasted) {
        downcasted = int184(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(184, value);
        }
    }

    /**
     * @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
     */
    function toInt176(int256 value) internal pure returns (int176 downcasted) {
        downcasted = int176(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(176, value);
        }
    }

    /**
     * @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
     */
    function toInt168(int256 value) internal pure returns (int168 downcasted) {
        downcasted = int168(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(168, value);
        }
    }

    /**
     * @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
     */
    function toInt160(int256 value) internal pure returns (int160 downcasted) {
        downcasted = int160(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(160, value);
        }
    }

    /**
     * @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
     */
    function toInt152(int256 value) internal pure returns (int152 downcasted) {
        downcasted = int152(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(152, value);
        }
    }

    /**
     * @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
     */
    function toInt144(int256 value) internal pure returns (int144 downcasted) {
        downcasted = int144(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(144, value);
        }
    }

    /**
     * @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
     */
    function toInt136(int256 value) internal pure returns (int136 downcasted) {
        downcasted = int136(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(136, value);
        }
    }

    /**
     * @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
     */
    function toInt128(int256 value) internal pure returns (int128 downcasted) {
        downcasted = int128(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(128, value);
        }
    }

    /**
     * @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
     */
    function toInt120(int256 value) internal pure returns (int120 downcasted) {
        downcasted = int120(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(120, value);
        }
    }

    /**
     * @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
     */
    function toInt112(int256 value) internal pure returns (int112 downcasted) {
        downcasted = int112(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(112, value);
        }
    }

    /**
     * @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
     */
    function toInt104(int256 value) internal pure returns (int104 downcasted) {
        downcasted = int104(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(104, value);
        }
    }

    /**
     * @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
     */
    function toInt96(int256 value) internal pure returns (int96 downcasted) {
        downcasted = int96(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(96, value);
        }
    }

    /**
     * @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
     */
    function toInt88(int256 value) internal pure returns (int88 downcasted) {
        downcasted = int88(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(88, value);
        }
    }

    /**
     * @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
     */
    function toInt80(int256 value) internal pure returns (int80 downcasted) {
        downcasted = int80(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(80, value);
        }
    }

    /**
     * @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
     */
    function toInt72(int256 value) internal pure returns (int72 downcasted) {
        downcasted = int72(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(72, value);
        }
    }

    /**
     * @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
     */
    function toInt64(int256 value) internal pure returns (int64 downcasted) {
        downcasted = int64(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(64, value);
        }
    }

    /**
     * @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
     */
    function toInt56(int256 value) internal pure returns (int56 downcasted) {
        downcasted = int56(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(56, value);
        }
    }

    /**
     * @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
     */
    function toInt48(int256 value) internal pure returns (int48 downcasted) {
        downcasted = int48(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(48, value);
        }
    }

    /**
     * @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
     */
    function toInt40(int256 value) internal pure returns (int40 downcasted) {
        downcasted = int40(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(40, value);
        }
    }

    /**
     * @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
     */
    function toInt32(int256 value) internal pure returns (int32 downcasted) {
        downcasted = int32(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(32, value);
        }
    }

    /**
     * @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
     */
    function toInt24(int256 value) internal pure returns (int24 downcasted) {
        downcasted = int24(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(24, value);
        }
    }

    /**
     * @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
     */
    function toInt16(int256 value) internal pure returns (int16 downcasted) {
        downcasted = int16(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(16, value);
        }
    }

    /**
     * @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
     */
    function toInt8(int256 value) internal pure returns (int8 downcasted) {
        downcasted = int8(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(8, value);
        }
    }

    /**
     * @dev Converts an unsigned uint256 into a signed int256.
     *
     * Requirements:
     *
     * - input must be less than or equal to maxInt256.
     */
    function toInt256(uint256 value) internal pure returns (int256) {
        // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
        if (value > uint256(type(int256).max)) {
            revert SafeCastOverflowedUintToInt(value);
        }
        return int256(value);
    }
}

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

pragma solidity ^0.8.20;

/**
 * @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 (last updated v5.0.0) (utils/Strings.sol)

pragma solidity ^0.8.20;

import {Math} from "./math/Math.sol";
import {SignedMath} from "./math/SignedMath.sol";

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

    /**
     * @dev The `value` string doesn't fit in the specified `length`.
     */
    error StringsInsufficientHexLength(uint256 value, uint256 length);

    /**
     * @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), HEX_DIGITS))
                }
                value /= 10;
                if (value == 0) break;
            }
            return buffer;
        }
    }

    /**
     * @dev Converts a `int256` to its ASCII `string` decimal representation.
     */
    function toStringSigned(int256 value) internal pure returns (string memory) {
        return string.concat(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) {
        uint256 localValue = value;
        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] = HEX_DIGITS[localValue & 0xf];
            localValue >>= 4;
        }
        if (localValue != 0) {
            revert StringsInsufficientHexLength(value, length);
        }
        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 bytes(a).length == bytes(b).length && keccak256(bytes(a)) == keccak256(bytes(b));
    }
}

File 16 of 20 : DataTypes.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

library DataTypes {
    struct OptionInfo {
        address underlyingToken;
        uint48 expiry;
        address settlementToken;
        uint48 earliestExercise;
        uint128 notional;
        uint128 strike;
        AdvancedSettings advancedSettings;
    }

    struct AdvancedSettings {
        uint64 borrowCap;
        address oracle;
        bool premiumTokenIsUnderlying;
        bool votingDelegationAllowed;
        address allowedDelegateRegistry;
    }

    struct AuctionInitialization {
        address underlyingToken;
        address settlementToken;
        uint128 notional;
        AuctionParams auctionParams;
        AdvancedSettings advancedSettings;
    }

    struct AuctionParams {
        uint128 relStrike;
        uint48 tenor;
        uint48 earliestExerciseTenor;
        uint32 decayStartTime;
        uint32 decayDuration;
        uint64 relPremiumStart;
        uint64 relPremiumFloor;
        uint128 minSpot;
        uint128 maxSpot;
    }

    struct RFQInitialization {
        OptionInfo optionInfo;
        RFQQuote rfqQuote;
    }

    struct RFQQuote {
        uint128 premium;
        uint256 validUntil;
        bytes signature;
        address eip1271Maker;
    }

    enum BidStatus {
        Success,
        SpotPriceTooLow,
        OutOfRangeSpotPrice,
        OptionAlreadyMinted,
        PremiumTooLow,
        AuctionCancelled
    }

    struct BidPreview {
        BidStatus status;
        address settlementToken;
        address underlyingToken;
        uint128 strike;
        uint48 expiry;
        uint48 earliestExercise;
        uint128 premium;
        address premiumToken;
        uint256 oracleSpotPrice;
        uint64 currAsk;
        uint128 matchFeeProtocol;
        uint128 matchFeeDistPartner;
    }

    enum RFQStatus {
        Expired,
        InvalidQuote,
        AlreadyExecuted,
        QuotesPaused,
        Success,
        InvalidEIP1271Signature
    }

    struct TakeQuotePreview {
        RFQStatus status;
        bytes32 msgHash;
        address quoter;
        uint128 premium;
        address premiumToken;
        uint128 matchFeeProtocol;
        uint128 matchFeeDistPartner;
    }

    struct SwapQuote {
        address takerGiveToken;
        uint256 takerGiveAmount;
        address makerGiveToken;
        uint256 makerGiveAmount;
        uint256 validUntil;
        bytes signature;
        address eip1271Maker;
    }

    struct OptionNaming {
        string name;
        string symbol;
    }

    struct MatchFeePerPair {
        bool isSet;
        uint96 matchFee;
    }
}

File 17 of 20 : Errors.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

library Errors {
    error AlreadyInitialized();
    error DistPartnerFeeUnchanged();
    error FeeHandlerAlreadySet();
    error InvalidAddress();
    error InvalidArrayLength();
    error InvalidBid();
    error InvalidBorrowAmount();
    error InvalidBorrowCap();
    error InvalidBorrowTime();
    error InvalidDistPartnerFeeShare();
    error InvalidEarliestExercise();
    error InvalidEarliestExerciseTenor();
    error InvalidEIP1271Signature();
    error InvalidExpiry();
    error InvalidExercise();
    error InvalidExerciseAmount();
    error InvalidExerciseFee();
    error InvalidExerciseTime();
    error InvalidGetEscrowsQuery();
    error InvalidMatchFee();
    error InvalidMaxTimeSinceLastUpdate();
    error InvalidMintFee();
    error InvalidMinMaxSpot();
    error InvalidNotional();
    error InvalidOracle();
    error InvalidOracleAnswer();
    error InvalidOracleDecimals();
    error InvalidRelPremiums();
    error InvalidRepayAmount();
    error InvalidRepayTime();
    error InvalidSender();
    error InvalidStrike();
    error InvalidTakeQuote();
    error InvalidTenor();
    error InvalidTokenPair();
    error InvalidWithdraw();
    error NoAllowedDelegateRegistry();
    error NoOptionMinted();
    error NoOracle();
    error NotAnEscrow();
    error NothingToRedeem();
    error NothingToRepay();
    error OnlyAvailableForAuctions();
    error OracleAlreadySet(address oracleAddr);
    error OwnerAlreadySet();
    error SwapQuoteAlreadyUsed();
    error SwapQuoteExpired();
    error SwapQuotePaused();
    error VotingDelegationNotAllowed();
}

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

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

/**
 * @title IEscrow
 * @dev Interface for the Escrow contract.
 * Provides functionality for initializing auctions, bids, options, and managing underlying tokens.
 */
interface IEscrow {
    /// @notice Emitted when on-chain voting rights are delegated.
    /// @param delegate The address delegated for on-chain voting.
    event OnChainVotingDelegation(address delegate);

    /// @notice Emitted when off-chain voting rights are delegated.
    /// @param allowedDelegateRegistry Address of the allowed delegate registry.
    /// @param spaceId ID of the delegation space (e.g., for Snapshot).
    /// @param delegate The address delegated for off-chain voting.
    event OffChainVotingDelegation(
        address allowedDelegateRegistry,
        bytes32 spaceId,
        address delegate
    );

    /// @notice Emitted when tokens are withdrawn from the escrow.
    /// @param sender The address initiating the withdrawal.
    /// @param to The address receiving the withdrawn funds.
    /// @param token The address of the token being withdrawn.
    /// @param amount The amount of tokens withdrawn.
    event Withdraw(
        address indexed sender,
        address indexed to,
        address indexed token,
        uint256 amount
    );

    /// @notice Emitted when escrow ownership is transferred to a new owner.
    /// @param sender The address initiating the ownership transfer.
    /// @param oldOwner The address of the previous owner.
    /// @param newOwner The address of the new owner.
    event TransferOwnership(
        address indexed sender,
        address oldOwner,
        address newOwner
    );

    /// @notice Emitted when a redeem action is performed.
    /// @param sender The address initiating the redemption.
    /// @param to The address receiving the redeemed underlying tokens.
    /// @param underlyingToken The address of the underlying token being redeemed.
    /// @param amount The amount of underlying tokens redeemed.
    event Redeem(
        address indexed sender,
        address to,
        address underlyingToken,
        uint256 amount
    );

    /// @notice Returns the premium paid for the option.
    /// @return premiumPaid Amount of premium paid for the option.
    function premiumPaid() external view returns (uint128);

    /// @notice Returns the total amount borrowed in the underlying token.
    /// @return totalBorrowed Total borrowed amount.
    function totalBorrowed() external view returns (uint128);

    /// @notice Returns the address of the router linked to this Escrow.
    /// @return router The address of the router.
    function router() external view returns (address);

    /// @notice Returns the exercise fee set in the Escrow.
    /// @return exerciseFee The exercise fee as a percentage.
    function exerciseFee() external view returns (uint96);

    /// @notice Returns the address of the owner of the Escrow.
    /// @return owner The address of the current owner.
    function owner() external view returns (address);

    /// @notice Indicates if the escrow is set up for an auction.
    /// @return isAuction Boolean indicating auction status.
    function isAuction() external view returns (bool);

    /// @notice Indicates if the option has been minted.
    /// @return optionMinted Boolean indicating if the option is minted.
    function optionMinted() external view returns (bool);

    /// @notice Returns the distribution partner of an auction.
    /// @return Returns the distribution partner of an auction.
    function distPartner() external view returns (address);

    /// @notice Initializes the Escrow contract for an auction.
    /// @param _router The router address.
    /// @param _owner The address of the auction owner.
    /// @param _exerciseFee The exercise fee to be applied in case of exercise.
    /// @param _auctionInitialization Struct containing auction parameters.
    /// @param oTokenIndex Index for identifying the option token.
    /// @param distPartner The distribution partner address.
    function initializeAuction(
        address _router,
        address _owner,
        uint96 _exerciseFee,
        DataTypes.AuctionInitialization calldata _auctionInitialization,
        uint256 oTokenIndex,
        address distPartner
    ) external;

    /// @notice Initializes the Escrow for a matched RFQ.
    /// @param _router The router address.
    /// @param _owner The address of the escrow owner.
    /// @param optionReceiver Address receiving the option tokens.
    /// @param _exerciseFee The exercise fee to be applied in case of exercise.
    /// @param _rfqInitialization Struct containing RFQ parameters.
    /// @param oTokenIndex Index for identifying the option token.
    function initializeRFQMatch(
        address _router,
        address _owner,
        address optionReceiver,
        uint96 _exerciseFee,
        DataTypes.RFQInitialization calldata _rfqInitialization,
        uint256 oTokenIndex
    ) external;

    /// @notice Initializes the Escrow when minting a standalone option.
    /// @param _router The router address.
    /// @param _owner The address of the option owner.
    /// @param optionReceiver Address receiving the minted option tokens.
    /// @param _exerciseFee The exercise fee to be applied in case of exercise.
    /// @param _optionInfo Struct containing option information.
    /// @param _optionNaming Struct containing the name and symbol of the option token.
    function initializeMintOption(
        address _router,
        address _owner,
        address optionReceiver,
        uint96 _exerciseFee,
        DataTypes.OptionInfo calldata _optionInfo,
        DataTypes.OptionNaming calldata _optionNaming
    ) external;

    /// @notice Handles bidding on an auction.
    /// @param relBid Relative bid as percentage of notional.
    /// @param optionReceiver Address receiving the option tokens.
    /// @param _refSpot Reference spot price.
    /// @param _oracleData Additional optional oracle data.
    /// @return preview Returns a BidPreview struct with the bid's outcome.
    /// @return distPartner Returns the address of the distribution partner.
    function handleAuctionBid(
        uint256 relBid,
        address optionReceiver,
        uint256 _refSpot,
        bytes[] memory _oracleData
    )
        external
        returns (DataTypes.BidPreview memory preview, address distPartner);

    /// @notice Executes option exercise.
    /// @param exerciser The address exercising the option.
    /// @param underlyingReceiver Address receiving the underlying tokens.
    /// @param underlyingExerciseAmount Amount of underlying tokens to exercise.
    /// @param payInSettlementToken True if settlement is in the settlement token.
    /// @param oracleData Additional optional oracle data.
    /// @return settlementToken Address of the settlement token.
    /// @return settlementAmount Amount of settlement tokens paid.
    /// @return exerciseFeeAmount The fee applied for exercising.
    function handleExercise(
        address exerciser,
        address underlyingReceiver,
        uint256 underlyingExerciseAmount,
        bool payInSettlementToken,
        bytes[] memory oracleData
    )
        external
        returns (
            address settlementToken,
            uint256 settlementAmount,
            uint256 exerciseFeeAmount
        );

    /// @notice Handles borrowing of underlying tokens.
    /// @param borrower The address borrowing the tokens.
    /// @param underlyingReceiver Address receiving the borrowed tokens.
    /// @param underlyingBorrowAmount Amount of underlying tokens to borrow.
    /// @return settlementToken Address of the settlement token.
    /// @return collateralAmount Amount of collateral required.
    /// @return collateralFeeAmount The collateral fee applied for borrowing.
    function handleBorrow(
        address borrower,
        address underlyingReceiver,
        uint128 underlyingBorrowAmount
    )
        external
        returns (
            address settlementToken,
            uint256 collateralAmount,
            uint256 collateralFeeAmount
        );

    /// @notice Handles repayment of borrowed underlying tokens.
    /// @param borrower The address repaying the loan.
    /// @param collateralReceiver Address receiving the unlocked collateral.
    /// @param underlyingRepayAmount Amount of underlying tokens to repay.
    /// @return underlyingToken Address of the underlying token.
    /// @return unlockedCollateralAmount Amount of collateral unlocked upon repayment.
    function handleRepay(
        address borrower,
        address collateralReceiver,
        uint128 underlyingRepayAmount
    )
        external
        returns (address underlyingToken, uint256 unlockedCollateralAmount);

    /// @notice Delegates voting rights on-chain to a specified delegate.
    /// @param delegate The address to delegate voting rights to.
    function handleOnChainVoting(address delegate) external;

    /// @notice Delegates voting rights off-chain to a specified delegate.
    /// @param spaceId ID of the delegation space (e.g., for Snapshot).
    /// @param delegate The address to delegate voting rights to.
    function handleOffChainVoting(bytes32 spaceId, address delegate) external;

    /// @notice Withdraws a specified amount of tokens to a given address.
    /// @param to Address receiving the withdrawn tokens.
    /// @param token Address of the token to withdraw.
    /// @param amount Amount of tokens to withdraw.
    function handleWithdraw(address to, address token, uint256 amount) external;

    /// @notice Allows the escrow owner to redeem option tokens for underlying.
    /// @param to The address that will receive the underlying tokens upon redemption.
    function redeem(address to) external;

    /// @notice Transfers ownership of the Escrow to a new owner.
    /// @param newOwner Address of the new owner.
    function transferOwnership(address newOwner) external;

    /// @notice Previews the result of a bid on the auction.
    /// @param relBid Relative bid in percentage of notional.
    /// @param _refSpot Reference spot price.
    /// @param _oracleData Additional optional oracle data.
    /// @return preview Returns a BidPreview struct with the bid's outcome.
    /// @return __distPartner Returns the address of the distribution partner.
    function previewBid(
        uint256 relBid,
        uint256 _refSpot,
        bytes[] memory _oracleData
    )
        external
        view
        returns (DataTypes.BidPreview memory preview, address __distPartner);

    /// @notice Returns the current ask of the auction in percentage of notional.
    /// @return Current ask percentage.
    function currAsk() external view returns (uint64);
}

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

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

/// @title IFeeHandler
/// @dev Interface for the FeeHandler contract.
/// Provides functionality for managing and distributing fees, and setting fee configurations.

interface IFeeHandler {
    /// @notice Emitted when fees are provisioned.
    /// @param token The address of the token in which fees are provisioned.
    /// @param amount The amount of the token provisioned as fees.
    event ProvisionFees(address indexed token, uint256 amount);

    /// @notice Emitted when tokens are withdrawn from the FeeHandler.
    /// @param to The address receiving the withdrawn tokens.
    /// @param token The address of the token being withdrawn.
    /// @param amount The amount of tokens withdrawn.
    event Withdraw(address indexed to, address indexed token, uint256 amount);

    /// @notice Emitted when match fee is set.
    /// @param matchFee The match fee set as a percentage.
    event SetMatchFee(uint256 matchFee);

    /// @notice Emitted when token pair-specific match fees are set or removed.
    /// @param underlyingTokens The underlying tokens per pair.
    /// @param settlementTokens The settlement tokens per pair.
    /// @param matchFeesPerPair The match fee structs per pair.
    event SetMatchFeesPerPair(
        address[] underlyingTokens,
        address[] settlementTokens,
        DataTypes.MatchFeePerPair[] matchFeesPerPair
    );

    /// @notice Emitted when mint fee is set.
    /// @param mintFee The mint fee set as a percentage.
    event SetMintFee(uint256 mintFee);

    /// @notice Emitted when the exercise fee is set.
    /// @param exerciseFee The exercise fee set as a percentage.
    event SetExerciseFee(uint96 exerciseFee);

    /// @notice Emitted when distribution partners are set.
    /// @param accounts The addresses of the distribution partners.
    /// @param feeShares The fee shares for given distribution partners.
    event SetDistPartnerFeeShares(address[] accounts, uint256[] feeShares);

    /// @notice Provisions fees in a specified token.
    /// @param token The address of the token in which fees are provisioned.
    /// @param amount The amount of the token provisioned as fees.
    function provisionFees(address token, uint256 amount) external;

    /// @notice Withdraws a specified amount of tokens to a given address.
    /// @param to The address receiving the withdrawn tokens.
    /// @param token The address of the token to withdraw.
    /// @param amount The amount of tokens to withdraw.
    function withdraw(address to, address token, uint256 amount) external;

    /// @notice Returns the match fee and distribution partner fee share for a given option match.
    /// @param distPartner The address of the distribution partner.
    /// @param optionPremium The given option premium.
    /// @param optionInfo The given option info.
    /// @return _matchFee The applicable match fee for the given option.
    /// @return _matchFeeDistPartnerShare The distribution partner's share of the match fee.
    function getMatchFeeInfo(
        address distPartner,
        uint128 optionPremium,
        DataTypes.OptionInfo calldata optionInfo
    )
        external
        view
        returns (uint256 _matchFee, uint256 _matchFeeDistPartnerShare);

    /// @notice Returns the mint fee and fee share for given distribution partner.
    /// @param distPartner The address of the distribution partner.
    /// @return _mintFee The mint fee as a percentage.
    /// @return _mintFeeDistPartnerShare The share of the mint fee for the distribution partner.
    function getMintFeeInfo(
        address distPartner
    )
        external
        view
        returns (uint256 _mintFee, uint256 _mintFeeDistPartnerShare);

    /// @notice Sets distribution partners and their status.
    /// @param accounts The addresses of the distribution partners.
    /// @param feeShares The fee shares for given distribution partners.
    function setDistPartnerFeeShares(
        address[] calldata accounts,
        uint256[] calldata feeShares
    ) external;

    /// @notice Sets the match fee and distribution partner share.
    /// @param _matchFee The match fee as a percentage.
    function setMatchFee(uint96 _matchFee) external;

    /// @notice Sets or removes a token pair-specific match fee.
    /// @param underlyingTokens The underlying tokens per pair.
    /// @param settlementTokens The settlement tokens per pair.
    /// @param _matchFeePerPair The match fee structs per pair.
    function setMatchFeesPerPair(
        address[] calldata underlyingTokens,
        address[] calldata settlementTokens,
        DataTypes.MatchFeePerPair[] calldata _matchFeePerPair
    ) external;

    /// @notice Sets the exercise fee.
    /// @param _exerciseFee The exercise fee as a percentage.
    function setExerciseFee(uint96 _exerciseFee) external;

    /// @notice Returns the match fee set in the FeeHandler.
    /// @return The match fee as a percentage.
    function matchFee() external view returns (uint96);

    /// @notice Returns the distribution fee share for a given account.
    /// @param account The account to query the distribution fee share for.
    /// @return The fee share for the given distribution partner.
    function distPartnerFeeShare(
        address account
    ) external view returns (uint256);

    /// @notice Returns the exercise fee set in the FeeHandler.
    /// @return The exercise fee as a percentage.
    function exerciseFee() external view returns (uint96);
}

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

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

/// @title IRouter
/// @notice Interface for the Router contract handling auction creation, bidding, exercising, and other related functionalities
interface IRouter {
    /// @notice Emitted when a new auction is created
    /// @param escrowOwner The address of the escrow owner
    /// @param escrow The address of the created escrow contract
    /// @param auctionInitialization The initialization data for the auction
    /// @param exerciseFee The applicable exercise fee
    /// @param distPartner The distribution partner's address
    event CreateAuction(
        address indexed escrowOwner,
        address indexed escrow,
        DataTypes.AuctionInitialization auctionInitialization,
        uint96 exerciseFee,
        address distPartner
    );

    /// @notice Emitted when a withdrawal from escrow occurs and a new auction is created
    /// @param escrowOwner The address of the escrow owner
    /// @param oldEscrow The address of the old escrow
    /// @param newEscrow The address of the new escrow
    /// @param auctionInitialization The initialization data for the auction
    event WithdrawFromEscrowAndCreateAuction(
        address indexed escrowOwner,
        address indexed oldEscrow,
        address indexed newEscrow,
        DataTypes.AuctionInitialization auctionInitialization
    );

    /// @notice Emitted when a withdrawal is made from an escrow
    /// @param sender The address initiating the withdrawal
    /// @param escrow The escrow address from which withdrawal occurs
    /// @param to The address receiving the withdrawn tokens
    /// @param token The token address being withdrawn
    /// @param amount The amount of tokens withdrawn
    event Withdraw(
        address indexed sender,
        address indexed escrow,
        address to,
        address indexed token,
        uint256 amount
    );

    /// @notice Emitted when a bid is placed on an auction
    /// @param sender The address initiating the bid
    /// @param escrow The address of the escrow on which the bid is placed
    /// @param optionReceiver The address receiving the option
    /// @param bidPreview The bid preview
    /// @param distPartner The distribution partner's address
    event BidOnAuction(
        address indexed sender,
        address indexed escrow,
        address optionReceiver,
        DataTypes.BidPreview bidPreview,
        address indexed distPartner
    );

    /// @notice Emitted when an exercise occurs on an option
    /// @param sender The address initiating the exercise
    /// @param escrow The address of the escrow smart contract = option token
    /// @param underlyingReceiver The address receiving the underlying asset
    /// @param underlyingAmount The amount of underlying tokens being exercised
    /// @param exerciseFeeAmount The fee amount for exercising
    event Exercise(
        address indexed sender,
        address indexed escrow,
        address underlyingReceiver,
        uint256 underlyingAmount,
        uint256 exerciseFeeAmount
    );

    /// @notice Emitted when a loan is taken from an escrow
    /// @param sender The address initiating the borrow
    /// @param escrow The address of the escrow providing the loan
    /// @param underlyingReceiver The address receiving the loaned underlying asset
    /// @param underlyingAmount The amount of the loan
    /// @param collateralAmount The amount of collateral provided
    /// @param collateralFeeAmount The fee amount that is deducted from the collateral
    event Borrow(
        address indexed sender,
        address indexed escrow,
        address underlyingReceiver,
        uint128 underlyingAmount,
        uint256 collateralAmount,
        uint256 collateralFeeAmount
    );

    /// @notice Emitted when a repayment is made to an escrow
    /// @param sender The address initiating the repay
    /// @param escrow The address of the escrow receiving the repayment
    /// @param collateralReceiver The address receiving the collateral
    /// @param repayUnderlyingAmount The amount of the underlying asset being repaid
    /// @param unlockedCollateralAmount The amount of collateral being unlocked
    event Repay(
        address indexed sender,
        address indexed escrow,
        address collateralReceiver,
        uint128 repayUnderlyingAmount,
        uint256 unlockedCollateralAmount
    );

    /// @notice Emitted when a quote is taken
    /// @param sender The address initiating the take quote
    /// @param escrowOwner The owner of the escrow
    /// @param escrow The escrow address
    /// @param rfqInitialization The initialization data for the RFQ
    /// @param takeQuotePreview The take quote preview
    /// @param exerciseFee The applicable exercise fee
    /// @param distPartner The distribution partner's address
    event TakeQuote(
        address indexed sender,
        address escrowOwner,
        address indexed escrow,
        DataTypes.RFQInitialization rfqInitialization,
        DataTypes.TakeQuotePreview takeQuotePreview,
        uint96 exerciseFee,
        address indexed distPartner
    );

    /// @notice Emitted when a swap quote is taken
    /// @param sender The address initiating the take swap quote
    /// @param to The address receiving the swapped tokens
    /// @param maker The maker's address providing the swap
    /// @param swapQuote The details of the swap quote
    event TakeSwapQuote(
        address indexed sender,
        address indexed to,
        address indexed maker,
        DataTypes.SwapQuote swapQuote
    );

    /// @notice Emitted when an option is minted
    /// @param sender The address initiating the mint option
    /// @param optionReceiver The address receiving the minted option
    /// @param escrowOwner The owner of the escrow minting the option
    /// @param optionInfo The details of the option being minted
    /// @param mintFeeProtocol The mint fee amount for the protocol
    /// @param mintFeeDistPartner The mint fee amount for the distribution partner
    /// @param distPartner The distribution partner
    event MintOption(
        address indexed sender,
        address indexed optionReceiver,
        address escrowOwner,
        DataTypes.OptionInfo optionInfo,
        uint256 mintFeeProtocol,
        uint256 mintFeeDistPartner,
        address indexed distPartner
    );

    /// @notice Emitted when a new fee handler is set
    /// @param oldFeeHandler The previous fee handler address
    /// @param newFeeHandler The new fee handler address
    event NewFeeHandler(address oldFeeHandler, address newFeeHandler);

    /// @notice Emitted when quote pause status changes
    /// @param quoter The address whose pause status changed
    /// @param isPaused The new pause status
    event PauseQuotes(address indexed quoter, bool isPaused);

    /// @notice Emitted when tokens are transferred between addresses.
    /// @param token The address of the token contract.
    /// @param from The address transferring the tokens.
    /// @param to The address receiving the tokens.
    /// @param value The amount of tokens transferred.
    event Transfer(
        address indexed token,
        address indexed from,
        address indexed to,
        uint256 value
    );

    /// @notice Emitted when ownership of escrow is transferred
    /// @param escrow The address of the escrow contract.
    /// @param oldOwner The address of the old owner.
    /// @param newOwner The address of the new owner.
    event TransferOwnership(
        address indexed escrow,
        address indexed oldOwner,
        address indexed newOwner
    );

    /// @notice Returns the address of the escrow implementation contract.
    /// @return escrowImpl The address of the escrow implementation contract.
    function escrowImpl() external view returns (address);

    /// @notice Returns the address of the fee handler contract.
    /// @return feeHandler The address of the fee handler.
    function feeHandler() external view returns (address);

    /// @notice Returns the total number of escrows created.
    /// @return numEscrows The total number of escrows.
    function numEscrows() external view returns (uint256);

    /// @notice Checks if a specific address is registered as an escrow.
    /// @param escrow The address to check.
    /// @return True if the address is an escrow, false otherwise.
    function isEscrow(address escrow) external view returns (bool);

    /// @notice Checks if a specific quote has been used.
    /// @param quoteHash The hash of the quote.
    /// @return True if the quote has been used, false otherwise.
    function isQuoteUsed(bytes32 quoteHash) external view returns (bool);

    /// @notice Checks if a specific swap quote has been used.
    /// @param swapQuoteHash The hash of the swap quote.
    /// @return True if the swap quote has been used, false otherwise.
    function isSwapQuoteUsed(
        bytes32 swapQuoteHash
    ) external view returns (bool);

    /// @notice Checks if quotes are paused for a specific address.
    /// @param quoter The address to check.
    /// @return True if quotes are paused for the address, false otherwise.
    function quotesPaused(address quoter) external view returns (bool);

    /// @notice Creates a new Dutch auction
    /// @param escrowOwner The address of the escrow owner
    /// @param auctionInitialization The initialization data for the auction
    /// @param distPartner The address of the distribution partner
    function createAuction(
        address escrowOwner,
        DataTypes.AuctionInitialization calldata auctionInitialization,
        address distPartner
    ) external;

    /// @notice Withdraws from an existing escrow and creates a new auction
    /// @param oldEscrow The address of the old escrow
    /// @param escrowOwner The address of the escrow owner
    /// @param auctionInitialization The initialization data for the auction
    /// @param distPartner The address of the distribution partner
    function withdrawFromEscrowAndCreateAuction(
        address oldEscrow,
        address escrowOwner,
        DataTypes.AuctionInitialization calldata auctionInitialization,
        address distPartner
    ) external;

    /// @notice Withdraws tokens from a specified escrow
    /// @param escrow The escrow address from which to withdraw
    /// @param to The address to receive the withdrawn tokens
    /// @param token The token to be withdrawn
    /// @param amount The amount of tokens to withdraw
    function withdraw(
        address escrow,
        address to,
        address token,
        uint256 amount
    ) external;

    /// @notice Places a bid on an auction
    /// @param escrow The escrow address for the auction
    /// @param optionReceiver The address to receive the option
    /// @param relBid The relative bid in percentage of notional
    /// @param refSpot The reference spot price
    /// @param oracleData Additional optional oracle data for validation
    /// @return preview The bid preview data
    /// @return distPartner The distribution partner
    function bidOnAuction(
        address escrow,
        address optionReceiver,
        uint256 relBid,
        uint256 refSpot,
        bytes[] memory oracleData
    )
        external
        returns (DataTypes.BidPreview memory preview, address distPartner);

    /// @notice Exercises an option in an escrow
    /// @param escrow The escrow address holding the option
    /// @param underlyingReceiver The address receiving the underlying asset
    /// @param underlyingAmount The amount of the underlying asset exercised
    /// @param payInSettlementToken Whether payment is in the settlement token
    /// @param oracleData Additional optional oracle data for validation
    function exercise(
        address escrow,
        address underlyingReceiver,
        uint256 underlyingAmount,
        bool payInSettlementToken,
        bytes[] memory oracleData
    ) external;

    /// @notice Borrows underlying tokens from an escrow
    /// @param escrow The escrow address providing the loan
    /// @param underlyingReceiver The address to receive the loaned tokens
    /// @param borrowUnderlyingAmount The amount of the loan
    function borrow(
        address escrow,
        address underlyingReceiver,
        uint128 borrowUnderlyingAmount
    ) external;

    /// @notice Repays a loan to an escrow
    /// @param escrow The escrow address receiving the repayment
    /// @param collateralReceiver The address to receive collateral
    /// @param repayUnderlyingAmount The amount of the underlying asset repaid
    function repay(
        address escrow,
        address collateralReceiver,
        uint128 repayUnderlyingAmount
    ) external;

    /// @notice Takes an RFQ quote
    /// @param escrowOwner The address of the escrow owner
    /// @param rfqInitialization The initialization data for the RFQ
    /// @param distPartner The distribution partner's address
    function takeQuote(
        address escrowOwner,
        DataTypes.RFQInitialization calldata rfqInitialization,
        address distPartner
    ) external;

    /// @notice Takes a swap quote
    /// @param to The address to receive the swapped tokens
    /// @param swapQuote The swap quote details
    function takeSwapQuote(
        address to,
        DataTypes.SwapQuote calldata swapQuote
    ) external;

    /// @notice Toggles the pause status of quotes for the sender
    function togglePauseQuotes() external;

    /// @notice Mints a new option
    /// @param optionReceiver The address to receive the minted option
    /// @param escrowOwner The owner of the escrow minting the option
    /// @param optionInfo The details of the option being minted
    /// @param optionNaming The name and symbol of the option being minted
    /// @param distPartner The distribution partner's address
    function mintOption(
        address optionReceiver,
        address escrowOwner,
        DataTypes.OptionInfo calldata optionInfo,
        DataTypes.OptionNaming calldata optionNaming,
        address distPartner
    ) external;

    /// @notice Sets a new fee handler address
    /// @param newFeeHandler The new fee handler address
    function setFeeHandler(address newFeeHandler) external;

    /// @notice Retrieves the current exercise fee
    /// @return exerciseFee The current exercise fee as a `uint96`
    function getExerciseFee() external view returns (uint96 exerciseFee);

    /// @notice Returns the match fee and distribution partner fee share for a given option match.
    /// @param distPartner The address of the distribution partner.
    /// @param optionPremium The given option premium.
    /// @param optionInfo The given option info.
    /// @return matchFeeProtocol The protocol's match fee
    /// @return matchFeeDistPartner The distribution partner's match fee
    function getMatchFees(
        address distPartner,
        uint128 optionPremium,
        DataTypes.OptionInfo calldata optionInfo
    )
        external
        view
        returns (uint128 matchFeeProtocol, uint128 matchFeeDistPartner);

    /// @notice Calculates mint fees for a given distribution partner and notional
    /// @param distPartner The distribution partner's address
    /// @param notional The notional of the option in underlying token units
    /// @return mintFeeProtocol The protocol's mint fee in option tokens
    /// @return mintFeeDistPartner The distribution partner's mint fee in option tokens
    function getMintFees(
        address distPartner,
        uint128 notional
    )
        external
        view
        returns (uint256 mintFeeProtocol, uint256 mintFeeDistPartner);

    /// @notice Previews the result of taking a quote
    /// @param rfqInitialization The initialization data for the RFQ
    /// @param distPartner The distribution partner's address
    /// @return A preview of the quote as a `TakeQuotePreview` struct
    function previewTakeQuote(
        DataTypes.RFQInitialization calldata rfqInitialization,
        address distPartner
    ) external view returns (DataTypes.TakeQuotePreview memory);

    /// @notice Retrieves a range of escrows
    /// @param from The starting index of escrows to retrieve
    /// @param numElements The number of escrows to retrieve
    /// @return _escrows An array of escrow addresses
    function getEscrows(
        uint256 from,
        uint256 numElements
    ) external view returns (address[] memory _escrows);

    /// @notice Emits a `Transfer` event for a token.
    /// @dev Callable only by registered escrows. Reverts if not an escrow.
    /// @param from Address sending the tokens.
    /// @param to Address receiving the tokens.
    /// @param value Amount of tokens transferred.
    function emitTransferEvent(
        address from,
        address to,
        uint256 value
    ) external;

    /// @notice Emits a `TransferOwnership` event for an escrow contract.
    /// @dev Callable only by registered escrows. Reverts if not an escrow.
    /// @param oldOwner Address of the old owner.
    /// @param newOwner Address of the new owner.
    function emitTransferOwnershipEvent(
        address oldOwner,
        address newOwner
    ) external;
}

Settings
{
  "optimizer": {
    "enabled": true,
    "runs": 1000
  },
  "evmVersion": "paris",
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "libraries": {}
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"address","name":"initOwner","type":"address"},{"internalType":"address","name":"_escrowImpl","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"AddressEmptyCode","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"AddressInsufficientBalance","type":"error"},{"inputs":[],"name":"ECDSAInvalidSignature","type":"error"},{"inputs":[{"internalType":"uint256","name":"length","type":"uint256"}],"name":"ECDSAInvalidSignatureLength","type":"error"},{"inputs":[{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"ECDSAInvalidSignatureS","type":"error"},{"inputs":[],"name":"ERC1167FailedCreateClone","type":"error"},{"inputs":[],"name":"FailedInnerCall","type":"error"},{"inputs":[],"name":"FeeHandlerAlreadySet","type":"error"},{"inputs":[],"name":"InvalidAddress","type":"error"},{"inputs":[],"name":"InvalidBorrowCap","type":"error"},{"inputs":[],"name":"InvalidEIP1271Signature","type":"error"},{"inputs":[],"name":"InvalidEarliestExercise","type":"error"},{"inputs":[],"name":"InvalidExpiry","type":"error"},{"inputs":[],"name":"InvalidGetEscrowsQuery","type":"error"},{"inputs":[],"name":"InvalidNotional","type":"error"},{"inputs":[],"name":"InvalidSender","type":"error"},{"inputs":[],"name":"InvalidTakeQuote","type":"error"},{"inputs":[],"name":"InvalidTokenPair","type":"error"},{"inputs":[],"name":"NotAnEscrow","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"inputs":[{"internalType":"uint8","name":"bits","type":"uint8"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"SafeCastOverflowedUintDowncast","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"inputs":[],"name":"SwapQuoteAlreadyUsed","type":"error"},{"inputs":[],"name":"SwapQuoteExpired","type":"error"},{"inputs":[],"name":"SwapQuotePaused","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"escrow","type":"address"},{"indexed":false,"internalType":"address","name":"optionReceiver","type":"address"},{"components":[{"internalType":"enum DataTypes.BidStatus","name":"status","type":"uint8"},{"internalType":"address","name":"settlementToken","type":"address"},{"internalType":"address","name":"underlyingToken","type":"address"},{"internalType":"uint128","name":"strike","type":"uint128"},{"internalType":"uint48","name":"expiry","type":"uint48"},{"internalType":"uint48","name":"earliestExercise","type":"uint48"},{"internalType":"uint128","name":"premium","type":"uint128"},{"internalType":"address","name":"premiumToken","type":"address"},{"internalType":"uint256","name":"oracleSpotPrice","type":"uint256"},{"internalType":"uint64","name":"currAsk","type":"uint64"},{"internalType":"uint128","name":"matchFeeProtocol","type":"uint128"},{"internalType":"uint128","name":"matchFeeDistPartner","type":"uint128"}],"indexed":false,"internalType":"struct DataTypes.BidPreview","name":"bidPreview","type":"tuple"},{"indexed":true,"internalType":"address","name":"distPartner","type":"address"}],"name":"BidOnAuction","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"escrow","type":"address"},{"indexed":false,"internalType":"address","name":"underlyingReceiver","type":"address"},{"indexed":false,"internalType":"uint128","name":"underlyingAmount","type":"uint128"},{"indexed":false,"internalType":"uint256","name":"collateralAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"collateralFeeAmount","type":"uint256"}],"name":"Borrow","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"escrowOwner","type":"address"},{"indexed":true,"internalType":"address","name":"escrow","type":"address"},{"components":[{"internalType":"address","name":"underlyingToken","type":"address"},{"internalType":"address","name":"settlementToken","type":"address"},{"internalType":"uint128","name":"notional","type":"uint128"},{"components":[{"internalType":"uint128","name":"relStrike","type":"uint128"},{"internalType":"uint48","name":"tenor","type":"uint48"},{"internalType":"uint48","name":"earliestExerciseTenor","type":"uint48"},{"internalType":"uint32","name":"decayStartTime","type":"uint32"},{"internalType":"uint32","name":"decayDuration","type":"uint32"},{"internalType":"uint64","name":"relPremiumStart","type":"uint64"},{"internalType":"uint64","name":"relPremiumFloor","type":"uint64"},{"internalType":"uint128","name":"minSpot","type":"uint128"},{"internalType":"uint128","name":"maxSpot","type":"uint128"}],"internalType":"struct DataTypes.AuctionParams","name":"auctionParams","type":"tuple"},{"components":[{"internalType":"uint64","name":"borrowCap","type":"uint64"},{"internalType":"address","name":"oracle","type":"address"},{"internalType":"bool","name":"premiumTokenIsUnderlying","type":"bool"},{"internalType":"bool","name":"votingDelegationAllowed","type":"bool"},{"internalType":"address","name":"allowedDelegateRegistry","type":"address"}],"internalType":"struct DataTypes.AdvancedSettings","name":"advancedSettings","type":"tuple"}],"indexed":false,"internalType":"struct DataTypes.AuctionInitialization","name":"auctionInitialization","type":"tuple"},{"indexed":false,"internalType":"uint96","name":"exerciseFee","type":"uint96"},{"indexed":false,"internalType":"address","name":"distPartner","type":"address"}],"name":"CreateAuction","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"escrow","type":"address"},{"indexed":false,"internalType":"address","name":"underlyingReceiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"underlyingAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"exerciseFeeAmount","type":"uint256"}],"name":"Exercise","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"optionReceiver","type":"address"},{"indexed":false,"internalType":"address","name":"escrowOwner","type":"address"},{"components":[{"internalType":"address","name":"underlyingToken","type":"address"},{"internalType":"uint48","name":"expiry","type":"uint48"},{"internalType":"address","name":"settlementToken","type":"address"},{"internalType":"uint48","name":"earliestExercise","type":"uint48"},{"internalType":"uint128","name":"notional","type":"uint128"},{"internalType":"uint128","name":"strike","type":"uint128"},{"components":[{"internalType":"uint64","name":"borrowCap","type":"uint64"},{"internalType":"address","name":"oracle","type":"address"},{"internalType":"bool","name":"premiumTokenIsUnderlying","type":"bool"},{"internalType":"bool","name":"votingDelegationAllowed","type":"bool"},{"internalType":"address","name":"allowedDelegateRegistry","type":"address"}],"internalType":"struct DataTypes.AdvancedSettings","name":"advancedSettings","type":"tuple"}],"indexed":false,"internalType":"struct DataTypes.OptionInfo","name":"optionInfo","type":"tuple"},{"indexed":false,"internalType":"uint256","name":"mintFeeProtocol","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"mintFeeDistPartner","type":"uint256"},{"indexed":true,"internalType":"address","name":"distPartner","type":"address"}],"name":"MintOption","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldFeeHandler","type":"address"},{"indexed":false,"internalType":"address","name":"newFeeHandler","type":"address"}],"name":"NewFeeHandler","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"quoter","type":"address"},{"indexed":false,"internalType":"bool","name":"isPaused","type":"bool"}],"name":"PauseQuotes","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"escrow","type":"address"},{"indexed":false,"internalType":"address","name":"collateralReceiver","type":"address"},{"indexed":false,"internalType":"uint128","name":"repayUnderlyingAmount","type":"uint128"},{"indexed":false,"internalType":"uint256","name":"unlockedCollateralAmount","type":"uint256"}],"name":"Repay","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"address","name":"escrowOwner","type":"address"},{"indexed":true,"internalType":"address","name":"escrow","type":"address"},{"components":[{"components":[{"internalType":"address","name":"underlyingToken","type":"address"},{"internalType":"uint48","name":"expiry","type":"uint48"},{"internalType":"address","name":"settlementToken","type":"address"},{"internalType":"uint48","name":"earliestExercise","type":"uint48"},{"internalType":"uint128","name":"notional","type":"uint128"},{"internalType":"uint128","name":"strike","type":"uint128"},{"components":[{"internalType":"uint64","name":"borrowCap","type":"uint64"},{"internalType":"address","name":"oracle","type":"address"},{"internalType":"bool","name":"premiumTokenIsUnderlying","type":"bool"},{"internalType":"bool","name":"votingDelegationAllowed","type":"bool"},{"internalType":"address","name":"allowedDelegateRegistry","type":"address"}],"internalType":"struct DataTypes.AdvancedSettings","name":"advancedSettings","type":"tuple"}],"internalType":"struct DataTypes.OptionInfo","name":"optionInfo","type":"tuple"},{"components":[{"internalType":"uint128","name":"premium","type":"uint128"},{"internalType":"uint256","name":"validUntil","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"},{"internalType":"address","name":"eip1271Maker","type":"address"}],"internalType":"struct DataTypes.RFQQuote","name":"rfqQuote","type":"tuple"}],"indexed":false,"internalType":"struct DataTypes.RFQInitialization","name":"rfqInitialization","type":"tuple"},{"components":[{"internalType":"enum DataTypes.RFQStatus","name":"status","type":"uint8"},{"internalType":"bytes32","name":"msgHash","type":"bytes32"},{"internalType":"address","name":"quoter","type":"address"},{"internalType":"uint128","name":"premium","type":"uint128"},{"internalType":"address","name":"premiumToken","type":"address"},{"internalType":"uint128","name":"matchFeeProtocol","type":"uint128"},{"internalType":"uint128","name":"matchFeeDistPartner","type":"uint128"}],"indexed":false,"internalType":"struct DataTypes.TakeQuotePreview","name":"takeQuotePreview","type":"tuple"},{"indexed":false,"internalType":"uint96","name":"exerciseFee","type":"uint96"},{"indexed":true,"internalType":"address","name":"distPartner","type":"address"}],"name":"TakeQuote","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"address","name":"maker","type":"address"},{"components":[{"internalType":"address","name":"takerGiveToken","type":"address"},{"internalType":"uint256","name":"takerGiveAmount","type":"uint256"},{"internalType":"address","name":"makerGiveToken","type":"address"},{"internalType":"uint256","name":"makerGiveAmount","type":"uint256"},{"internalType":"uint256","name":"validUntil","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"},{"internalType":"address","name":"eip1271Maker","type":"address"}],"indexed":false,"internalType":"struct DataTypes.SwapQuote","name":"swapQuote","type":"tuple"}],"name":"TakeSwapQuote","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"escrow","type":"address"},{"indexed":true,"internalType":"address","name":"oldOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"TransferOwnership","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"escrow","type":"address"},{"indexed":false,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Withdraw","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"escrowOwner","type":"address"},{"indexed":true,"internalType":"address","name":"oldEscrow","type":"address"},{"indexed":true,"internalType":"address","name":"newEscrow","type":"address"},{"components":[{"internalType":"address","name":"underlyingToken","type":"address"},{"internalType":"address","name":"settlementToken","type":"address"},{"internalType":"uint128","name":"notional","type":"uint128"},{"components":[{"internalType":"uint128","name":"relStrike","type":"uint128"},{"internalType":"uint48","name":"tenor","type":"uint48"},{"internalType":"uint48","name":"earliestExerciseTenor","type":"uint48"},{"internalType":"uint32","name":"decayStartTime","type":"uint32"},{"internalType":"uint32","name":"decayDuration","type":"uint32"},{"internalType":"uint64","name":"relPremiumStart","type":"uint64"},{"internalType":"uint64","name":"relPremiumFloor","type":"uint64"},{"internalType":"uint128","name":"minSpot","type":"uint128"},{"internalType":"uint128","name":"maxSpot","type":"uint128"}],"internalType":"struct DataTypes.AuctionParams","name":"auctionParams","type":"tuple"},{"components":[{"internalType":"uint64","name":"borrowCap","type":"uint64"},{"internalType":"address","name":"oracle","type":"address"},{"internalType":"bool","name":"premiumTokenIsUnderlying","type":"bool"},{"internalType":"bool","name":"votingDelegationAllowed","type":"bool"},{"internalType":"address","name":"allowedDelegateRegistry","type":"address"}],"internalType":"struct DataTypes.AdvancedSettings","name":"advancedSettings","type":"tuple"}],"indexed":false,"internalType":"struct DataTypes.AuctionInitialization","name":"auctionInitialization","type":"tuple"}],"name":"WithdrawFromEscrowAndCreateAuction","type":"event"},{"inputs":[{"internalType":"address","name":"escrow","type":"address"},{"internalType":"address","name":"optionReceiver","type":"address"},{"internalType":"uint256","name":"relBid","type":"uint256"},{"internalType":"uint256","name":"_refSpot","type":"uint256"},{"internalType":"bytes[]","name":"_oracleData","type":"bytes[]"}],"name":"bidOnAuction","outputs":[{"components":[{"internalType":"enum DataTypes.BidStatus","name":"status","type":"uint8"},{"internalType":"address","name":"settlementToken","type":"address"},{"internalType":"address","name":"underlyingToken","type":"address"},{"internalType":"uint128","name":"strike","type":"uint128"},{"internalType":"uint48","name":"expiry","type":"uint48"},{"internalType":"uint48","name":"earliestExercise","type":"uint48"},{"internalType":"uint128","name":"premium","type":"uint128"},{"internalType":"address","name":"premiumToken","type":"address"},{"internalType":"uint256","name":"oracleSpotPrice","type":"uint256"},{"internalType":"uint64","name":"currAsk","type":"uint64"},{"internalType":"uint128","name":"matchFeeProtocol","type":"uint128"},{"internalType":"uint128","name":"matchFeeDistPartner","type":"uint128"}],"internalType":"struct DataTypes.BidPreview","name":"preview","type":"tuple"},{"internalType":"address","name":"distPartner","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"escrow","type":"address"},{"internalType":"address","name":"underlyingReceiver","type":"address"},{"internalType":"uint128","name":"borrowUnderlyingAmount","type":"uint128"}],"name":"borrow","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"escrowOwner","type":"address"},{"components":[{"internalType":"address","name":"underlyingToken","type":"address"},{"internalType":"address","name":"settlementToken","type":"address"},{"internalType":"uint128","name":"notional","type":"uint128"},{"components":[{"internalType":"uint128","name":"relStrike","type":"uint128"},{"internalType":"uint48","name":"tenor","type":"uint48"},{"internalType":"uint48","name":"earliestExerciseTenor","type":"uint48"},{"internalType":"uint32","name":"decayStartTime","type":"uint32"},{"internalType":"uint32","name":"decayDuration","type":"uint32"},{"internalType":"uint64","name":"relPremiumStart","type":"uint64"},{"internalType":"uint64","name":"relPremiumFloor","type":"uint64"},{"internalType":"uint128","name":"minSpot","type":"uint128"},{"internalType":"uint128","name":"maxSpot","type":"uint128"}],"internalType":"struct DataTypes.AuctionParams","name":"auctionParams","type":"tuple"},{"components":[{"internalType":"uint64","name":"borrowCap","type":"uint64"},{"internalType":"address","name":"oracle","type":"address"},{"internalType":"bool","name":"premiumTokenIsUnderlying","type":"bool"},{"internalType":"bool","name":"votingDelegationAllowed","type":"bool"},{"internalType":"address","name":"allowedDelegateRegistry","type":"address"}],"internalType":"struct DataTypes.AdvancedSettings","name":"advancedSettings","type":"tuple"}],"internalType":"struct DataTypes.AuctionInitialization","name":"auctionInitialization","type":"tuple"},{"internalType":"address","name":"distPartner","type":"address"}],"name":"createAuction","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"emitTransferEvent","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"oldOwner","type":"address"},{"internalType":"address","name":"newOwner","type":"address"}],"name":"emitTransferOwnershipEvent","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"escrowImpl","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"escrow","type":"address"},{"internalType":"address","name":"underlyingReceiver","type":"address"},{"internalType":"uint256","name":"underlyingAmount","type":"uint256"},{"internalType":"bool","name":"payInSettlementToken","type":"bool"},{"internalType":"bytes[]","name":"oracleData","type":"bytes[]"}],"name":"exercise","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"feeHandler","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"from","type":"uint256"},{"internalType":"uint256","name":"numElements","type":"uint256"}],"name":"getEscrows","outputs":[{"internalType":"address[]","name":"_escrowArray","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getExerciseFee","outputs":[{"internalType":"uint96","name":"exerciseFee","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"distPartner","type":"address"},{"internalType":"uint128","name":"optionPremium","type":"uint128"},{"components":[{"internalType":"address","name":"underlyingToken","type":"address"},{"internalType":"uint48","name":"expiry","type":"uint48"},{"internalType":"address","name":"settlementToken","type":"address"},{"internalType":"uint48","name":"earliestExercise","type":"uint48"},{"internalType":"uint128","name":"notional","type":"uint128"},{"internalType":"uint128","name":"strike","type":"uint128"},{"components":[{"internalType":"uint64","name":"borrowCap","type":"uint64"},{"internalType":"address","name":"oracle","type":"address"},{"internalType":"bool","name":"premiumTokenIsUnderlying","type":"bool"},{"internalType":"bool","name":"votingDelegationAllowed","type":"bool"},{"internalType":"address","name":"allowedDelegateRegistry","type":"address"}],"internalType":"struct DataTypes.AdvancedSettings","name":"advancedSettings","type":"tuple"}],"internalType":"struct DataTypes.OptionInfo","name":"optionInfo","type":"tuple"}],"name":"getMatchFees","outputs":[{"internalType":"uint128","name":"matchFeeProtocol","type":"uint128"},{"internalType":"uint128","name":"matchFeeDistPartner","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"distPartner","type":"address"},{"internalType":"uint128","name":"notional","type":"uint128"}],"name":"getMintFees","outputs":[{"internalType":"uint256","name":"mintFeeProtocol","type":"uint256"},{"internalType":"uint256","name":"mintFeeDistPartner","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isEscrow","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"isQuoteUsed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"isSwapQuoteUsed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"optionReceiver","type":"address"},{"internalType":"address","name":"escrowOwner","type":"address"},{"components":[{"internalType":"address","name":"underlyingToken","type":"address"},{"internalType":"uint48","name":"expiry","type":"uint48"},{"internalType":"address","name":"settlementToken","type":"address"},{"internalType":"uint48","name":"earliestExercise","type":"uint48"},{"internalType":"uint128","name":"notional","type":"uint128"},{"internalType":"uint128","name":"strike","type":"uint128"},{"components":[{"internalType":"uint64","name":"borrowCap","type":"uint64"},{"internalType":"address","name":"oracle","type":"address"},{"internalType":"bool","name":"premiumTokenIsUnderlying","type":"bool"},{"internalType":"bool","name":"votingDelegationAllowed","type":"bool"},{"internalType":"address","name":"allowedDelegateRegistry","type":"address"}],"internalType":"struct DataTypes.AdvancedSettings","name":"advancedSettings","type":"tuple"}],"internalType":"struct DataTypes.OptionInfo","name":"optionInfo","type":"tuple"},{"components":[{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"symbol","type":"string"}],"internalType":"struct DataTypes.OptionNaming","name":"optionNaming","type":"tuple"},{"internalType":"address","name":"distPartner","type":"address"}],"name":"mintOption","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"numEscrows","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"components":[{"internalType":"address","name":"underlyingToken","type":"address"},{"internalType":"uint48","name":"expiry","type":"uint48"},{"internalType":"address","name":"settlementToken","type":"address"},{"internalType":"uint48","name":"earliestExercise","type":"uint48"},{"internalType":"uint128","name":"notional","type":"uint128"},{"internalType":"uint128","name":"strike","type":"uint128"},{"components":[{"internalType":"uint64","name":"borrowCap","type":"uint64"},{"internalType":"address","name":"oracle","type":"address"},{"internalType":"bool","name":"premiumTokenIsUnderlying","type":"bool"},{"internalType":"bool","name":"votingDelegationAllowed","type":"bool"},{"internalType":"address","name":"allowedDelegateRegistry","type":"address"}],"internalType":"struct DataTypes.AdvancedSettings","name":"advancedSettings","type":"tuple"}],"internalType":"struct DataTypes.OptionInfo","name":"optionInfo","type":"tuple"},{"components":[{"internalType":"uint128","name":"premium","type":"uint128"},{"internalType":"uint256","name":"validUntil","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"},{"internalType":"address","name":"eip1271Maker","type":"address"}],"internalType":"struct DataTypes.RFQQuote","name":"rfqQuote","type":"tuple"}],"internalType":"struct DataTypes.RFQInitialization","name":"rfqInitialization","type":"tuple"},{"internalType":"address","name":"distPartner","type":"address"}],"name":"previewTakeQuote","outputs":[{"components":[{"internalType":"enum DataTypes.RFQStatus","name":"status","type":"uint8"},{"internalType":"bytes32","name":"msgHash","type":"bytes32"},{"internalType":"address","name":"quoter","type":"address"},{"internalType":"uint128","name":"premium","type":"uint128"},{"internalType":"address","name":"premiumToken","type":"address"},{"internalType":"uint128","name":"matchFeeProtocol","type":"uint128"},{"internalType":"uint128","name":"matchFeeDistPartner","type":"uint128"}],"internalType":"struct DataTypes.TakeQuotePreview","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"quotesPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"escrow","type":"address"},{"internalType":"address","name":"collateralReceiver","type":"address"},{"internalType":"uint128","name":"repayUnderlyingAmount","type":"uint128"}],"name":"repay","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newFeeHandler","type":"address"}],"name":"setFeeHandler","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"escrowOwner","type":"address"},{"components":[{"components":[{"internalType":"address","name":"underlyingToken","type":"address"},{"internalType":"uint48","name":"expiry","type":"uint48"},{"internalType":"address","name":"settlementToken","type":"address"},{"internalType":"uint48","name":"earliestExercise","type":"uint48"},{"internalType":"uint128","name":"notional","type":"uint128"},{"internalType":"uint128","name":"strike","type":"uint128"},{"components":[{"internalType":"uint64","name":"borrowCap","type":"uint64"},{"internalType":"address","name":"oracle","type":"address"},{"internalType":"bool","name":"premiumTokenIsUnderlying","type":"bool"},{"internalType":"bool","name":"votingDelegationAllowed","type":"bool"},{"internalType":"address","name":"allowedDelegateRegistry","type":"address"}],"internalType":"struct DataTypes.AdvancedSettings","name":"advancedSettings","type":"tuple"}],"internalType":"struct DataTypes.OptionInfo","name":"optionInfo","type":"tuple"},{"components":[{"internalType":"uint128","name":"premium","type":"uint128"},{"internalType":"uint256","name":"validUntil","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"},{"internalType":"address","name":"eip1271Maker","type":"address"}],"internalType":"struct DataTypes.RFQQuote","name":"rfqQuote","type":"tuple"}],"internalType":"struct DataTypes.RFQInitialization","name":"rfqInitialization","type":"tuple"},{"internalType":"address","name":"distPartner","type":"address"}],"name":"takeQuote","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"components":[{"internalType":"address","name":"takerGiveToken","type":"address"},{"internalType":"uint256","name":"takerGiveAmount","type":"uint256"},{"internalType":"address","name":"makerGiveToken","type":"address"},{"internalType":"uint256","name":"makerGiveAmount","type":"uint256"},{"internalType":"uint256","name":"validUntil","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"},{"internalType":"address","name":"eip1271Maker","type":"address"}],"internalType":"struct DataTypes.SwapQuote","name":"swapQuote","type":"tuple"}],"name":"takeSwapQuote","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"togglePauseQuotes","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"escrow","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"oldEscrow","type":"address"},{"internalType":"address","name":"escrowOwner","type":"address"},{"components":[{"internalType":"address","name":"underlyingToken","type":"address"},{"internalType":"address","name":"settlementToken","type":"address"},{"internalType":"uint128","name":"notional","type":"uint128"},{"components":[{"internalType":"uint128","name":"relStrike","type":"uint128"},{"internalType":"uint48","name":"tenor","type":"uint48"},{"internalType":"uint48","name":"earliestExerciseTenor","type":"uint48"},{"internalType":"uint32","name":"decayStartTime","type":"uint32"},{"internalType":"uint32","name":"decayDuration","type":"uint32"},{"internalType":"uint64","name":"relPremiumStart","type":"uint64"},{"internalType":"uint64","name":"relPremiumFloor","type":"uint64"},{"internalType":"uint128","name":"minSpot","type":"uint128"},{"internalType":"uint128","name":"maxSpot","type":"uint128"}],"internalType":"struct DataTypes.AuctionParams","name":"auctionParams","type":"tuple"},{"components":[{"internalType":"uint64","name":"borrowCap","type":"uint64"},{"internalType":"address","name":"oracle","type":"address"},{"internalType":"bool","name":"premiumTokenIsUnderlying","type":"bool"},{"internalType":"bool","name":"votingDelegationAllowed","type":"bool"},{"internalType":"address","name":"allowedDelegateRegistry","type":"address"}],"internalType":"struct DataTypes.AdvancedSettings","name":"advancedSettings","type":"tuple"}],"internalType":"struct DataTypes.AuctionInitialization","name":"auctionInitialization","type":"tuple"},{"internalType":"address","name":"distPartner","type":"address"}],"name":"withdrawFromEscrowAndCreateAuction","outputs":[],"stateMutability":"nonpayable","type":"function"}]

60a06040523480156200001157600080fd5b506040516200493c3803806200493c833981016040819052620000349162000118565b816001600160a01b0381166200006457604051631e4fbdf760e01b81526000600482015260240160405180910390fd5b6200006f81620000ab565b506001600160a01b038116620000985760405163e6c4247b60e01b815260040160405180910390fd5b6001600160a01b03166080525062000150565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b80516001600160a01b03811681146200011357600080fd5b919050565b600080604083850312156200012c57600080fd5b6200013783620000fb565b91506200014760208401620000fb565b90509250929050565b6080516147c962000173600039600081816104b70152612b6a01526147c96000f3fe608060405234801561001057600080fd5b50600436106101cf5760003560e01c806377c05dec11610104578063b166a09f116100a2578063ca41363e11610071578063ca41363e14610479578063f2fde38b1461048c578063f9361aef1461049f578063fda25df4146104b257600080fd5b8063b166a09f146103fd578063b35c845e14610420578063b4df001e14610441578063c18abe0d1461046657600080fd5b80638da5cb5b116100de5780638da5cb5b146103a357806393594d7f146103b4578063a597ec1f146103d7578063a6d49dca146103ea57600080fd5b806377c05dec146103455780637d431c7c146103685780638c35dc4d1461037b57600080fd5b806347e5dc20116101715780635920d5771161014b5780635920d577146102f75780635a696cb81461030a578063715018a61461032a578063733140771461033257600080fd5b806347e5dc20146102915780634dcfbba4146102b15780635468fe95146102c457600080fd5b8063369bd2fe116101ad578063369bd2fe146102185780633849153b146102205780633c93adee146102535780633df8f62e1461027e57600080fd5b8063065d4a6d146101d45780630b620b81146101e957806318e2a560146101fc575b600080fd5b6101e76101e2366004613217565b6104d9565b005b6101e76101f736600461326e565b610805565b61020560025481565b6040519081526020015b60405180910390f35b6101e761099d565b61023361022e3660046132fd565b6109f8565b604080516001600160801b0393841681529290911660208301520161020f565b600154610266906001600160a01b031681565b6040516001600160a01b03909116815260200161020f565b6101e761028c366004613359565b610b45565b6102a461029f3660046133bd565b610e5b565b60405161020f91906133df565b6101e76102bf36600461343f565b610f71565b6102e76102d236600461349c565b60056020526000908152604090205460ff1681565b604051901515815260200161020f565b6101e76103053660046134b5565b6112e5565b61031d6103183660046134f6565b611407565b60405161020f91906135e1565b6101e7611826565b6101e76103403660046135ef565b61183a565b6102e76103533660046135ef565b60066020526000908152604090205460ff1681565b6101e761037636600461360c565b6118f7565b61038e61038936600461364c565b611af6565b6040805192835260208301919091520161020f565b6000546001600160a01b0316610266565b6102e76103c236600461349c565b60046020526000908152604090205460ff1681565b6101e76103e536600461360c565b611c42565b6101e76103f836600461367a565b611d9c565b6102e761040b3660046135ef565b60036020526000908152604090205460ff1681565b61043361042e366004613822565b611e1b565b60405161020f9291906139bf565b61044961215d565b6040516bffffffffffffffffffffffff909116815260200161020f565b6101e76104743660046139e5565b61220e565b6101e7610487366004613a21565b61228d565b6101e761049a3660046135ef565b6124c1565b6101e76104ad366004613a87565b61251d565b6102667f000000000000000000000000000000000000000000000000000000000000000081565b8060800135421115610517576040517f5c32099600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60004661052760208401846135ef565b602084013561053c60608601604087016135ef565b6040805160208101959095526001600160a01b03938416908501526060808501929092529091166080808401919091529084013560a083015283013560c082015260e00160408051601f19818403018152919052805160209091012090506000806105ad60e0850160c086016135ef565b6001600160a01b03160361063c577f19457468657265756d205369676e6564204d6573736167653a0a3332000000006000908152601c839052603c9020610635906105fb60a0860186613b18565b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061292192505050565b90506106b1565b600061066461065160e0860160c087016135ef565b8461065f60a0880188613b18565b61294b565b90508061069d576040517f5d52cbe300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6106ad60e0850160c086016135ef565b9150505b6001600160a01b03811660009081526006602052604090205460ff1615610704576040517f6cff8dd500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008281526005602052604090205460ff161561074d576040517f9b1475af00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000828152600560209081526040909120805460ff191660011790556107919033908390860180359061078090886135ef565b6001600160a01b0316929190612a7e565b6107aa81856060860180359061078090604089016135ef565b806001600160a01b0316846001600160a01b0316336001600160a01b03167fc09ea7f7280174a395e9010b2e05ab1888cd36f5ad99b4429478af303ce78c3d866040516107f79190613bd5565b60405180910390a450505050565b6001600160a01b03841660009081526003602052604090205460ff1661083e576040516340d8c4db60e11b815260040160405180910390fd5b836001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561087c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108a09190613c7e565b6001600160a01b0316336001600160a01b0316146108d157604051636edaef2f60e11b815260040160405180910390fd5b6040517fd0e8dcff0000000000000000000000000000000000000000000000000000000081526001600160a01b03848116600483015283811660248301526044820183905285169063d0e8dcff90606401600060405180830381600087803b15801561093c57600080fd5b505af1158015610950573d6000803e3d6000fd5b5050604080516001600160a01b0387811682526020820186905280871694508816925033917fe826ecb5c03d4897f8ab426ee94064e06179dff39cd9fdd0776904cd935c95d891016107f7565b33600081815260066020908152604091829020805460ff8116801560ff199092168217909255925192835292917fdcbed411faf93d1cef89c9e1923cd8e047c29e2d465b976b24b0b22c054b340e910160405180910390a250565b60015460009081906001600160a01b03168015610b3c57600080826001600160a01b031663dd07ab1c8989896040518463ffffffff1660e01b8152600401610a4293929190613e04565b6040805180830381865afa158015610a5e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a829190613e31565b909250905060006702c68af0bb1400008311610a9e5782610aa8565b6702c68af0bb1400005b90506000670de0b6b3a76400008311610ac15782610acb565b670de0b6b3a76400005b90506000670de0b6b3a7640000610aeb846001600160801b038d16613e6b565b610af59190613e82565b9050610b1c670de0b6b3a7640000610b0d8484613e6b565b610b179190613e82565b612b00565b9650610b34610b176001600160801b03891683613ea4565b975050505050505b50935093915050565b6000610b518383611407565b9050600481516005811115610b6857610b6861353d565b14610b9f576040517fe30578ab00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6020808201516000908152600490915260408120805460ff1916600117905580610bc7612b51565b915091506000610bd561215d565b60408086015190517f537952080000000000000000000000000000000000000000000000000000000081529192506001600160a01b03851691635379520891610c2a9130918c9187908d908a90600401613f60565b600060405180830381600087803b158015610c4457600080fd5b505af1158015610c58573d6000803e3d6000fd5b50610c8a9250339150859050610c7460a08a0160808b01613fbb565b6001600160801b031661078060208b018b6135ef565b610cef8460400151338660a001518760c001518a806101600190610cae9190613fd8565b610cbc906020810190613fbb565b610cc69190613ff8565b610cd09190613ff8565b60808801516001600160a01b03169291906001600160801b0316612a7e565b60c08401516001600160801b031615610d3757610d378460400151868660c001516001600160801b031687608001516001600160a01b0316612a7e909392919063ffffffff16565b60a08401516001600160801b031615610df757600154604085015160a086015160808701516001600160a01b0393841693610d7f9391169184906001600160801b0316612a7e565b608085015160a08601516040516358c0528f60e01b81526001600160a01b0392831660048201526001600160801b039091166024820152908216906358c0528f90604401600060405180830381600087803b158015610ddd57600080fd5b505af1158015610df1573d6000803e3d6000fd5b50505050505b846001600160a01b0316836001600160a01b0316336001600160a01b03167f2603b553dc2ab4d1a644bb67a527695755c7c1adc290f4718813e649cc6c71888a8a8987604051610e4a949392919061401f565b60405180910390a450505050505050565b600754606090821580610e76575080610e748486614070565b115b15610ead576040517f6269996b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8267ffffffffffffffff811115610ec657610ec66136bb565b604051908082528060200260200182016040528015610eef578160200160208202803683370190505b50915060005b83811015610f69576007610f098287614070565b81548110610f1957610f19614083565b9060005260206000200160009054906101000a90046001600160a01b0316838281518110610f4957610f49614083565b6001600160a01b0390921660209283029190910190910152600101610ef5565b505092915050565b6001600160a01b03841660009081526003602052604090205460ff16610faa576040516340d8c4db60e11b815260040160405180910390fd5b836001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610fe8573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061100c9190613c7e565b6001600160a01b0316336001600160a01b03161461103d57604051636edaef2f60e11b815260040160405180910390fd5b600080611048612b51565b91509150816001600160a01b03166323c8affa308761106561215d565b8886896040518763ffffffff1660e01b8152600401611089969594939291906141ec565b600060405180830381600087803b1580156110a357600080fd5b505af11580156110b7573d6000803e3d6000fd5b50600092506110cc91505060208601866135ef565b6040517f70a082310000000000000000000000000000000000000000000000000000000081526001600160a01b03898116600483015291909116906370a0823190602401602060405180830381865afa15801561112d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111519190614244565b90506000816111666060880160408901613fbb565b6001600160801b0316101561117b573361117d565b835b90506000826111926060890160408a01613fbb565b6001600160801b031610156111bf576111b16060880160408901613fbb565b6001600160801b03166111e3565b826111d06060890160408a01613fbb565b6001600160801b03166111e39190613ea4565b90506001600160a01b03891663d0e8dcff8361120260208b018b6135ef565b6040516001600160e01b031960e085901b1681526001600160a01b0392831660048201529116602482015260448101869052606401600060405180830381600087803b15801561125157600080fd5b505af1158015611265573d6000803e3d6000fd5b5050505060008111156112855761128533868361078060208c018c6135ef565b846001600160a01b0316896001600160a01b0316896001600160a01b03167f957cced42c73d5e844e49787802497a4b4e27b8a7f239e76e7c35133f4a1ada08a6040516112d2919061425d565b60405180910390a4505050505050505050565b6000806112f0612b51565b9150915060006112fe61215d565b6040517f23c8affa0000000000000000000000000000000000000000000000000000000081529091506001600160a01b038416906323c8affa906113509030908a9086908b9089908c906004016141ec565b600060405180830381600087803b15801561136a57600080fd5b505af115801561137e573d6000803e3d6000fd5b505050506113b0338487604001602081019061139a9190613fbb565b6001600160801b031661078060208a018a6135ef565b826001600160a01b0316866001600160a01b03167f081c9ad95e93f5f9d217a45f3b87fd8f86ab622929ac7342f8eafacbd2c4ad3e8784886040516113f79392919061426c565b60405180910390a3505050505050565b6040805160e081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c08101829052904684611450610160820182613fd8565b61145e906020810190613fbb565b61146c610160880188613fd8565b6020013560405160200161148394939291906142a8565b60408051601f19818403018152919052805160209091012090506000806114ae610160870187613fd8565b6114bf9060808101906060016135ef565b6001600160a01b031603611523577f19457468657265756d205369676e6564204d6573736167653a0a3332000000006000908152601c839052603c902061151c9061150e610160880188613fd8565b6105fb906040810190613b18565b90506115a4565b6000611564611536610160880188613fd8565b6115479060808101906060016135ef565b846115566101608a018a613fd8565b61065f906040810190613b18565b9050806115815761157760058484612c34565b9350505050611820565b61158f610160870187613fd8565b6115a09060808101906060016135ef565b9150505b6115b460608601604087016135ef565b6001600160a01b03166115ca60208701876135ef565b6001600160a01b031614806115f557506115ea60a0860160808701613fbb565b6001600160801b0316155b80611616575061160b60c0860160a08701613fbb565b6001600160801b0316155b8061165b575061162c60808601606087016142da565b61163990620151806142f7565b65ffffffffffff1661165160408701602088016142da565b65ffffffffffff16105b806116865750670de0b6b3a764000061167a60e0870160c08801614316565b67ffffffffffffffff16115b156116a05761169760018383612c34565b92505050611820565b6116ae610160860186613fd8565b602001354211806116d557506116ca60408601602087016142da565b65ffffffffffff1642115b156116e65761169760008383612c34565b60008281526004602052604090205460ff16156117095761169760028383612c34565b6001600160a01b03811660009081526006602052604090205460ff16156117365761169760038383612c34565b60008061175f8661174b6101608a018a613fd8565b611759906020810190613fbb565b896109f8565b6040805160e0810190915291935091508060048152602081018690526001600160a01b038516604082015260600161179b6101608a018a613fd8565b6117a9906020810190613fbb565b6001600160801b031681526020016117c96101208a016101008b01614333565b6117e2576117dd60608a0160408b016135ef565b6117ef565b6117ef60208a018a6135ef565b6001600160a01b03168152602001836001600160801b03168152602001826001600160801b03168152509450505050505b92915050565b61182e612cc9565b6118386000612d0f565b565b611842612cc9565b6001546001600160a01b03908116908216810361188b576040517fc927103e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0384811691821790925560408051928416835260208301919091527f9223780305f6b8602877f20db7cc3a6770c99586cb92db707a89375807e12337910160405180910390a15050565b6001600160a01b03831660009081526003602052604090205460ff16611930576040516340d8c4db60e11b815260040160405180910390fd5b6040517f67ff93a60000000000000000000000000000000000000000000000000000000081523360048201526001600160a01b0383811660248301526001600160801b038316604483015260009182918291908716906367ff93a6906064016060604051808303816000875af11580156119ae573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119d29190614350565b919450925090506119ee6001600160a01b038416338885612a7e565b6001546001600160a01b03168015801590611a095750600082115b15611a8d57600154611a2a906001600160a01b038681169133911685612a7e565b6040516358c0528f60e01b81526001600160a01b038581166004830152602482018490528216906358c0528f90604401600060405180830381600087803b158015611a7457600080fd5b505af1158015611a88573d6000803e3d6000fd5b505050505b604080516001600160a01b0388811682526001600160801b0388166020830152918101859052606081018490529088169033907f0f6b57650350b4f610474b25b5969726ade85bd32d17cf9de29015bd617cc9fd9060800160405180910390a350505050505050565b60015460009081906001600160a01b03168015611c3a576040517f7eda99570000000000000000000000000000000000000000000000000000000081526001600160a01b0386811660048301526000918291841690637eda9957906024016040805180830381865afa158015611b70573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b949190613e31565b909250905060006702c68af0bb1400008311611bb05782611bba565b6702c68af0bb1400005b90506000670de0b6b3a76400008311611bd35782611bdd565b670de0b6b3a76400005b90506000670de0b6b3a7640000611bfd846001600160801b038c16613e6b565b611c079190613e82565b9050670de0b6b3a7640000611c1c8383613e6b565b611c269190613e82565b9650611c328782613ea4565b975050505050505b509250929050565b6001600160a01b03831660009081526003602052604090205460ff16611c7b576040516340d8c4db60e11b815260040160405180910390fd5b6040517f9076e9040000000000000000000000000000000000000000000000000000000081523360048201526001600160a01b0383811660248301526001600160801b03831660448301526000918291861690639076e9049060640160408051808303816000875af1158015611cf5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d199190614387565b9092509050611d3c6001600160a01b03831633876001600160801b038716612a7e565b604080516001600160a01b0386811682526001600160801b03861660208301529181018390529086169081907ff6c7cbea93a9318a02e83b81b74aa67de77eaf4585af0f3118f30108f513849a9060600160405180910390a35050505050565b3360008181526003602052604090205460ff16611dcc576040516340d8c4db60e11b815260040160405180910390fd5b826001600160a01b0316846001600160a01b0316826001600160a01b03167fd1398bee19313d6bf672ccb116e51f4a1a947e91c757907f51fbb5b5e56c698f856040516107f791815260200190565b6040805161018081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e081018290526101008101829052610120810182905261014081018290526101608101919091526001600160a01b03861660009081526003602052604081205460ff16611eb5576040516340d8c4db60e11b815260040160405180910390fd5b6040517fa3620ec40000000000000000000000000000000000000000000000000000000081526001600160a01b0388169063a3620ec490611f009088908a9089908990600401614449565b6101a0604051808303816000875af1158015611f20573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f4491906144a7565b8092508193505050611ff633886001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611f8e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611fb29190613c7e565b8461014001518561016001518660c00151611fcd9190613ff8565b611fd79190613ff8565b60e08601516001600160a01b03169291906001600160801b0316612a7e565b6101608201516001600160801b03161561203c5761203c33828461016001516001600160801b03168560e001516001600160a01b0316612a7e909392919063ffffffff16565b6101408201516001600160801b0316156120fc5760015461014083015160e08401516001600160a01b039283169261208392911690339084906001600160801b0316612a7e565b60e08301516101408401516040516358c0528f60e01b81526001600160a01b0392831660048201526001600160801b039091166024820152908216906358c0528f90604401600060405180830381600087803b1580156120e257600080fd5b505af11580156120f6573d6000803e3d6000fd5b50505050505b806001600160a01b0316876001600160a01b0316336001600160a01b03167f7ebea06143b46f005fced4223cf841b88854a21cb67516c71838990a9f56f8e8898660405161214b9291906145b3565b60405180910390a49550959350505050565b6001546000906001600160a01b03168061217957600091505090565b806001600160a01b031663d1c699aa6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156121b7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121db91906145d1565b91506611c37937e080006bffffffffffffffffffffffff8316116121ff5781612208565b6611c37937e080005b91505090565b3360008181526003602052604090205460ff1661223e576040516340d8c4db60e11b815260040160405180910390fd5b816001600160a01b0316836001600160a01b0316826001600160a01b03167f6954f1cdad46901994f29d9b1f78744c873c527bad04d294b4954cc8caf367da60405160405180910390a4505050565b6001600160a01b03851660009081526003602052604090205460ff166122c6576040516340d8c4db60e11b815260040160405180910390fd5b6000806000876001600160a01b03166331cd4e6233898989896040518663ffffffff1660e01b81526004016122ff9594939291906145ff565b6060604051808303816000875af115801561231e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123429190614350565b92509250925084156123c6576123c633896001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612390573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123b49190613c7e565b6001600160a01b038616919085612a7e565b6001546001600160a01b031680158015906123e15750600082115b1561246557600154612402906001600160a01b038681169133911685612a7e565b6040516358c0528f60e01b81526001600160a01b038581166004830152602482018490528216906358c0528f90604401600060405180830381600087803b15801561244c57600080fd5b505af1158015612460573d6000803e3d6000fd5b505050505b604080516001600160a01b038a81168252602082018a9052918101849052908a169033907f5ee515779e0ca72dbfbc93b30480bbecf67910142933d89d3d89be8e28a9c8509060600160405180910390a3505050505050505050565b6124c9612cc9565b6001600160a01b038116612511576040517f1e4fbdf7000000000000000000000000000000000000000000000000000000008152600060048201526024015b60405180910390fd5b61251a81612d0f565b50565b61252d60608401604085016135ef565b6001600160a01b031661254360208501856135ef565b6001600160a01b031603612583576040517f8686656d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61259360a0840160808501613fbb565b6001600160801b03166000036125d5576040517f950466ea00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6125e560408401602085016142da565b65ffffffffffff16421115612626576040517fd36c850000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61263660808401606085016142da565b61264390620151806142f7565b65ffffffffffff1661265b60408501602086016142da565b65ffffffffffff16101561269b576040517f1e617b1e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b670de0b6b3a76400006126b460e0850160c08601614316565b67ffffffffffffffff1611156126f6576040517fa7e9b5b600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000612700612b51565b50905060008061271a8461038960a0890160808a01613fbb565b9150915060008083118061272e5750600082115b612738578861273a565b305b9050836001600160a01b031663d951124d308a8461275661215d565b8c8c6040518763ffffffff1660e01b815260040161277996959493929190614644565b600060405180830381600087803b15801561279357600080fd5b505af11580156127a7573d6000803e3d6000fd5b505050506127d933858960800160208101906127c39190613fbb565b6001600160801b031661078060208c018c6135ef565b306001600160a01b038216036128ce5761282d8983856127ff60a08c0160808d01613fbb565b6001600160801b03166128129190613ea4565b61281c9190613ea4565b6001600160a01b0387169190612d6c565b8115612847576128476001600160a01b0385168684612d6c565b82156128ce57600154612867906001600160a01b03868116911685612d6c565b6001546040516358c0528f60e01b81526001600160a01b03868116600483015260248201869052909116906358c0528f90604401600060405180830381600087803b1580156128b557600080fd5b505af11580156128c9573d6000803e3d6000fd5b505050505b846001600160a01b0316896001600160a01b0316336001600160a01b03167f2e8d3d935843ef13282538bbbb45c3c49faa9797fe651d3fcb327f436a91116f8b8b88886040516112d294939291906146e5565b6000806000806129318686612da2565b9250925092506129418282612def565b5090949350505050565b6000806000866001600160a01b0316631626ba7e60e01b87878760405160240161297793929190614717565b60408051601f198184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff166001600160e01b03199094169390931790925290516129ca919061473a565b600060405180830381855afa9150503d8060008114612a05576040519150601f19603f3d011682016040523d82523d6000602084013e612a0a565b606091505b5091509150818015612a1d575080516020145b15612a6f57600081806020019051810190612a38919061474c565b6001600160e01b0319167f1626ba7e00000000000000000000000000000000000000000000000000000000149350612a7692505050565b6000925050505b949350505050565b6040516001600160a01b038481166024830152838116604483015260648201839052612afa9186918216906323b872dd906084015b604051602081830303815290604052915060e01b6020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050612ef7565b50505050565b60006001600160801b03821115612b4d576040517f6dfcc6500000000000000000000000000000000000000000000000000000000081526080600482015260248101839052604401612508565b5090565b6000806002546001612b639190614070565b9050612bb77f000000000000000000000000000000000000000000000000000000000000000082604051602001612b9c91815260200190565b60405160208183030381529060405280519060200120612f73565b60028290556001600160a01b0381166000818152600360205260408120805460ff191660019081179091556007805491820181559091527fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c68801805473ffffffffffffffffffffffffffffffffffffffff1916909117905592909150565b6040805160e081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c08101919091526040518060e00160405280856005811115612c8a57612c8a61353d565b8152602081018590526001600160a01b03841660408201526000606082018190526080820181905260a0820181905260c09091015290505b9392505050565b6000546001600160a01b03163314611838576040517f118cdaa7000000000000000000000000000000000000000000000000000000008152336004820152602401612508565b600080546001600160a01b0383811673ffffffffffffffffffffffffffffffffffffffff19831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6040516001600160a01b03838116602483015260448201839052612d9d91859182169063a9059cbb90606401612ab3565b505050565b60008060008351604103612ddc5760208401516040850151606086015160001a612dce88828585612ffa565b955095509550505050612de8565b50508151600091506002905b9250925092565b6000826003811115612e0357612e0361353d565b03612e0c575050565b6001826003811115612e2057612e2061353d565b03612e57576040517ff645eedf00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6002826003811115612e6b57612e6b61353d565b03612ea5576040517ffce698f700000000000000000000000000000000000000000000000000000000815260048101829052602401612508565b6003826003811115612eb957612eb961353d565b03612ef3576040517fd78bce0c00000000000000000000000000000000000000000000000000000000815260048101829052602401612508565b5050565b6000612f0c6001600160a01b038416836130c9565b90508051600014158015612f31575080806020019051810190612f2f9190614776565b155b15612d9d576040517f5274afe70000000000000000000000000000000000000000000000000000000081526001600160a01b0384166004820152602401612508565b6000763d602d80600a3d3981f3363d3d373d3d3d363d730000008360601b60e81c176000526e5af43d82803e903d91602b57fd5bf38360781b1760205281603760096000f590506001600160a01b038116611820576040517fc2f868f400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a084111561303557506000915060039050826130bf565b604080516000808252602082018084528a905260ff891692820192909252606081018790526080810186905260019060a0016020604051602081039080840390855afa158015613089573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b0381166130b5575060009250600191508290506130bf565b9250600091508190505b9450945094915050565b6060612cc28383600084600080856001600160a01b031684866040516130ef919061473a565b60006040518083038185875af1925050503d806000811461312c576040519150601f19603f3d011682016040523d82523d6000602084013e613131565b606091505b509150915061314186838361314b565b9695505050505050565b6060826131605761315b826131c0565b612cc2565b815115801561317757506001600160a01b0384163b155b156131b9576040517f9996b3150000000000000000000000000000000000000000000000000000000081526001600160a01b0385166004820152602401612508565b5080612cc2565b8051156131d05780518082602001fd5b6040517f1425ea4200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b038116811461251a57600080fd5b6000806040838503121561322a57600080fd5b823561323581613202565b9150602083013567ffffffffffffffff81111561325157600080fd5b830160e0818603121561326357600080fd5b809150509250929050565b6000806000806080858703121561328457600080fd5b843561328f81613202565b9350602085013561329f81613202565b925060408501356132af81613202565b9396929550929360600135925050565b6001600160801b038116811461251a57600080fd5b80356132df816132bf565b919050565b600061016082840312156132f757600080fd5b50919050565b60008060006101a0848603121561331357600080fd5b833561331e81613202565b9250602084013561332e816132bf565b915061333d85604086016132e4565b90509250925092565b600061018082840312156132f757600080fd5b60008060006060848603121561336e57600080fd5b833561337981613202565b9250602084013567ffffffffffffffff81111561339557600080fd5b6133a186828701613346565b92505060408401356133b281613202565b809150509250925092565b600080604083850312156133d057600080fd5b50508035926020909101359150565b6020808252825182820181905260009190848201906040850190845b818110156134205783516001600160a01b0316835292840192918401916001016133fb565b50909695505050505050565b600061022082840312156132f757600080fd5b600080600080610280858703121561345657600080fd5b843561346181613202565b9350602085013561347181613202565b9250613480866040870161342c565b915061026085013561349181613202565b939692955090935050565b6000602082840312156134ae57600080fd5b5035919050565b600080600061026084860312156134cb57600080fd5b83356134d681613202565b92506134e5856020860161342c565b91506102408401356133b281613202565b6000806040838503121561350957600080fd5b823567ffffffffffffffff81111561352057600080fd5b61352c85828601613346565b925050602083013561326381613202565b634e487b7160e01b600052602160045260246000fd5b6006811061251a57634e487b7160e01b600052602160045260246000fd5b805161357c81613553565b808352506020810151602083015260408101516001600160a01b038082166040850152606083015191506001600160801b0380831660608601528160808501511660808601528060a08501511660a08601528060c08501511660c08601525050505050565b60e081016118208284613571565b60006020828403121561360157600080fd5b8135612cc281613202565b60008060006060848603121561362157600080fd5b833561362c81613202565b9250602084013561363c81613202565b915060408401356133b2816132bf565b6000806040838503121561365f57600080fd5b823561366a81613202565b91506020830135613263816132bf565b60008060006060848603121561368f57600080fd5b833561369a81613202565b925060208401356136aa81613202565b929592945050506040919091013590565b634e487b7160e01b600052604160045260246000fd5b604051610180810167ffffffffffffffff811182821017156136f5576136f56136bb565b60405290565b604051601f8201601f1916810167ffffffffffffffff81118282101715613724576137246136bb565b604052919050565b6000601f83601f84011261373f57600080fd5b8235602067ffffffffffffffff8083111561375c5761375c6136bb565b8260051b61376b8382016136fb565b938452868101830193838101908986111561378557600080fd5b84890192505b85831015613815578235848111156137a35760008081fd5b8901603f81018b136137b55760008081fd5b858101356040868211156137cb576137cb6136bb565b6137dc828b01601f191689016136fb565b8281528d828486010111156137f15760008081fd5b828285018a830137600092810189019290925250835250918401919084019061378b565b9998505050505050505050565b600080600080600060a0868803121561383a57600080fd5b853561384581613202565b9450602086013561385581613202565b93506040860135925060608601359150608086013567ffffffffffffffff81111561387f57600080fd5b61388b8882890161372c565b9150509295509295909350565b6138a181613553565b9052565b6138b0828251613898565b60208101516138ca60208401826001600160a01b03169052565b5060408101516138e560408401826001600160a01b03169052565b50606081015161390060608401826001600160801b03169052565b50608081015161391a608084018265ffffffffffff169052565b5060a081015161393460a084018265ffffffffffff169052565b5060c081015161394f60c08401826001600160801b03169052565b5060e081015161396a60e08401826001600160a01b03169052565b5061010081810151908301526101208082015167ffffffffffffffff8116828501525050610140818101516001600160801b038116848301525050610160818101516001600160801b03811684830152612afa565b6101a081016139ce82856138a5565b6001600160a01b0383166101808301529392505050565b600080604083850312156139f857600080fd5b8235613a0381613202565b9150602083013561326381613202565b801515811461251a57600080fd5b600080600080600060a08688031215613a3957600080fd5b8535613a4481613202565b94506020860135613a5481613202565b9350604086013592506060860135613a6b81613a13565b9150608086013567ffffffffffffffff81111561387f57600080fd5b60008060008060006101e08688031215613aa057600080fd5b8535613aab81613202565b94506020860135613abb81613202565b9350613aca87604088016132e4565b92506101a086013567ffffffffffffffff811115613ae757600080fd5b860160408189031215613af957600080fd5b91506101c0860135613b0a81613202565b809150509295509295909350565b6000808335601e19843603018112613b2f57600080fd5b83018035915067ffffffffffffffff821115613b4a57600080fd5b602001915036819003821315613b5f57600080fd5b9250929050565b6000808335601e19843603018112613b7d57600080fd5b830160208101925035905067ffffffffffffffff811115613b9d57600080fd5b803603821315613b5f57600080fd5b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b6020815260008235613be681613202565b6001600160a01b0380821660208501526020850135604085015260408501359150613c1082613202565b808216606085015260608501356080850152608085013560a0850152613c3960a0860186613b66565b925060e060c0860152613c5161010086018483613bac565b92505060c0850135613c6281613202565b1660e0939093019290925250919050565b80516132df81613202565b600060208284031215613c9057600080fd5b8151612cc281613202565b65ffffffffffff8116811461251a57600080fd5b80356132df81613c9b565b67ffffffffffffffff8116811461251a57600080fd5b80356132df81613cba565b8035613ce681613cba565b67ffffffffffffffff1682526020810135613d0081613202565b6001600160a01b039081166020840152604082013590613d1f82613a13565b9015156040840152606082013590613d3682613a13565b9015156060840152608082013590613d4d82613202565b808216608085015250505050565b8035613d6681613202565b6001600160a01b039081168352602082013590613d8282613c9b565b65ffffffffffff9182166020850152604083013591613da083613202565b9181166040850152606083013591613db783613c9b565b919091166060840152506080810135613dcf816132bf565b6001600160801b03908116608084015260a082013590613dee826132bf565b1660a0830152612ef360c0808401908301613cdb565b6001600160a01b03841681526001600160801b03831660208201526101a08101612a766040830184613d5b565b60008060408385031215613e4457600080fd5b505080516020909101519092909150565b634e487b7160e01b600052601160045260246000fd5b808202811582820484141761182057611820613e55565b600082613e9f57634e487b7160e01b600052601260045260246000fd5b500490565b8181038181111561182057611820613e55565b6000610180613ec68484613d5b565b61016080840135607e19853603018112613edf57600080fd5b9085018290528301803590613ef3826132bf565b6001600160801b0382168387015260208101356101a0870152613f196040820182613b66565b9350915060806101c0870152613f3461020087018484613bac565b9250606001359050613f4581613202565b6001600160a01b0381166101e0860152508091505092915050565b60006001600160a01b03808916835280881660208401528087166040840152506bffffffffffffffffffffffff8516606083015260c06080830152613fa860c0830185613eb7565b90508260a0830152979650505050505050565b600060208284031215613fcd57600080fd5b8135612cc2816132bf565b60008235607e19833603018112613fee57600080fd5b9190910192915050565b6001600160801b0382811682821603908082111561401857614018613e55565b5092915050565b60006101406001600160a01b038716835280602084015261404281840187613eb7565b9150506140526040830185613571565b6bffffffffffffffffffffffff831661012083015295945050505050565b8082018082111561182057611820613e55565b634e487b7160e01b600052603260045260246000fd5b803563ffffffff811681146132df57600080fd5b80356140b881613202565b6001600160a01b0390811683526020820135906140d482613202565b16602083015260408101356140e8816132bf565b6001600160801b039081166040840152606082013590614107826132bf565b16606083015261411960808201613caf565b65ffffffffffff16608083015261413260a08201613caf565b65ffffffffffff1660a083015261414b60c08201614099565b63ffffffff1660c083015261416260e08201614099565b63ffffffff1660e083015261010061417b828201613cd0565b67ffffffffffffffff1690830152610120614197828201613cd0565b67ffffffffffffffff16908301526101406141b38282016132d4565b6001600160801b0316908301526101606141ce8282016132d4565b6001600160801b031690830152610180612d9d818401838301613cdb565b6001600160a01b03878116825286811660208301526bffffffffffffffffffffffff861660408301526102c082019061422860608401876140ad565b846102808401528084166102a084015250979650505050505050565b60006020828403121561425657600080fd5b5051919050565b610220810161182082846140ad565b610260810161427b82866140ad565b6bffffffffffffffffffffffff84166102208301526001600160a01b038316610240830152949350505050565b8481526101c081016142bd6020830186613d5b565b6001600160801b03939093166101808201526101a0015292915050565b6000602082840312156142ec57600080fd5b8135612cc281613c9b565b65ffffffffffff81811683821601908082111561401857614018613e55565b60006020828403121561432857600080fd5b8135612cc281613cba565b60006020828403121561434557600080fd5b8135612cc281613a13565b60008060006060848603121561436557600080fd5b835161437081613202565b602085015160409095015190969495509392505050565b6000806040838503121561439a57600080fd5b82516143a581613202565b6020939093015192949293505050565b60005b838110156143d05781810151838201526020016143b8565b50506000910152565b600082825180855260208086019550808260051b84010181860160005b8481101561443c57601f1980878503018a528251805180865261441e818888018985016143b5565b9a86019a601f019091169390930184019250908301906001016143f6565b5090979650505050505050565b8481526001600160a01b038416602082015282604082015260806060820152600061314160808301846143d9565b8051600681106132df57600080fd5b80516132df816132bf565b80516132df81613c9b565b80516132df81613cba565b6000808284036101a08112156144bc57600080fd5b610180808212156144cc57600080fd5b6144d46136d1565b91506144df85614477565b82526144ed60208601613c73565b60208301526144fe60408601613c73565b604083015261450f60608601614486565b606083015261452060808601614491565b608083015261453160a08601614491565b60a083015261454260c08601614486565b60c083015261455360e08601613c73565b60e0830152610100858101519083015261012061457181870161449c565b90830152610140614583868201614486565b90830152610160614595868201614486565b81840152508193506145a8818601613c73565b925050509250929050565b6001600160a01b03831681526101a08101612cc260208301846138a5565b6000602082840312156145e357600080fd5b81516bffffffffffffffffffffffff81168114612cc257600080fd5b60006001600160a01b038088168352808716602084015250846040830152831515606083015260a0608083015261463960a08301846143d9565b979650505050505050565b6001600160a01b0387811682528681166020830152851660408201526bffffffffffffffffffffffff8416606082015260006102006146866080840186613d5b565b806101e08401526146978485613b66565b6040838601526146ac61024086018284613bac565b925050506146bd6020850185613b66565b8483036101ff19016102208601526146d6838284613bac565b9b9a5050505050505050505050565b6001600160a01b03851681526101c081016147036020830186613d5b565b6101808201939093526101a0015292915050565b838152604060208201526000614731604083018486613bac565b95945050505050565b60008251613fee8184602087016143b5565b60006020828403121561475e57600080fd5b81516001600160e01b031981168114612cc257600080fd5b60006020828403121561478857600080fd5b8151612cc281613a1356fea2646970667358221220f6e245ecce5ab5f48ad0f43e2292f95a39682fe0e92baa1ff4185ef2a7f1a9e664736f6c63430008180033000000000000000000000000c47e7bd855efaee7fb938ac593b9c74fd144728d0000000000000000000000008abf5358a88ca2586635d646aaaff172572fb0ed

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106101cf5760003560e01c806377c05dec11610104578063b166a09f116100a2578063ca41363e11610071578063ca41363e14610479578063f2fde38b1461048c578063f9361aef1461049f578063fda25df4146104b257600080fd5b8063b166a09f146103fd578063b35c845e14610420578063b4df001e14610441578063c18abe0d1461046657600080fd5b80638da5cb5b116100de5780638da5cb5b146103a357806393594d7f146103b4578063a597ec1f146103d7578063a6d49dca146103ea57600080fd5b806377c05dec146103455780637d431c7c146103685780638c35dc4d1461037b57600080fd5b806347e5dc20116101715780635920d5771161014b5780635920d577146102f75780635a696cb81461030a578063715018a61461032a578063733140771461033257600080fd5b806347e5dc20146102915780634dcfbba4146102b15780635468fe95146102c457600080fd5b8063369bd2fe116101ad578063369bd2fe146102185780633849153b146102205780633c93adee146102535780633df8f62e1461027e57600080fd5b8063065d4a6d146101d45780630b620b81146101e957806318e2a560146101fc575b600080fd5b6101e76101e2366004613217565b6104d9565b005b6101e76101f736600461326e565b610805565b61020560025481565b6040519081526020015b60405180910390f35b6101e761099d565b61023361022e3660046132fd565b6109f8565b604080516001600160801b0393841681529290911660208301520161020f565b600154610266906001600160a01b031681565b6040516001600160a01b03909116815260200161020f565b6101e761028c366004613359565b610b45565b6102a461029f3660046133bd565b610e5b565b60405161020f91906133df565b6101e76102bf36600461343f565b610f71565b6102e76102d236600461349c565b60056020526000908152604090205460ff1681565b604051901515815260200161020f565b6101e76103053660046134b5565b6112e5565b61031d6103183660046134f6565b611407565b60405161020f91906135e1565b6101e7611826565b6101e76103403660046135ef565b61183a565b6102e76103533660046135ef565b60066020526000908152604090205460ff1681565b6101e761037636600461360c565b6118f7565b61038e61038936600461364c565b611af6565b6040805192835260208301919091520161020f565b6000546001600160a01b0316610266565b6102e76103c236600461349c565b60046020526000908152604090205460ff1681565b6101e76103e536600461360c565b611c42565b6101e76103f836600461367a565b611d9c565b6102e761040b3660046135ef565b60036020526000908152604090205460ff1681565b61043361042e366004613822565b611e1b565b60405161020f9291906139bf565b61044961215d565b6040516bffffffffffffffffffffffff909116815260200161020f565b6101e76104743660046139e5565b61220e565b6101e7610487366004613a21565b61228d565b6101e761049a3660046135ef565b6124c1565b6101e76104ad366004613a87565b61251d565b6102667f0000000000000000000000008abf5358a88ca2586635d646aaaff172572fb0ed81565b8060800135421115610517576040517f5c32099600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60004661052760208401846135ef565b602084013561053c60608601604087016135ef565b6040805160208101959095526001600160a01b03938416908501526060808501929092529091166080808401919091529084013560a083015283013560c082015260e00160408051601f19818403018152919052805160209091012090506000806105ad60e0850160c086016135ef565b6001600160a01b03160361063c577f19457468657265756d205369676e6564204d6573736167653a0a3332000000006000908152601c839052603c9020610635906105fb60a0860186613b18565b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061292192505050565b90506106b1565b600061066461065160e0860160c087016135ef565b8461065f60a0880188613b18565b61294b565b90508061069d576040517f5d52cbe300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6106ad60e0850160c086016135ef565b9150505b6001600160a01b03811660009081526006602052604090205460ff1615610704576040517f6cff8dd500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008281526005602052604090205460ff161561074d576040517f9b1475af00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000828152600560209081526040909120805460ff191660011790556107919033908390860180359061078090886135ef565b6001600160a01b0316929190612a7e565b6107aa81856060860180359061078090604089016135ef565b806001600160a01b0316846001600160a01b0316336001600160a01b03167fc09ea7f7280174a395e9010b2e05ab1888cd36f5ad99b4429478af303ce78c3d866040516107f79190613bd5565b60405180910390a450505050565b6001600160a01b03841660009081526003602052604090205460ff1661083e576040516340d8c4db60e11b815260040160405180910390fd5b836001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561087c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108a09190613c7e565b6001600160a01b0316336001600160a01b0316146108d157604051636edaef2f60e11b815260040160405180910390fd5b6040517fd0e8dcff0000000000000000000000000000000000000000000000000000000081526001600160a01b03848116600483015283811660248301526044820183905285169063d0e8dcff90606401600060405180830381600087803b15801561093c57600080fd5b505af1158015610950573d6000803e3d6000fd5b5050604080516001600160a01b0387811682526020820186905280871694508816925033917fe826ecb5c03d4897f8ab426ee94064e06179dff39cd9fdd0776904cd935c95d891016107f7565b33600081815260066020908152604091829020805460ff8116801560ff199092168217909255925192835292917fdcbed411faf93d1cef89c9e1923cd8e047c29e2d465b976b24b0b22c054b340e910160405180910390a250565b60015460009081906001600160a01b03168015610b3c57600080826001600160a01b031663dd07ab1c8989896040518463ffffffff1660e01b8152600401610a4293929190613e04565b6040805180830381865afa158015610a5e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a829190613e31565b909250905060006702c68af0bb1400008311610a9e5782610aa8565b6702c68af0bb1400005b90506000670de0b6b3a76400008311610ac15782610acb565b670de0b6b3a76400005b90506000670de0b6b3a7640000610aeb846001600160801b038d16613e6b565b610af59190613e82565b9050610b1c670de0b6b3a7640000610b0d8484613e6b565b610b179190613e82565b612b00565b9650610b34610b176001600160801b03891683613ea4565b975050505050505b50935093915050565b6000610b518383611407565b9050600481516005811115610b6857610b6861353d565b14610b9f576040517fe30578ab00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6020808201516000908152600490915260408120805460ff1916600117905580610bc7612b51565b915091506000610bd561215d565b60408086015190517f537952080000000000000000000000000000000000000000000000000000000081529192506001600160a01b03851691635379520891610c2a9130918c9187908d908a90600401613f60565b600060405180830381600087803b158015610c4457600080fd5b505af1158015610c58573d6000803e3d6000fd5b50610c8a9250339150859050610c7460a08a0160808b01613fbb565b6001600160801b031661078060208b018b6135ef565b610cef8460400151338660a001518760c001518a806101600190610cae9190613fd8565b610cbc906020810190613fbb565b610cc69190613ff8565b610cd09190613ff8565b60808801516001600160a01b03169291906001600160801b0316612a7e565b60c08401516001600160801b031615610d3757610d378460400151868660c001516001600160801b031687608001516001600160a01b0316612a7e909392919063ffffffff16565b60a08401516001600160801b031615610df757600154604085015160a086015160808701516001600160a01b0393841693610d7f9391169184906001600160801b0316612a7e565b608085015160a08601516040516358c0528f60e01b81526001600160a01b0392831660048201526001600160801b039091166024820152908216906358c0528f90604401600060405180830381600087803b158015610ddd57600080fd5b505af1158015610df1573d6000803e3d6000fd5b50505050505b846001600160a01b0316836001600160a01b0316336001600160a01b03167f2603b553dc2ab4d1a644bb67a527695755c7c1adc290f4718813e649cc6c71888a8a8987604051610e4a949392919061401f565b60405180910390a450505050505050565b600754606090821580610e76575080610e748486614070565b115b15610ead576040517f6269996b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8267ffffffffffffffff811115610ec657610ec66136bb565b604051908082528060200260200182016040528015610eef578160200160208202803683370190505b50915060005b83811015610f69576007610f098287614070565b81548110610f1957610f19614083565b9060005260206000200160009054906101000a90046001600160a01b0316838281518110610f4957610f49614083565b6001600160a01b0390921660209283029190910190910152600101610ef5565b505092915050565b6001600160a01b03841660009081526003602052604090205460ff16610faa576040516340d8c4db60e11b815260040160405180910390fd5b836001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610fe8573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061100c9190613c7e565b6001600160a01b0316336001600160a01b03161461103d57604051636edaef2f60e11b815260040160405180910390fd5b600080611048612b51565b91509150816001600160a01b03166323c8affa308761106561215d565b8886896040518763ffffffff1660e01b8152600401611089969594939291906141ec565b600060405180830381600087803b1580156110a357600080fd5b505af11580156110b7573d6000803e3d6000fd5b50600092506110cc91505060208601866135ef565b6040517f70a082310000000000000000000000000000000000000000000000000000000081526001600160a01b03898116600483015291909116906370a0823190602401602060405180830381865afa15801561112d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111519190614244565b90506000816111666060880160408901613fbb565b6001600160801b0316101561117b573361117d565b835b90506000826111926060890160408a01613fbb565b6001600160801b031610156111bf576111b16060880160408901613fbb565b6001600160801b03166111e3565b826111d06060890160408a01613fbb565b6001600160801b03166111e39190613ea4565b90506001600160a01b03891663d0e8dcff8361120260208b018b6135ef565b6040516001600160e01b031960e085901b1681526001600160a01b0392831660048201529116602482015260448101869052606401600060405180830381600087803b15801561125157600080fd5b505af1158015611265573d6000803e3d6000fd5b5050505060008111156112855761128533868361078060208c018c6135ef565b846001600160a01b0316896001600160a01b0316896001600160a01b03167f957cced42c73d5e844e49787802497a4b4e27b8a7f239e76e7c35133f4a1ada08a6040516112d2919061425d565b60405180910390a4505050505050505050565b6000806112f0612b51565b9150915060006112fe61215d565b6040517f23c8affa0000000000000000000000000000000000000000000000000000000081529091506001600160a01b038416906323c8affa906113509030908a9086908b9089908c906004016141ec565b600060405180830381600087803b15801561136a57600080fd5b505af115801561137e573d6000803e3d6000fd5b505050506113b0338487604001602081019061139a9190613fbb565b6001600160801b031661078060208a018a6135ef565b826001600160a01b0316866001600160a01b03167f081c9ad95e93f5f9d217a45f3b87fd8f86ab622929ac7342f8eafacbd2c4ad3e8784886040516113f79392919061426c565b60405180910390a3505050505050565b6040805160e081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c08101829052904684611450610160820182613fd8565b61145e906020810190613fbb565b61146c610160880188613fd8565b6020013560405160200161148394939291906142a8565b60408051601f19818403018152919052805160209091012090506000806114ae610160870187613fd8565b6114bf9060808101906060016135ef565b6001600160a01b031603611523577f19457468657265756d205369676e6564204d6573736167653a0a3332000000006000908152601c839052603c902061151c9061150e610160880188613fd8565b6105fb906040810190613b18565b90506115a4565b6000611564611536610160880188613fd8565b6115479060808101906060016135ef565b846115566101608a018a613fd8565b61065f906040810190613b18565b9050806115815761157760058484612c34565b9350505050611820565b61158f610160870187613fd8565b6115a09060808101906060016135ef565b9150505b6115b460608601604087016135ef565b6001600160a01b03166115ca60208701876135ef565b6001600160a01b031614806115f557506115ea60a0860160808701613fbb565b6001600160801b0316155b80611616575061160b60c0860160a08701613fbb565b6001600160801b0316155b8061165b575061162c60808601606087016142da565b61163990620151806142f7565b65ffffffffffff1661165160408701602088016142da565b65ffffffffffff16105b806116865750670de0b6b3a764000061167a60e0870160c08801614316565b67ffffffffffffffff16115b156116a05761169760018383612c34565b92505050611820565b6116ae610160860186613fd8565b602001354211806116d557506116ca60408601602087016142da565b65ffffffffffff1642115b156116e65761169760008383612c34565b60008281526004602052604090205460ff16156117095761169760028383612c34565b6001600160a01b03811660009081526006602052604090205460ff16156117365761169760038383612c34565b60008061175f8661174b6101608a018a613fd8565b611759906020810190613fbb565b896109f8565b6040805160e0810190915291935091508060048152602081018690526001600160a01b038516604082015260600161179b6101608a018a613fd8565b6117a9906020810190613fbb565b6001600160801b031681526020016117c96101208a016101008b01614333565b6117e2576117dd60608a0160408b016135ef565b6117ef565b6117ef60208a018a6135ef565b6001600160a01b03168152602001836001600160801b03168152602001826001600160801b03168152509450505050505b92915050565b61182e612cc9565b6118386000612d0f565b565b611842612cc9565b6001546001600160a01b03908116908216810361188b576040517fc927103e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0384811691821790925560408051928416835260208301919091527f9223780305f6b8602877f20db7cc3a6770c99586cb92db707a89375807e12337910160405180910390a15050565b6001600160a01b03831660009081526003602052604090205460ff16611930576040516340d8c4db60e11b815260040160405180910390fd5b6040517f67ff93a60000000000000000000000000000000000000000000000000000000081523360048201526001600160a01b0383811660248301526001600160801b038316604483015260009182918291908716906367ff93a6906064016060604051808303816000875af11580156119ae573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119d29190614350565b919450925090506119ee6001600160a01b038416338885612a7e565b6001546001600160a01b03168015801590611a095750600082115b15611a8d57600154611a2a906001600160a01b038681169133911685612a7e565b6040516358c0528f60e01b81526001600160a01b038581166004830152602482018490528216906358c0528f90604401600060405180830381600087803b158015611a7457600080fd5b505af1158015611a88573d6000803e3d6000fd5b505050505b604080516001600160a01b0388811682526001600160801b0388166020830152918101859052606081018490529088169033907f0f6b57650350b4f610474b25b5969726ade85bd32d17cf9de29015bd617cc9fd9060800160405180910390a350505050505050565b60015460009081906001600160a01b03168015611c3a576040517f7eda99570000000000000000000000000000000000000000000000000000000081526001600160a01b0386811660048301526000918291841690637eda9957906024016040805180830381865afa158015611b70573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b949190613e31565b909250905060006702c68af0bb1400008311611bb05782611bba565b6702c68af0bb1400005b90506000670de0b6b3a76400008311611bd35782611bdd565b670de0b6b3a76400005b90506000670de0b6b3a7640000611bfd846001600160801b038c16613e6b565b611c079190613e82565b9050670de0b6b3a7640000611c1c8383613e6b565b611c269190613e82565b9650611c328782613ea4565b975050505050505b509250929050565b6001600160a01b03831660009081526003602052604090205460ff16611c7b576040516340d8c4db60e11b815260040160405180910390fd5b6040517f9076e9040000000000000000000000000000000000000000000000000000000081523360048201526001600160a01b0383811660248301526001600160801b03831660448301526000918291861690639076e9049060640160408051808303816000875af1158015611cf5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d199190614387565b9092509050611d3c6001600160a01b03831633876001600160801b038716612a7e565b604080516001600160a01b0386811682526001600160801b03861660208301529181018390529086169081907ff6c7cbea93a9318a02e83b81b74aa67de77eaf4585af0f3118f30108f513849a9060600160405180910390a35050505050565b3360008181526003602052604090205460ff16611dcc576040516340d8c4db60e11b815260040160405180910390fd5b826001600160a01b0316846001600160a01b0316826001600160a01b03167fd1398bee19313d6bf672ccb116e51f4a1a947e91c757907f51fbb5b5e56c698f856040516107f791815260200190565b6040805161018081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e081018290526101008101829052610120810182905261014081018290526101608101919091526001600160a01b03861660009081526003602052604081205460ff16611eb5576040516340d8c4db60e11b815260040160405180910390fd5b6040517fa3620ec40000000000000000000000000000000000000000000000000000000081526001600160a01b0388169063a3620ec490611f009088908a9089908990600401614449565b6101a0604051808303816000875af1158015611f20573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f4491906144a7565b8092508193505050611ff633886001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611f8e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611fb29190613c7e565b8461014001518561016001518660c00151611fcd9190613ff8565b611fd79190613ff8565b60e08601516001600160a01b03169291906001600160801b0316612a7e565b6101608201516001600160801b03161561203c5761203c33828461016001516001600160801b03168560e001516001600160a01b0316612a7e909392919063ffffffff16565b6101408201516001600160801b0316156120fc5760015461014083015160e08401516001600160a01b039283169261208392911690339084906001600160801b0316612a7e565b60e08301516101408401516040516358c0528f60e01b81526001600160a01b0392831660048201526001600160801b039091166024820152908216906358c0528f90604401600060405180830381600087803b1580156120e257600080fd5b505af11580156120f6573d6000803e3d6000fd5b50505050505b806001600160a01b0316876001600160a01b0316336001600160a01b03167f7ebea06143b46f005fced4223cf841b88854a21cb67516c71838990a9f56f8e8898660405161214b9291906145b3565b60405180910390a49550959350505050565b6001546000906001600160a01b03168061217957600091505090565b806001600160a01b031663d1c699aa6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156121b7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121db91906145d1565b91506611c37937e080006bffffffffffffffffffffffff8316116121ff5781612208565b6611c37937e080005b91505090565b3360008181526003602052604090205460ff1661223e576040516340d8c4db60e11b815260040160405180910390fd5b816001600160a01b0316836001600160a01b0316826001600160a01b03167f6954f1cdad46901994f29d9b1f78744c873c527bad04d294b4954cc8caf367da60405160405180910390a4505050565b6001600160a01b03851660009081526003602052604090205460ff166122c6576040516340d8c4db60e11b815260040160405180910390fd5b6000806000876001600160a01b03166331cd4e6233898989896040518663ffffffff1660e01b81526004016122ff9594939291906145ff565b6060604051808303816000875af115801561231e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123429190614350565b92509250925084156123c6576123c633896001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612390573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123b49190613c7e565b6001600160a01b038616919085612a7e565b6001546001600160a01b031680158015906123e15750600082115b1561246557600154612402906001600160a01b038681169133911685612a7e565b6040516358c0528f60e01b81526001600160a01b038581166004830152602482018490528216906358c0528f90604401600060405180830381600087803b15801561244c57600080fd5b505af1158015612460573d6000803e3d6000fd5b505050505b604080516001600160a01b038a81168252602082018a9052918101849052908a169033907f5ee515779e0ca72dbfbc93b30480bbecf67910142933d89d3d89be8e28a9c8509060600160405180910390a3505050505050505050565b6124c9612cc9565b6001600160a01b038116612511576040517f1e4fbdf7000000000000000000000000000000000000000000000000000000008152600060048201526024015b60405180910390fd5b61251a81612d0f565b50565b61252d60608401604085016135ef565b6001600160a01b031661254360208501856135ef565b6001600160a01b031603612583576040517f8686656d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61259360a0840160808501613fbb565b6001600160801b03166000036125d5576040517f950466ea00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6125e560408401602085016142da565b65ffffffffffff16421115612626576040517fd36c850000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61263660808401606085016142da565b61264390620151806142f7565b65ffffffffffff1661265b60408501602086016142da565b65ffffffffffff16101561269b576040517f1e617b1e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b670de0b6b3a76400006126b460e0850160c08601614316565b67ffffffffffffffff1611156126f6576040517fa7e9b5b600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000612700612b51565b50905060008061271a8461038960a0890160808a01613fbb565b9150915060008083118061272e5750600082115b612738578861273a565b305b9050836001600160a01b031663d951124d308a8461275661215d565b8c8c6040518763ffffffff1660e01b815260040161277996959493929190614644565b600060405180830381600087803b15801561279357600080fd5b505af11580156127a7573d6000803e3d6000fd5b505050506127d933858960800160208101906127c39190613fbb565b6001600160801b031661078060208c018c6135ef565b306001600160a01b038216036128ce5761282d8983856127ff60a08c0160808d01613fbb565b6001600160801b03166128129190613ea4565b61281c9190613ea4565b6001600160a01b0387169190612d6c565b8115612847576128476001600160a01b0385168684612d6c565b82156128ce57600154612867906001600160a01b03868116911685612d6c565b6001546040516358c0528f60e01b81526001600160a01b03868116600483015260248201869052909116906358c0528f90604401600060405180830381600087803b1580156128b557600080fd5b505af11580156128c9573d6000803e3d6000fd5b505050505b846001600160a01b0316896001600160a01b0316336001600160a01b03167f2e8d3d935843ef13282538bbbb45c3c49faa9797fe651d3fcb327f436a91116f8b8b88886040516112d294939291906146e5565b6000806000806129318686612da2565b9250925092506129418282612def565b5090949350505050565b6000806000866001600160a01b0316631626ba7e60e01b87878760405160240161297793929190614717565b60408051601f198184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff166001600160e01b03199094169390931790925290516129ca919061473a565b600060405180830381855afa9150503d8060008114612a05576040519150601f19603f3d011682016040523d82523d6000602084013e612a0a565b606091505b5091509150818015612a1d575080516020145b15612a6f57600081806020019051810190612a38919061474c565b6001600160e01b0319167f1626ba7e00000000000000000000000000000000000000000000000000000000149350612a7692505050565b6000925050505b949350505050565b6040516001600160a01b038481166024830152838116604483015260648201839052612afa9186918216906323b872dd906084015b604051602081830303815290604052915060e01b6020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050612ef7565b50505050565b60006001600160801b03821115612b4d576040517f6dfcc6500000000000000000000000000000000000000000000000000000000081526080600482015260248101839052604401612508565b5090565b6000806002546001612b639190614070565b9050612bb77f0000000000000000000000008abf5358a88ca2586635d646aaaff172572fb0ed82604051602001612b9c91815260200190565b60405160208183030381529060405280519060200120612f73565b60028290556001600160a01b0381166000818152600360205260408120805460ff191660019081179091556007805491820181559091527fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c68801805473ffffffffffffffffffffffffffffffffffffffff1916909117905592909150565b6040805160e081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c08101919091526040518060e00160405280856005811115612c8a57612c8a61353d565b8152602081018590526001600160a01b03841660408201526000606082018190526080820181905260a0820181905260c09091015290505b9392505050565b6000546001600160a01b03163314611838576040517f118cdaa7000000000000000000000000000000000000000000000000000000008152336004820152602401612508565b600080546001600160a01b0383811673ffffffffffffffffffffffffffffffffffffffff19831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6040516001600160a01b03838116602483015260448201839052612d9d91859182169063a9059cbb90606401612ab3565b505050565b60008060008351604103612ddc5760208401516040850151606086015160001a612dce88828585612ffa565b955095509550505050612de8565b50508151600091506002905b9250925092565b6000826003811115612e0357612e0361353d565b03612e0c575050565b6001826003811115612e2057612e2061353d565b03612e57576040517ff645eedf00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6002826003811115612e6b57612e6b61353d565b03612ea5576040517ffce698f700000000000000000000000000000000000000000000000000000000815260048101829052602401612508565b6003826003811115612eb957612eb961353d565b03612ef3576040517fd78bce0c00000000000000000000000000000000000000000000000000000000815260048101829052602401612508565b5050565b6000612f0c6001600160a01b038416836130c9565b90508051600014158015612f31575080806020019051810190612f2f9190614776565b155b15612d9d576040517f5274afe70000000000000000000000000000000000000000000000000000000081526001600160a01b0384166004820152602401612508565b6000763d602d80600a3d3981f3363d3d373d3d3d363d730000008360601b60e81c176000526e5af43d82803e903d91602b57fd5bf38360781b1760205281603760096000f590506001600160a01b038116611820576040517fc2f868f400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a084111561303557506000915060039050826130bf565b604080516000808252602082018084528a905260ff891692820192909252606081018790526080810186905260019060a0016020604051602081039080840390855afa158015613089573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b0381166130b5575060009250600191508290506130bf565b9250600091508190505b9450945094915050565b6060612cc28383600084600080856001600160a01b031684866040516130ef919061473a565b60006040518083038185875af1925050503d806000811461312c576040519150601f19603f3d011682016040523d82523d6000602084013e613131565b606091505b509150915061314186838361314b565b9695505050505050565b6060826131605761315b826131c0565b612cc2565b815115801561317757506001600160a01b0384163b155b156131b9576040517f9996b3150000000000000000000000000000000000000000000000000000000081526001600160a01b0385166004820152602401612508565b5080612cc2565b8051156131d05780518082602001fd5b6040517f1425ea4200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b038116811461251a57600080fd5b6000806040838503121561322a57600080fd5b823561323581613202565b9150602083013567ffffffffffffffff81111561325157600080fd5b830160e0818603121561326357600080fd5b809150509250929050565b6000806000806080858703121561328457600080fd5b843561328f81613202565b9350602085013561329f81613202565b925060408501356132af81613202565b9396929550929360600135925050565b6001600160801b038116811461251a57600080fd5b80356132df816132bf565b919050565b600061016082840312156132f757600080fd5b50919050565b60008060006101a0848603121561331357600080fd5b833561331e81613202565b9250602084013561332e816132bf565b915061333d85604086016132e4565b90509250925092565b600061018082840312156132f757600080fd5b60008060006060848603121561336e57600080fd5b833561337981613202565b9250602084013567ffffffffffffffff81111561339557600080fd5b6133a186828701613346565b92505060408401356133b281613202565b809150509250925092565b600080604083850312156133d057600080fd5b50508035926020909101359150565b6020808252825182820181905260009190848201906040850190845b818110156134205783516001600160a01b0316835292840192918401916001016133fb565b50909695505050505050565b600061022082840312156132f757600080fd5b600080600080610280858703121561345657600080fd5b843561346181613202565b9350602085013561347181613202565b9250613480866040870161342c565b915061026085013561349181613202565b939692955090935050565b6000602082840312156134ae57600080fd5b5035919050565b600080600061026084860312156134cb57600080fd5b83356134d681613202565b92506134e5856020860161342c565b91506102408401356133b281613202565b6000806040838503121561350957600080fd5b823567ffffffffffffffff81111561352057600080fd5b61352c85828601613346565b925050602083013561326381613202565b634e487b7160e01b600052602160045260246000fd5b6006811061251a57634e487b7160e01b600052602160045260246000fd5b805161357c81613553565b808352506020810151602083015260408101516001600160a01b038082166040850152606083015191506001600160801b0380831660608601528160808501511660808601528060a08501511660a08601528060c08501511660c08601525050505050565b60e081016118208284613571565b60006020828403121561360157600080fd5b8135612cc281613202565b60008060006060848603121561362157600080fd5b833561362c81613202565b9250602084013561363c81613202565b915060408401356133b2816132bf565b6000806040838503121561365f57600080fd5b823561366a81613202565b91506020830135613263816132bf565b60008060006060848603121561368f57600080fd5b833561369a81613202565b925060208401356136aa81613202565b929592945050506040919091013590565b634e487b7160e01b600052604160045260246000fd5b604051610180810167ffffffffffffffff811182821017156136f5576136f56136bb565b60405290565b604051601f8201601f1916810167ffffffffffffffff81118282101715613724576137246136bb565b604052919050565b6000601f83601f84011261373f57600080fd5b8235602067ffffffffffffffff8083111561375c5761375c6136bb565b8260051b61376b8382016136fb565b938452868101830193838101908986111561378557600080fd5b84890192505b85831015613815578235848111156137a35760008081fd5b8901603f81018b136137b55760008081fd5b858101356040868211156137cb576137cb6136bb565b6137dc828b01601f191689016136fb565b8281528d828486010111156137f15760008081fd5b828285018a830137600092810189019290925250835250918401919084019061378b565b9998505050505050505050565b600080600080600060a0868803121561383a57600080fd5b853561384581613202565b9450602086013561385581613202565b93506040860135925060608601359150608086013567ffffffffffffffff81111561387f57600080fd5b61388b8882890161372c565b9150509295509295909350565b6138a181613553565b9052565b6138b0828251613898565b60208101516138ca60208401826001600160a01b03169052565b5060408101516138e560408401826001600160a01b03169052565b50606081015161390060608401826001600160801b03169052565b50608081015161391a608084018265ffffffffffff169052565b5060a081015161393460a084018265ffffffffffff169052565b5060c081015161394f60c08401826001600160801b03169052565b5060e081015161396a60e08401826001600160a01b03169052565b5061010081810151908301526101208082015167ffffffffffffffff8116828501525050610140818101516001600160801b038116848301525050610160818101516001600160801b03811684830152612afa565b6101a081016139ce82856138a5565b6001600160a01b0383166101808301529392505050565b600080604083850312156139f857600080fd5b8235613a0381613202565b9150602083013561326381613202565b801515811461251a57600080fd5b600080600080600060a08688031215613a3957600080fd5b8535613a4481613202565b94506020860135613a5481613202565b9350604086013592506060860135613a6b81613a13565b9150608086013567ffffffffffffffff81111561387f57600080fd5b60008060008060006101e08688031215613aa057600080fd5b8535613aab81613202565b94506020860135613abb81613202565b9350613aca87604088016132e4565b92506101a086013567ffffffffffffffff811115613ae757600080fd5b860160408189031215613af957600080fd5b91506101c0860135613b0a81613202565b809150509295509295909350565b6000808335601e19843603018112613b2f57600080fd5b83018035915067ffffffffffffffff821115613b4a57600080fd5b602001915036819003821315613b5f57600080fd5b9250929050565b6000808335601e19843603018112613b7d57600080fd5b830160208101925035905067ffffffffffffffff811115613b9d57600080fd5b803603821315613b5f57600080fd5b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b6020815260008235613be681613202565b6001600160a01b0380821660208501526020850135604085015260408501359150613c1082613202565b808216606085015260608501356080850152608085013560a0850152613c3960a0860186613b66565b925060e060c0860152613c5161010086018483613bac565b92505060c0850135613c6281613202565b1660e0939093019290925250919050565b80516132df81613202565b600060208284031215613c9057600080fd5b8151612cc281613202565b65ffffffffffff8116811461251a57600080fd5b80356132df81613c9b565b67ffffffffffffffff8116811461251a57600080fd5b80356132df81613cba565b8035613ce681613cba565b67ffffffffffffffff1682526020810135613d0081613202565b6001600160a01b039081166020840152604082013590613d1f82613a13565b9015156040840152606082013590613d3682613a13565b9015156060840152608082013590613d4d82613202565b808216608085015250505050565b8035613d6681613202565b6001600160a01b039081168352602082013590613d8282613c9b565b65ffffffffffff9182166020850152604083013591613da083613202565b9181166040850152606083013591613db783613c9b565b919091166060840152506080810135613dcf816132bf565b6001600160801b03908116608084015260a082013590613dee826132bf565b1660a0830152612ef360c0808401908301613cdb565b6001600160a01b03841681526001600160801b03831660208201526101a08101612a766040830184613d5b565b60008060408385031215613e4457600080fd5b505080516020909101519092909150565b634e487b7160e01b600052601160045260246000fd5b808202811582820484141761182057611820613e55565b600082613e9f57634e487b7160e01b600052601260045260246000fd5b500490565b8181038181111561182057611820613e55565b6000610180613ec68484613d5b565b61016080840135607e19853603018112613edf57600080fd5b9085018290528301803590613ef3826132bf565b6001600160801b0382168387015260208101356101a0870152613f196040820182613b66565b9350915060806101c0870152613f3461020087018484613bac565b9250606001359050613f4581613202565b6001600160a01b0381166101e0860152508091505092915050565b60006001600160a01b03808916835280881660208401528087166040840152506bffffffffffffffffffffffff8516606083015260c06080830152613fa860c0830185613eb7565b90508260a0830152979650505050505050565b600060208284031215613fcd57600080fd5b8135612cc2816132bf565b60008235607e19833603018112613fee57600080fd5b9190910192915050565b6001600160801b0382811682821603908082111561401857614018613e55565b5092915050565b60006101406001600160a01b038716835280602084015261404281840187613eb7565b9150506140526040830185613571565b6bffffffffffffffffffffffff831661012083015295945050505050565b8082018082111561182057611820613e55565b634e487b7160e01b600052603260045260246000fd5b803563ffffffff811681146132df57600080fd5b80356140b881613202565b6001600160a01b0390811683526020820135906140d482613202565b16602083015260408101356140e8816132bf565b6001600160801b039081166040840152606082013590614107826132bf565b16606083015261411960808201613caf565b65ffffffffffff16608083015261413260a08201613caf565b65ffffffffffff1660a083015261414b60c08201614099565b63ffffffff1660c083015261416260e08201614099565b63ffffffff1660e083015261010061417b828201613cd0565b67ffffffffffffffff1690830152610120614197828201613cd0565b67ffffffffffffffff16908301526101406141b38282016132d4565b6001600160801b0316908301526101606141ce8282016132d4565b6001600160801b031690830152610180612d9d818401838301613cdb565b6001600160a01b03878116825286811660208301526bffffffffffffffffffffffff861660408301526102c082019061422860608401876140ad565b846102808401528084166102a084015250979650505050505050565b60006020828403121561425657600080fd5b5051919050565b610220810161182082846140ad565b610260810161427b82866140ad565b6bffffffffffffffffffffffff84166102208301526001600160a01b038316610240830152949350505050565b8481526101c081016142bd6020830186613d5b565b6001600160801b03939093166101808201526101a0015292915050565b6000602082840312156142ec57600080fd5b8135612cc281613c9b565b65ffffffffffff81811683821601908082111561401857614018613e55565b60006020828403121561432857600080fd5b8135612cc281613cba565b60006020828403121561434557600080fd5b8135612cc281613a13565b60008060006060848603121561436557600080fd5b835161437081613202565b602085015160409095015190969495509392505050565b6000806040838503121561439a57600080fd5b82516143a581613202565b6020939093015192949293505050565b60005b838110156143d05781810151838201526020016143b8565b50506000910152565b600082825180855260208086019550808260051b84010181860160005b8481101561443c57601f1980878503018a528251805180865261441e818888018985016143b5565b9a86019a601f019091169390930184019250908301906001016143f6565b5090979650505050505050565b8481526001600160a01b038416602082015282604082015260806060820152600061314160808301846143d9565b8051600681106132df57600080fd5b80516132df816132bf565b80516132df81613c9b565b80516132df81613cba565b6000808284036101a08112156144bc57600080fd5b610180808212156144cc57600080fd5b6144d46136d1565b91506144df85614477565b82526144ed60208601613c73565b60208301526144fe60408601613c73565b604083015261450f60608601614486565b606083015261452060808601614491565b608083015261453160a08601614491565b60a083015261454260c08601614486565b60c083015261455360e08601613c73565b60e0830152610100858101519083015261012061457181870161449c565b90830152610140614583868201614486565b90830152610160614595868201614486565b81840152508193506145a8818601613c73565b925050509250929050565b6001600160a01b03831681526101a08101612cc260208301846138a5565b6000602082840312156145e357600080fd5b81516bffffffffffffffffffffffff81168114612cc257600080fd5b60006001600160a01b038088168352808716602084015250846040830152831515606083015260a0608083015261463960a08301846143d9565b979650505050505050565b6001600160a01b0387811682528681166020830152851660408201526bffffffffffffffffffffffff8416606082015260006102006146866080840186613d5b565b806101e08401526146978485613b66565b6040838601526146ac61024086018284613bac565b925050506146bd6020850185613b66565b8483036101ff19016102208601526146d6838284613bac565b9b9a5050505050505050505050565b6001600160a01b03851681526101c081016147036020830186613d5b565b6101808201939093526101a0015292915050565b838152604060208201526000614731604083018486613bac565b95945050505050565b60008251613fee8184602087016143b5565b60006020828403121561475e57600080fd5b81516001600160e01b031981168114612cc257600080fd5b60006020828403121561478857600080fd5b8151612cc281613a1356fea2646970667358221220f6e245ecce5ab5f48ad0f43e2292f95a39682fe0e92baa1ff4185ef2a7f1a9e664736f6c63430008180033

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

000000000000000000000000c47e7bd855efaee7fb938ac593b9c74fd144728d0000000000000000000000008abf5358a88ca2586635d646aaaff172572fb0ed

-----Decoded View---------------
Arg [0] : initOwner (address): 0xc47e7bd855EfaeE7fb938AC593B9c74fd144728d
Arg [1] : _escrowImpl (address): 0x8AbF5358a88CA2586635d646aaaFF172572fB0eD

-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 000000000000000000000000c47e7bd855efaee7fb938ac593b9c74fd144728d
Arg [1] : 0000000000000000000000008abf5358a88ca2586635d646aaaff172572fb0ed


Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export  ]
[ 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.