ETH Price: $2,649.26 (+1.11%)

Contract

0x429B47C4AEADD42BBcB118651C8984086Bfc4551
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Token Holdings

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Lend187319982023-12-07 4:20:59328 days ago1701922859IN
0x429B47C4...86Bfc4551
0 ETH0.0100756530
Lend180475112023-09-02 8:01:47424 days ago1693641707IN
0x429B47C4...86Bfc4551
0 ETH0.0039450511.97362362
Lend179331932023-08-17 7:57:35440 days ago1692259055IN
0x429B47C4...86Bfc4551
0 ETH0.0046701616.87968442
Lend178167752023-08-01 1:08:35456 days ago1690852115IN
0x429B47C4...86Bfc4551
0 ETH0.0043557416
Lend178057452023-07-30 12:07:23458 days ago1690718843IN
0x429B47C4...86Bfc4551
0 ETH0.0060734218.81865983
Lend177462272023-07-22 4:16:47466 days ago1689999407IN
0x429B47C4...86Bfc4551
0 ETH0.0075981414.91156217
Lend176980212023-07-15 9:51:35473 days ago1689414695IN
0x429B47C4...86Bfc4551
0 ETH0.0065432712.89196226
Lend176979782023-07-15 9:42:59473 days ago1689414179IN
0x429B47C4...86Bfc4551
0 ETH0.0090449813.55590536
Lend176979582023-07-15 9:38:59473 days ago1689413939IN
0x429B47C4...86Bfc4551
0 ETH0.0064764913
Lend176916782023-07-14 12:23:47474 days ago1689337427IN
0x429B47C4...86Bfc4551
0 ETH0.0103885220.4543981
Lend176916212023-07-14 12:12:23474 days ago1689336743IN
0x429B47C4...86Bfc4551
0 ETH0.0126158525.17048642
Lend176915522023-07-14 11:58:23474 days ago1689335903IN
0x429B47C4...86Bfc4551
0 ETH0.0086545617.26767928
Lend176915302023-07-14 11:53:59474 days ago1689335639IN
0x429B47C4...86Bfc4551
0 ETH0.0077020115.35722813
Lend176908422023-07-14 9:34:59474 days ago1689327299IN
0x429B47C4...86Bfc4551
0 ETH0.0092786718.50878608
Lend176907312023-07-14 9:12:35474 days ago1689325955IN
0x429B47C4...86Bfc4551
0 ETH0.008706817.35973022
Lend176906632023-07-14 8:58:35474 days ago1689325115IN
0x429B47C4...86Bfc4551
0 ETH0.007939715.83519295
Lend176904262023-07-14 8:10:23474 days ago1689322223IN
0x429B47C4...86Bfc4551
0 ETH0.0085230516.8960667
Lend176807332023-07-12 23:26:35475 days ago1689204395IN
0x429B47C4...86Bfc4551
0 ETH0.008329216.09896198
Lend176805362023-07-12 22:46:35475 days ago1689201995IN
0x429B47C4...86Bfc4551
0 ETH0.0164829718.46598301
Lend176795072023-07-12 19:19:23476 days ago1689189563IN
0x429B47C4...86Bfc4551
0 ETH0.0198858727.54150599
Lend176791452023-07-12 18:06:47476 days ago1689185207IN
0x429B47C4...86Bfc4551
0 ETH0.0174882652.13482701
Lend176791382023-07-12 18:05:23476 days ago1689185123IN
0x429B47C4...86Bfc4551
0 ETH0.0168051752.73915842
Lend176790762023-07-12 17:52:35476 days ago1689184355IN
0x429B47C4...86Bfc4551
0 ETH0.0231786570.08481052
Lend176790712023-07-12 17:51:35476 days ago1689184295IN
0x429B47C4...86Bfc4551
0 ETH0.0238658975.03274457
Lend176780292023-07-12 14:18:47476 days ago1689171527IN
0x429B47C4...86Bfc4551
0 ETH0.011379333.98745347
View all transactions

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
Lender

Compiler Version
v0.8.16+commit.07a7930e

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion
File 1 of 44 : Lender.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.8.16;

import 'src/Marketplace.sol';
import 'src/lib/Swivel.sol';
import 'src/lib/Pendle.sol';
import 'src/lib/Element.sol';
import 'src/lib/Safe.sol';
import 'src/lib/Cast.sol';
import 'src/lib/RevertMsgExtractor.sol';
import 'src/lib/Maturities.sol';
import 'src/errors/Exception.sol';

import 'src/interfaces/ITempus.sol';
import 'src/interfaces/ITempusAMM.sol';
import 'src/interfaces/ITempusPool.sol';
import 'src/interfaces/ITempusToken.sol';
import 'src/interfaces/IERC20.sol';
import 'src/interfaces/IERC5095.sol';
import 'src/interfaces/ISensePeriphery.sol';
import 'src/interfaces/ISenseDivider.sol';
import 'src/interfaces/IYield.sol';
import 'src/interfaces/ISwivel.sol';
import 'src/interfaces/IElementVault.sol';
import 'src/interfaces/IAPWineAMMPool.sol';
import 'src/interfaces/IAPWineRouter.sol';
import 'src/interfaces/INotional.sol';
import 'src/interfaces/IPendle.sol';
import 'src/interfaces/IPendleMarket.sol';

/// @title Lender
/// @author Sourabh Marathe, Julian Traversa, Rob Robbins
/// @notice The lender contract executes loans on behalf of users
/// @notice The contract holds the principal tokens and mints an ERC-5095 tokens to users to represent their loans
contract Lender {
    /// @notice minimum wait before the admin may withdraw funds or change the fee rate
    uint256 public constant HOLD = 3 days;

    /// @notice address that is allowed to set and withdraw fees, disable principals, etc. It is commonly used in the authorized modifier.
    address public admin;
    /// @notice address of the MarketPlace contract, used to access the markets mapping
    address public marketPlace;
    /// @notice mapping that determines if a principal has been paused by the admin
    mapping(uint8 => bool) public paused;
    /// @notice flag that allows admin to stop all lending and minting across the entire protocol
    bool public halted;

    /// @notice contract used to execute swaps on Swivel's exchange
    address public immutable swivelAddr;
    /// @notice a SushiSwap router used by Pendle to execute swaps
    address public immutable pendleAddr;
    /// @notice a pool router used by APWine to execute swaps
    address public immutable apwineAddr;

    /// @notice a mapping that tracks the amount of unswapped premium by market. This underlying is later transferred to the Redeemer during Swivel's redeem call
    mapping(address => mapping(uint256 => uint256)) public premiums;

    /// @notice this value determines the amount of fees paid on loans
    uint256 public feenominator;
    /// @notice represents a point in time where the feenominator may change
    uint256 public feeChange;
    /// @notice represents a minimum that the feenominator must exceed
    uint256 public constant MIN_FEENOMINATOR = 500;

    /// @notice maps underlying tokens to the amount of fees accumulated for that token
    mapping(address => uint256) public fees;
    /// @notice maps a token address to a point in time, a hold, after which a withdrawal can be made
    mapping(address => uint256) public withdrawals;

    // Reantrancy protection
    uint256 private constant _NOT_ENTERED = 1;
    uint256 private constant _ENTERED = 2;
    uint256 private _status = _NOT_ENTERED;

    // Rate limiting protection
    /// @notice maximum amount of value that can flow through a protocol in a day (in USD)
    uint256 public maximumValue = 250_000e27;
    /// @notice maps protocols to how much value, in USD, has flowed through each protocol
    mapping(uint8 => uint256) public protocolFlow;
    /// @notice timestamp from which values flowing through protocol has begun
    mapping(uint8 => uint256) public periodStart;
    /// @notice estimated price of ether, set by the admin
    uint256 public etherPrice = 2_500;

    /// @notice emitted upon lending to a protocol
    event Lend(
        uint8 principal,
        address indexed underlying,
        uint256 indexed maturity,
        uint256 returned,
        uint256 spent,
        address sender
    );
    /// @notice emitted upon minting Illuminate principal tokens
    event Mint(
        uint8 principal,
        address indexed underlying,
        uint256 indexed maturity,
        uint256 amount
    );
    /// @notice emitted upon scheduling a withdrawal
    event ScheduleWithdrawal(address indexed token, uint256 hold);
    /// @notice emitted upon blocking a scheduled withdrawal
    event BlockWithdrawal(address indexed token);
    /// @notice emitted upon changing the admin
    event SetAdmin(address indexed admin);
    /// @notice emitted upon setting the fee rate
    event SetFee(uint256 indexed fee);
    /// @notice emitted upon scheduling a fee change
    event ScheduleFeeChange(uint256 when);
    /// @notice emitted upon blocking a scheduled fee change
    event BlockFeeChange();
    /// @notice emitted upon pausing or unpausing of a principal
    event PausePrincipal(uint8 principal, bool indexed state);
    /// @notice emitted upon pausing or unpausing minting, lending and redeeming
    event PauseIlluminate(bool state);

    /// @notice ensures that only a certain address can call the function
    /// @param a address that msg.sender must be to be authorized
    modifier authorized(address a) {
        if (msg.sender != a) {
            revert Exception(0, 0, 0, msg.sender, a);
        }
        _;
    }

    /// @notice reverts on all markets where the paused mapping returns true
    /// @param u address of an underlying asset
    /// @param m maturity (timestamp) of the market
    /// @param p principal value according to the MarketPlace's Principals Enum
    modifier unpaused(
        address u,
        uint256 m,
        uint8 p
    ) {
        if (paused[p] || halted) {
            revert Exception(1, p, 0, address(0), address(0));
        }
        _;
    }

    /// @notice reverts if called after maturity
    /// @param m maturity (timestamp) of the market
    modifier matured(uint256 m) {
        if (block.timestamp > m) {
            revert Exception(2, block.timestamp, m, address(0), address(0));
        }
        _;
    }

    /// @notice prevents users from re-entering contract
    modifier nonReentrant() {
        // On the first call to nonReentrant, _notEntered will be true
        if (_status == _ENTERED) {
            revert Exception(30, 0, 0, address(0), address(0));
        }

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

        _;

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

    /// @notice initializes the Lender contract
    /// @param s the Swivel contract
    /// @param p the Pendle contract
    /// @param a the APWine contract
    constructor(address s, address p, address a) {
        admin = msg.sender;
        swivelAddr = s;
        pendleAddr = p;
        apwineAddr = a;
        feenominator = 1000;
    }

    /// @notice approves the redeemer contract to spend the principal tokens held by the lender contract.
    /// @param u address of an underlying asset
    /// @param m maturity (timestamp) of the market
    /// @param r the address being approved, in this case the redeemer contract
    /// @return bool true if the approval was successful
    function approve(
        address u,
        uint256 m,
        address r
    ) external authorized(admin) returns (bool) {
        // approve the underlying for max per given principal
        for (uint8 i; i != 9; ) {
            // get the principal token's address
            address token = IMarketPlace(marketPlace).markets(u, m, i);
            // check that the token is defined for this particular market
            if (token != address(0)) {
                // max approve the token
                Safe.approve(IERC20(token), r, type(uint256).max);
            }
            unchecked {
                ++i;
            }
        }
        // approve the redeemer to receive underlying from the lender
        Safe.approve(IERC20(u), r, type(uint256).max);
        return true;
    }

    /// @notice bulk approves the usage of addresses at the given ERC20 addresses.
    /// @dev the lengths of the inputs must match because the arrays are paired by index
    /// @param u array of ERC20 token addresses that will be approved on
    /// @param a array of addresses that will be approved
    /// @return true if successful
    function approve(
        address[] calldata u,
        address[] calldata a
    ) external authorized(admin) returns (bool) {
        for (uint256 i; i != u.length; ) {
            IERC20 uToken = IERC20(u[i]);
            if (address(0) != (address(uToken))) {
                Safe.approve(uToken, a[i], type(uint256).max);
            }
            unchecked {
                ++i;
            }
        }
        return true;
    }

    /// @notice approves market contracts that require lender approval
    /// @param u address of an underlying asset
    /// @param a APWine's router contract
    /// @param e Element's vault contract
    /// @param n Notional's token contract
    /// @param p Sense's periphery contract
    function approve(
        address u,
        address a,
        address e,
        address n,
        address p
    ) external authorized(marketPlace) {
        uint256 max = type(uint256).max;
        IERC20 uToken = IERC20(u);
        if (a != address(0)) {
            Safe.approve(uToken, a, max);
        }
        if (e != address(0)) {
            Safe.approve(uToken, e, max);
        }
        if (n != address(0)) {
            Safe.approve(uToken, n, max);
        }
        if (p != address(0)) {
            Safe.approve(uToken, p, max);
        }
        if (IERC20(u).allowance(address(this), swivelAddr) == 0) {
            Safe.approve(uToken, swivelAddr, max);
        }
    }

    /// @notice sets the admin address
    /// @param a address of a new admin
    /// @return bool true if successful
    function setAdmin(address a) external authorized(admin) returns (bool) {
        admin = a;
        emit SetAdmin(a);
        return true;
    }

    /// @notice sets the feenominator to the given value
    /// @param f the new value of the feenominator, fees are not collected when the feenominator is 0
    /// @return bool true if successful
    function setFee(uint256 f) external authorized(admin) returns (bool) {
        uint256 feeTime = feeChange;
        if (feeTime == 0) {
            revert Exception(23, 0, 0, address(0), address(0));
        } else if (block.timestamp < feeTime) {
            revert Exception(
                24,
                block.timestamp,
                feeTime,
                address(0),
                address(0)
            );
        } else if (f < MIN_FEENOMINATOR) {
            revert Exception(25, 0, 0, address(0), address(0));
        }
        feenominator = f;
        delete feeChange;
        emit SetFee(f);
        return true;
    }

    /// @notice sets the address of the marketplace contract which contains the addresses of all the fixed rate markets
    /// @param m the address of the marketplace contract
    /// @return bool true if the address was set
    function setMarketPlace(
        address m
    ) external authorized(admin) returns (bool) {
        if (marketPlace != address(0)) {
            revert Exception(5, 0, 0, marketPlace, address(0));
        }
        marketPlace = m;
        return true;
    }

    /// @notice sets the ethereum price which is used in rate limiting
    /// @param p the new price
    /// @return bool true if the price was set
    function setEtherPrice(
        uint256 p
    ) external authorized(admin) returns (bool) {
        etherPrice = p;
        return true;
    }

    /// @notice sets the maximum value that can flow through a protocol
    /// @param m the maximum value by protocol
    /// @return bool true if the price was set
    function setMaxValue(uint256 m) external authorized(admin) returns (bool) {
        maximumValue = m;
        return true;
    }

    /// @notice mint swaps the sender's principal tokens for Illuminate's ERC5095 tokens in effect, this opens a new fixed rate position for the sender on Illuminate
    /// @param p principal value according to the MarketPlace's Principals Enum
    /// @param u address of an underlying asset
    /// @param m maturity (timestamp) of the market
    /// @param a amount being minted
    /// @return bool true if the mint was successful
    function mint(
        uint8 p,
        address u,
        uint256 m,
        uint256 a
    ) external nonReentrant unpaused(u, m, p) returns (bool) {
        // Fetch the desired principal token
        address principal = IMarketPlace(marketPlace).markets(u, m, p);

        // Disallow mints if market is not initialized
        if (principal == address(0)) {
            revert Exception(26, 0, 0, address(0), address(0));
        }

        // Get the maturity of the principal token
        uint256 maturity;
        if (p == uint8(MarketPlace.Principals.Illuminate)) {
            revert Exception(32, 0, 0, address(0), address(0));
        } else if (p == uint8(MarketPlace.Principals.Swivel)) {
            maturity = Maturities.swivel(principal);
        } else if (p == uint8(MarketPlace.Principals.Yield)) {
            maturity = Maturities.yield(principal);
        } else if (p == uint8(MarketPlace.Principals.Element)) {
            maturity = Maturities.element(principal);
        } else if (p == uint8(MarketPlace.Principals.Pendle)) {
            maturity = Maturities.pendle(principal);
        } else if (p == uint8(MarketPlace.Principals.Tempus)) {
            maturity = Maturities.tempus(principal);
        } else if (p == uint8(MarketPlace.Principals.Apwine)) {
            maturity = Maturities.apwine(principal);
        } else if (p == uint8(MarketPlace.Principals.Notional)) {
            maturity = Maturities.notional(principal);
        }

        // Confirm that the principal token has not matured yet
        if (block.timestamp > maturity || maturity == 0) {
            revert Exception(
                7,
                maturity,
                block.timestamp,
                address(0),
                address(0)
            );
        }

        // Transfer the users principal tokens to the lender contract
        Safe.transferFrom(IERC20(principal), msg.sender, address(this), a);

        // Calculate how much should be minted based on the decimal difference
        uint256 mintable = convertDecimals(u, principal, a);

        // Confirm that minted iPT amount will not exceed rate limit for the protocol
        rateLimit(p, u, mintable);

        // Mint the tokens received from the user
        IERC5095(principalToken(u, m)).authMint(msg.sender, mintable);

        emit Mint(p, u, m, mintable);

        return true;
    }

    /// @notice lend method for the Illuminate and Yield protocols
    /// @param p principal value according to the MarketPlace's Principals Enum
    /// @param u address of an underlying asset
    /// @param m maturity (timestamp) of the market
    /// @param a amount of underlying tokens to lend
    /// @param y Yield Space Pool for the principal token
    /// @param minimum slippage limit, minimum amount to PTs to buy
    /// @return uint256 the amount of principal tokens lent out
    function lend(
        uint8 p,
        address u,
        uint256 m,
        uint256 a,
        address y,
        uint256 minimum
    ) external nonReentrant unpaused(u, m, p) matured(m) returns (uint256) {
        // Get principal token for this market
        address principal = IMarketPlace(marketPlace).markets(u, m, p);

        // Extract fee
        fees[u] = fees[u] + a / feenominator;

        // Transfer underlying from user to the lender contract
        Safe.transferFrom(IERC20(u), msg.sender, address(this), a);

        // Make sure the Yield Space Pool matches the market
        address fyToken = IYield(y).fyToken();
        if (IYield(y).fyToken() != principal) {
            revert Exception(12, 0, 0, fyToken, principal);
        }
        address base = address(IYield(y).base());
        if (base != u) {
            revert Exception(27, 0, 0, base, u);
        }

        // Set who should get the tokens that are swapped for
        address receiver = address(this);

        // If lending on Illuminate, swap directly to the caller
        if (p == uint8(MarketPlace.Principals.Illuminate)) {
            receiver = msg.sender;
        }

        // Swap underlying for PTs to lender
        uint256 returned = yield(
            u,
            y,
            a - a / feenominator,
            receiver,
            principal,
            minimum
        );

        // Convert decimals from principal token to underlying
        returned = convertDecimals(u, principal, returned);

        // Only mint iPTs to user if lending through Yield protocol
        if (p == uint8(MarketPlace.Principals.Yield)) {
            // Confirm that minted iPT amount will not exceed rate limit for the protocol
            rateLimit(p, u, returned);

            // Mint Illuminate PTs to msg.sender
            IERC5095(principalToken(u, m)).authMint(msg.sender, returned);
        }

        emit Lend(p, u, m, returned, a, msg.sender);

        return returned;
    }

    /// @notice lend method signature for Swivel
    /// @param p principal value according to the MarketPlace's Principals Enum
    /// @param u address of an underlying asset
    /// @param m maturity (timestamp) of the market
    /// @param a array of amounts of underlying tokens lent to each order in the orders array
    /// @param y Yield Space Pool for the Illuminate PT in this market
    /// @param o array of Swivel orders being filled
    /// @param s array of signatures for each order in the orders array
    /// @param e flag to indicate if returned funds should be swapped in Yield Space Pool
    /// @param premiumSlippage slippage limit, minimum amount to PTs to buy
    /// @return uint256 the amount of principal tokens lent out
    function lend(
        uint8 p,
        address u,
        uint256 m,
        uint256[] memory a,
        address y,
        Swivel.Order[] calldata o,
        Swivel.Components[] calldata s,
        bool e,
        uint256 premiumSlippage
    ) external nonReentrant unpaused(u, m, p) matured(m) returns (uint256) {
        // Ensure all the orders are for the underlying asset
        swivelVerify(o, u);

        // Lent represents the total amount of underlying to be lent
        uint256 lent = swivelAmount(a);

        // Get the underlying balance prior to calling initiate
        uint256 starting = IERC20(u).balanceOf(address(this));

        // Transfer underlying token from user to Illuminate
        Safe.transferFrom(IERC20(u), msg.sender, address(this), lent);

        // Calculate fee for the total amount to be lent
        uint256 fee = lent / feenominator;

        {
            // Get last order to be processed's index
            uint256 lastIndex = a.length - 1;

            // Add the accumulated fees to the total
            a[lastIndex] = a[lastIndex] - fee; // Revert here if fee not paid

            // Extract fee
            fees[u] += fee;
        }

        uint256 received;
        {
            // Get the starting amount of principal tokens
            uint256 startingZcTokens = IERC20(
                IMarketPlace(marketPlace).markets(u, m, p)
            ).balanceOf(address(this));

            // Fill the given orders on Swivel
            ISwivel(swivelAddr).initiate(o, a, s);

            // Compute how many principal tokens were received
            received = (IERC20(IMarketPlace(marketPlace).markets(u, m, p))
                .balanceOf(address(this)) - startingZcTokens);

            // Calculate the premium
            uint256 premium = (IERC20(u).balanceOf(address(this)) - starting) -
                fee;

            // Calculate the fee on the premium
            uint256 premiumFee = premium / feenominator;

            // Extract fee from premium
            fees[u] += premiumFee;

            // Remove the fee from the premium
            premium = premium - premiumFee;

            // Store how much the user received in exchange for swapping the premium for iPTs
            uint256 swapped;

            if (e) {
                // Swap the premium for Illuminate principal tokens
                swapped += yield(
                    u,
                    y,
                    premium,
                    msg.sender,
                    IMarketPlace(marketPlace).markets(u, m, 0),
                    premiumSlippage
                );
            } else {
                // Send the premium to the redeemer to hold until redemption
                premiums[u][m] = premiums[u][m] + premium;

                // Account for the premium
                received = received + premium;
            }

            // Mint Illuminate principal tokens to the user
            IERC5095(principalToken(u, m)).authMint(msg.sender, received);

            emit Lend(
                uint8(MarketPlace.Principals.Swivel),
                u,
                m,
                received + swapped,
                lent,
                msg.sender
            );
        }

        // Confirm that minted iPT amount will not exceed rate limit for the protocol
        rateLimit(p, u, received);
        return received;
    }

    /// @notice lend method signature for Element
    /// @param p principal value according to the MarketPlace's Principals Enum
    /// @param u address of an underlying asset
    /// @param m maturity (timestamp) of the market
    /// @param a amount of underlying tokens to lend
    /// @param r slippage limit, minimum amount to PTs to buy
    /// @param d deadline is a timestamp by which the swap must be executed
    /// @param e Element pool that is lent to
    /// @param i the id of the pool
    /// @return uint256 the amount of principal tokens lent out
    function lend(
        uint8 p,
        address u,
        uint256 m,
        uint256 a,
        uint256 r,
        uint256 d,
        address e,
        bytes32 i
    ) external nonReentrant unpaused(u, m, p) matured(m) returns (uint256) {
        // Get the principal token for this market for Element
        address principal = IMarketPlace(marketPlace).markets(u, m, p);

        // Transfer underlying token from user to Illuminate
        Safe.transferFrom(IERC20(u), msg.sender, address(this), a);

        // Track the accumulated fees
        fees[u] = fees[u] + a / feenominator;

        uint256 purchased;
        {
            // Calculate the amount to be lent
            uint256 lent = a - a / feenominator;

            // Create the variables needed to execute an Element swap
            Element.FundManagement memory fund = Element.FundManagement({
                sender: address(this),
                recipient: payable(address(this)),
                fromInternalBalance: false,
                toInternalBalance: false
            });

            Element.SingleSwap memory swap = Element.SingleSwap({
                poolId: i,
                amount: lent,
                kind: Element.SwapKind.GIVEN_IN,
                assetIn: IAny(u),
                assetOut: IAny(principal),
                userData: '0x00000000000000000000000000000000000000000000000000000000000000'
            });

            // Conduct the swap on Element
            purchased = elementSwap(e, swap, fund, r, d);

            // Convert decimals from principal token to underlying
            purchased = convertDecimals(u, principal, purchased);
        }

        // Confirm that minted iPT amount will not exceed rate limit for the protocol
        rateLimit(p, u, purchased);

        // Mint tokens to the user
        IERC5095(principalToken(u, m)).authMint(msg.sender, purchased);

        emit Lend(p, u, m, purchased, a, msg.sender);
        return purchased;
    }

    /// @notice lend method signature for Pendle
    /// @param p principal value according to the MarketPlace's Principals Enum
    /// @param u address of an underlying asset
    /// @param m maturity (timestamp) of the market
    /// @param a amount of underlying tokens to lend
    /// @param r slippage limit, minimum amount to PTs to buy
    /// @param g guess parameters for the swap
    /// @param market contract that corresponds to the market for the PT
    /// @return uint256 the amount of principal tokens lent out
    function lend(
        uint8 p,
        address u,
        uint256 m,
        uint256 a,
        uint256 r,
        Pendle.ApproxParams calldata g,
        address market
    ) external nonReentrant unpaused(u, m, p) matured(m) returns (uint256) {
        // Instantiate market and tokens
        address principal = IMarketPlace(marketPlace).markets(u, m, p);

        // Confirm the market corresponds to this Illuminate market
        (, address marketPrincipal, ) = IPendleMarket(market).readTokens();
        if (marketPrincipal != principal) {
            revert Exception(27, 0, 0, market, principal);
        }

        // Transfer funds from user to Illuminate
        Safe.transferFrom(IERC20(u), msg.sender, address(this), a);

        uint256 returned;
        {
            // Add the accumulated fees to the total
            uint256 fee = a / feenominator;
            fees[u] = fees[u] + fee;

            // Setup the token input
            Pendle.TokenInput memory input = Pendle.TokenInput(
                u,
                a - fee,
                u,
                address(0),
                address(0),
                '0x00000000000000000000000000000000000000000000000000000000000000'
            );

            // Swap on the Pendle Router using the provided market and params
            (returned, ) = IPendle(pendleAddr).swapExactTokenForPt(
                address(this),
                market,
                r,
                g,
                input
            );

            // Convert decimals from principal token to underlying
            returned = convertDecimals(u, principal, returned);
        }

        // Confirm that minted iPT amount will not exceed rate limit for the protocol
        rateLimit(p, u, returned);

        // Mint Illuminate zero coupons
        IERC5095(principalToken(u, m)).authMint(msg.sender, returned);

        emit Lend(p, u, m, returned, a, msg.sender);
        return returned;
    }

    /// @notice lend method signature for Tempus and APWine
    /// @param p value of a specific principal according to the Illuminate Principals Enum
    /// @param u address of an underlying asset
    /// @param m maturity (timestamp) of the market
    /// @param a amount of principal tokens to lend
    /// @param r minimum amount to return when executing the swap (sets a limit to slippage)
    /// @param d deadline is a timestamp by which the swap must be executed
    /// @param x Tempus or APWine AMM that executes the swap
    /// @return uint256 the amount of principal tokens lent out
    function lend(
        uint8 p,
        address u,
        uint256 m,
        uint256 a,
        uint256 r,
        uint256 d,
        address x
    ) external nonReentrant unpaused(u, m, p) matured(m) returns (uint256) {
        address principal = IMarketPlace(marketPlace).markets(u, m, p);

        // Transfer funds from user to Illuminate
        Safe.transferFrom(IERC20(u), msg.sender, address(this), a);

        uint256 lent;
        {
            // Add the accumulated fees to the total
            uint256 fee = a / feenominator;
            fees[u] = fees[u] + fee;

            // Calculate amount to be lent out
            lent = a - fee;
        }

        // Get the starting balance of the principal token
        uint256 start = IERC20(principal).balanceOf(address(this));

        if (p == uint8(MarketPlace.Principals.Tempus)) {
            // Get the Tempus pool from the principal token
            ITempusPool pool = ITempusPool(ITempusToken(principal).pool());

            // Get the pool of the contract the user submitted
            address userPool = ITempusAMM(x).tempusPool();

            // Confirm that the pool matches the principal token
            if (address(pool) != userPool) {
                revert Exception(27, 0, 0, address(pool), userPool);
            }

            // Get the Tempus router from the principal token
            address controller = pool.controller();

            // Swap on the Tempus router using the provided market and params
            ITempus(controller).depositAndFix(x, lent, true, 0, d);
        } else if (p == uint8(MarketPlace.Principals.Apwine)) {
            address poolUnderlying = IAPWineAMMPool(x)
                .getUnderlyingOfIBTAddress();
            if (u != poolUnderlying) {
                revert Exception(27, 0, 0, u, poolUnderlying);
            }
            address poolPrincipal = IAPWineAMMPool(x).getPTAddress();
            if (principal != poolPrincipal) {
                revert Exception(27, 0, 0, principal, poolPrincipal);
            }
            // Swap on the APWine Pool using the provided market and params
            IAPWineRouter(apwineAddr).swapExactAmountIn(
                x,
                apwinePairPath(),
                apwineTokenPath(),
                lent,
                r,
                address(this),
                d,
                address(0)
            );
        }

        // Calculate the amount of Tempus principal tokens received after the deposit
        uint256 received = IERC20(principal).balanceOf(address(this)) - start;

        // Convert decimals from principal token to underlying
        received = convertDecimals(u, principal, received);

        // Verify that a minimum number of principal tokens were received
        if (p == uint8(MarketPlace.Principals.Tempus) && received < r) {
            revert Exception(11, received, r, address(0), address(0));
        }

        // Confirm that minted iPT amount will not exceed rate limit for the protocol
        rateLimit(p, u, received);

        // Mint Illuminate zero coupons
        IERC5095(principalToken(u, m)).authMint(msg.sender, received);

        emit Lend(p, u, m, received, a, msg.sender);
        return received;
    }

    /// @notice lend method signature for Sense
    /// @dev this method can be called before maturity to lend to Sense while minting Illuminate tokens
    /// @dev Sense provides a [divider] contract that splits [target] assets (underlying) into PTs and YTs. Each [target] asset has a [series] of contracts, each identifiable by their [maturity].
    /// @param p principal value according to the MarketPlace's Principals Enum
    /// @param u address of an underlying asset
    /// @param m maturity (timestamp) of the market
    /// @param a amount of underlying tokens to lend
    /// @param r slippage limit, minimum amount to PTs to buy
    /// @param x periphery contract that is used to conduct the swap
    /// @param s Sense's maturity for the given market
    /// @param adapter Sense's adapter necessary to facilitate the swap
    /// @return uint256 the amount of principal tokens lent out
    function lend(
        uint8 p,
        address u,
        uint256 m,
        uint128 a,
        uint256 r,
        address x,
        uint256 s,
        address adapter
    ) external nonReentrant unpaused(u, m, p) matured(m) returns (uint256) {
        // Get the periphery's divider contract to verify that it maps to the prinicpal token of the market
        address divider = ISensePeriphery(x).divider();

        // Get the principal token for the user submitted periphery
        address userPrincipal = ISenseDivider(divider).pt(adapter, s);

        // Retrieve the principal token for this market
        IERC20 token = IERC20(IMarketPlace(marketPlace).markets(u, m, p));

        // Verify that the `x` parameter matches the market's Sense principal token
        if (address(token) != userPrincipal) {
            revert Exception(27, 0, 0, address(token), userPrincipal);
        }

        // Transfer funds from user to Illuminate
        Safe.transferFrom(IERC20(u), msg.sender, address(this), a);

        // Determine the fee
        uint256 fee = a / feenominator;

        // Add the accumulated fees to the total
        fees[u] = fees[u] + fee;

        // Determine lent amount after fees
        uint256 lent = a - fee;

        // Stores the amount of principal tokens received in swap for underlying
        uint256 received;
        {
            // Get the starting balance of the principal token
            uint256 starting = token.balanceOf(address(this));

            // Swap those tokens for the principal tokens
            ISensePeriphery(x).swapUnderlyingForPTs(adapter, s, lent, r);

            // Calculate number of principal tokens received in the swap
            received = token.balanceOf(address(this)) - starting;

            // Verify that we received the principal tokens
            if (received < r) {
                revert Exception(11, 0, 0, address(0), address(0));
            }
        }

        // Get the Illuminate PT
        address ipt = principalToken(u, m);

        // Calculate the mintable amount of tokens for Sense due to decimal mismatch
        uint256 mintable = convertDecimals(u, address(token), received);

        // Confirm that minted iPT amount will not exceed rate limit for the protocol
        rateLimit(p, u, mintable);

        // Mint the Illuminate tokens based on the returned amount
        IERC5095(ipt).authMint(msg.sender, mintable);

        emit Lend(p, u, m, mintable, a, msg.sender);
        return mintable;
    }

    /// @dev lend method signature for Notional
    /// @param p principal value according to the MarketPlace's Principals Enum
    /// @param u address of an underlying asset
    /// @param m maturity (timestamp) of the market
    /// @param a amount of underlying tokens to lend
    /// @param r slippage limit, minimum amount to PTs to buy
    /// @return uint256 the amount of principal tokens lent out
    function lend(
        uint8 p,
        address u,
        uint256 m,
        uint256 a,
        uint256 r
    ) external nonReentrant unpaused(u, m, p) matured(m) returns (uint256) {
        // Confirm that we are using Notional's PT to avoid conflicts with other ERC4626 tokens
        if (p != uint8(MarketPlace.Principals.Notional)) {
            revert Exception(6, p, 0, address(0), address(0));
        }

        // Instantiate Notional princpal token
        address token = IMarketPlace(marketPlace).markets(u, m, p);

        // Transfer funds from user to Illuminate
        Safe.transferFrom(IERC20(u), msg.sender, address(this), a);

        // Determine the fee
        uint256 fee = a / feenominator;

        // Add the accumulated fees to the total
        fees[u] = fees[u] + fee;

        // Swap on the Notional Token wrapper
        uint256 received = INotional(token).deposit(a - fee, address(this));

        // Convert decimals from principal token to underlying
        received = convertDecimals(u, token, received);

        // Verify that we received the principal tokens
        if (received < r) {
            revert Exception(16, received, r, address(0), address(0));
        }

        // Confirm that minted iPT amount will not exceed rate limit for the protocol
        rateLimit(p, u, received);

        // Mint Illuminate zero coupons
        IERC5095(principalToken(u, m)).authMint(msg.sender, received);

        emit Lend(p, u, m, received, a, msg.sender);
        return received;
    }

    /// @notice allows the admin to schedule the withdrawal of tokens
    /// @param e address of (erc20) token to withdraw
    /// @return bool true if successful
    function scheduleWithdrawal(
        address e
    ) external authorized(admin) returns (bool) {
        // Calculate the timestamp that must be passed prior to withdrawal
        uint256 when = block.timestamp + HOLD;

        // Set the timestamp threshold for the token being withdrawn
        withdrawals[e] = when;

        emit ScheduleWithdrawal(e, when);
        return true;
    }

    /// @notice emergency function to block unplanned withdrawals
    /// @param e address of token withdrawal to block
    /// @return bool true if successful
    function blockWithdrawal(
        address e
    ) external authorized(admin) returns (bool) {
        // Resets threshold to 0 for the token, stopping withdrawl of the token
        delete withdrawals[e];

        emit BlockWithdrawal(e);
        return true;
    }

    /// @notice allows the admin to schedule a change to the fee denominators
    function scheduleFeeChange() external authorized(admin) returns (bool) {
        // Calculate the timestamp that must be passed prior to setting thew new fee
        uint256 when = block.timestamp + HOLD;

        // Store the timestamp that must be passed to update the fee rate
        feeChange = when;

        emit ScheduleFeeChange(when);
        return true;
    }

    /// @notice Emergency function to block unplanned changes to fee structure
    function blockFeeChange() external authorized(admin) returns (bool) {
        // Resets threshold to 0 for the token, stopping the scheduling of a fee rate change
        delete feeChange;

        emit BlockFeeChange();
        return true;
    }

    /// @notice allows the admin to withdraw the given token, provided the holding period has been observed
    /// @param e Address of token to withdraw
    /// @return bool true if successful
    function withdraw(address e) external authorized(admin) returns (bool) {
        // Get the minimum timestamp to withdraw the token
        uint256 when = withdrawals[e];

        // Check that the withdrawal was scheduled for the token
        if (when == 0) {
            revert Exception(18, 0, 0, address(0), address(0));
        }

        // Check that it is now past the scheduled timestamp for withdrawing the token
        if (block.timestamp < when) {
            revert Exception(19, 0, 0, address(0), address(0));
        }

        // Reset the scheduled withdrawal
        delete withdrawals[e];

        // Reset the fees for the token (relevant when withdrawing underlying for markets)
        delete fees[e];

        // Send the token to the admin
        IERC20 token = IERC20(e);
        Safe.transfer(token, admin, token.balanceOf(address(this)));

        return true;
    }

    /// @notice withdraws accumulated lending fees of the underlying token
    /// @param e address of the underlying token to withdraw
    /// @return bool true if successful
    function withdrawFee(address e) external authorized(admin) returns (bool) {
        // Get the token to be withdrawn
        IERC20 token = IERC20(e);

        // Get the balance to be transferred
        uint256 balance = fees[e];

        // Reset accumulated fees of the token to 0
        fees[e] = 0;

        // Transfer the accumulated fees to the admin
        Safe.transfer(token, admin, balance);

        return true;
    }

    /// @notice pauses a market and prevents execution of all lending for that principal
    /// @param p principal value according to the MarketPlace's Principals Enum
    /// @param b bool representing whether to pause or unpause
    /// @return bool true if successful
    function pause(uint8 p, bool b) external authorized(admin) returns (bool) {
        // Set the paused state for the principal token in the market
        paused[p] = b;

        emit PausePrincipal(p, b);
        return true;
    }

    /// @notice pauses Illuminate's redeem, mint and lend methods from being used
    /// @param b bool representing whether to pause or unpause Illuminate
    /// @return bool true if successfully set
    function pauseIlluminate(bool b) external authorized(admin) returns (bool) {
        halted = b;
        emit PauseIlluminate(b);
        return true;
    }

    /// @notice Tranfers FYTs to Redeemer (used specifically for APWine redemptions)
    /// @param f FYT contract address
    /// @param a amount of tokens to send to the redeemer
    function transferFYTs(
        address f,
        uint256 a
    ) external authorized(IMarketPlace(marketPlace).redeemer()) {
        // Transfer the Lender's FYT tokens to the Redeemer
        Safe.transfer(IERC20(f), IMarketPlace(marketPlace).redeemer(), a);
    }

    /// @notice Transfers premium from the market to Redeemer (used specifically for Swivel redemptions)
    /// @param u address of an underlying asset
    /// @param m maturity (timestamp) of the market
    function transferPremium(
        address u,
        uint256 m
    ) external authorized(IMarketPlace(marketPlace).redeemer()) {
        Safe.transfer(
            IERC20(u),
            IMarketPlace(marketPlace).redeemer(),
            premiums[u][m]
        );

        premiums[u][m] = 0;
    }

    /// @notice Allows batched call to self (this contract).
    /// @param c An array of inputs for each call.
    function batch(
        bytes[] calldata c
    ) external payable returns (bytes[] memory results) {
        results = new bytes[](c.length);

        for (uint256 i; i < c.length; i++) {
            (bool success, bytes memory result) = address(this).delegatecall(
                c[i]
            );

            if (!success) revert(RevertMsgExtractor.getRevertMsg(result));
            results[i] = result;
        }
    }

    /// @notice swaps underlying premium via a Yield Space Pool
    /// @dev this method is only used by the Yield, Illuminate and Swivel protocols
    /// @param u address of an underlying asset
    /// @param y Yield Space Pool for the principal token
    /// @param a amount of underlying tokens to lend
    /// @param r the receiving address for PTs
    /// @param p the principal token in the Yield Space Pool
    /// @param m the minimum amount to purchase
    /// @return uint256 the amount of tokens sent to the Yield Space Pool
    function yield(
        address u,
        address y,
        uint256 a,
        address r,
        address p,
        uint256 m
    ) internal returns (uint256) {
        // Get the starting balance (to verify receipt of tokens)
        uint256 starting = IERC20(p).balanceOf(r);

        // Get the amount of tokens received for swapping underlying
        uint128 returned = IYield(y).sellBasePreview(Cast.u128(a));

        // Send the remaining amount to the Yield pool
        Safe.transfer(IERC20(u), y, a);

        // Lend out the remaining tokens in the Yield pool
        IYield(y).sellBase(r, returned);

        // Get the ending balance of principal tokens (must be at least starting + returned)
        uint256 received = IERC20(p).balanceOf(r) - starting;

        // Verify receipt of PTs from Yield Space Pool
        if (received < m) {
            revert Exception(11, received, m, address(0), address(0));
        }

        return received;
    }

    /// @notice returns the amount of underlying tokens to be used in a Swivel lend
    function swivelAmount(uint256[] memory a) internal pure returns (uint256) {
        uint256 lent;

        // Iterate through each order a calculate the total lent and returned
        for (uint256 i; i != a.length; ) {
            {
                // Sum the total amount lent to Swivel
                lent = lent + a[i];
            }

            unchecked {
                ++i;
            }
        }

        return lent;
    }

    /// @notice reverts if any orders are not for the market
    function swivelVerify(Swivel.Order[] memory o, address u) internal pure {
        for (uint256 i; i != o.length; ) {
            address orderUnderlying = o[i].underlying;
            if (u != orderUnderlying) {
                revert Exception(3, 0, 0, u, orderUnderlying);
            }
            unchecked {
                ++i;
            }
        }
    }

    /// @notice executes a swap for and verifies receipt of Element PTs
    function elementSwap(
        address e,
        Element.SingleSwap memory s,
        Element.FundManagement memory f,
        uint256 r,
        uint256 d
    ) internal returns (uint256) {
        // Get the principal token
        address principal = address(s.assetOut);

        // Get the intial balance
        uint256 starting = IERC20(principal).balanceOf(address(this));

        // Conduct the swap on Element
        IElementVault(e).swap(s, f, r, d);

        // Get how many PTs were purchased by the swap call
        uint256 purchased = IERC20(principal).balanceOf(address(this)) -
            starting;

        // Verify that a minimum amount was received
        if (purchased < r) {
            revert Exception(11, 0, 0, address(0), address(0));
        }

        // Return the net amount of principal tokens acquired after the swap
        return purchased;
    }

    /// @notice returns array token path required for APWine's swap method
    /// @return array of uint256[] as laid out in APWine's docs
    function apwineTokenPath() internal pure returns (uint256[] memory) {
        uint256[] memory tokenPath = new uint256[](2);
        tokenPath[0] = 1;
        tokenPath[1] = 0;
        return tokenPath;
    }

    /// @notice returns array pair path required for APWine's swap method
    /// @return array of uint256[] as laid out in APWine's docs
    function apwinePairPath() internal pure returns (uint256[] memory) {
        uint256[] memory pairPath = new uint256[](1);
        pairPath[0] = 0;
        return pairPath;
    }

    /// @notice retrieves the ERC5095 token for the given market
    /// @param u address of an underlying asset
    /// @param m maturity (timestamp) of the market
    /// @return address of the ERC5095 token for the market
    function principalToken(address u, uint256 m) internal returns (address) {
        return IMarketPlace(marketPlace).markets(u, m, 0);
    }

    /// @notice converts principal decimal amount to underlying's decimal amount
    /// @param u address of an underlying asset
    /// @param p address of a principal token
    /// @param a amount denominated in principal token's decimals
    /// @return uint256 in underlying decimals
    function convertDecimals(
        address u,
        address p,
        uint256 a
    ) internal view returns (uint256) {
        // Get the decimals of the underlying asset
        uint8 underlyingDecimals = IERC20(u).decimals();

        // Get the decimals of the principal token
        uint8 principalDecimals = IERC20(p).decimals();

        // Determine which asset has more decimals
        if (underlyingDecimals > principalDecimals) {
            // Shift decimals accordingly
            return a * 10 ** (underlyingDecimals - principalDecimals);
        }
        return a / 10 ** (principalDecimals - underlyingDecimals);
    }

    /// @notice limits the amount of funds (in USD value) that can flow through a principal in a day
    /// @param p principal value according to the MarketPlace's Principals Enum
    /// @param u address of an underlying asset
    /// @param a amount being minted which is normalized to 18 decimals prior to check
    /// @return bool true if successful, reverts otherwise
    function rateLimit(uint8 p, address u, uint256 a) internal returns (bool) {
        // Get amount in USD to be minted
        uint256 valueToMint = a;

        // In case of stETH, we will calculate an approximate USD value
        // 0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84 (stETH address)
        if (u == 0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84) {
            valueToMint = etherPrice * valueToMint;
        }

        // Normalize the value to be minted to 27 decimals
        valueToMint = valueToMint * 10 ** (27 - IERC20(u).decimals());

        // Cache max value
        uint256 maxValue = maximumValue;

        // Transactions of greater than the max value of USD are rate limited
        if (valueToMint > maxValue) {
            revert Exception(31, protocolFlow[p], p, address(0), address(0));
        }

        // Cache protocol flow value
        uint256 flow = protocolFlow[p] + valueToMint;

        // Update the amount of USD value flowing through the protocol
        protocolFlow[p] = flow;

        // If more than one day has passed, do not rate limit
        if (block.timestamp - periodStart[p] > 1 days) {
            // Reset the flow amount
            protocolFlow[p] = valueToMint;

            // Reset the period
            periodStart[p] = block.timestamp;
        }
        // If more than the max USD has flowed through the protocol, revert
        else if (flow > maxValue) {
            revert Exception(31, protocolFlow[p], p, address(0), address(0));
        }

        return true;
    }
}

File 2 of 44 : Marketplace.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.8.16;

import 'src/tokens/ERC5095.sol';
import 'src/lib/Safe.sol';
import 'src/lib/RevertMsgExtractor.sol';
import 'src/errors/Exception.sol';

import 'src/interfaces/ILender.sol';
import 'src/interfaces/ICreator.sol';
import 'src/interfaces/IPool.sol';
import 'src/interfaces/IPendleToken.sol';
import 'src/interfaces/IAPWineToken.sol';
import 'src/interfaces/IAPWineFutureVault.sol';

/// @title MarketPlace
/// @author Sourabh Marathe, Julian Traversa, Rob Robbins
/// @notice This contract is in charge of managing the available principals for each loan market.
/// @notice In addition, this contract routes swap orders between Illuminate PTs and their respective underlying to YieldSpace pools.
contract MarketPlace {
    /// @notice the available principals
    /// @dev the order of this enum is used to select principals from the markets
    /// mapping (e.g. Illuminate => 0, Swivel => 1, and so on)
    enum Principals {
        Illuminate, // 0
        Swivel, // 1
        Yield, // 2
        Element, // 3
        Pendle, // 4
        Tempus, // 5
        Sense, // 6
        Apwine, // 7
        Notional // 8
    }

    /// @notice markets are defined by a tuple that points to a fixed length array of principal token addresses.
    mapping(address => mapping(uint256 => address[9])) public markets;

    /// @notice pools map markets to their respective YieldSpace pools for the MetaPrincipal token
    mapping(address => mapping(uint256 => address)) public pools;

    /// @notice address that is allowed to create markets, set pools, etc. It is commonly used in the authorized modifier.
    address public admin;
    /// @notice address of the deployed redeemer contract
    address public immutable redeemer;
    /// @notice address of the deployed lender contract
    address public immutable lender;
    /// @notice address of the deployed creator contract
    address public immutable creator;

    /// @notice emitted upon the creation of a new market
    event CreateMarket(
        address indexed underlying,
        uint256 indexed maturity,
        address[9] tokens,
        address element,
        address apwine
    );
    /// @notice emitted upon setting a principal token
    event SetPrincipal(
        address indexed underlying,
        uint256 indexed maturity,
        address indexed principal,
        uint8 protocol
    );
    /// @notice emitted upon swapping with the pool
    event Swap(
        address indexed underlying,
        uint256 indexed maturity,
        address sold,
        address bought,
        uint256 received,
        uint256 spent,
        address spender
    );
    /// @notice emitted upon minting tokens with the pool
    event Mint(
        address indexed underlying,
        uint256 indexed maturity,
        uint256 underlyingIn,
        uint256 principalTokensIn,
        uint256 minted,
        address minter
    );
    /// @notice emitted upon burning tokens with the pool
    event Burn(
        address indexed underlying,
        uint256 indexed maturity,
        uint256 tokensBurned,
        uint256 underlyingReceived,
        uint256 principalTokensReceived,
        address burner
    );
    /// @notice emitted upon changing the admin
    event SetAdmin(address indexed admin);
    /// @notice emitted upon setting a pool
    event SetPool(
        address indexed underlying,
        uint256 indexed maturity,
        address indexed pool
    );

    /// @notice ensures that only a certain address can call the function
    /// @param a address that msg.sender must be to be authorized
    modifier authorized(address a) {
        if (msg.sender != a) {
            revert Exception(0, 0, 0, msg.sender, a);
        }
        _;
    }

    /// @notice initializes the MarketPlace contract
    /// @param r address of the deployed redeemer contract
    /// @param l address of the deployed lender contract
    /// @param c address of the deployed creator contract
    constructor(address r, address l, address c) {
        admin = msg.sender;
        redeemer = r;
        lender = l;
        creator = c;
    }

    /// @notice creates a new market for the given underlying token and maturity
    /// @param u address of an underlying asset
    /// @param m maturity (timestamp) of the market
    /// @param t principal token addresses for this market
    /// @param n name for the Illuminate token
    /// @param s symbol for the Illuminate token
    /// @param a address of the APWine router that corresponds to this market
    /// @param e address of the Element vault that corresponds to this market
    /// @param h address of a helper contract, used for Sense approvals if active in the market
    /// @param sensePeriphery address of the Sense periphery contract that must be approved by the lender
    /// @return bool true if successful
    function createMarket(
        address u,
        uint256 m,
        address[8] calldata t,
        string calldata n,
        string calldata s,
        address a,
        address e,
        address h,
        address sensePeriphery
    ) external authorized(admin) returns (bool) {
        {
            // Get the Illuminate principal token for this market (if one exists)
            address illuminate = markets[u][m][0];

            // If illuminate PT already exists, a new market cannot be created
            if (illuminate != address(0)) {
                revert Exception(9, 0, 0, illuminate, address(0));
            }
        }

        // Create an Illuminate principal token for the new market
        address illuminateToken = ICreator(creator).create(
            u,
            m,
            redeemer,
            lender,
            address(this),
            n,
            s
        );

        {
            // create the principal tokens array
            address[9] memory market = [
                illuminateToken, // Illuminate
                t[0], // Swivel
                t[1], // Yield
                t[2], // Element
                t[3], // Pendle
                t[4], // Tempus
                t[5], // Sense
                t[6], // APWine
                t[7] // Notional
            ];

            // Set the market
            markets[u][m] = market;

            // Have the lender contract approve the several contracts
            ILender(lender).approve(u, a, e, t[7], sensePeriphery);

            // Allow converter to spend interest bearing asset
            if (t[5] != address(0)) {
                IRedeemer(redeemer).approve(h);
            }

            // Approve interest bearing token conversion to underlying for APWine
            if (t[6] != address(0)) {
                address futureVault = IAPWineToken(t[6]).futureVault();
                address interestBearingToken = IAPWineFutureVault(futureVault)
                    .getIBTAddress();
                IRedeemer(redeemer).approve(interestBearingToken);
            }

            emit CreateMarket(u, m, market, e, a);
        }
        return true;
    }

    /// @notice allows the admin to set an individual market
    /// @param p principal value according to the MarketPlace's Principals Enum
    /// @param u address of an underlying asset
    /// @param m maturity (timestamp) of the market
    /// @param a address of the new principal token
    /// @param h a supplementary address (apwine needs a router, element needs a vault, sense needs interest bearing asset)
    /// @param sensePeriphery address of the Sense periphery contract that must be approved by the lender
    /// @return bool true if the principal set, false otherwise
    function setPrincipal(
        uint8 p,
        address u,
        uint256 m,
        address a,
        address h,
        address sensePeriphery
    ) external authorized(admin) returns (bool) {
        // Set the principal token in the markets mapping
        markets[u][m][p] = a;

        if (p == uint8(Principals.Element)) {
            // Approve Element vault if setting Element's principal token
            ILender(lender).approve(u, address(0), h, address(0), address(0));
        } else if (p == uint8(Principals.Sense)) {
            // Approve converter to transfer yield token for Sense's redeem
            IRedeemer(redeemer).approve(h);

            // Approve Periphery to be used from Lender
            ILender(lender).approve(
                u,
                address(0),
                address(0),
                address(0),
                sensePeriphery
            );
        } else if (p == uint8(Principals.Apwine)) {
            // Approve converter to transfer yield token for APWine's redeem
            address futureVault = IAPWineToken(a).futureVault();
            address interestBearingToken = IAPWineFutureVault(futureVault)
                .getIBTAddress();
            IRedeemer(redeemer).approve(interestBearingToken);

            // Approve APWine's router if setting APWine's principal token
            ILender(lender).approve(u, h, address(0), address(0), address(0));
        } else if (p == uint8(Principals.Notional)) {
            // Principal token must be approved for Notional's lend
            ILender(lender).approve(u, address(0), address(0), a, address(0));
        }

        emit SetPrincipal(u, m, a, p);
        return true;
    }

    /// @notice sets the admin address
    /// @param a Address of a new admin
    /// @return bool true if the admin set, false otherwise
    function setAdmin(address a) external authorized(admin) returns (bool) {
        admin = a;
        emit SetAdmin(a);
        return true;
    }

    /// @notice sets the address for a pool
    /// @param u address of an underlying asset
    /// @param m maturity (timestamp) of the market
    /// @param a address of the pool
    /// @return bool true if the pool set, false otherwise
    function setPool(
        address u,
        uint256 m,
        address a
    ) external authorized(admin) returns (bool) {
        // Set the pool
        pools[u][m] = a;

        // Get the principal token
        ERC5095 pt = ERC5095(markets[u][m][uint8(Principals.Illuminate)]);

        // Set the pool for the principal token
        pt.setPool(a);

        // Approve the marketplace to spend the principal and underlying tokens
        pt.approveMarketPlace();

        emit SetPool(u, m, a);
        return true;
    }

    /// @notice sells the PT for the underlying via the pool
    /// @param u address of an underlying asset
    /// @param m maturity (timestamp) of the market
    /// @param a amount of PTs to sell
    /// @param s slippage cap, minimum amount of underlying that must be received
    /// @return uint128 amount of underlying bought
    function sellPrincipalToken(
        address u,
        uint256 m,
        uint128 a,
        uint128 s
    ) external returns (uint128) {
        // Get the pool for the market
        IPool pool = IPool(pools[u][m]);

        // Preview amount of underlying received by selling `a` PTs
        uint256 expected = pool.sellFYTokenPreview(a);

        // Verify that the amount needed does not exceed the slippage parameter
        if (expected < s) {
            revert Exception(16, expected, s, address(0), address(0));
        }

        // Transfer the principal tokens to the pool
        Safe.transferFrom(
            IERC20(address(pool.fyToken())),
            msg.sender,
            address(pool),
            a
        );

        // Execute the swap
        uint128 received = pool.sellFYToken(msg.sender, s);
        emit Swap(u, m, address(pool.fyToken()), u, received, a, msg.sender);

        return received;
    }

    /// @notice buys the PT for the underlying via the pool
    /// @notice determines how many underlying to sell by using the preview
    /// @param u address of an underlying asset
    /// @param m maturity (timestamp) of the market
    /// @param a amount of PTs to be purchased
    /// @param s slippage cap, maximum number of underlying that can be sold
    /// @return uint128 amount of underlying sold
    function buyPrincipalToken(
        address u,
        uint256 m,
        uint128 a,
        uint128 s
    ) external returns (uint128) {
        // Get the pool for the market
        IPool pool = IPool(pools[u][m]);

        // Get the amount of base hypothetically required to purchase `a` PTs
        uint128 expected = pool.buyFYTokenPreview(a);

        // Verify that the amount needed does not exceed the slippage parameter
        if (expected > s) {
            revert Exception(16, expected, 0, address(0), address(0));
        }

        // Transfer the underlying tokens to the pool
        Safe.transferFrom(
            IERC20(pool.base()),
            msg.sender,
            address(pool),
            expected
        );

        // Execute the swap to purchase `a` base tokens
        uint128 spent = pool.buyFYToken(msg.sender, a, s);

        emit Swap(u, m, u, address(pool.fyToken()), a, spent, msg.sender);
        return spent;
    }

    /// @notice sells the underlying for the PT via the pool
    /// @param u address of an underlying asset
    /// @param m maturity (timestamp) of the market
    /// @param a amount of underlying to sell
    /// @param s slippage cap, minimum number of PTs that must be received
    /// @return uint128 amount of PT purchased
    function sellUnderlying(
        address u,
        uint256 m,
        uint128 a,
        uint128 s
    ) external returns (uint128) {
        // Get the pool for the market
        IPool pool = IPool(pools[u][m]);

        // Get the number of PTs received for selling `a` underlying tokens
        uint128 expected = pool.sellBasePreview(a);

        // Verify slippage does not exceed the one set by the user
        if (expected < s) {
            revert Exception(16, expected, 0, address(0), address(0));
        }

        // Transfer the underlying tokens to the pool
        Safe.transferFrom(IERC20(pool.base()), msg.sender, address(pool), a);

        // Execute the swap
        uint128 received = pool.sellBase(msg.sender, s);

        emit Swap(u, m, u, address(pool.fyToken()), received, a, msg.sender);
        return received;
    }

    /// @notice buys the underlying for the PT via the pool
    /// @notice determines how many PTs to sell by using the preview
    /// @param u address of an underlying asset
    /// @param m maturity (timestamp) of the market
    /// @param a amount of underlying to be purchased
    /// @param s slippage cap, maximum number of PTs that can be sold
    /// @return uint128 amount of PTs sold
    function buyUnderlying(
        address u,
        uint256 m,
        uint128 a,
        uint128 s
    ) external returns (uint128) {
        // Get the pool for the market
        IPool pool = IPool(pools[u][m]);

        // Get the amount of PTs hypothetically required to purchase `a` underlying
        uint256 expected = pool.buyBasePreview(a);

        // Verify that the amount needed does not exceed the slippage parameter
        if (expected > s) {
            revert Exception(16, expected, 0, address(0), address(0));
        }

        // Transfer the principal tokens to the pool
        Safe.transferFrom(
            IERC20(address(pool.fyToken())),
            msg.sender,
            address(pool),
            expected
        );

        // Execute the swap to purchase `a` underlying tokens
        uint128 spent = pool.buyBase(msg.sender, a, s);

        emit Swap(u, m, address(pool.fyToken()), u, a, spent, msg.sender);
        return spent;
    }

    /// @notice mint liquidity tokens in exchange for adding underlying and PT
    /// @dev amount of liquidity tokens to mint is calculated from the amount of unaccounted for PT in this contract.
    /// @dev A proportional amount of underlying tokens need to be present in this contract, also unaccounted for.
    /// @param u the address of the underlying token
    /// @param m the maturity of the principal token
    /// @param b number of base tokens
    /// @param p the principal token amount being sent
    /// @param minRatio minimum ratio of LP tokens to PT in the pool.
    /// @param maxRatio maximum ratio of LP tokens to PT in the pool.
    /// @return uint256 number of base tokens passed to the method
    /// @return uint256 number of yield tokens passed to the method
    /// @return uint256 the amount of tokens minted.
    function mint(
        address u,
        uint256 m,
        uint256 b,
        uint256 p,
        uint256 minRatio,
        uint256 maxRatio
    ) external returns (uint256, uint256, uint256) {
        // Get the pool for the market
        IPool pool = IPool(pools[u][m]);

        // Transfer the underlying tokens to the pool
        Safe.transferFrom(IERC20(pool.base()), msg.sender, address(pool), b);

        // Transfer the principal tokens to the pool
        Safe.transferFrom(
            IERC20(address(pool.fyToken())),
            msg.sender,
            address(pool),
            p
        );

        // Mint the tokens and return the leftover assets to the caller
        (uint256 underlyingIn, uint256 principalTokensIn, uint256 minted) = pool
            .mint(msg.sender, msg.sender, minRatio, maxRatio);

        emit Mint(u, m, underlyingIn, principalTokensIn, minted, msg.sender);
        return (underlyingIn, principalTokensIn, minted);
    }

    /// @notice Mint liquidity tokens in exchange for adding only underlying
    /// @dev amount of liquidity tokens is calculated from the amount of PT to buy from the pool,
    /// plus the amount of unaccounted for PT in this contract.
    /// @param u the address of the underlying token
    /// @param m the maturity of the principal token
    /// @param a the underlying amount being sent
    /// @param p amount of `PT` being bought in the Pool, from this we calculate how much underlying it will be taken in.
    /// @param minRatio minimum ratio of LP tokens to PT in the pool.
    /// @param maxRatio maximum ratio of LP tokens to PT in the pool.
    /// @return uint256 number of base tokens passed to the method
    /// @return uint256 number of yield tokens passed to the method
    /// @return uint256 the amount of tokens minted.
    function mintWithUnderlying(
        address u,
        uint256 m,
        uint256 a,
        uint256 p,
        uint256 minRatio,
        uint256 maxRatio
    ) external returns (uint256, uint256, uint256) {
        // Get the pool for the market
        IPool pool = IPool(pools[u][m]);

        // Transfer the underlying tokens to the pool
        Safe.transferFrom(IERC20(pool.base()), msg.sender, address(pool), a);

        // Mint the tokens to the user
        (uint256 underlyingIn, , uint256 minted) = pool.mintWithBase(
            msg.sender,
            msg.sender,
            p,
            minRatio,
            maxRatio
        );

        emit Mint(u, m, underlyingIn, 0, minted, msg.sender);
        return (underlyingIn, 0, minted);
    }

    /// @notice burn liquidity tokens in exchange for underlying and PT.
    /// @param u the address of the underlying token
    /// @param m the maturity of the principal token
    /// @param a the amount of liquidity tokens to burn
    /// @param minRatio minimum ratio of LP tokens to PT in the pool
    /// @param maxRatio maximum ratio of LP tokens to PT in the pool
    /// @return uint256 amount of LP tokens burned
    /// @return uint256 amount of base tokens received
    /// @return uint256 amount of fyTokens received
    function burn(
        address u,
        uint256 m,
        uint256 a,
        uint256 minRatio,
        uint256 maxRatio
    ) external returns (uint256, uint256, uint256) {
        // Get the pool for the market
        IPool pool = IPool(pools[u][m]);

        // Transfer the underlying tokens to the pool
        Safe.transferFrom(IERC20(address(pool)), msg.sender, address(pool), a);

        // Burn the tokens
        (
            uint256 tokensBurned,
            uint256 underlyingReceived,
            uint256 principalTokensReceived
        ) = pool.burn(msg.sender, msg.sender, minRatio, maxRatio);

        emit Burn(
            u,
            m,
            tokensBurned,
            underlyingReceived,
            principalTokensReceived,
            msg.sender
        );
        return (tokensBurned, underlyingReceived, principalTokensReceived);
    }

    /// @notice burn liquidity tokens in exchange for underlying.
    /// @param u the address of the underlying token
    /// @param m the maturity of the principal token
    /// @param a the amount of liquidity tokens to burn
    /// @param minRatio minimum ratio of LP tokens to PT in the pool.
    /// @param maxRatio minimum ratio of LP tokens to PT in the pool.
    /// @return uint256 amount of PT tokens sent to the pool
    /// @return uint256 amount of underlying tokens returned
    function burnForUnderlying(
        address u,
        uint256 m,
        uint256 a,
        uint256 minRatio,
        uint256 maxRatio
    ) external returns (uint256, uint256) {
        // Get the pool for the market
        IPool pool = IPool(pools[u][m]);

        // Transfer the underlying tokens to the pool
        Safe.transferFrom(IERC20(address(pool)), msg.sender, address(pool), a);

        // Burn the tokens in exchange for underlying tokens
        (uint256 tokensBurned, uint256 underlyingReceived) = pool.burnForBase(
            msg.sender,
            minRatio,
            maxRatio
        );

        emit Burn(u, m, tokensBurned, underlyingReceived, 0, msg.sender);
        return (tokensBurned, underlyingReceived);
    }

    /// @notice Allows batched call to self (this contract).
    /// @param c An array of inputs for each call.
    function batch(
        bytes[] calldata c
    ) external payable returns (bytes[] memory results) {
        results = new bytes[](c.length);
        for (uint256 i; i < c.length; i++) {
            (bool success, bytes memory result) = address(this).delegatecall(
                c[i]
            );
            if (!success) revert(RevertMsgExtractor.getRevertMsg(result));
            results[i] = result;
        }
    }
}

File 3 of 44 : Exception.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.8.16;

/// @dev A single custom error capable of indicating a wide range of detected errors by providing
/// an error code value whose string representation is documented in errors.txt, and any possible other values
/// that are pertinent to the error.
error Exception(uint8, uint256, uint256, address, address);

File 4 of 44 : IAPWineAMMPool.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.8.16;

interface IAPWineAMMPool {
    function getUnderlyingOfIBTAddress() external view returns (address);

    function getPTAddress() external view returns (address);
}

File 5 of 44 : IAPWineController.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.8.16;

interface IAPWineController {
    function getNextPeriodStart(uint256) external view returns (uint256);

    function withdraw(address, uint256) external;

    function createFYTDelegationTo(
        address,
        address,
        uint256
    ) external;
}

File 6 of 44 : IAPWineFutureVault.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.8.16;

interface IAPWineFutureVault {
    function PERIOD_DURATION() external view returns (uint256);

    function getControllerAddress() external view returns (address);

    function getCurrentPeriodIndex() external view returns (uint256);

    function getFYTofPeriod(uint256) external view returns (address);

    function getIBTAddress() external view returns (address);

    function startNewPeriod() external;
}

File 7 of 44 : IAPWineRouter.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.8.16;

interface IAPWineRouter {
    function swapExactAmountIn(
        address,
        uint256[] calldata,
        uint256[] calldata,
        uint256,
        uint256,
        address,
        uint256,
        address
    ) external returns (uint256);
}

File 8 of 44 : IAPWineToken.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.8.16;

interface IAPWineToken {
    function futureVault() external view returns (address);
}

File 9 of 44 : IAny.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.8.16;

interface IAny {}

File 10 of 44 : ICreator.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.8.16;

interface ICreator {
    function create(
        address,
        uint256,
        address,
        address,
        address,
        string calldata,
        string calldata
    ) external returns (address);
}

File 11 of 44 : IERC20.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.8.16;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

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

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

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

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

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

    /**
     * @dev Returns the number of decimals the token uses - e.g. 8, means to
     * divide the token amount by 100000000 to get its user representation.
     */
    function decimals() external view returns (uint8);

    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

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

File 12 of 44 : IERC20Metadata.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.8.16;

import 'src/interfaces/IERC20.sol';

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);
}

File 13 of 44 : IERC2612.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.8.16;

import 'src/interfaces/IERC20Metadata.sol';

/**
 * @dev Interface of the ERC2612 standard as defined in the EIP.
 *
 * Adds the {permit} method, which can be used to change one's
 * {IERC20-allowance} without having to send a transaction, by signing a
 * message. This allows users to spend tokens without having to hold Ether.
 *
 * See https://eips.ethereum.org/EIPS/eip-2612.
 */
interface IERC2612 is IERC20Metadata {
    /**
     * @dev Sets `amount` 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:
     *
     * - `owner` cannot be the zero address.
     * - `spender` cannot be the zero address.
     * - `deadline` must be a timestamp in the future.
     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
     * over the EIP712-formatted function arguments.
     * - the signature must use ``owner``'s current nonce (see {nonces}).
     *
     * For more information on the signature format, see the
     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
     * section].
     */
    function permit(
        address owner,
        address spender,
        uint256 amount,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    /**
     * @dev Returns the current ERC2612 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);
}

File 14 of 44 : IERC5095.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.8.16;

import 'src/interfaces/IERC2612.sol';

interface IERC5095 is IERC2612 {
    function maturity() external view returns (uint256);

    function underlying() external view returns (address);

    function convertToUnderlying(uint256) external view returns (uint256);

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

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

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

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

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

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

    function withdraw(
        uint256,
        address,
        address
    ) external returns (uint256);

    function redeem(
        uint256,
        address,
        address
    ) external returns (uint256);

    function deposit(uint256, address) external returns (uint256);

    function mint(uint256, address) external returns (uint256);

    function authMint(address, uint256) external returns (bool);

    function authBurn(address, uint256) external returns (bool);

    function authApprove(
        address,
        address,
        uint256
    ) external returns (bool);
}

File 15 of 44 : IElementToken.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.8.16;

interface IElementToken {
    function unlockTimestamp() external view returns (uint256);

    function underlying() external returns (address);

    function withdrawPrincipal(uint256 amount, address destination)
        external
        returns (uint256);
}

File 16 of 44 : IElementVault.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.16;

import 'src/lib/Element.sol';

interface IElementVault {
    function swap(
        Element.SingleSwap memory,
        Element.FundManagement memory,
        uint256,
        uint256
    ) external returns (uint256);
}

File 17 of 44 : ILender.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.8.16;

interface ILender {
    function approve(
        address,
        address,
        address,
        address,
        address
    ) external;

    function transferFYTs(address, uint256) external;

    function transferPremium(address, uint256) external;

    function paused(uint8) external returns (bool);

    function halted() external returns (bool);
}

File 18 of 44 : IMarketPlace.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.8.16;

interface IMarketPlace {
    function markets(
        address,
        uint256,
        uint256
    ) external returns (address);

    function pools(address, uint256) external view returns (address);

    function sellPrincipalToken(
        address,
        uint256,
        uint128,
        uint128
    ) external returns (uint128);

    function buyPrincipalToken(
        address,
        uint256,
        uint128,
        uint128
    ) external returns (uint128);

    function sellUnderlying(
        address,
        uint256,
        uint128,
        uint128
    ) external returns (uint128);

    function buyUnderlying(
        address,
        uint256,
        uint128,
        uint128
    ) external returns (uint128);

    function redeemer() external view returns (address);
}

File 19 of 44 : INotional.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.8.16;

import 'src/interfaces/IERC20.sol';

interface INotional {
    function getUnderlyingToken() external view returns (IERC20, int256);

    function getMaturity() external view returns (uint40);

    function deposit(uint256, address) external returns (uint256);

    function maxRedeem(address) external returns (uint256);

    function redeem(
        uint256,
        address,
        address
    ) external returns (uint256);
}

File 20 of 44 : IPendle.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.16;

import 'src/lib/Pendle.sol';

interface IPendle {
    function swapExactTokenForPt(
        address,
        address,
        uint256,
        Pendle.ApproxParams calldata,
        Pendle.TokenInput calldata
    ) external returns (uint256, uint256);
}

File 21 of 44 : IPendleMarket.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.16;

import 'src/lib/Pendle.sol';

interface IPendleMarket {
    function readTokens()
        external
        view
        returns (
            address,
            address,
            address
        );
}

File 22 of 44 : IPendleToken.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.8.16;

interface IPendleToken {
    function SY() external view returns (address);

    function YT() external view returns (address);

    function expiry() external view returns (uint256);
}

File 23 of 44 : IPool.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.16;

import 'src/interfaces/IERC20.sol';
import 'src/interfaces/IERC5095.sol';

interface IPool {
    function ts() external view returns (int128);

    function g1() external view returns (int128);

    function g2() external view returns (int128);

    function maturity() external view returns (uint32);

    function scaleFactor() external view returns (uint96);

    function getCache()
        external
        view
        returns (
            uint112,
            uint112,
            uint32
        );

    // NOTE This will be deprecated
    function base() external view returns (IERC20);

    function baseToken() external view returns (address);

    function fyToken() external view returns (IERC5095);

    function getBaseBalance() external view returns (uint112);

    function getFYTokenBalance() external view returns (uint112);

    function retrieveBase(address) external returns (uint128 retrieved);

    function retrieveFYToken(address) external returns (uint128 retrieved);

    function sellBase(address, uint128) external returns (uint128);

    function buyBase(
        address,
        uint128,
        uint128
    ) external returns (uint128);

    function sellFYToken(address, uint128) external returns (uint128);

    function buyFYToken(
        address,
        uint128,
        uint128
    ) external returns (uint128);

    function sellBasePreview(uint128) external view returns (uint128);

    function buyBasePreview(uint128) external view returns (uint128);

    function sellFYTokenPreview(uint128) external view returns (uint128);

    function buyFYTokenPreview(uint128) external view returns (uint128);

    function mint(
        address,
        address,
        uint256,
        uint256
    )
        external
        returns (
            uint256,
            uint256,
            uint256
        );

    function mintWithBase(
        address,
        address,
        uint256,
        uint256,
        uint256
    )
        external
        returns (
            uint256,
            uint256,
            uint256
        );

    function burn(
        address,
        address,
        uint256,
        uint256
    )
        external
        returns (
            uint256,
            uint256,
            uint256
        );

    function burnForBase(
        address,
        uint256,
        uint256
    ) external returns (uint256, uint256);

    function cumulativeBalancesRatio() external view returns (uint256);

    function sync() external;
}

File 24 of 44 : IRedeemer.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.16;

interface IRedeemer {
    function authRedeem(
        address underlying,
        uint256 maturity,
        address from,
        address to,
        uint256 amount
    ) external returns (uint256);

    function approve(address p) external;

    function holdings(address u, uint256 m) external view returns (uint256);
}

File 25 of 44 : ISenseDivider.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.8.16;

interface ISenseDivider {
    function redeem(
        address,
        uint256,
        uint256
    ) external returns (uint256);

    function pt(address, uint256) external view returns (address);

    // only used by integration tests
    function settleSeries(address, uint256) external;

    function adapterAddresses(uint256) external view returns (address);
}

File 26 of 44 : ISensePeriphery.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.8.16;

interface ISensePeriphery {
    function divider() external view returns (address);

    function swapUnderlyingForPTs(
        address,
        uint256,
        uint256,
        uint256
    ) external returns (uint256);

    function verified(address) external view returns (bool);
}

File 27 of 44 : ISwivel.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.8.16;

import 'src/lib/Swivel.sol';

interface ISwivel {
    function initiate(
        Swivel.Order[] calldata,
        uint256[] calldata,
        Swivel.Components[] calldata
    ) external returns (bool);

    function redeemZcToken(
        uint8 p,
        address u,
        uint256 m,
        uint256 a
    ) external returns (bool);
}

File 28 of 44 : ISwivelToken.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.8.16;

interface ISwivelToken {
    function maturity() external view returns (uint256);
}

File 29 of 44 : ITempus.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.8.16;

import 'src/interfaces/IERC20Metadata.sol';
import 'src/interfaces/IAny.sol';

interface ITempus {
    function depositAndFix(
        address,
        uint256,
        bool,
        uint256,
        uint256
    ) external;

    function redeemToBacking(
        address,
        uint256,
        uint256,
        address
    ) external;
}

File 30 of 44 : ITempusAMM.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.8.16;

interface ITempusAMM {
    function balanceOf(address) external view returns (uint256);

    function tempusPool() external view returns (address);
}

File 31 of 44 : ITempusPool.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.8.16;

import 'src/interfaces/IERC20Metadata.sol';

interface ITempusPool {
    function maturityTime() external view returns (uint256);

    function backingToken() external view returns (address);

    function controller() external view returns (address);

    // Used for integration testing
    function principalShare() external view returns (address);

    function currentInterestRate() external view returns (uint256);

    function initialInterestRate() external view returns (uint256);
}

File 32 of 44 : ITempusToken.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.8.16;

interface ITempusToken {
    function balanceOf(address) external returns (uint256);

    function pool() external view returns (address);
}

File 33 of 44 : IYield.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.8.16;

import 'src/interfaces/IERC20.sol';

interface IYield {
    function maturity() external view returns (uint32);

    function base() external view returns (IERC20);

    function sellBase(address, uint128) external returns (uint128);

    function sellBasePreview(uint128) external view returns (uint128);

    function fyToken() external returns (address);

    function sellFYToken(address, uint128) external returns (uint128);

    function sellFYTokenPreview(uint128) external view returns (uint128);

    function buyBase(
        address,
        uint128,
        uint128
    ) external returns (uint128);

    function buyBasePreview(uint128) external view returns (uint128);

    function buyFYToken(
        address,
        uint128,
        uint128
    ) external returns (uint128);

    function buyFYTokenPreview(uint128) external view returns (uint128);
}

File 34 of 44 : IYieldToken.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.8.16;

interface IYieldToken {
    function redeem(address, uint256) external returns (uint256);

    function underlying() external returns (address);

    function maturity() external view returns (uint256);
}

File 35 of 44 : Cast.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity ^0.8.13;

library Cast {
    /// @dev Safely cast an uint256 to an uint128
    /// @param n the u256 to cast to u128
    function u128(uint256 n) internal pure returns (uint128) {
        if (n > type(uint128).max) {
            revert();
        }
        return uint128(n);
    }
}

File 36 of 44 : Element.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity ^0.8.13;

import 'src/interfaces/IAny.sol';

library Element {
    enum SwapKind {
        GIVEN_IN,
        GIVEN_OUT
    }

    struct SingleSwap {
        bytes32 poolId;
        SwapKind kind;
        IAny assetIn;
        IAny assetOut;
        uint256 amount;
        bytes userData;
    }

    struct FundManagement {
        address sender;
        bool fromInternalBalance;
        address payable recipient;
        bool toInternalBalance;
    }
}

File 37 of 44 : Maturities.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.8.16;

import 'src/interfaces/IERC5095.sol';
import 'src/interfaces/ISwivelToken.sol';
import 'src/interfaces/IYieldToken.sol';
import 'src/interfaces/IElementToken.sol';
import 'src/interfaces/IPendleToken.sol';
import 'src/interfaces/ITempusToken.sol';
import 'src/interfaces/ITempusPool.sol';
import 'src/interfaces/IAPWineToken.sol';
import 'src/interfaces/IAPWineFutureVault.sol';
import 'src/interfaces/IAPWineController.sol';
import 'src/interfaces/INotional.sol';

library Maturities {
    /// @notice returns the maturity for an Illumiante principal token
    /// @param p address of the principal token contract
    /// @return uint256 maturity of the principal token
    function illuminate(address p) internal view returns (uint256) {
        return IERC5095(p).maturity();
    }

    /// @notice returns the maturity for a Swivel principal token
    /// @param p address of the principal token contract
    /// @return uint256 maturity of the principal token
    function swivel(address p) internal view returns (uint256) {
        return ISwivelToken(p).maturity();
    }

    function yield(address p) internal view returns (uint256) {
        return IYieldToken(p).maturity();
    }

    /// @notice returns the maturity for an Element principal token
    /// @param p address of the principal token contract
    /// @return uint256 maturity of the principal token
    function element(address p) internal view returns (uint256) {
        return IElementToken(p).unlockTimestamp();
    }

    /// @notice returns the maturity for a Pendle principal token
    /// @param p address of the principal token contract
    /// @return uint256 maturity of the principal token
    function pendle(address p) internal view returns (uint256) {
        return IPendleToken(p).expiry();
    }

    /// @notice returns the maturity for a Tempus principal token
    /// @param p address of the principal token contract
    /// @return uint256 maturity of the principal token
    function tempus(address p) internal view returns (uint256) {
        return ITempusPool(ITempusToken(p).pool()).maturityTime();
    }

    /// @notice returns the maturity for a APWine principal token
    /// @param p address of the principal token contract
    /// @return uint256 maturity of the principal token
    function apwine(address p) internal view returns (uint256) {
        address futureVault = IAPWineToken(p).futureVault();

        address controller = IAPWineFutureVault(futureVault)
            .getControllerAddress();

        uint256 duration = IAPWineFutureVault(futureVault).PERIOD_DURATION();

        return IAPWineController(controller).getNextPeriodStart(duration);
    }

    /// @notice returns the maturity for a Notional principal token
    /// @param p address of the principal token contract
    /// @return uint256 maturity of the principal token
    function notional(address p) internal view returns (uint256) {
        return INotional(p).getMaturity();
    }
}

File 38 of 44 : Pendle.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.8.16;

library Pendle {
    struct ApproxParams {
        uint256 guessMin;
        uint256 guessMax;
        uint256 guessOffchain;
        uint256 maxIteration;
        uint256 eps;
    }

    struct TokenInput {
        // Token/Sy data
        address tokenIn;
        uint256 netTokenIn;
        address tokenMintSy;
        address bulk;
        // Kyber data
        address kyberRouter;
        bytes kybercall;
    }
}

File 39 of 44 : RevertMsgExtractor.sol
// SPDX-License-Identifier: MIT
// Taken from https://github.com/sushiswap/BoringSolidity/blob/441e51c0544cf2451e6116fe00515e71d7c42e2c/contracts/BoringBatchable.sol

pragma solidity >=0.6.0;

library RevertMsgExtractor {
    /// @dev Helper function to extract a useful revert message from a failed call.
    /// If the returned data is malformed or not correctly abi encoded then this call can fail itself.
    function getRevertMsg(bytes memory returnData)
        internal
        pure
        returns (string memory)
    {
        // If the _res length is less than 68, then the transaction failed silently (without a revert message)
        if (returnData.length < 68) return 'Transaction reverted silently';

        assembly {
            // Slice the sighash.
            returnData := add(returnData, 0x04)
        }
        return abi.decode(returnData, (string)); // All that remains is the revert string
    }
}

File 40 of 44 : Safe.sol
// SPDX-License-Identifier: UNLICENSED
// Adapted from: https://github.com/Rari-Capital/solmate/blob/main/src/utils/SafeTransferLib.sol

pragma solidity ^0.8.13;

import 'src/interfaces/IERC20.sol';

/**
  @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
  @author Modified from Gnosis (https://github.com/gnosis/gp-v2-contracts/blob/main/src/contracts/libraries/GPv2SafeERC20.sol)
  @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer.
*/

library Safe {
    /// @param e Erc20 token to execute the call with
    /// @param t To address
    /// @param a Amount being transferred
    function transfer(
        IERC20 e,
        address t,
        uint256 a
    ) internal {
        bool result;

        assembly {
            // Get a pointer to some free memory.
            let pointer := mload(0x40)

            // Write the abi-encoded calldata to memory piece by piece:
            mstore(
                pointer,
                0xa9059cbb00000000000000000000000000000000000000000000000000000000
            ) // Begin with the function selector.
            mstore(
                add(pointer, 4),
                and(t, 0xffffffffffffffffffffffffffffffffffffffff)
            ) // Mask and append the "to" argument.
            mstore(add(pointer, 36), a) // Finally append the "amount" argument. No mask as it's a full 32 byte value.

            // Call the token and store if it succeeded or not.
            // We use 68 because the calldata length is 4 + 32 * 2.
            result := call(gas(), e, 0, pointer, 68, 0, 0)
        }

        require(success(result), 'transfer failed');
    }

    /// @param e Erc20 token to execute the call with
    /// @param f From address
    /// @param t To address
    /// @param a Amount being transferred
    function transferFrom(
        IERC20 e,
        address f,
        address t,
        uint256 a
    ) internal {
        bool result;

        assembly {
            // Get a pointer to some free memory.
            let pointer := mload(0x40)

            // Write the abi-encoded calldata to memory piece by piece:
            mstore(
                pointer,
                0x23b872dd00000000000000000000000000000000000000000000000000000000
            ) // Begin with the function selector.
            mstore(
                add(pointer, 4),
                and(f, 0xffffffffffffffffffffffffffffffffffffffff)
            ) // Mask and append the "from" argument.
            mstore(
                add(pointer, 36),
                and(t, 0xffffffffffffffffffffffffffffffffffffffff)
            ) // Mask and append the "to" argument.
            mstore(add(pointer, 68), a) // Finally append the "amount" argument. No mask as it's a full 32 byte value.

            // Call the token and store if it succeeded or not.
            // We use 100 because the calldata length is 4 + 32 * 3.
            result := call(gas(), e, 0, pointer, 100, 0, 0)
        }

        require(success(result), 'transfer from failed');
    }

    /// @notice normalize the acceptable values of true or null vs the unacceptable value of false (or something malformed)
    /// @param r Return value from the assembly `call()` to Erc20['selector']
    function success(bool r) private pure returns (bool) {
        bool result;

        assembly {
            // Get how many bytes the call returned.
            let returnDataSize := returndatasize()

            // If the call reverted:
            if iszero(r) {
                // Copy the revert message into memory.
                returndatacopy(0, 0, returnDataSize)

                // Revert with the same message.
                revert(0, returnDataSize)
            }

            switch returnDataSize
            case 32 {
                // Copy the return data into memory.
                returndatacopy(0, 0, returnDataSize)

                // Set success to whether it returned true.
                result := iszero(iszero(mload(0)))
            }
            case 0 {
                // There was no return data.
                result := 1
            }
            default {
                // It returned some malformed input.
                result := 0
            }
        }

        return result;
    }

    function approve(
        IERC20 token,
        address to,
        uint256 amount
    ) internal {
        bool callStatus;

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

            // Write the abi-encoded calldata to memory piece by piece:
            mstore(
                freeMemoryPointer,
                0x095ea7b300000000000000000000000000000000000000000000000000000000
            ) // Begin with the function selector.
            mstore(
                add(freeMemoryPointer, 4),
                and(to, 0xffffffffffffffffffffffffffffffffffffffff)
            ) // Mask and append the "to" argument.
            mstore(add(freeMemoryPointer, 36), amount) // Finally append the "amount" argument. No mask as it's a full 32 byte value.

            // Call the token and store if it succeeded or not.
            // We use 68 because the calldata length is 4 + 32 * 2.
            callStatus := call(gas(), token, 0, freeMemoryPointer, 68, 0, 0)
        }

        require(didLastOptionalReturnCallSucceed(callStatus), 'APPROVE_FAILED');
    }

    /*///////////////////////////////////////////////////////////////
                         INTERNAL HELPER LOGIC
    //////////////////////////////////////////////////////////////*/

    function didLastOptionalReturnCallSucceed(bool callStatus)
        private
        pure
        returns (bool)
    {
        bool result;
        assembly {
            // Get how many bytes the call returned.
            let returnDataSize := returndatasize()

            // If the call reverted:
            if iszero(callStatus) {
                // Copy the revert message into memory.
                returndatacopy(0, 0, returnDataSize)

                // Revert with the same message.
                revert(0, returnDataSize)
            }

            switch returnDataSize
            case 32 {
                // Copy the return data into memory.
                returndatacopy(0, 0, returnDataSize)

                // Set success to whether it returned true.
                result := iszero(iszero(mload(0)))
            }
            case 0 {
                // There was no return data.
                result := 1
            }
            default {
                // It returned some malformed input.
                result := 0
            }
        }

        return result;
    }
}

File 41 of 44 : Swivel.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity ^0.8.13;

library Swivel {
    // the components of a ECDSA signature
    struct Components {
        uint8 v;
        bytes32 r;
        bytes32 s;
    }

    struct Order {
        bytes32 key;
        uint8 protocol;
        address maker;
        address underlying;
        bool vault;
        bool exit;
        uint256 principal;
        uint256 premium;
        uint256 maturity;
        uint256 expiry;
    }
}

File 42 of 44 : ERC20.sol
// SPDX-License-Identifier: MIT
// Inspired on token.sol from DappHub. Natspec adpated from OpenZeppelin.
pragma solidity 0.8.16;

import 'src/interfaces/IERC20Metadata.sol';

/**
 * @dev Implementation of the {IERC20} interface.
 *
 * This implementation is agnostic to the way tokens are created. This means
 * that a supply mechanism has to be added in a derived contract using {_mint}.
 *
 * We have followed general OpenZeppelin guidelines: functions revert instead
 * of returning `false` on failure. This behavior is nonetheless conventional
 * and does not conflict with the expectations of ERC20 applications.
 *
 * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
 * This allows applications to reconstruct the allowance for all accounts just
 * by listening to said events. Other implementations of the EIP may not emit
 * these events, as it isn't required by the specification.
 *
 * Calls to {transferFrom} do not check for allowance if the caller is the owner
 * of the funds. This allows to reduce the number of approvals that are necessary.
 *
 * Finally, {transferFrom} does not decrease the allowance if it is set to
 * type(uint256).max. This reduces the gas costs without any likely impact.
 */
contract ERC20 is IERC20Metadata {
    uint256 internal _totalSupply;
    mapping(address => uint256) internal _balanceOf;
    mapping(address => mapping(address => uint256)) internal _allowance;
    string public override name = '???';
    string public override symbol = '???';
    uint8 public override decimals = 18;

    /**
     *  @dev Sets the values for {name}, {symbol} and {decimals}.
     */
    constructor(
        string memory name_,
        string memory symbol_,
        uint8 decimals_
    ) {
        name = name_;
        symbol = symbol_;
        decimals = decimals_;
    }

    /**
     * @dev See {IERC20-totalSupply}.
     */
    function totalSupply() external view virtual override returns (uint256) {
        return _totalSupply;
    }

    /**
     * @dev See {IERC20-balanceOf}.
     */
    function balanceOf(address guy)
        external
        view
        virtual
        override
        returns (uint256)
    {
        return _balanceOf[guy];
    }

    /**
     * @dev See {IERC20-allowance}.
     */
    function allowance(address owner, address spender)
        external
        view
        virtual
        override
        returns (uint256)
    {
        return _allowance[owner][spender];
    }

    /**
     * @dev See {IERC20-approve}.
     */
    function approve(address spender, uint256 wad)
        external
        virtual
        override
        returns (bool)
    {
        return _setAllowance(msg.sender, spender, wad);
    }

    /**
     * @dev See {IERC20-transfer}.
     *
     * Requirements:
     *
     * - the caller must have a balance of at least `wad`.
     */
    function transfer(address dst, uint256 wad)
        external
        virtual
        override
        returns (bool)
    {
        return _transfer(msg.sender, dst, wad);
    }

    /**
     * @dev See {IERC20-transferFrom}.
     *
     * Emits an {Approval} event indicating the updated allowance. This is not
     * required by the EIP. See the note at the beginning of {ERC20}.
     *
     * Requirements:
     *
     * - `src` must have a balance of at least `wad`.
     * - the caller is not `src`, it must have allowance for ``src``'s tokens of at least
     * `wad`.
     */
    /// if_succeeds {:msg "TransferFrom - decrease allowance"} msg.sender != src ==> old(_allowance[src][msg.sender]) >= wad;
    function transferFrom(
        address src,
        address dst,
        uint256 wad
    ) external virtual override returns (bool) {
        _decreaseAllowance(src, wad);

        return _transfer(src, dst, wad);
    }

    /**
     * @dev Moves tokens `wad` from `src` to `dst`.
     *
     * Emits a {Transfer} event.
     *
     * Requirements:
     *
     * - `src` must have a balance of at least `amount`.
     */
    /// if_succeeds {:msg "Transfer - src decrease"} old(_balanceOf[src]) >= _balanceOf[src];
    /// if_succeeds {:msg "Transfer - dst increase"} _balanceOf[dst] >= old(_balanceOf[dst]);
    /// if_succeeds {:msg "Transfer - supply"} old(_balanceOf[src]) + old(_balanceOf[dst]) == _balanceOf[src] + _balanceOf[dst];
    function _transfer(
        address src,
        address dst,
        uint256 wad
    ) internal virtual returns (bool) {
        require(_balanceOf[src] >= wad, 'ERC20: Insufficient balance');
        unchecked {
            _balanceOf[src] = _balanceOf[src] - wad;
        }
        _balanceOf[dst] = _balanceOf[dst] + wad;

        emit Transfer(src, dst, wad);

        return true;
    }

    /**
     * @dev Sets the allowance granted to `spender` by `owner`.
     *
     * Emits an {Approval} event indicating the updated allowance.
     */
    function _setAllowance(
        address owner,
        address spender,
        uint256 wad
    ) internal virtual returns (bool) {
        _allowance[owner][spender] = wad;
        emit Approval(owner, spender, wad);

        return true;
    }

    /**
     * @dev Decreases the allowance granted to the caller by `src`, unless src == msg.sender or _allowance[src][msg.sender] == MAX
     *
     * Emits an {Approval} event indicating the updated allowance, if the allowance is updated.
     *
     * Requirements:
     *
     * - `spender` must have allowance for the caller of at least
     * `wad`, unless src == msg.sender
     */
    /// if_succeeds {:msg "Decrease allowance - underflow"} old(_allowance[src][msg.sender]) <= _allowance[src][msg.sender];
    function _decreaseAllowance(address src, uint256 wad)
        internal
        virtual
        returns (bool)
    {
        if (src != msg.sender) {
            uint256 allowed = _allowance[src][msg.sender];
            if (allowed != type(uint256).max) {
                require(allowed >= wad, 'ERC20: Insufficient approval');
                unchecked {
                    _setAllowance(src, msg.sender, allowed - wad);
                }
            }
        }

        return true;
    }

    /** @dev Creates `wad` tokens and assigns them to `dst`, increasing
     * the total supply.
     *
     * Emits a {Transfer} event with `from` set to the zero address.
     */
    /// if_succeeds {:msg "Mint - balance overflow"} old(_balanceOf[dst]) >= _balanceOf[dst];
    /// if_succeeds {:msg "Mint - supply overflow"} old(_totalSupply) >= _totalSupply;
    function _mint(address dst, uint256 wad) internal virtual returns (bool) {
        _balanceOf[dst] = _balanceOf[dst] + wad;
        _totalSupply = _totalSupply + wad;
        emit Transfer(address(0), dst, wad);

        return true;
    }

    /**
     * @dev Destroys `wad` tokens from `src`, reducing the
     * total supply.
     *
     * Emits a {Transfer} event with `to` set to the zero address.
     *
     * Requirements:
     *
     * - `src` must have at least `wad` tokens.
     */
    /// if_succeeds {:msg "Burn - balance underflow"} old(_balanceOf[src]) <= _balanceOf[src];
    /// if_succeeds {:msg "Burn - supply underflow"} old(_totalSupply) <= _totalSupply;
    function _burn(address src, uint256 wad) internal virtual returns (bool) {
        unchecked {
            require(_balanceOf[src] >= wad, 'ERC20: Insufficient balance');
            _balanceOf[src] = _balanceOf[src] - wad;
            _totalSupply = _totalSupply - wad;
            emit Transfer(src, address(0), wad);
        }

        return true;
    }
}

File 43 of 44 : ERC20Permit.sol
// SPDX-License-Identifier: MIT
// Adapted from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/53516bc555a454862470e7860a9b5254db4d00f5/contracts/token/ERC20/ERC20Permit.sol
pragma solidity 0.8.16;

import 'src/tokens/ERC20.sol';
import 'src/interfaces/IERC2612.sol';

/**
 * @dev Extension of {ERC20} that allows token holders to use their tokens
 * without sending any transactions by setting {IERC20-allowance} with a
 * signature using the {permit} method, and then spend them via
 * {IERC20-transferFrom}.
 *
 * The {permit} signature mechanism conforms to the {IERC2612} interface.
 */
abstract contract ERC20Permit is ERC20, IERC2612 {
    mapping(address => uint256) public override nonces;

    bytes32 public immutable PERMIT_TYPEHASH =
        keccak256(
            'Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)'
        );
    bytes32 private immutable _DOMAIN_SEPARATOR;
    uint256 public immutable deploymentChainId;

    constructor(
        string memory name_,
        string memory symbol_,
        uint8 decimals_
    ) ERC20(name_, symbol_, decimals_) {
        deploymentChainId = block.chainid;
        _DOMAIN_SEPARATOR = _calculateDomainSeparator(block.chainid);
    }

    /// @dev Calculate the DOMAIN_SEPARATOR.
    function _calculateDomainSeparator(uint256 chainId)
        private
        view
        returns (bytes32)
    {
        return
            keccak256(
                abi.encode(
                    keccak256(
                        'EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)'
                    ),
                    keccak256(bytes(name)),
                    keccak256(bytes(version())),
                    chainId,
                    address(this)
                )
            );
    }

    /// @dev Return the DOMAIN_SEPARATOR.
    function DOMAIN_SEPARATOR() external view returns (bytes32) {
        return
            block.chainid == deploymentChainId
                ? _DOMAIN_SEPARATOR
                : _calculateDomainSeparator(block.chainid);
    }

    /// @dev Setting the version as a function so that it can be overriden
    function version() public pure virtual returns (string memory) {
        return '1';
    }

    /**
     * @dev See {IERC2612-permit}.
     *
     * In cases where the free option is not a concern, deadline can simply be
     * set to uint(-1), so it should be seen as an optional parameter
     */
    function permit(
        address owner,
        address spender,
        uint256 amount,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external virtual override {
        require(deadline >= block.timestamp, 'ERC20Permit: expired deadline');

        bytes32 hashStruct = keccak256(
            abi.encode(
                PERMIT_TYPEHASH,
                owner,
                spender,
                amount,
                nonces[owner]++,
                deadline
            )
        );

        bytes32 hash = keccak256(
            abi.encodePacked(
                '\x19\x01',
                block.chainid == deploymentChainId
                    ? _DOMAIN_SEPARATOR
                    : _calculateDomainSeparator(block.chainid),
                hashStruct
            )
        );

        address signer = ecrecover(hash, v, r, s);
        require(
            signer != address(0) && signer == owner,
            'ERC20Permit: invalid signature'
        );

        _setAllowance(owner, spender, amount);
    }
}

File 44 of 44 : ERC5095.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;

import 'src/tokens/ERC20Permit.sol';
import 'src/interfaces/IERC5095.sol';
import 'src/interfaces/IRedeemer.sol';
import 'src/interfaces/IMarketPlace.sol';
import 'src/interfaces/IYield.sol';
import 'src/errors/Exception.sol';

import 'src/lib/Cast.sol';
import 'src/lib/Safe.sol';

contract ERC5095 is ERC20Permit, IERC5095 {
    /// @dev unix timestamp when the ERC5095 token can be redeemed
    uint256 public immutable override maturity;
    /// @dev address of the ERC20 token that is returned on ERC5095 redemption
    address public immutable override underlying;
    /// @dev address of the minting authority
    address public immutable lender;
    /// @dev address of the "marketplace" YieldSpace AMM router
    address public immutable marketplace;
    ///@dev Interface to interact with the pool
    address public pool;

    /// @dev address and interface for an external custody contract (necessary for some project's backwards compatability)
    address public immutable redeemer;

    /// @notice ensures that only a certain address can call the function
    /// @param a address that msg.sender must be to be authorized
    modifier authorized(address a) {
        if (msg.sender != a) {
            revert Exception(0, 0, 0, msg.sender, a);
        }
        _;
    }

    constructor(
        address _underlying,
        uint256 _maturity,
        address _redeemer,
        address _lender,
        address _marketplace,
        string memory name_,
        string memory symbol_,
        uint8 decimals_
    ) ERC20Permit(name_, symbol_, decimals_) {
        underlying = _underlying;
        maturity = _maturity;
        redeemer = _redeemer;
        lender = _lender;
        marketplace = _marketplace;
        pool = address(0);
    }

    /// @notice Allows the marketplace to set the pool
    /// @param p Address of the pool
    /// @return bool True if successful
    function setPool(address p)
        external
        authorized(marketplace)
        returns (bool)
    {
        pool = p;
        return true;
    }

    /// @notice Allows the marketplace to spend underlying, principal tokens held by the token
    /// @dev This is necessary when MarketPlace calls pool methods to swap tokens
    /// @return True if successful
    function approveMarketPlace()
        external
        authorized(marketplace)
        returns (bool)
    {
        // Approve the marketplace to spend the token's underlying
        Safe.approve(IERC20(underlying), marketplace, type(uint256).max);

        // Approve the marketplace to spend illuminate PTs
        Safe.approve(IERC20(address(this)), marketplace, type(uint256).max);

        return true;
    }

    /// @notice Post or at maturity, converts an amount of principal tokens to an amount of underlying that would be returned.
    /// @param s The amount of principal tokens to convert
    /// @return uint256 The amount of underlying tokens returned by the conversion
    function convertToUnderlying(uint256 s)
        external
        view
        override
        returns (uint256)
    {
        if (block.timestamp < maturity) {
            return previewRedeem(s);
        }
        return s;
    }

    /// @notice Post or at maturity, converts a desired amount of underlying tokens returned to principal tokens needed.
    /// @param a The amount of underlying tokens to convert
    /// @return uint256 The amount of principal tokens returned by the conversion
    function convertToShares(uint256 a)
        external
        view
        override
        returns (uint256)
    {
        if (block.timestamp < maturity) {
            return previewWithdraw(a);
        }
        return a;
    }

    /// @notice Returns user's PT balance
    /// @param o The address of the owner for which redemption is calculated
    /// @return uint256 The maximum amount of principal tokens that `owner` can redeem.
    function maxRedeem(address o) external view override returns (uint256) {
        return _balanceOf[o];
    }

    /// @notice Post or at maturity, returns user's PT balance. Prior to maturity, returns a previewRedeem for owner's PT balance.
    /// @param  o The address of the owner for which withdrawal is calculated
    /// @return uint256 maximum amount of underlying tokens that `owner` can withdraw.
    function maxWithdraw(address o) external view override returns (uint256) {
        if (block.timestamp < maturity) {
            return previewRedeem(_balanceOf[o]);
        }
        return _balanceOf[o];
    }

    /// @notice After maturity, returns 0. Prior to maturity, returns the amount of `shares` when spending `a` in underlying on a YieldSpace AMM.
    /// @param a The amount of underlying spent
    /// @return uint256 The amount of PT purchased by spending `a` of underlying
    function previewDeposit(uint256 a) public view returns (uint256) {
        if (block.timestamp < maturity) {
            return IYield(pool).sellBasePreview(Cast.u128(a));
        }
        return 0;
    }

    /// @notice After maturity, returns 0. Prior to maturity, returns the amount of `assets` in underlying spent on a purchase of `s` in PT on a YieldSpace AMM.
    /// @param s The amount of principal tokens bought in the simulation
    /// @return uint256 The amount of underlying required to purchase `s` of PT
    function previewMint(uint256 s) public view returns (uint256) {
        if (block.timestamp < maturity) {
            return IYield(pool).buyFYTokenPreview(Cast.u128(s));
        }
        return 0;
    }

    /// @notice Post or at maturity, simulates the effects of redemption. Prior to maturity, returns the amount of `assets` from a sale of `s` PTs on a YieldSpace AMM.
    /// @param s The amount of principal tokens redeemed in the simulation
    /// @return uint256 The amount of underlying returned by `s` of PT redemption
    function previewRedeem(uint256 s) public view override returns (uint256) {
        if (block.timestamp >= maturity) {
            // After maturity, the amount redeemed is based on the Redeemer contract's holdings of the underlying
            return
                Cast.u128(
                    s *
                        Cast.u128(
                            IRedeemer(redeemer).holdings(underlying, maturity)
                        )
                ) / _totalSupply;
        }

        // Prior to maturity, return a a preview of a swap on the pool
        return IYield(pool).sellFYTokenPreview(Cast.u128(s));
    }

    /// @notice Post or at maturity, simulates the effects of withdrawal at the current block. Prior to maturity, simulates the amount of PTs necessary to receive `a` in underlying from the sale of PTs on a YieldSpace AMM.
    /// @param a The amount of underlying tokens withdrawn in the simulation
    /// @return uint256 The amount of principal tokens required for the withdrawal of `a`
    function previewWithdraw(uint256 a) public view override returns (uint256) {
        if (block.timestamp >= maturity) {
            // After maturity, the amount redeemed is based on the Redeemer contract's holdings of the underlying
            return
                (a * _totalSupply) /
                IRedeemer(redeemer).holdings(underlying, maturity);
        }

        // Prior to maturity, return a a preview of a swap on the pool
        return IYield(pool).buyBasePreview(Cast.u128(a));
    }

    /// @notice Before maturity spends `a` of underlying, and sends PTs to `r`. Post or at maturity, reverts.
    /// @param a The amount of underlying tokens deposited
    /// @param r The receiver of the principal tokens
    /// @param m Minimum number of shares that the user will receive
    /// @return uint256 The amount of principal tokens purchased
    function deposit(
        uint256 a,
        address r,
        uint256 m
    ) external returns (uint256) {
        // Execute the deposit
        return _deposit(r, a, m);
    }

    /// @notice Before maturity spends `assets` of underlying, and sends `shares` of PTs to `receiver`. Post or at maturity, reverts.
    /// @param a The amount of underlying tokens deposited
    /// @param r The receiver of the principal tokens
    /// @return uint256 The amount of principal tokens burnt by the withdrawal
    function deposit(uint256 a, address r) external override returns (uint256) {
        // Execute the deposit
        return _deposit(r, a, 0);
    }

    /// @notice Before maturity mints `s` of PTs to `r` by spending underlying. Post or at maturity, reverts.
    /// @param s The amount of shares being minted
    /// @param r The receiver of the underlying tokens being withdrawn
    /// @param m Maximum amount of underlying that the user will spend
    /// @return uint256 The amount of principal tokens purchased
    function mint(
        uint256 s,
        address r,
        uint256 m
    ) external returns (uint256) {
        // Execute the mint
        return _mint(r, s, m);
    }

    /// @notice Before maturity mints `shares` of PTs to `receiver` by spending underlying. Post or at maturity, reverts.
    /// @param s The amount of shares being minted
    /// @param r The receiver of the underlying tokens being withdrawn
    /// @return uint256 The amount of principal tokens purchased
    function mint(uint256 s, address r) external override returns (uint256) {
        // Execute the mint
        return _mint(r, s, type(uint128).max);
    }

    /// @notice At or after maturity, burns PTs from owner and sends `a` underlying to `r`. Before maturity, sends `a` by selling shares of PT on a YieldSpace AMM.
    /// @param a The amount of underlying tokens withdrawn
    /// @param r The receiver of the underlying tokens being withdrawn
    /// @param o The owner of the underlying tokens
    /// @param m Maximum amount of PTs to be sold
    /// @return uint256 The amount of principal tokens burnt by the withdrawal
    function withdraw(
        uint256 a,
        address r,
        address o,
        uint256 m
    ) external returns (uint256) {
        // Execute the withdrawal
        return _withdraw(a, r, o, m);
    }

    /// @notice At or after maturity, burns PTs from owner and sends `a` underlying to `r`. Before maturity, sends `a` by selling shares of PT on a YieldSpace AMM.
    /// @param a The amount of underlying tokens withdrawn
    /// @param r The receiver of the underlying tokens being withdrawn
    /// @param o The owner of the underlying tokens
    /// @return uint256 The amount of principal tokens burnt by the withdrawal
    function withdraw(
        uint256 a,
        address r,
        address o
    ) external override returns (uint256) {
        // Execute the withdrawal
        return _withdraw(a, r, o, type(uint128).max);
    }

    /// @notice At or after maturity, burns exactly `s` of Principal Tokens from `o` and sends underlying tokens to `r`. Before maturity, sends underlying by selling `s` of PT on a YieldSpace AMM.
    /// @param s The number of shares to be burned in exchange for the underlying asset
    /// @param r The receiver of the underlying tokens being withdrawn
    /// @param o Address of the owner of the shares being burned
    /// @param m Minimum amount of underlying that must be received
    /// @return uint256 The amount of underlying tokens distributed by the redemption
    function redeem(
        uint256 s,
        address r,
        address o,
        uint256 m
    ) external returns (uint256) {
        // Execute the redemption
        return _redeem(s, r, o, m);
    }

    /// @notice At or after maturity, burns exactly `shares` of Principal Tokens from `owner` and sends `assets` of underlying tokens to `receiver`. Before maturity, sells `s` of PT on a YieldSpace AMM.
    /// @param s The number of shares to be burned in exchange for the underlying asset
    /// @param r The receiver of the underlying tokens being withdrawn
    /// @param o Address of the owner of the shares being burned
    /// @return uint256 The amount of underlying tokens distributed by the redemption
    function redeem(
        uint256 s,
        address r,
        address o
    ) external override returns (uint256) {
        // Execute the redemption
        return _redeem(s, r, o, 0);
    }

    /// @param f Address to burn from
    /// @param a Amount to burn
    /// @return bool true if successful
    function authBurn(address f, uint256 a)
        external
        authorized(redeemer)
        returns (bool)
    {
        _burn(f, a);
        return true;
    }

    /// @param t Address recieving the minted amount
    /// @param a The amount to mint
    /// @return bool True if successful
    function authMint(address t, uint256 a)
        external
        authorized(lender)
        returns (bool)
    {
        _mint(t, a);
        return true;
    }

    /// @param o Address of the owner of the tokens
    /// @param s Address of the spender
    /// @param a Amount to be approved
    function authApprove(
        address o,
        address s,
        uint256 a
    ) external authorized(redeemer) returns (bool) {
        _allowance[o][s] = a;
        return true;
    }

    function _deposit(
        address r,
        uint256 a,
        uint256 m
    ) internal returns (uint256) {
        // Revert if called at or after maturity
        if (block.timestamp >= maturity) {
            revert Exception(
                21,
                block.timestamp,
                maturity,
                address(0),
                address(0)
            );
        }

        // Receive the funds from the sender
        Safe.transferFrom(IERC20(underlying), msg.sender, address(this), a);

        // Sell the underlying assets for PTs
        uint128 returned = IMarketPlace(marketplace).sellUnderlying(
            underlying,
            maturity,
            Cast.u128(a),
            Cast.u128(m)
        );

        // Pass the received shares onto the intended receiver
        _transfer(address(this), r, returned);

        return returned;
    }

    function _mint(
        address r,
        uint256 s,
        uint256 m
    ) internal returns (uint256) {
        // Revert if called at or after maturity
        if (block.timestamp >= maturity) {
            revert Exception(
                21,
                block.timestamp,
                maturity,
                address(0),
                address(0)
            );
        }

        // Determine how many underlying tokens are needed to mint the shares
        uint256 required = IYield(pool).buyFYTokenPreview(Cast.u128(s));

        // Transfer the underlying to the token
        Safe.transferFrom(
            IERC20(underlying),
            msg.sender,
            address(this),
            required
        );

        // Swap the underlying for principal tokens via the pool
        uint128 sold = IMarketPlace(marketplace).buyPrincipalToken(
            underlying,
            maturity,
            Cast.u128(s),
            Cast.u128(m)
        );

        // Transfer the principal tokens to the desired receiver
        _transfer(address(this), r, s);

        return sold;
    }

    function _withdraw(
        uint256 a,
        address r,
        address o,
        uint256 m
    ) internal returns (uint256) {
        // Determine how many principal tokens are needed to purchase the underlying
        uint256 needed = previewWithdraw(a);

        // Pre maturity
        if (block.timestamp < maturity) {
            // Receive the shares from the caller
            _transfer(o, address(this), needed);

            // If owner is the sender, sell PT without allowance check
            if (o == msg.sender) {
                uint128 returned = IMarketPlace(marketplace).buyUnderlying(
                    underlying,
                    maturity,
                    Cast.u128(a),
                    Cast.u128(m)
                );

                // Transfer the underlying to the desired receiver
                Safe.transfer(IERC20(underlying), r, a);

                return returned;
            } else {
                // Else, sell PT with allowance check
                // Get the allowance of the user spending the tokens
                uint256 allowance = _allowance[o][msg.sender];

                // Check for sufficient allowance
                if (allowance < needed) {
                    revert Exception(20, allowance, a, address(0), address(0));
                }

                // Update the caller's allowance
                _allowance[o][msg.sender] = allowance - needed;

                // Sell the principal tokens for underlying
                uint128 returned = IMarketPlace(marketplace).buyUnderlying(
                    underlying,
                    maturity,
                    Cast.u128(a),
                    Cast.u128(m)
                );

                // Transfer the underlying to the desired receiver
                Safe.transfer(IERC20(underlying), r, returned);

                return returned;
            }
        }
        // Post maturity
        else {
            // If owner is the sender, redeem PT without allowance check
            if (o == msg.sender) {
                // Execute the redemption to the desired receiver
                return
                    IRedeemer(redeemer).authRedeem(
                        underlying,
                        maturity,
                        msg.sender,
                        r,
                        needed
                    );
            } else {
                // Get the allowance of the user spending the tokens
                uint256 allowance = _allowance[o][msg.sender];

                // Check for sufficient allowance
                if (allowance < needed) {
                    revert Exception(
                        20,
                        allowance,
                        needed,
                        address(0),
                        address(0)
                    );
                }

                // Update the callers's allowance
                _allowance[o][msg.sender] = allowance - needed;

                // Execute the redemption to the desired receiver
                return
                    IRedeemer(redeemer).authRedeem(
                        underlying,
                        maturity,
                        o,
                        r,
                        needed
                    );
            }
        }
    }

    function _redeem(
        uint256 s,
        address r,
        address o,
        uint256 m
    ) internal returns (uint256) {
        // Pre-maturity
        if (block.timestamp < maturity) {
            // Receive the funds from the user
            _transfer(o, address(this), s);

            // If owner is the sender, sell PT without allowance check
            if (o == msg.sender) {
                // Swap principal tokens for the underlying asset
                uint128 returned = IMarketPlace(marketplace).sellPrincipalToken(
                    underlying,
                    maturity,
                    Cast.u128(s),
                    Cast.u128(m)
                );

                // Transfer underlying to the desired receiver
                Safe.transfer(IERC20(underlying), r, returned);
                return returned;
                // Else, sell PT with allowance check
            } else {
                // Get the allowance of the user spending the tokens
                uint256 allowance = _allowance[o][msg.sender];

                // Check for sufficient allowance
                if (allowance < s) {
                    revert Exception(20, allowance, s, address(0), address(0));
                }

                // Update the caller's allowance
                _allowance[o][msg.sender] = allowance - s;

                // Sell the principal tokens for the underlying
                uint128 returned = IMarketPlace(marketplace).sellPrincipalToken(
                    underlying,
                    maturity,
                    Cast.u128(s),
                    Cast.u128(m)
                );

                // Transfer the underlying to the desired receiver
                Safe.transfer(IERC20(underlying), r, returned);
                return returned;
            }
            // Post-maturity
        } else {
            // If owner is the sender, redeem PT without allowance check
            if (o == msg.sender) {
                // Execute the redemption to the desired receiver
                return
                    IRedeemer(redeemer).authRedeem(
                        underlying,
                        maturity,
                        msg.sender,
                        r,
                        s
                    );
            } else {
                // Get the allowance of the user spending the tokens
                uint256 allowance = _allowance[o][msg.sender];

                // Check for sufficient allowance
                if (allowance < s) {
                    revert Exception(20, allowance, s, address(0), address(0));
                }

                // Update the caller's allowance
                _allowance[o][msg.sender] = allowance - s;

                // Execute the redemption to the desired receiver
                return
                    IRedeemer(redeemer).authRedeem(
                        underlying,
                        maturity,
                        o,
                        r,
                        s
                    );
            }
        }
    }
}

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

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"s","type":"address"},{"internalType":"address","name":"p","type":"address"},{"internalType":"address","name":"a","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint8","name":"","type":"uint8"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"Exception","type":"error"},{"anonymous":false,"inputs":[],"name":"BlockFeeChange","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"}],"name":"BlockWithdrawal","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"principal","type":"uint8"},{"indexed":true,"internalType":"address","name":"underlying","type":"address"},{"indexed":true,"internalType":"uint256","name":"maturity","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"returned","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"spent","type":"uint256"},{"indexed":false,"internalType":"address","name":"sender","type":"address"}],"name":"Lend","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"principal","type":"uint8"},{"indexed":true,"internalType":"address","name":"underlying","type":"address"},{"indexed":true,"internalType":"uint256","name":"maturity","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Mint","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"state","type":"bool"}],"name":"PauseIlluminate","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"principal","type":"uint8"},{"indexed":true,"internalType":"bool","name":"state","type":"bool"}],"name":"PausePrincipal","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"when","type":"uint256"}],"name":"ScheduleFeeChange","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"hold","type":"uint256"}],"name":"ScheduleWithdrawal","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"admin","type":"address"}],"name":"SetAdmin","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"fee","type":"uint256"}],"name":"SetFee","type":"event"},{"inputs":[],"name":"HOLD","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MIN_FEENOMINATOR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"admin","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"u","type":"address"},{"internalType":"uint256","name":"m","type":"uint256"},{"internalType":"address","name":"r","type":"address"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"u","type":"address"},{"internalType":"address","name":"a","type":"address"},{"internalType":"address","name":"e","type":"address"},{"internalType":"address","name":"n","type":"address"},{"internalType":"address","name":"p","type":"address"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"u","type":"address[]"},{"internalType":"address[]","name":"a","type":"address[]"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"apwineAddr","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes[]","name":"c","type":"bytes[]"}],"name":"batch","outputs":[{"internalType":"bytes[]","name":"results","type":"bytes[]"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"blockFeeChange","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"e","type":"address"}],"name":"blockWithdrawal","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"etherPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"feeChange","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"feenominator","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"fees","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"halted","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"p","type":"uint8"},{"internalType":"address","name":"u","type":"address"},{"internalType":"uint256","name":"m","type":"uint256"},{"internalType":"uint256","name":"a","type":"uint256"},{"internalType":"uint256","name":"r","type":"uint256"},{"internalType":"uint256","name":"d","type":"uint256"},{"internalType":"address","name":"e","type":"address"},{"internalType":"bytes32","name":"i","type":"bytes32"}],"name":"lend","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"p","type":"uint8"},{"internalType":"address","name":"u","type":"address"},{"internalType":"uint256","name":"m","type":"uint256"},{"internalType":"uint256[]","name":"a","type":"uint256[]"},{"internalType":"address","name":"y","type":"address"},{"components":[{"internalType":"bytes32","name":"key","type":"bytes32"},{"internalType":"uint8","name":"protocol","type":"uint8"},{"internalType":"address","name":"maker","type":"address"},{"internalType":"address","name":"underlying","type":"address"},{"internalType":"bool","name":"vault","type":"bool"},{"internalType":"bool","name":"exit","type":"bool"},{"internalType":"uint256","name":"principal","type":"uint256"},{"internalType":"uint256","name":"premium","type":"uint256"},{"internalType":"uint256","name":"maturity","type":"uint256"},{"internalType":"uint256","name":"expiry","type":"uint256"}],"internalType":"struct Swivel.Order[]","name":"o","type":"tuple[]"},{"components":[{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"internalType":"struct Swivel.Components[]","name":"s","type":"tuple[]"},{"internalType":"bool","name":"e","type":"bool"},{"internalType":"uint256","name":"premiumSlippage","type":"uint256"}],"name":"lend","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"p","type":"uint8"},{"internalType":"address","name":"u","type":"address"},{"internalType":"uint256","name":"m","type":"uint256"},{"internalType":"uint128","name":"a","type":"uint128"},{"internalType":"uint256","name":"r","type":"uint256"},{"internalType":"address","name":"x","type":"address"},{"internalType":"uint256","name":"s","type":"uint256"},{"internalType":"address","name":"adapter","type":"address"}],"name":"lend","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"p","type":"uint8"},{"internalType":"address","name":"u","type":"address"},{"internalType":"uint256","name":"m","type":"uint256"},{"internalType":"uint256","name":"a","type":"uint256"},{"internalType":"address","name":"y","type":"address"},{"internalType":"uint256","name":"minimum","type":"uint256"}],"name":"lend","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"p","type":"uint8"},{"internalType":"address","name":"u","type":"address"},{"internalType":"uint256","name":"m","type":"uint256"},{"internalType":"uint256","name":"a","type":"uint256"},{"internalType":"uint256","name":"r","type":"uint256"}],"name":"lend","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"p","type":"uint8"},{"internalType":"address","name":"u","type":"address"},{"internalType":"uint256","name":"m","type":"uint256"},{"internalType":"uint256","name":"a","type":"uint256"},{"internalType":"uint256","name":"r","type":"uint256"},{"internalType":"uint256","name":"d","type":"uint256"},{"internalType":"address","name":"x","type":"address"}],"name":"lend","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"p","type":"uint8"},{"internalType":"address","name":"u","type":"address"},{"internalType":"uint256","name":"m","type":"uint256"},{"internalType":"uint256","name":"a","type":"uint256"},{"internalType":"uint256","name":"r","type":"uint256"},{"components":[{"internalType":"uint256","name":"guessMin","type":"uint256"},{"internalType":"uint256","name":"guessMax","type":"uint256"},{"internalType":"uint256","name":"guessOffchain","type":"uint256"},{"internalType":"uint256","name":"maxIteration","type":"uint256"},{"internalType":"uint256","name":"eps","type":"uint256"}],"internalType":"struct Pendle.ApproxParams","name":"g","type":"tuple"},{"internalType":"address","name":"market","type":"address"}],"name":"lend","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"marketPlace","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maximumValue","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"p","type":"uint8"},{"internalType":"address","name":"u","type":"address"},{"internalType":"uint256","name":"m","type":"uint256"},{"internalType":"uint256","name":"a","type":"uint256"}],"name":"mint","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"p","type":"uint8"},{"internalType":"bool","name":"b","type":"bool"}],"name":"pause","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"b","type":"bool"}],"name":"pauseIlluminate","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"","type":"uint8"}],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendleAddr","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"","type":"uint8"}],"name":"periodStart","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"premiums","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"","type":"uint8"}],"name":"protocolFlow","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"scheduleFeeChange","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"e","type":"address"}],"name":"scheduleWithdrawal","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"a","type":"address"}],"name":"setAdmin","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"p","type":"uint256"}],"name":"setEtherPrice","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"f","type":"uint256"}],"name":"setFee","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"m","type":"address"}],"name":"setMarketPlace","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"m","type":"uint256"}],"name":"setMaxValue","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"swivelAddr","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"f","type":"address"},{"internalType":"uint256","name":"a","type":"uint256"}],"name":"transferFYTs","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"u","type":"address"},{"internalType":"uint256","name":"m","type":"uint256"}],"name":"transferPremium","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"e","type":"address"}],"name":"withdraw","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"e","type":"address"}],"name":"withdrawFee","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"withdrawals","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]

60e034620000f257601f6200581c38819003918201601f19168301916001600160401b03831184841017620000f757808492606094604052833981010312620000f2576200004d816200010d565b906200006a604062000062602084016200010d565b92016200010d565b60016009556d0c5371912364ce3056c280000000600a556109c4600d55600080546001600160a01b0319163317905560809290925260a05260c0526103e86005556040516156f99081620001238239608051818181610dfa01528181612b9f0152613c44015260a0518181816132f80152613534015260c051818181610f24015261317d0152f35b600080fd5b634e487b7160e01b600052604160045260246000fd5b51906001600160a01b0382168203620000f25756fe60806040526004361015610013575b600080fd5b60003560e01c806303799f87146103975780630cdc7ec61461038e5780630d3f5352146103855780631177ec301461037c5780631271f09a146103735780631ac3ddeb1461036a5780631e897afb1461036157806327187991146103585780632e25d2a61461034f57806330568a8d1461034657806335197f9e1461033d578063355d63f11461033457806337a3eeec1461032b5780633faf30ad1461032257806351cff8d9146103195780635ac86ab71461031057806362a4520e1461030757806365dff1e1146102fe57806369fe0e2d146102f55780636b42450d146102ec578063704b6c02146102e35780637114d92c146102da57806375a9b172146102d15780637a9262a2146102c85780637c7cde83146102bf578063884e21d6146102b65780639984f30d146102ad5780639e307955146102a45780639e6b51731461029b578063a102e38414610292578063adea384414610289578063b49e0d7214610280578063b9b8af0b14610277578063c244e1ff1461026e578063d0886f9714610265578063dc4c7ca91461025c578063ea08c03114610253578063eda8ff6b1461024a578063ef60356914610241578063f7adf98014610238578063f851a4401461022f578063f8eaad3514610226578063f9ad473d1461021d578063faaebd21146102145763fe3ee1691461020c57600080fd5b61000e613824565b5061000e6137e6565b5061000e613789565b5061000e6136f0565b5061000e6136c6565b5061000e613327565b5061000e6132e1565b5061000e612bce565b5061000e612b88565b5061000e6127ee565b5061000e6127cf565b5061000e612489565b5061000e612465565b5061000e611fc8565b5061000e611f8f565b5061000e611efb565b5061000e611edc565b5061000e611ebd565b5061000e611e86565b5061000e61189c565b5061000e61186c565b5061000e61182e565b5061000e611787565b5061000e6116d6565b5061000e611469565b5061000e6113c2565b5061000e611298565b5061000e6111d5565b5061000e61119c565b5061000e61115e565b5061000e610fef565b5061000e610f75565b5061000e610f0d565b5061000e610d56565b5061000e610d37565b5061000e610ca5565b5061000e610c7b565b5061000e610c44565b5061000e610b41565b5061000e610a0e565b5061000e6108c2565b5061000e610829565b5061000e61080b565b5061000e6107b4565b5061000e6103fb565b60ff81160361000e57565b600435906103b8826103a0565b565b35906103b8826103a0565b6001600160a01b0381160361000e57565b602435906103b8826103c5565b608435906103b8826103c5565b35906103b8826103c5565b503461000e5761010036600319011261000e57600480359061041c826103a0565b60243590610429826103c5565b60443560c43560643561043b826103c5565b60026009541461077a57600260095560009560ff811687526020916002835260409560ff878a205416801561076e575b610753578542116107165783906104e382858a8a8e8e6104a461049861049860015460018060a01b031690565b6001600160a01b031690565b935163125cf47f60e01b81526001600160a01b03909116888201908152602081019390935260ff90941660408301529294859384929091839160600190565b03925af1908115610709575b8b916106dc575b506001600160a01b038a811697908a908d906105148830338e615576565b6001600160a01b038e1660009081526007602052604090208e905460055461053c908b6139c1565b610545916139e1565b6001600160a01b0390911660009081526007602052604090205560055461056c90896139c1565b6105769089613a34565b908b610580611593565b30815294858a01859052308187015284606087015261059d6115b2565b60e43581528a8101959095526001600160a01b039091169084015285166001600160a01b0316606083015260808201526105d561440b565b60a082015260a43591608435916105eb94614b7f565b6105f5918b614e1b565b98876106028b8388614f26565b5061060c91614d32565b8851630fa9d3b160e01b815233928101928352602083018b90529a8b92839003604001918391906001600160a01b03165af161069d986000805160206156a4833981519152949180156106cf575b6106a1575b5050855160ff909216825260208201879052604082015233606082015280608081015b0390a361068f6001600955565b519081529081906020820190565b0390f35b816106c092903d106106c8575b6106b88183611572565b810190613995565b50388061065f565b503d6106ae565b6106d761390a565b61065a565b6106fc9150833d8511610702575b6106f48183611572565b8101906138f2565b386104f6565b503d6106ea565b61071161390a565b6104ef565b8651636d4c6c8960e01b8152600291810191825242602083015260408201879052600060608301819052608083015290819060a0010390fd5b0390fd5b8651636d4c6c8960e01b81529283925061074f918301613955565b5060ff6003541661046b565b61074f84604051918291636d4c6c8960e01b83528201906000608060a0840193601e81528260208201528260408201528260608201520152565b503461000e57604036600319011261000e576004356107d2816103c5565b60018060a01b0316600052600460205260406000206024356000526020526020604060002054604051908152f35b600091031261000e57565b503461000e57600036600319011261000e5760206040516101f48152f35b503461000e57600036600319011261000e576000546001600160a01b0316338190036108a4577ff339d7864b1b8839e8a8870c012fc6eb9a89844861a87a26ce35979018603a1b60206203f4804201804211610897575b80600655604051908152a160405160018152602090f35b61089f6139aa565b610880565b604051636d4c6c8960e01b815290819061074f9033600484016138c4565b503461000e57606036600319011261000e57600480356108e1816103c5565b602435906044356108f1816103c5565b600080546001600160a01b0391908216338190036109f15750805b60ff906009828216146109d85760019084876109788b84610939610498610498895460018060a01b031690565b8d8a60405180968195829463125cf47f60e01b845260209a8b99850191604091949360ff91606085019660018060a01b03168552602085015216910152565b03925af19182156109cb575b87926109ae575b505016868161099e575b5050011661090c565b6109a79161562c565b3886610995565b6109c49250803d10610702576106f48183611572565b388061098b565b6109d361390a565b610984565b5050506109e5921661562c565b60405160018152602090f35b604051636d4c6c8960e01b815290819061074f9033838b016138c4565b503461000e57602036600319011261000e57600435610a2c816103c5565b600080546001600160a01b03908116338190036108a45750806109e59316908183526007602052604083205492806040812055541690615505565b9181601f8401121561000e578235916001600160401b03831161000e576020808501948460051b01011161000e57565b60005b838110610aaa5750506000910152565b8181015183820152602001610a9a565b90602091610ad381518092818552858086019101610a97565b601f01601f1916010190565b602080820190808352835180925260408301928160408460051b8301019501936000915b848310610b135750505050505090565b9091929394958480610b31600193603f198682030187528a51610aba565b9801930193019194939290610b03565b5060208060031936011261000e57600480356001600160401b03811161000e57610b6e9036908301610a67565b9290610b79846115f1565b92604092610b8984519586611572565b858552601f19610b98876115f1565b0160005b818110610c355750505060005b858110610bbd5783518061069d8782610adf565b600080610bcb838987614700565b90610bda88518093819361474f565b0390305af4610be761475d565b9015610c125790610c0d91610bfc828861421e565b52610c07818761421e565b506146e4565b610ba9565b8261074f610c208793615451565b925162461bcd60e51b8152928392830161478d565b60608782018401528201610b9c565b503461000e57602036600319011261000e576000546001600160a01b0316338190036108a457600435600d55602060405160018152f35b503461000e57600036600319011261000e576001546040516001600160a01b039091168152602090f35b503461000e57602036600319011261000e57600435610cc3816103c5565b6000546001600160a01b03908116338190036108a457506001541680610d0357600180546001600160a01b0319166001600160a01b0384161790556109e5565b60a49060405190636d4c6c8960e01b8252600560048301526000602483015260006044830152606482015260006084820152fd5b503461000e57600036600319011261000e576020600654604051908152f35b503461000e5760a036600319011261000e57600435610d74816103c5565b60243590610d81826103c5565b60443590610d8e826103c5565b606435610d9a816103c5565b60843592610da7846103c5565b6001546001600160a01b039390841633819003610ecf5750831694838116610ebf575b50828116610eaf575b50818116610e9f575b508116610e8f575b50604051636eb1769f60e11b81523060048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b038116602483015290602081604481865afa908115610e82575b600091610e54575b5015610e4b575b005b610e499161562c565b610e75915060203d8111610e7b575b610e6d8183611572565b810190613946565b38610e42565b503d610e63565b610e8a61390a565b610e3a565b610e99908261562c565b38610de4565b610ea9908461562c565b38610ddc565b610eb9908561562c565b38610dd3565b610ec9908661562c565b38610dca565b604051636d4c6c8960e01b81526000600482018190526024820181905260448201523360648201526001600160a01b0391909116608482015260a490fd5b503461000e57600036600319011261000e576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b8015150361000e57565b60e435906103b882610f53565b35906103b882610f53565b503461000e57602036600319011261000e57600435610f9381610f53565b6000546001600160a01b0316338190036108a4577fb4252ba7ada6e65ca524b94dd554b37a638732fc8c63021e66782fc6e75bbb0c602083151560ff196003541660ff821617600355604051908152a160405160018152602090f35b503461000e57602036600319011261000e5760043561100d816103c5565b6000546001600160a01b03908116338190036108a457506001600160a01b03821660009081526008602052604090205480156111295742106110f4576001600160a01b03821660009081526008602052604081206109e5939190556001600160a01b0381166000908152600760205260408120556000549116906001600160a01b03166040516370a0823160e01b81523060048201529160208380602481010381845afa9283156110e7575b6000936110c7575b50615505565b6110e091935060203d8111610e7b57610e6d8183611572565b91386110c1565b6110ef61390a565b6110b9565b604051636d4c6c8960e01b8152601360048201526000602482018190526044820181905260648201819052608482015260a490fd5b604051636d4c6c8960e01b8152601260048201526000602482018190526044820181905260648201819052608482015260a490fd5b503461000e57602036600319011261000e5760ff60043561117e816103a0565b166000526002602052602060ff604060002054166040519015158152f35b503461000e57602036600319011261000e5760ff6004356111bc816103a0565b16600052600c6020526020604060002054604051908152f35b503461000e57604036600319011261000e576001600160401b0360043581811161000e57611207903690600401610a67565b909160243590811161000e57611221903690600401610a67565b9060018060a01b0380600054168033036108a4575060005b84810361124c5760405160018152602090f35b808261125b600193888a61392e565b35611265816103c5565b1680611273575b5001611239565b6112929061128283888861392e565b359061128d826103c5565b61562c565b3861126c565b503461000e57602036600319011261000e57600054600435906001600160a01b0316338190036108a457506006548061130057604051636d4c6c8960e01b8152601760048201526000602482018190526044820181905260648201819052608482015260a490fd5b80421060001461133f57604051636d4c6c8960e01b8152601860048201524260248201526044810191909152600060648201819052608482015260a490fd5b506101f4811061138d5761135281600555565b61135c6000600655565b604051907e172ddfc5ae88d08b3de01a5a187667c37a5a53989e8c175055cb6c993792a7600083a260018152602090f35b604051636d4c6c8960e01b8152601960048201526000602482018190526044820181905260648201819052608482015260a490fd5b503461000e57604036600319011261000e576004356113e0816103c5565b6001546004906020906113fd90610498906001600160a01b031681565b60405163057453a760e31b815292839182905afa90811561145c575b60009161143e575b506001600160a01b03811633036108a457610e4960243583614621565b611456915060203d8111610702576106f48183611572565b38611421565b61146461390a565b611419565b503461000e57602036600319011261000e57600435611487816103c5565b6000546001600160a01b0380821692338490036114e957602093501680916bffffffffffffffffffffffff60a01b1617600055604051907f5a272403b402d892977df56625f4164ccaf70ca3863991c43ecfe76a6905b0a1600083a260018152f35b604051636d4c6c8960e01b81528061074f8633600484016138c4565b50634e487b7160e01b600052604160045260246000fd5b6001600160401b03811161152f57604052565b611537611505565b604052565b606081019081106001600160401b0382111761152f57604052565b604081019081106001600160401b0382111761152f57604052565b90601f801991011681019081106001600160401b0382111761152f57604052565b60405190608082018281106001600160401b0382111761152f57604052565b6040519060c082018281106001600160401b0382111761152f57604052565b6040519061014082018281106001600160401b0382111761152f57604052565b6020906001600160401b03811161160a575b60051b0190565b611612611505565b611603565b81601f8201121561000e5780359161162e836115f1565b9261163c6040519485611572565b808452602092838086019260051b82010192831161000e578301905b828210611666575050505090565b81358152908301908301611658565b9181601f8401121561000e578235916001600160401b03831161000e57602080850194610140850201011161000e57565b9181601f8401121561000e578235916001600160401b03831161000e576020808501946060850201011161000e57565b503461000e5761012036600319011261000e576116f16103ab565b6116f96103d6565b6001600160401b039060643582811161000e5761171a903690600401611617565b926117236103e3565b60a43584811161000e5761173b903690600401611675565b60c49691963595861161000e5761069d9661175d6117779736906004016116a6565b949093611768610f5d565b96610104359860443591613a41565b6040519081529081906020820190565b503461000e57604036600319011261000e576004356117a5816103c5565b6001546004906020906117c290610498906001600160a01b031681565b60405163057453a760e31b815292839182905afa908115611821575b600091611803575b506001600160a01b03811633036108a457610e49602435836145ae565b61181b915060203d8111610702576106f48183611572565b386117e6565b61182961390a565b6117de565b503461000e57602036600319011261000e5760043561184c816103c5565b60018060a01b031660005260086020526020604060002054604051908152f35b503461000e57600036600319011261000e576020600a54604051908152f35b6001600160801b0381160361000e57565b503461000e5761010036600319011261000e576118ba6004356103a0565b6118c56024356103c5565b6118d060643561188b565b6118db60a4356103c5565b6118e660e4356103c5565b600260095414611e5157600260095560ff60043516600052600260205260ff604060002054168015611e45575b611e28576044354211611df35760405163378efa3760e01b815260208160048160a4356001600160a01b03165afa908115611de6575b600091611dc7575b5060405163d10eb4b960e01b81526001600160a01b0360e4358116600483015260c4356024830152909160209183916044918391165afa908115611dba575b600091611d9b575b506001546119b090610498906001600160a01b031681565b906020604051809363125cf47f60e01b8252816000816119f96004356044356024356004850191604091949360ff91606085019660018060a01b03168552602085015216910152565b03925af1918215611d8e575b600092611d6d575b506001600160a01b0381811690831603611d475750611a446064356001600160801b031630336024356001600160a01b0316615576565b611ab1611a5e6005546001600160801b03606435166139c1565b6001600160a01b03602435166000908152600760205260409020611a859082905b546139e1565b6001600160a01b03602435166000908152600760205260409020556001600160801b0360643516613a34565b6040516370a0823160e01b808252306004830152919291611b8191906020826024816001600160a01b0388165afa918215611d3a575b600092611d19575b506040516308f1f30f60e41b81526001600160a01b0360e4358116600483015260c435602483015260448201969096526084803560648301819052966020918391829060009060a435165af18015611d0c575b611ced575b5060405190815230600482015260208180602481015b03816001600160a01b0388165afa908115611ce0575b600091611cc1575b50613a34565b918210611c8c57611baf61069d92611b9d604435602435614d32565b926001600160a01b0316602435614e1b565b90611bbf82602435600435614f26565b50604051630fa9d3b160e01b81523360048201526024810183905290602090829060449082906000906001600160a01b03165af18015611c7f575b611c60575b506040805160ff600435168152602081018390526001600160801b036064351691810191909152336060820152604435906024356001600160a01b0316906000805160206156a48339815191529080608081015b0390a36117776001600955565b611c789060203d6020116106c8576106b88183611572565b5038611bff565b611c8761390a565b611bfa565b604051636d4c6c8960e01b8152600b60048201526000602482018190526044820181905260648201819052608482015260a490fd5b611cda915060203d602011610e7b57610e6d8183611572565b38611b7b565b611ce861390a565b611b73565b611d059060203d602011610e7b57610e6d8183611572565b5038611b47565b611d1461390a565b611b42565b611d3391925060203d602011610e7b57610e6d8183611572565b9038611aef565b611d4261390a565b611ae7565b604051636d4c6c8960e01b815291829161074f916001600160a01b0316600484016139f6565b611d8791925060203d602011610702576106f48183611572565b9038611a0d565b611d9661390a565b611a05565b611db4915060203d602011610702576106f48183611572565b38611998565b611dc261390a565b611990565b611de0915060203d602011610702576106f48183611572565b38611951565b611dee61390a565b611949565b604051636d4c6c8960e01b8152600260048201524260248201526044803590820152600060648201819052608482015260a490fd5b604051636d4c6c8960e01b81528061074f60048035908301613955565b5060ff60035416611913565b604051636d4c6c8960e01b8152601e60048201526000602482018190526044820181905260648201819052608482015260a490fd5b503461000e57602036600319011261000e576000546001600160a01b0316338190036108a457600435600a55602060405160018152f35b503461000e57600036600319011261000e576020600d54604051908152f35b503461000e57600036600319011261000e576020600554604051908152f35b503461000e57602036600319011261000e57600435611f19816103c5565b6000546001600160a01b039081169133839003611f73576020925016806000526008825260006040812055604051907fb1c1232c5dd039bb1c46cc05eaf25828e4f8596b7f68bdb23073ba78b9ca382d600083a260018152f35b604051636d4c6c8960e01b81528061074f8533600484016138c4565b503461000e57602036600319011261000e5760ff600435611faf816103a0565b16600052600b6020526020604060002054604051908152f35b503461000e5760c036600319011261000e576004803590611fe8826103a0565b602435611ff4816103c5565b6044359260643560843592612008846103c5565b60026009541461242b57600260095560ff8316906000918083526020926002845260409760ff8983205416801561241f575b612403578942116123ca576120a285888b8d8661206461049861049860015460018060a01b031690565b925163125cf47f60e01b81526001600160a01b038b16888201908152602081019390935260ff90941660408301529294859384929091839160600190565b03925af19081156123bd575b83916123a0575b506001600160a01b03851660009081526007602052604090206120e590546120df6005548a6139c1565b906139e1565b6001600160a01b0386166000908152600760205260409020556001600160a01b0385811699906121178930338e615576565b8b5163dc3bfba960e01b80825283831691908e8b8389818c885af1928315612393575b8993612374575b50519081528a8188818b875af1908115612367575b889161234a575b508380871691160361230657508885918e5192838092635001f3b560e01b82525afa9081156122f9575b86916122dc575b50168a81036122c15750936000805160206156a483398151915296938996938d6121f361069d9f9c9560029f9e9c998a826121ec9230908a156122b9575b6121e560a435946121df600554826139c1565b90613a34565b908d6147b3565b9088614e1b565b9d8e9414612228575b50508a5160ff909716875250505050602083018890525060408201523360608201528060808101610682565b6104986104986122489288612242886122749b8d9b614f26565b50614d32565b8c51630fa9d3b160e01b8152339281019283526020830194909452929485939284929091839160400190565b03925af180156122ac575b61228e575b838982808a6121fc565b816122a492903d106106c8576106b88183611572565b503880612284565b6122b461390a565b61227f565b3391506121cc565b8361074f888e51938493636d4c6c8960e01b855284016139f6565b6122f39150893d8b11610702576106f48183611572565b3861218e565b61230161390a565b612187565b8d51636d4c6c8960e01b8152600c81880190815260006020820181905260408201526001600160a01b039283166060820152918616608083015290819060a0010390fd5b61236191508b3d8d11610702576106f48183611572565b3861215d565b61236f61390a565b612156565b61238c9193508c8d3d10610702576106f48183611572565b9138612141565b61239b61390a565b61213a565b6123b79150863d8811610702576106f48183611572565b386120b5565b6123c561390a565b6120ae565b8851636d4c6c8960e01b81526002918101918252426020830152604082018b9052600060608301819052608083015290819060a0010390fd5b8851636d4c6c8960e01b815290819061074f9089908301613955565b5060ff6003541661203a565b61074f85604051918291636d4c6c8960e01b83528201906000608060a0840193601e81528260208201528260408201528260608201520152565b503461000e57600036600319011261000e57602060ff600354166040519015158152f35b503461000e5760a036600319011261000e5760048035906124a9826103a0565b6024356124b5816103c5565b60443590606435916084359260026009541461242b57600260095560ff86169260008481526020916002835260409560ff878420541680156127c3575b6127aa5785421161277257600803612737579661256394959697838a61252561049861049860015460018060a01b031690565b8a5163125cf47f60e01b81526001600160a01b038516868201908152602081018c905260ff9093166040840152988992839188918391606090910190565b03925af195861561272a575b839661270b575b506001600160a01b03818116966126339185919087906126179061259c8b30338f615576565b8d6125f06125ad8d600554906139c1565b6001600160a01b038a1660009081526007602052604090206125d0908290611a7f565b6001600160a01b038b1660009081526007602052604090205b558d613a34565b9051636e553f6560e01b815289810191825230602083015295869384928391604090910190565b039286165af19182156126fe575b86926126df575b5083614e1b565b98808a106126a4575083896000805160206156a483398151915296948c969461224861049861069d9f8d886122428861266f9b61049895614f26565b03925af180156106cf576106a1575050855160ff90921682526020820187905260408201523360608201528060808101610682565b8851636d4c6c8960e01b81526010818501908152602081018c90526040810192909252600060608301819052608083015290819060a0010390fd5b6126f7919250873d8911610e7b57610e6d8183611572565b903861262c565b61270661390a565b612625565b612723919650843d8611610702576106f48183611572565b9438612576565b61273261390a565b61256f565b8551636d4c6c8960e01b81526006818a0190815260ff8b166020820152600060408201819052606082018190526080820152819060a0010390fd5b8651636d4c6c8960e01b81526002818b01908152426020820152604081018890526000606082018190526080820152819060a0010390fd5b8651636d4c6c8960e01b81528061074f8c828d01613955565b5060ff600354166124f2565b503461000e57600036600319011261000e5760206040516203f4808152f35b503461000e57608036600319011261000e57600480359061280e826103a0565b6024359161281b836103c5565b6044356064359160026009541461077a57600260095560ff81169460008681526020906002825260409560ff87832054168015612b7c575b612b6357908291849387898897858d6128b461287c61049861049860015460018060a01b031690565b94519b8c968795869463125cf47f60e01b8652850191604091949360ff91606085019660018060a01b03168552602085015216910152565b03925af1948515612b56575b8295612b37575b506001600160a01b038581169a9095908b15612afe5783908061291d5761074f8c8c51918291636d4c6c8960e01b83528201906000608060a0840193602081528260208201528260408201528260608201520152565b8c9b949392919060018103612a8a57505061293781615100565b8042118015612a82575b612a48575093878a879583956129ae8d9e9f6104987f309b03ba657e17f1beadbc6eb3c06ba79b38084eb8d0e5452cc222462a17f1f69e9f6129a761299e8b612a069f8f6129996129d99f6104989930903390615576565b614e1b565b809d819a614f26565b508d614d32565b9251630fa9d3b160e01b81523392810192835260208301949094529294859384929091839160400190565b03925af18015612a3b575b612a1d575b5050875160ff909616865260208601529116929081906040820190565b0390a3612a136001600955565b5160018152602090f35b81612a3392903d106106c8576106b88183611572565b5088806129e9565b612a4361390a565b6129e4565b8a51636d4c6c8960e01b815260078187019081526020810192909252426040830152600060608301819052608083015290819060a0010390fd5b508015612941565b60028103612aa2575050612a9d81615100565b612937565b60038103612ab5575050612a9d8161515b565b808603612ac7575050612a9d81615190565b60058103612ada575050612a9d816151c5565b60078103612aed575050612a9d81615275565b600803612a9d5750612a9d816153be565b8951636d4c6c8960e01b8152601a818d0190815260006020820181905260408201819052606082018190526080820152819060a0010390fd5b612b4f919550833d8511610702576106f48183611572565b93386128c7565b612b5e61390a565b6128c0565b8651636d4c6c8960e01b81528061074f87828c01613955565b5060ff60035416612853565b503461000e57600036600319011261000e576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b503461000e5760e036600319011261000e57612beb6004356103a0565b612bf66024356103c5565b612c0160c4356103c5565b600260095414611e5157600260095560ff60043516600052600260205260ff6040600020541680156132d5575b611e28576044354211611df357600154612c5290610498906001600160a01b031681565b6020604051809263125cf47f60e01b825281600081612c9a6004356044356024356004850191604091949360ff91606085019660018060a01b03168552602085015216910152565b03925af19081156132c8575b6000916132a9575b50612cc860643530336024356001600160a01b0316615576565b612d21612cd96005546064356139c1565b6001600160a01b03602435166000908152600760205260409020612cfe908290611a7f565b6001600160a01b0360243516600090815260076020526040902055606435613a34565b6040516370a0823160e01b81523060048201529091906020816024816001600160a01b0386165afa90811561329c575b60009161327d575b5060043560ff16600503613085576040516316f0115b60e01b81526020816004816001600160a01b0387165afa908115613078575b600091613059575b5060405163f1563de960e01b815260208160048160c4356001600160a01b03165afa90811561304c575b60009161302d575b506001600160a01b0381811690831603611d47575060405163f77c479160e01b81529190602090839060049082906001600160a01b03165afa918215613020575b600092612fff575b506001600160a01b0382163b1561000e5760405163507a3baf60e01b81526001600160a01b0360c4358116600483015260248201959095526001604482015260006064820181905260a480356084840152612eab96612ea29584929183918591165af18015612ff2575b612fd9575b505b6040516370a0823160e01b81523060048201526020818060248101611b5d565b90602435614e1b565b60043560ff1660051480612fce575b612f975780612ed161069d92602435600435614f26565b50612ee6610498610498604435602435614d32565b604051630fa9d3b160e01b81523360048201526024810183905290602090829060449082906000905af18015612f8a575b612f6b575b506040805160ff6004351681526020810183905260643591810191909152336060820152604435906024356001600160a01b0316906000805160206156a4833981519152908060808101611c53565b612f839060203d6020116106c8576106b88183611572565b5038612f1c565b612f9261390a565b612f17565b604051636d4c6c8960e01b8152600b600482015260248101919091526084803560448301526000606483018190529082015260a490fd5b506084358110612eba565b80612fe6612fec9261151c565b80610800565b38612e80565b612ffa61390a565b612e7b565b61301991925060203d602011610702576106f48183611572565b9038612e11565b61302861390a565b612e09565b613046915060203d602011610702576106f48183611572565b38612dc8565b61305461390a565b612dc0565b613072915060203d602011610702576106f48183611572565b38612d96565b61308061390a565b612d8e565b91600760ff60043516146130a1575b50612ea2612eab92612e82565b60405163c6ec27bf60e01b815260208160048160c4356001600160a01b03165afa908115613270575b600091613251575b506001600160a01b03818116602435909116036132315750604051631e5f74a160e01b815260208160048160c4356001600160a01b03165afa908115613224575b600091613205575b506001600160a01b03818116908416036131e75750612eab92612ea2916020613142614d00565b61314a614cad565b60405163e7ffb5f760e01b815293849283926131779260a435923092608435929160c43560048901614556565b038160007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165af180156131da575b6131bb575b509250613094565b6131d39060203d602011610e7b57610e6d8183611572565b50386131b3565b6131e261390a565b6131ae565b604051636d4c6c8960e01b815290819061074f9085600484016139f6565b61321e915060203d602011610702576106f48183611572565b3861311b565b61322c61390a565b613113565b604051636d4c6c8960e01b815290819061074f90602435600484016139f6565b61326a915060203d602011610702576106f48183611572565b386130d2565b61327861390a565b6130ca565b613296915060203d602011610e7b57610e6d8183611572565b38612d59565b6132a461390a565b612d51565b6132c2915060203d602011610702576106f48183611572565b38612cae565b6132d061390a565b612ca6565b5060ff60035416612c2e565b503461000e57600036600319011261000e576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b503461000e5761016036600319011261000e576004803590613348826103a0565b60243590613355826103c5565b60443560643560a03660a319011261000e576101443592613375846103c5565b60026009541461368c57600260095560009560ff811687526020916002835260409560ff878a2054168015613680575b6136665785421161362e5761340b9084846133cd61049861049860015460018060a01b031690565b8a5163125cf47f60e01b81526001600160a01b038d16878201908152602081018c905260ff909316604084015294859283918f918391606090910190565b03925af1918215613621575b8a92613602575b508751630b2339af60e21b81526001600160a01b03919060608186818686165afa9081156135f5575b8c916135c4575b50828085169116036135a95761353086938a8c858f956105f59661347b8e9b9985169e8f30903390615576565b6134f76134ce61348e8d600554906139c1565b6001600160a01b03871660009081526007602052604090206134b1908290611a7f565b6001600160a01b03881660009081526007602052604090206125e9565b946134d76115b2565b6001600160a01b038216815295868d01526001600160a01b031686860152565b80606085015280608085015261350b61440b565b60a08501528451809781958294631b7077d560e01b8452608435908d309086016144b3565b03927f0000000000000000000000000000000000000000000000000000000000000000165af191821561359c575b8d9261356c575b508b614e1b565b61358d9192508b3d8d11613595575b6135858183611572565b81019061449d565b509038613565565b503d61357b565b6135a461390a565b61355e565b8361074f848b51938493636d4c6c8960e01b855284016139f6565b6135e5915060603d81116135ee575b6135dd8183611572565b81019061446a565b5090503861344e565b503d6135d3565b6135fd61390a565b613447565b61361a919250853d8711610702576106f48183611572565b903861341e565b61362961390a565b613417565b8651636d4c6c8960e01b81526002818401908152426020820152604081018890526000606082018190526080820152819060a0010390fd5b5061074f8651928392636d4c6c8960e01b84528301613955565b5060ff600354166133a5565b61074f90604051918291636d4c6c8960e01b83528201906000608060a0840193601e81528260208201528260408201528260608201520152565b503461000e57600036600319011261000e576000546040516001600160a01b039091168152602090f35b503461000e57602036600319011261000e5760043561370e816103c5565b6000546001600160a01b03908116338190036108a4575060207fe4b67652e856f57a7747dd2473850ce987087f4b1744a870504f1c047cb56f4f916203f48042019384421161377c575b1692836000526008825280604060002055604051908152a260405160018152602090f35b6137846139aa565b613758565b503461000e576000806003193601126137e35780546001600160a01b0316338190036108a457602082806006557f6875685eb5dbc8e2796d75d2dc9e9cb607b610d0558ee7336df418a26d4846e86040519182a160018152f35b80fd5b503461000e57602036600319011261000e57600435613804816103c5565b60018060a01b031660005260076020526020604060002054604051908152f35b503461000e57604036600319011261000e57600435613842816103a0565b6024359061384f82610f53565b6000546001600160a01b0316338190036108a45750602060ff7f09f6e15731965d66c152a2c1c1b62c94420ba4530dafe8818e1a01906641fb1e92169283600052600282526138ae8160406000209060ff801983541691151516179055565b604051938452151592a260405160018152602090f35b60008082526020820181905260408201526001600160a01b0391821660608201529116608082015260a00190565b9081602091031261000e5751613907816103c5565b90565b506040513d6000823e3d90fd5b50634e487b7160e01b600052603260045260246000fd5b919081101561393e5760051b0190565b611612613917565b9081602091031261000e575190565b608060009193929360ff60a0820195600183521660208201528260408201528260608201520152565b50634e487b7160e01b600052602160045260246000fd5b9081602091031261000e575161390781610f53565b50634e487b7160e01b600052601160045260246000fd5b81156139cb570490565b634e487b7160e01b600052601260045260246000fd5b919082018092116139ee57565b6103b86139aa565b601b815260006020820181905260408201526001600160a01b0391821660608201529116608082015260a00190565b6000198101919082116139ee57565b919082039182116139ee57565b9799949598929098969196600260095414611e5157600260095560ff8916600052600260205260ff60406000205416801561412c575b614111578742116140dd57613a968a613a9136848b614138565b614a52565b613a9f82614a13565b6040516370a0823160e01b81523060048201529b90979060208d8d81806024810103916001600160a01b03165afa9c8d156140d0575b60009d614091575b50613bc38b8d8c613d5397613d8c9997613d4e978f97613b1789613b0e613d299b303360018060a01b038c16615576565b600554906139c1565b9a8b613b41613b3a613b298851613a25565b92613b34848a61421e565b51613a34565b918761421e565b526001600160a01b0387166000908152600760205260409020613b658d82546139e1565b90556001546020908990613b8390610498906001600160a01b031681565b60405163125cf47f60e01b81526001600160a01b038b166004820152602481018a905260ff9092166044830152909a8b9190829060009082906064820190565b03925af1988915614084575b600099614063575b506040516370a0823160e01b8152306004820152986020908a9060249082906001600160a01b03165afa988915614056575b60009961401b575b50926020969492613c3e8893613cd39a989660405196879586956310510f1160e01b8752600487016142cb565b038160007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165af1801561400e575b613ff1575b50600154600090613c9590610498906001600160a01b031681565b60405163125cf47f60e01b81526001600160a01b039094166004850152602484019290925260ff909316604483015290938492839182906064820190565b03925af1908115613fe4575b600091613fc5575b506040516370a0823160e01b81523060048201529060209082908180602481015b03916001600160a01b03165afa908115611ce057600091611cc15750613a34565b6040516370a0823160e01b8152306004820152909c906020818e818060248101613d08565b613a34565b613d5f600554826139c1565b6001600160a01b038a166000908152600760205260409020909190613d858382546139e1565b9055613a34565b9060009015613f215750613eb5613e848860006020613e6d613e446104988f9d9b99613ebd9f9d613e3e819d613e22610498946000805160206156a48339815191529e8a868e613de961049861049860015460018060a01b031690565b908f60405180988195829463125cf47f60e01b8452600484016040906000929493606082019560018060a01b0316825260208201520152565b03925af1928315613f14575b8c93613ef5575b5033918d6147b3565b97614d32565b604051630fa9d3b160e01b8152336004820152602481018e905293849283919082906044820190565b03925af18015613ee8575b613ec9575b50886139e1565b604080516001815260208101929092528101939093523360608401526001600160a01b038616929081906080820190565b0390a3614f26565b50906103b86001600955565b613ee19060203d6020116106c8576106b88183611572565b5038613e7d565b613ef061390a565b613e78565b613f0d9193508b3d8d11610702576106f48183611572565b9138613e35565b613f1c61390a565b613e2e565b9695939150849790859350808796613f4b8860018060a01b03166000526004602052604060002090565b6000918252602052604090205490613f62916139e1565b6001600160a01b0387166000908152600460205260409020859060009182526020526040902055613f92916139e1565b96613eb5613e84613ebd9860006020613e6d613e446104988f9d6000805160206156a48339815191529961049891614d32565b613fde915060203d602011610702576106f48183611572565b38613ce7565b613fec61390a565b613cdf565b61400790853d87116106c8576106b88183611572565b5038613c7a565b61401661390a565b613c75565b613cd398969491995092613c3e6020989694936140458a95863d8811610e7b57610e6d8183611572565b9b9396989a50935050929496613c11565b61405e61390a565b613c09565b61407d91995060203d602011610702576106f48183611572565b9738613bd7565b61408c61390a565b613bcf565b8a9d508b8d8b926020989697983d6020116140c9575b6140b18183611572565b81016140bc91613946565b9f50505094939294613add565b503d6140a7565b6140d861390a565b613ad5565b604051636d4c6c8960e01b81526002600482015242602482015260448101899052600060648201819052608482015260a490fd5b604051636d4c6c8960e01b81528061074f8b60048301613955565b5060ff60035416613a77565b929192614144826115f1565b60409461415386519283611572565b819584835260208093019161014080960285019481861161000e57925b8584106141805750505050505050565b868483031261000e578487916141946115d1565b863581526141a38388016103ba565b838201526141b28688016103f0565b8682015260606141c38189016103f0565b9082015260806141d4818901610f6a565b9082015260a06141e5818901610f6a565b9082015260c0808801359082015260e0808801359082015261010080880135908201526101208088013590820152815201930192614170565b6020918151811015614233575b60051b010190565b61423b613917565b61422b565b90815180825260208080930193019160005b828110614260575050505090565b835185529381019392810192600101614252565b9190808252602080920192916000905b828210614292575050505090565b9091929360019060ff86356142a6816103a0565b1681528583013583820152604080870135908201526060908101950193920190614284565b9391959492606096808887018988525260809081870193916000905b82821061431857505050506139079596509061430a918582036020870152614240565b926040818503910152614274565b909192946001908635815261433c6020614333818a016103ba565b60ff1690830152565b61435b604061434c818a016103f0565b6001600160a01b031690830152565b6143786143698d89016103f0565b6001600160a01b0316828e0152565b61438e614386848901610f6a565b151582850152565b6143a660a061439e818a01610f6a565b151590830152565b60c0878101359082015260e08088013590820152610100808801359082015261012080880135908201526101409081019601939201906142e7565b6020906001600160401b0381116143fe575b601f01601f19160190565b614406611505565b6143f3565b604051906144188261153c565b604082527f30303030303030303030303030303030303030303030303030303030303030306040837f307830303030303030303030303030303030303030303030303030303030303060208201520152565b9081606091031261000e578051614480816103c5565b9160406020830151614491816103c5565b920151613907816103c5565b919082604091031261000e576020825192015190565b92936101e0926139079560a093600180861b0392838092168852166020870152604086015260a435606086015260c435608086015260e435838601526101043560c08601526101243560e0860152610120806101008701528183511690860152602082015161014086015280604083015116610160860152806060830151166101808601526080820151166101a085015201519160c06101c08201520190610aba565b939796929461459460e096614586600099949560018060a01b0380971689526101008060208b0152890190614240565b908782036040890152614240565b98606086015260808501521660a083015260c08201520152565b60015460405163057453a760e31b81526103b8939290916001600160a01b0391906020908490600490829086165afa928315614614575b6000936145f4575b5016615505565b61460d91935060203d8111610702576106f48183611572565b91386145ed565b61461c61390a565b6145e5565b60015460405163057453a760e31b81526146a9929161468f916001600160a01b03916020908290600490829086165afa9081156146d7575b6000916146b9575b506001600160a01b038416600090815260046020526040902086600052602052604060002054918416615505565b6001600160a01b0316600090815260046020526040902090565b9060005260205260006040812055565b6146d1915060203d8111610702576106f48183611572565b38614661565b6146df61390a565b614659565b60019060001981146146f4570190565b6146fc6139aa565b0190565b9190811015614742575b60051b81013590601e198136030182121561000e5701908135916001600160401b03831161000e57602001823603811361000e579190565b61474a613917565b61470a565b908092918237016000815290565b3d15614788573d9061476e826143e1565b9161477c6040519384611572565b82523d6000602084013e565b606090565b906020613907928181520190610aba565b9081602091031261000e57516139078161188b565b6040516370a0823160e01b8082526001600160a01b0386811660048401526148f697929660209690959382169492939192918789602481895afa988915614a06575b6000996149c8575b506148a4846148db989694614849600095858d9b998c97169487614820846150ec565b6040516304f9ef2360e21b81526001600160801b03909116600482015294859081906024820190565b0381895afa9384156149bb575b899461499a575b50614869939416615505565b60405163bcc1694f60e01b81526001600160a01b03891660048201526001600160801b03909116602482015293849283919082906044820190565b03925af1801561498d575b614960575b506040519081526001600160a01b0390921660048301529092839190829081906024820190565b03915afa918215614953575b600092614936575b5050613a34565b90808210614902575090565b604051636d4c6c8960e01b8152600b600482015260248101929092526044820152600060648201819052608482015260a490fd5b61494c9250803d10610e7b57610e6d8183611572565b38806148ef565b61495b61390a565b6148e7565b61497f90853d8711614986575b6149778183611572565b81019061479e565b50386148b4565b503d61496d565b61499561390a565b6148af565b61486994506149b590893d8b11614986576149778183611572565b9361485d565b6149c361390a565b614856565b88969487929a506148db9896946148496000956149f46148a495873d8911610e7b57610e6d8183611572565b9d9550955050949698509496506147fd565b614a0e61390a565b6147f5565b906000805b83518214614a4d57600190614a2d838661421e565b518101809111614a40575b910190614a18565b614a486139aa565b614a38565b925050565b90600091825b81518114614abe576001600160a01b03806060614a75848661421e565b51015116908416818103614a8d575050600101614a58565b60a491869160405192636d4c6c8960e01b845260036004850152806024850152604484015260648301526084820152fd5b50505050565b9092614b69614b3260a060c09598979660e08652805160e087015260208101516002811015614b72575b610100870152600180831b038060408301511661012088015260608201511661014087015260808101516101608701520151856101808601526101a0850190610aba565b9660208401906060809160018060a01b03808251168552602082015115156020860152604082015116604085015201511515910152565b60a08201520152565b614b7a61397e565b614aee565b9193614c3b9394614ba2610498610498610498606087015160018060a01b031690565b6040516370a0823160e01b808252306004830152909560209592949193919289929087896024818a5afa988915614ca0575b600099614c6f575b5091879594939160008794614c07604051978896879586946352bbbe2960e01b865260048601614ac4565b03926001600160a01b03165af18015614c62575b614c45575b506040519081523060048201529182908180602481016148db565b908110611c8c5790565b614c5b90833d8511610e7b57610e6d8183611572565b5038614c20565b614c6a61390a565b614c1b565b8896959492908794929a50614c92600091863d8811610e7b57610e6d8183611572565b9a9294505091939495614bdc565b614ca861390a565b614bd4565b604051614cb98161153c565b600281526001602082016040368237825115614cf3575b52805160011015614ce6575b6000604082015290565b614cee613917565b614cdc565b614cfb613917565b614cd0565b604051614d0c81611557565b600181526000602082016020368237825115614d26575290565b614d2e613917565b5290565b602090606460018060a01b039160008360015416604051968795869463125cf47f60e01b865216600485015260248401528160448401525af1908115614d96575b600091614d7e575090565b613907915060203d8111610702576106f48183611572565b614d9e61390a565b614d73565b9081602091031261000e5751613907816103a0565b60ff16601b039060ff82116139ee57565b9060ff8091169116039060ff82116139ee57565b60ff16604d8111614def575b600a0a90565b614df76139aa565b614de9565b8060001904821181151516614e0f570290565b614e176139aa565b0290565b60405163313ce56760e01b8082529093926020926001600160a01b03929084908790600490829087165afa958615614f19575b600096614ef3575b50906004849260405194859384928352165afa918215614ee6575b600092614eb9575b505060ff811660ff841611614ea357614e9861390793614e9d92614dc9565b614ddd565b906139c1565b614e98614eb39161390794614dc9565b90614dfc565b614ed89250803d10614edf575b614ed08183611572565b810190614da3565b3880614e79565b503d614ec6565b614eee61390a565b614e71565b8492919650614f10600491843d8611614edf57614ed08183611572565b96919250614e56565b614f2161390a565b614e4e565b91614f86916001600160a01b03168173ae7ab96520de3a18e5e111b5eaab095312d7fe8482146150c9575b50614e9860046020614eb3936040519283809263313ce56760e01b82525afa9081156150bc575b60009161509e575b50614db8565b600a5480821161508657614fab82611a7f8560ff16600052600b602052604060002090565b80614fc38560ff16600052600b602052604060002090565b5562015180614fe9614fe28660ff16600052600c602052604060002090565b5442613a34565b11156150275750506150088260ff16600052600b602052604060002090565b55615021429160ff16600052600c602052604060002090565b55600190565b1190506150345750600190565b61504b8160ff16600052600b602052604060002090565b54604051636d4c6c8960e01b8152601f6004820152602481019190915260ff919091166044820152600060648201819052608482015260a490fd5b8261504b8160ff16600052600b602052604060002090565b6150b6915060203d8111614edf57614ed08183611572565b38614f80565b6150c461390a565b614f78565b614eb3919250600460206150e2614e9893600d54614dfc565b9493505050614f51565b6001600160801b039081811161000e571690565b60405163204f83f960e01b815290602090829060049082906001600160a01b03165afa90811561514e575b600091615136575090565b613907915060203d8111610e7b57610e6d8183611572565b61515661390a565b61512b565b60405163aa082a9d60e01b815290602090829060049082906001600160a01b03165afa90811561514e57600091615136575090565b6040516370c264df60e11b815290602090829060049082906001600160a01b03165afa90811561514e57600091615136575090565b6040516316f0115b60e01b81526020916001600160a01b0391839182908290600490829087165afa908115615268575b60009161524b575b50600460405180948193632745fed560e11b8352165afa91821561523e575b60009261522857505090565b6139079250803d10610e7b57610e6d8183611572565b61524661390a565b61521c565b6152629150823d8411610702576106f48183611572565b386151fd565b61527061390a565b6151f5565b60405163247c579160e21b81526020916001600160a01b0391839161532591849184908290600490829086165afa9081156153b1575b600091615394575b50166040516362b9c05160e11b8152838082600481865afa918215615387575b600092615367575b50604051636558954f60e01b81529192829060049082905afa90811561535a575b60009161533d575b506040519485809481936220e10960e51b8352600483019190602083019252565b0392165afa91821561523e5760009261522857505090565b6153549150843d8611610e7b57610e6d8183611572565b38615304565b61536261390a565b6152fc565b6004925061538190823d8411610702576106f48183611572565b916152db565b61538f61390a565b6152d3565b6153ab9150843d8611610702576106f48183611572565b386152b3565b6153b961390a565b6152ab565b60405163e16695b560e01b815290602090829060049082906001600160a01b03165afa908115615444575b6000916153fc575b5064ffffffffff1690565b6020813d821161543c575b8161541460209383611572565b8101031261543857519064ffffffffff821682036137e3575064ffffffffff6153f1565b5080fd5b3d9150615407565b61544c61390a565b6153e9565b60448151106154cb5760048101518101906020816024840193031261000e576024810151906001600160401b03821161000e57018160438201121561000e57602481015161549e816143e1565b926154ac6040519485611572565b8184526044828401011161000e57613907916044602085019101610a97565b506040516154d881611557565b601d81527f5472616e73616374696f6e2072657665727465642073696c656e746c79000000602082015290565b6044600092838093615538966040519363a9059cbb60e01b855260018060a01b0316600485015260248401525af16155f5565b1561553f57565b60405162461bcd60e51b815260206004820152600f60248201526e1d1c985b9cd9995c8819985a5b1959608a1b6044820152606490fd5b600092836064926155b2968295604051946323b872dd60e01b865260018060a01b03809216600487015216602485015260448401525af16155f5565b156155b957565b60405162461bcd60e51b81526020600482015260146024820152731d1c985b9cd9995c88199c9bdb4819985a5b195960621b6044820152606490fd5b6000903d901561562557908160201461561857501561561357600090565b600190565b9050602081803e51151590565b908181803efd5b60405163095ea7b360e01b81526001600160a01b039092166004830152600019602483015261566691600091829160449183905af16155f5565b1561566d57565b60405162461bcd60e51b815260206004820152600e60248201526d1054141493d59157d1905253115160921b6044820152606490fdfe4dcca373512f0049f0e5732bf8deedaf846a121102837227c421cb8d8fc83522a2646970667358221220fc2f8cfc294ef1d52a654ce80df42688b2f5b2b9c4808b13bd14eaa3b6e5907f64736f6c63430008100033000000000000000000000000373a06bd3067f8da90239a47f316f09312b7800f00000000000000000000000041fad93f225b5c1c95f2445a5d7fcb85ba46713f000000000000000000000000f5ba2e5dded276fc0f7a7637a61157a4be79c626

Deployed Bytecode

0x60806040526004361015610013575b600080fd5b60003560e01c806303799f87146103975780630cdc7ec61461038e5780630d3f5352146103855780631177ec301461037c5780631271f09a146103735780631ac3ddeb1461036a5780631e897afb1461036157806327187991146103585780632e25d2a61461034f57806330568a8d1461034657806335197f9e1461033d578063355d63f11461033457806337a3eeec1461032b5780633faf30ad1461032257806351cff8d9146103195780635ac86ab71461031057806362a4520e1461030757806365dff1e1146102fe57806369fe0e2d146102f55780636b42450d146102ec578063704b6c02146102e35780637114d92c146102da57806375a9b172146102d15780637a9262a2146102c85780637c7cde83146102bf578063884e21d6146102b65780639984f30d146102ad5780639e307955146102a45780639e6b51731461029b578063a102e38414610292578063adea384414610289578063b49e0d7214610280578063b9b8af0b14610277578063c244e1ff1461026e578063d0886f9714610265578063dc4c7ca91461025c578063ea08c03114610253578063eda8ff6b1461024a578063ef60356914610241578063f7adf98014610238578063f851a4401461022f578063f8eaad3514610226578063f9ad473d1461021d578063faaebd21146102145763fe3ee1691461020c57600080fd5b61000e613824565b5061000e6137e6565b5061000e613789565b5061000e6136f0565b5061000e6136c6565b5061000e613327565b5061000e6132e1565b5061000e612bce565b5061000e612b88565b5061000e6127ee565b5061000e6127cf565b5061000e612489565b5061000e612465565b5061000e611fc8565b5061000e611f8f565b5061000e611efb565b5061000e611edc565b5061000e611ebd565b5061000e611e86565b5061000e61189c565b5061000e61186c565b5061000e61182e565b5061000e611787565b5061000e6116d6565b5061000e611469565b5061000e6113c2565b5061000e611298565b5061000e6111d5565b5061000e61119c565b5061000e61115e565b5061000e610fef565b5061000e610f75565b5061000e610f0d565b5061000e610d56565b5061000e610d37565b5061000e610ca5565b5061000e610c7b565b5061000e610c44565b5061000e610b41565b5061000e610a0e565b5061000e6108c2565b5061000e610829565b5061000e61080b565b5061000e6107b4565b5061000e6103fb565b60ff81160361000e57565b600435906103b8826103a0565b565b35906103b8826103a0565b6001600160a01b0381160361000e57565b602435906103b8826103c5565b608435906103b8826103c5565b35906103b8826103c5565b503461000e5761010036600319011261000e57600480359061041c826103a0565b60243590610429826103c5565b60443560c43560643561043b826103c5565b60026009541461077a57600260095560009560ff811687526020916002835260409560ff878a205416801561076e575b610753578542116107165783906104e382858a8a8e8e6104a461049861049860015460018060a01b031690565b6001600160a01b031690565b935163125cf47f60e01b81526001600160a01b03909116888201908152602081019390935260ff90941660408301529294859384929091839160600190565b03925af1908115610709575b8b916106dc575b506001600160a01b038a811697908a908d906105148830338e615576565b6001600160a01b038e1660009081526007602052604090208e905460055461053c908b6139c1565b610545916139e1565b6001600160a01b0390911660009081526007602052604090205560055461056c90896139c1565b6105769089613a34565b908b610580611593565b30815294858a01859052308187015284606087015261059d6115b2565b60e43581528a8101959095526001600160a01b039091169084015285166001600160a01b0316606083015260808201526105d561440b565b60a082015260a43591608435916105eb94614b7f565b6105f5918b614e1b565b98876106028b8388614f26565b5061060c91614d32565b8851630fa9d3b160e01b815233928101928352602083018b90529a8b92839003604001918391906001600160a01b03165af161069d986000805160206156a4833981519152949180156106cf575b6106a1575b5050855160ff909216825260208201879052604082015233606082015280608081015b0390a361068f6001600955565b519081529081906020820190565b0390f35b816106c092903d106106c8575b6106b88183611572565b810190613995565b50388061065f565b503d6106ae565b6106d761390a565b61065a565b6106fc9150833d8511610702575b6106f48183611572565b8101906138f2565b386104f6565b503d6106ea565b61071161390a565b6104ef565b8651636d4c6c8960e01b8152600291810191825242602083015260408201879052600060608301819052608083015290819060a0010390fd5b0390fd5b8651636d4c6c8960e01b81529283925061074f918301613955565b5060ff6003541661046b565b61074f84604051918291636d4c6c8960e01b83528201906000608060a0840193601e81528260208201528260408201528260608201520152565b503461000e57604036600319011261000e576004356107d2816103c5565b60018060a01b0316600052600460205260406000206024356000526020526020604060002054604051908152f35b600091031261000e57565b503461000e57600036600319011261000e5760206040516101f48152f35b503461000e57600036600319011261000e576000546001600160a01b0316338190036108a4577ff339d7864b1b8839e8a8870c012fc6eb9a89844861a87a26ce35979018603a1b60206203f4804201804211610897575b80600655604051908152a160405160018152602090f35b61089f6139aa565b610880565b604051636d4c6c8960e01b815290819061074f9033600484016138c4565b503461000e57606036600319011261000e57600480356108e1816103c5565b602435906044356108f1816103c5565b600080546001600160a01b0391908216338190036109f15750805b60ff906009828216146109d85760019084876109788b84610939610498610498895460018060a01b031690565b8d8a60405180968195829463125cf47f60e01b845260209a8b99850191604091949360ff91606085019660018060a01b03168552602085015216910152565b03925af19182156109cb575b87926109ae575b505016868161099e575b5050011661090c565b6109a79161562c565b3886610995565b6109c49250803d10610702576106f48183611572565b388061098b565b6109d361390a565b610984565b5050506109e5921661562c565b60405160018152602090f35b604051636d4c6c8960e01b815290819061074f9033838b016138c4565b503461000e57602036600319011261000e57600435610a2c816103c5565b600080546001600160a01b03908116338190036108a45750806109e59316908183526007602052604083205492806040812055541690615505565b9181601f8401121561000e578235916001600160401b03831161000e576020808501948460051b01011161000e57565b60005b838110610aaa5750506000910152565b8181015183820152602001610a9a565b90602091610ad381518092818552858086019101610a97565b601f01601f1916010190565b602080820190808352835180925260408301928160408460051b8301019501936000915b848310610b135750505050505090565b9091929394958480610b31600193603f198682030187528a51610aba565b9801930193019194939290610b03565b5060208060031936011261000e57600480356001600160401b03811161000e57610b6e9036908301610a67565b9290610b79846115f1565b92604092610b8984519586611572565b858552601f19610b98876115f1565b0160005b818110610c355750505060005b858110610bbd5783518061069d8782610adf565b600080610bcb838987614700565b90610bda88518093819361474f565b0390305af4610be761475d565b9015610c125790610c0d91610bfc828861421e565b52610c07818761421e565b506146e4565b610ba9565b8261074f610c208793615451565b925162461bcd60e51b8152928392830161478d565b60608782018401528201610b9c565b503461000e57602036600319011261000e576000546001600160a01b0316338190036108a457600435600d55602060405160018152f35b503461000e57600036600319011261000e576001546040516001600160a01b039091168152602090f35b503461000e57602036600319011261000e57600435610cc3816103c5565b6000546001600160a01b03908116338190036108a457506001541680610d0357600180546001600160a01b0319166001600160a01b0384161790556109e5565b60a49060405190636d4c6c8960e01b8252600560048301526000602483015260006044830152606482015260006084820152fd5b503461000e57600036600319011261000e576020600654604051908152f35b503461000e5760a036600319011261000e57600435610d74816103c5565b60243590610d81826103c5565b60443590610d8e826103c5565b606435610d9a816103c5565b60843592610da7846103c5565b6001546001600160a01b039390841633819003610ecf5750831694838116610ebf575b50828116610eaf575b50818116610e9f575b508116610e8f575b50604051636eb1769f60e11b81523060048201527f000000000000000000000000373a06bd3067f8da90239a47f316f09312b7800f6001600160a01b038116602483015290602081604481865afa908115610e82575b600091610e54575b5015610e4b575b005b610e499161562c565b610e75915060203d8111610e7b575b610e6d8183611572565b810190613946565b38610e42565b503d610e63565b610e8a61390a565b610e3a565b610e99908261562c565b38610de4565b610ea9908461562c565b38610ddc565b610eb9908561562c565b38610dd3565b610ec9908661562c565b38610dca565b604051636d4c6c8960e01b81526000600482018190526024820181905260448201523360648201526001600160a01b0391909116608482015260a490fd5b503461000e57600036600319011261000e576040517f000000000000000000000000f5ba2e5dded276fc0f7a7637a61157a4be79c6266001600160a01b03168152602090f35b8015150361000e57565b60e435906103b882610f53565b35906103b882610f53565b503461000e57602036600319011261000e57600435610f9381610f53565b6000546001600160a01b0316338190036108a4577fb4252ba7ada6e65ca524b94dd554b37a638732fc8c63021e66782fc6e75bbb0c602083151560ff196003541660ff821617600355604051908152a160405160018152602090f35b503461000e57602036600319011261000e5760043561100d816103c5565b6000546001600160a01b03908116338190036108a457506001600160a01b03821660009081526008602052604090205480156111295742106110f4576001600160a01b03821660009081526008602052604081206109e5939190556001600160a01b0381166000908152600760205260408120556000549116906001600160a01b03166040516370a0823160e01b81523060048201529160208380602481010381845afa9283156110e7575b6000936110c7575b50615505565b6110e091935060203d8111610e7b57610e6d8183611572565b91386110c1565b6110ef61390a565b6110b9565b604051636d4c6c8960e01b8152601360048201526000602482018190526044820181905260648201819052608482015260a490fd5b604051636d4c6c8960e01b8152601260048201526000602482018190526044820181905260648201819052608482015260a490fd5b503461000e57602036600319011261000e5760ff60043561117e816103a0565b166000526002602052602060ff604060002054166040519015158152f35b503461000e57602036600319011261000e5760ff6004356111bc816103a0565b16600052600c6020526020604060002054604051908152f35b503461000e57604036600319011261000e576001600160401b0360043581811161000e57611207903690600401610a67565b909160243590811161000e57611221903690600401610a67565b9060018060a01b0380600054168033036108a4575060005b84810361124c5760405160018152602090f35b808261125b600193888a61392e565b35611265816103c5565b1680611273575b5001611239565b6112929061128283888861392e565b359061128d826103c5565b61562c565b3861126c565b503461000e57602036600319011261000e57600054600435906001600160a01b0316338190036108a457506006548061130057604051636d4c6c8960e01b8152601760048201526000602482018190526044820181905260648201819052608482015260a490fd5b80421060001461133f57604051636d4c6c8960e01b8152601860048201524260248201526044810191909152600060648201819052608482015260a490fd5b506101f4811061138d5761135281600555565b61135c6000600655565b604051907e172ddfc5ae88d08b3de01a5a187667c37a5a53989e8c175055cb6c993792a7600083a260018152602090f35b604051636d4c6c8960e01b8152601960048201526000602482018190526044820181905260648201819052608482015260a490fd5b503461000e57604036600319011261000e576004356113e0816103c5565b6001546004906020906113fd90610498906001600160a01b031681565b60405163057453a760e31b815292839182905afa90811561145c575b60009161143e575b506001600160a01b03811633036108a457610e4960243583614621565b611456915060203d8111610702576106f48183611572565b38611421565b61146461390a565b611419565b503461000e57602036600319011261000e57600435611487816103c5565b6000546001600160a01b0380821692338490036114e957602093501680916bffffffffffffffffffffffff60a01b1617600055604051907f5a272403b402d892977df56625f4164ccaf70ca3863991c43ecfe76a6905b0a1600083a260018152f35b604051636d4c6c8960e01b81528061074f8633600484016138c4565b50634e487b7160e01b600052604160045260246000fd5b6001600160401b03811161152f57604052565b611537611505565b604052565b606081019081106001600160401b0382111761152f57604052565b604081019081106001600160401b0382111761152f57604052565b90601f801991011681019081106001600160401b0382111761152f57604052565b60405190608082018281106001600160401b0382111761152f57604052565b6040519060c082018281106001600160401b0382111761152f57604052565b6040519061014082018281106001600160401b0382111761152f57604052565b6020906001600160401b03811161160a575b60051b0190565b611612611505565b611603565b81601f8201121561000e5780359161162e836115f1565b9261163c6040519485611572565b808452602092838086019260051b82010192831161000e578301905b828210611666575050505090565b81358152908301908301611658565b9181601f8401121561000e578235916001600160401b03831161000e57602080850194610140850201011161000e57565b9181601f8401121561000e578235916001600160401b03831161000e576020808501946060850201011161000e57565b503461000e5761012036600319011261000e576116f16103ab565b6116f96103d6565b6001600160401b039060643582811161000e5761171a903690600401611617565b926117236103e3565b60a43584811161000e5761173b903690600401611675565b60c49691963595861161000e5761069d9661175d6117779736906004016116a6565b949093611768610f5d565b96610104359860443591613a41565b6040519081529081906020820190565b503461000e57604036600319011261000e576004356117a5816103c5565b6001546004906020906117c290610498906001600160a01b031681565b60405163057453a760e31b815292839182905afa908115611821575b600091611803575b506001600160a01b03811633036108a457610e49602435836145ae565b61181b915060203d8111610702576106f48183611572565b386117e6565b61182961390a565b6117de565b503461000e57602036600319011261000e5760043561184c816103c5565b60018060a01b031660005260086020526020604060002054604051908152f35b503461000e57600036600319011261000e576020600a54604051908152f35b6001600160801b0381160361000e57565b503461000e5761010036600319011261000e576118ba6004356103a0565b6118c56024356103c5565b6118d060643561188b565b6118db60a4356103c5565b6118e660e4356103c5565b600260095414611e5157600260095560ff60043516600052600260205260ff604060002054168015611e45575b611e28576044354211611df35760405163378efa3760e01b815260208160048160a4356001600160a01b03165afa908115611de6575b600091611dc7575b5060405163d10eb4b960e01b81526001600160a01b0360e4358116600483015260c4356024830152909160209183916044918391165afa908115611dba575b600091611d9b575b506001546119b090610498906001600160a01b031681565b906020604051809363125cf47f60e01b8252816000816119f96004356044356024356004850191604091949360ff91606085019660018060a01b03168552602085015216910152565b03925af1918215611d8e575b600092611d6d575b506001600160a01b0381811690831603611d475750611a446064356001600160801b031630336024356001600160a01b0316615576565b611ab1611a5e6005546001600160801b03606435166139c1565b6001600160a01b03602435166000908152600760205260409020611a859082905b546139e1565b6001600160a01b03602435166000908152600760205260409020556001600160801b0360643516613a34565b6040516370a0823160e01b808252306004830152919291611b8191906020826024816001600160a01b0388165afa918215611d3a575b600092611d19575b506040516308f1f30f60e41b81526001600160a01b0360e4358116600483015260c435602483015260448201969096526084803560648301819052966020918391829060009060a435165af18015611d0c575b611ced575b5060405190815230600482015260208180602481015b03816001600160a01b0388165afa908115611ce0575b600091611cc1575b50613a34565b918210611c8c57611baf61069d92611b9d604435602435614d32565b926001600160a01b0316602435614e1b565b90611bbf82602435600435614f26565b50604051630fa9d3b160e01b81523360048201526024810183905290602090829060449082906000906001600160a01b03165af18015611c7f575b611c60575b506040805160ff600435168152602081018390526001600160801b036064351691810191909152336060820152604435906024356001600160a01b0316906000805160206156a48339815191529080608081015b0390a36117776001600955565b611c789060203d6020116106c8576106b88183611572565b5038611bff565b611c8761390a565b611bfa565b604051636d4c6c8960e01b8152600b60048201526000602482018190526044820181905260648201819052608482015260a490fd5b611cda915060203d602011610e7b57610e6d8183611572565b38611b7b565b611ce861390a565b611b73565b611d059060203d602011610e7b57610e6d8183611572565b5038611b47565b611d1461390a565b611b42565b611d3391925060203d602011610e7b57610e6d8183611572565b9038611aef565b611d4261390a565b611ae7565b604051636d4c6c8960e01b815291829161074f916001600160a01b0316600484016139f6565b611d8791925060203d602011610702576106f48183611572565b9038611a0d565b611d9661390a565b611a05565b611db4915060203d602011610702576106f48183611572565b38611998565b611dc261390a565b611990565b611de0915060203d602011610702576106f48183611572565b38611951565b611dee61390a565b611949565b604051636d4c6c8960e01b8152600260048201524260248201526044803590820152600060648201819052608482015260a490fd5b604051636d4c6c8960e01b81528061074f60048035908301613955565b5060ff60035416611913565b604051636d4c6c8960e01b8152601e60048201526000602482018190526044820181905260648201819052608482015260a490fd5b503461000e57602036600319011261000e576000546001600160a01b0316338190036108a457600435600a55602060405160018152f35b503461000e57600036600319011261000e576020600d54604051908152f35b503461000e57600036600319011261000e576020600554604051908152f35b503461000e57602036600319011261000e57600435611f19816103c5565b6000546001600160a01b039081169133839003611f73576020925016806000526008825260006040812055604051907fb1c1232c5dd039bb1c46cc05eaf25828e4f8596b7f68bdb23073ba78b9ca382d600083a260018152f35b604051636d4c6c8960e01b81528061074f8533600484016138c4565b503461000e57602036600319011261000e5760ff600435611faf816103a0565b16600052600b6020526020604060002054604051908152f35b503461000e5760c036600319011261000e576004803590611fe8826103a0565b602435611ff4816103c5565b6044359260643560843592612008846103c5565b60026009541461242b57600260095560ff8316906000918083526020926002845260409760ff8983205416801561241f575b612403578942116123ca576120a285888b8d8661206461049861049860015460018060a01b031690565b925163125cf47f60e01b81526001600160a01b038b16888201908152602081019390935260ff90941660408301529294859384929091839160600190565b03925af19081156123bd575b83916123a0575b506001600160a01b03851660009081526007602052604090206120e590546120df6005548a6139c1565b906139e1565b6001600160a01b0386166000908152600760205260409020556001600160a01b0385811699906121178930338e615576565b8b5163dc3bfba960e01b80825283831691908e8b8389818c885af1928315612393575b8993612374575b50519081528a8188818b875af1908115612367575b889161234a575b508380871691160361230657508885918e5192838092635001f3b560e01b82525afa9081156122f9575b86916122dc575b50168a81036122c15750936000805160206156a483398151915296938996938d6121f361069d9f9c9560029f9e9c998a826121ec9230908a156122b9575b6121e560a435946121df600554826139c1565b90613a34565b908d6147b3565b9088614e1b565b9d8e9414612228575b50508a5160ff909716875250505050602083018890525060408201523360608201528060808101610682565b6104986104986122489288612242886122749b8d9b614f26565b50614d32565b8c51630fa9d3b160e01b8152339281019283526020830194909452929485939284929091839160400190565b03925af180156122ac575b61228e575b838982808a6121fc565b816122a492903d106106c8576106b88183611572565b503880612284565b6122b461390a565b61227f565b3391506121cc565b8361074f888e51938493636d4c6c8960e01b855284016139f6565b6122f39150893d8b11610702576106f48183611572565b3861218e565b61230161390a565b612187565b8d51636d4c6c8960e01b8152600c81880190815260006020820181905260408201526001600160a01b039283166060820152918616608083015290819060a0010390fd5b61236191508b3d8d11610702576106f48183611572565b3861215d565b61236f61390a565b612156565b61238c9193508c8d3d10610702576106f48183611572565b9138612141565b61239b61390a565b61213a565b6123b79150863d8811610702576106f48183611572565b386120b5565b6123c561390a565b6120ae565b8851636d4c6c8960e01b81526002918101918252426020830152604082018b9052600060608301819052608083015290819060a0010390fd5b8851636d4c6c8960e01b815290819061074f9089908301613955565b5060ff6003541661203a565b61074f85604051918291636d4c6c8960e01b83528201906000608060a0840193601e81528260208201528260408201528260608201520152565b503461000e57600036600319011261000e57602060ff600354166040519015158152f35b503461000e5760a036600319011261000e5760048035906124a9826103a0565b6024356124b5816103c5565b60443590606435916084359260026009541461242b57600260095560ff86169260008481526020916002835260409560ff878420541680156127c3575b6127aa5785421161277257600803612737579661256394959697838a61252561049861049860015460018060a01b031690565b8a5163125cf47f60e01b81526001600160a01b038516868201908152602081018c905260ff9093166040840152988992839188918391606090910190565b03925af195861561272a575b839661270b575b506001600160a01b03818116966126339185919087906126179061259c8b30338f615576565b8d6125f06125ad8d600554906139c1565b6001600160a01b038a1660009081526007602052604090206125d0908290611a7f565b6001600160a01b038b1660009081526007602052604090205b558d613a34565b9051636e553f6560e01b815289810191825230602083015295869384928391604090910190565b039286165af19182156126fe575b86926126df575b5083614e1b565b98808a106126a4575083896000805160206156a483398151915296948c969461224861049861069d9f8d886122428861266f9b61049895614f26565b03925af180156106cf576106a1575050855160ff90921682526020820187905260408201523360608201528060808101610682565b8851636d4c6c8960e01b81526010818501908152602081018c90526040810192909252600060608301819052608083015290819060a0010390fd5b6126f7919250873d8911610e7b57610e6d8183611572565b903861262c565b61270661390a565b612625565b612723919650843d8611610702576106f48183611572565b9438612576565b61273261390a565b61256f565b8551636d4c6c8960e01b81526006818a0190815260ff8b166020820152600060408201819052606082018190526080820152819060a0010390fd5b8651636d4c6c8960e01b81526002818b01908152426020820152604081018890526000606082018190526080820152819060a0010390fd5b8651636d4c6c8960e01b81528061074f8c828d01613955565b5060ff600354166124f2565b503461000e57600036600319011261000e5760206040516203f4808152f35b503461000e57608036600319011261000e57600480359061280e826103a0565b6024359161281b836103c5565b6044356064359160026009541461077a57600260095560ff81169460008681526020906002825260409560ff87832054168015612b7c575b612b6357908291849387898897858d6128b461287c61049861049860015460018060a01b031690565b94519b8c968795869463125cf47f60e01b8652850191604091949360ff91606085019660018060a01b03168552602085015216910152565b03925af1948515612b56575b8295612b37575b506001600160a01b038581169a9095908b15612afe5783908061291d5761074f8c8c51918291636d4c6c8960e01b83528201906000608060a0840193602081528260208201528260408201528260608201520152565b8c9b949392919060018103612a8a57505061293781615100565b8042118015612a82575b612a48575093878a879583956129ae8d9e9f6104987f309b03ba657e17f1beadbc6eb3c06ba79b38084eb8d0e5452cc222462a17f1f69e9f6129a761299e8b612a069f8f6129996129d99f6104989930903390615576565b614e1b565b809d819a614f26565b508d614d32565b9251630fa9d3b160e01b81523392810192835260208301949094529294859384929091839160400190565b03925af18015612a3b575b612a1d575b5050875160ff909616865260208601529116929081906040820190565b0390a3612a136001600955565b5160018152602090f35b81612a3392903d106106c8576106b88183611572565b5088806129e9565b612a4361390a565b6129e4565b8a51636d4c6c8960e01b815260078187019081526020810192909252426040830152600060608301819052608083015290819060a0010390fd5b508015612941565b60028103612aa2575050612a9d81615100565b612937565b60038103612ab5575050612a9d8161515b565b808603612ac7575050612a9d81615190565b60058103612ada575050612a9d816151c5565b60078103612aed575050612a9d81615275565b600803612a9d5750612a9d816153be565b8951636d4c6c8960e01b8152601a818d0190815260006020820181905260408201819052606082018190526080820152819060a0010390fd5b612b4f919550833d8511610702576106f48183611572565b93386128c7565b612b5e61390a565b6128c0565b8651636d4c6c8960e01b81528061074f87828c01613955565b5060ff60035416612853565b503461000e57600036600319011261000e576040517f000000000000000000000000373a06bd3067f8da90239a47f316f09312b7800f6001600160a01b03168152602090f35b503461000e5760e036600319011261000e57612beb6004356103a0565b612bf66024356103c5565b612c0160c4356103c5565b600260095414611e5157600260095560ff60043516600052600260205260ff6040600020541680156132d5575b611e28576044354211611df357600154612c5290610498906001600160a01b031681565b6020604051809263125cf47f60e01b825281600081612c9a6004356044356024356004850191604091949360ff91606085019660018060a01b03168552602085015216910152565b03925af19081156132c8575b6000916132a9575b50612cc860643530336024356001600160a01b0316615576565b612d21612cd96005546064356139c1565b6001600160a01b03602435166000908152600760205260409020612cfe908290611a7f565b6001600160a01b0360243516600090815260076020526040902055606435613a34565b6040516370a0823160e01b81523060048201529091906020816024816001600160a01b0386165afa90811561329c575b60009161327d575b5060043560ff16600503613085576040516316f0115b60e01b81526020816004816001600160a01b0387165afa908115613078575b600091613059575b5060405163f1563de960e01b815260208160048160c4356001600160a01b03165afa90811561304c575b60009161302d575b506001600160a01b0381811690831603611d47575060405163f77c479160e01b81529190602090839060049082906001600160a01b03165afa918215613020575b600092612fff575b506001600160a01b0382163b1561000e5760405163507a3baf60e01b81526001600160a01b0360c4358116600483015260248201959095526001604482015260006064820181905260a480356084840152612eab96612ea29584929183918591165af18015612ff2575b612fd9575b505b6040516370a0823160e01b81523060048201526020818060248101611b5d565b90602435614e1b565b60043560ff1660051480612fce575b612f975780612ed161069d92602435600435614f26565b50612ee6610498610498604435602435614d32565b604051630fa9d3b160e01b81523360048201526024810183905290602090829060449082906000905af18015612f8a575b612f6b575b506040805160ff6004351681526020810183905260643591810191909152336060820152604435906024356001600160a01b0316906000805160206156a4833981519152908060808101611c53565b612f839060203d6020116106c8576106b88183611572565b5038612f1c565b612f9261390a565b612f17565b604051636d4c6c8960e01b8152600b600482015260248101919091526084803560448301526000606483018190529082015260a490fd5b506084358110612eba565b80612fe6612fec9261151c565b80610800565b38612e80565b612ffa61390a565b612e7b565b61301991925060203d602011610702576106f48183611572565b9038612e11565b61302861390a565b612e09565b613046915060203d602011610702576106f48183611572565b38612dc8565b61305461390a565b612dc0565b613072915060203d602011610702576106f48183611572565b38612d96565b61308061390a565b612d8e565b91600760ff60043516146130a1575b50612ea2612eab92612e82565b60405163c6ec27bf60e01b815260208160048160c4356001600160a01b03165afa908115613270575b600091613251575b506001600160a01b03818116602435909116036132315750604051631e5f74a160e01b815260208160048160c4356001600160a01b03165afa908115613224575b600091613205575b506001600160a01b03818116908416036131e75750612eab92612ea2916020613142614d00565b61314a614cad565b60405163e7ffb5f760e01b815293849283926131779260a435923092608435929160c43560048901614556565b038160007f000000000000000000000000f5ba2e5dded276fc0f7a7637a61157a4be79c6266001600160a01b03165af180156131da575b6131bb575b509250613094565b6131d39060203d602011610e7b57610e6d8183611572565b50386131b3565b6131e261390a565b6131ae565b604051636d4c6c8960e01b815290819061074f9085600484016139f6565b61321e915060203d602011610702576106f48183611572565b3861311b565b61322c61390a565b613113565b604051636d4c6c8960e01b815290819061074f90602435600484016139f6565b61326a915060203d602011610702576106f48183611572565b386130d2565b61327861390a565b6130ca565b613296915060203d602011610e7b57610e6d8183611572565b38612d59565b6132a461390a565b612d51565b6132c2915060203d602011610702576106f48183611572565b38612cae565b6132d061390a565b612ca6565b5060ff60035416612c2e565b503461000e57600036600319011261000e576040517f00000000000000000000000041fad93f225b5c1c95f2445a5d7fcb85ba46713f6001600160a01b03168152602090f35b503461000e5761016036600319011261000e576004803590613348826103a0565b60243590613355826103c5565b60443560643560a03660a319011261000e576101443592613375846103c5565b60026009541461368c57600260095560009560ff811687526020916002835260409560ff878a2054168015613680575b6136665785421161362e5761340b9084846133cd61049861049860015460018060a01b031690565b8a5163125cf47f60e01b81526001600160a01b038d16878201908152602081018c905260ff909316604084015294859283918f918391606090910190565b03925af1918215613621575b8a92613602575b508751630b2339af60e21b81526001600160a01b03919060608186818686165afa9081156135f5575b8c916135c4575b50828085169116036135a95761353086938a8c858f956105f59661347b8e9b9985169e8f30903390615576565b6134f76134ce61348e8d600554906139c1565b6001600160a01b03871660009081526007602052604090206134b1908290611a7f565b6001600160a01b03881660009081526007602052604090206125e9565b946134d76115b2565b6001600160a01b038216815295868d01526001600160a01b031686860152565b80606085015280608085015261350b61440b565b60a08501528451809781958294631b7077d560e01b8452608435908d309086016144b3565b03927f00000000000000000000000041fad93f225b5c1c95f2445a5d7fcb85ba46713f165af191821561359c575b8d9261356c575b508b614e1b565b61358d9192508b3d8d11613595575b6135858183611572565b81019061449d565b509038613565565b503d61357b565b6135a461390a565b61355e565b8361074f848b51938493636d4c6c8960e01b855284016139f6565b6135e5915060603d81116135ee575b6135dd8183611572565b81019061446a565b5090503861344e565b503d6135d3565b6135fd61390a565b613447565b61361a919250853d8711610702576106f48183611572565b903861341e565b61362961390a565b613417565b8651636d4c6c8960e01b81526002818401908152426020820152604081018890526000606082018190526080820152819060a0010390fd5b5061074f8651928392636d4c6c8960e01b84528301613955565b5060ff600354166133a5565b61074f90604051918291636d4c6c8960e01b83528201906000608060a0840193601e81528260208201528260408201528260608201520152565b503461000e57600036600319011261000e576000546040516001600160a01b039091168152602090f35b503461000e57602036600319011261000e5760043561370e816103c5565b6000546001600160a01b03908116338190036108a4575060207fe4b67652e856f57a7747dd2473850ce987087f4b1744a870504f1c047cb56f4f916203f48042019384421161377c575b1692836000526008825280604060002055604051908152a260405160018152602090f35b6137846139aa565b613758565b503461000e576000806003193601126137e35780546001600160a01b0316338190036108a457602082806006557f6875685eb5dbc8e2796d75d2dc9e9cb607b610d0558ee7336df418a26d4846e86040519182a160018152f35b80fd5b503461000e57602036600319011261000e57600435613804816103c5565b60018060a01b031660005260076020526020604060002054604051908152f35b503461000e57604036600319011261000e57600435613842816103a0565b6024359061384f82610f53565b6000546001600160a01b0316338190036108a45750602060ff7f09f6e15731965d66c152a2c1c1b62c94420ba4530dafe8818e1a01906641fb1e92169283600052600282526138ae8160406000209060ff801983541691151516179055565b604051938452151592a260405160018152602090f35b60008082526020820181905260408201526001600160a01b0391821660608201529116608082015260a00190565b9081602091031261000e5751613907816103c5565b90565b506040513d6000823e3d90fd5b50634e487b7160e01b600052603260045260246000fd5b919081101561393e5760051b0190565b611612613917565b9081602091031261000e575190565b608060009193929360ff60a0820195600183521660208201528260408201528260608201520152565b50634e487b7160e01b600052602160045260246000fd5b9081602091031261000e575161390781610f53565b50634e487b7160e01b600052601160045260246000fd5b81156139cb570490565b634e487b7160e01b600052601260045260246000fd5b919082018092116139ee57565b6103b86139aa565b601b815260006020820181905260408201526001600160a01b0391821660608201529116608082015260a00190565b6000198101919082116139ee57565b919082039182116139ee57565b9799949598929098969196600260095414611e5157600260095560ff8916600052600260205260ff60406000205416801561412c575b614111578742116140dd57613a968a613a9136848b614138565b614a52565b613a9f82614a13565b6040516370a0823160e01b81523060048201529b90979060208d8d81806024810103916001600160a01b03165afa9c8d156140d0575b60009d614091575b50613bc38b8d8c613d5397613d8c9997613d4e978f97613b1789613b0e613d299b303360018060a01b038c16615576565b600554906139c1565b9a8b613b41613b3a613b298851613a25565b92613b34848a61421e565b51613a34565b918761421e565b526001600160a01b0387166000908152600760205260409020613b658d82546139e1565b90556001546020908990613b8390610498906001600160a01b031681565b60405163125cf47f60e01b81526001600160a01b038b166004820152602481018a905260ff9092166044830152909a8b9190829060009082906064820190565b03925af1988915614084575b600099614063575b506040516370a0823160e01b8152306004820152986020908a9060249082906001600160a01b03165afa988915614056575b60009961401b575b50926020969492613c3e8893613cd39a989660405196879586956310510f1160e01b8752600487016142cb565b038160007f000000000000000000000000373a06bd3067f8da90239a47f316f09312b7800f6001600160a01b03165af1801561400e575b613ff1575b50600154600090613c9590610498906001600160a01b031681565b60405163125cf47f60e01b81526001600160a01b039094166004850152602484019290925260ff909316604483015290938492839182906064820190565b03925af1908115613fe4575b600091613fc5575b506040516370a0823160e01b81523060048201529060209082908180602481015b03916001600160a01b03165afa908115611ce057600091611cc15750613a34565b6040516370a0823160e01b8152306004820152909c906020818e818060248101613d08565b613a34565b613d5f600554826139c1565b6001600160a01b038a166000908152600760205260409020909190613d858382546139e1565b9055613a34565b9060009015613f215750613eb5613e848860006020613e6d613e446104988f9d9b99613ebd9f9d613e3e819d613e22610498946000805160206156a48339815191529e8a868e613de961049861049860015460018060a01b031690565b908f60405180988195829463125cf47f60e01b8452600484016040906000929493606082019560018060a01b0316825260208201520152565b03925af1928315613f14575b8c93613ef5575b5033918d6147b3565b97614d32565b604051630fa9d3b160e01b8152336004820152602481018e905293849283919082906044820190565b03925af18015613ee8575b613ec9575b50886139e1565b604080516001815260208101929092528101939093523360608401526001600160a01b038616929081906080820190565b0390a3614f26565b50906103b86001600955565b613ee19060203d6020116106c8576106b88183611572565b5038613e7d565b613ef061390a565b613e78565b613f0d9193508b3d8d11610702576106f48183611572565b9138613e35565b613f1c61390a565b613e2e565b9695939150849790859350808796613f4b8860018060a01b03166000526004602052604060002090565b6000918252602052604090205490613f62916139e1565b6001600160a01b0387166000908152600460205260409020859060009182526020526040902055613f92916139e1565b96613eb5613e84613ebd9860006020613e6d613e446104988f9d6000805160206156a48339815191529961049891614d32565b613fde915060203d602011610702576106f48183611572565b38613ce7565b613fec61390a565b613cdf565b61400790853d87116106c8576106b88183611572565b5038613c7a565b61401661390a565b613c75565b613cd398969491995092613c3e6020989694936140458a95863d8811610e7b57610e6d8183611572565b9b9396989a50935050929496613c11565b61405e61390a565b613c09565b61407d91995060203d602011610702576106f48183611572565b9738613bd7565b61408c61390a565b613bcf565b8a9d508b8d8b926020989697983d6020116140c9575b6140b18183611572565b81016140bc91613946565b9f50505094939294613add565b503d6140a7565b6140d861390a565b613ad5565b604051636d4c6c8960e01b81526002600482015242602482015260448101899052600060648201819052608482015260a490fd5b604051636d4c6c8960e01b81528061074f8b60048301613955565b5060ff60035416613a77565b929192614144826115f1565b60409461415386519283611572565b819584835260208093019161014080960285019481861161000e57925b8584106141805750505050505050565b868483031261000e578487916141946115d1565b863581526141a38388016103ba565b838201526141b28688016103f0565b8682015260606141c38189016103f0565b9082015260806141d4818901610f6a565b9082015260a06141e5818901610f6a565b9082015260c0808801359082015260e0808801359082015261010080880135908201526101208088013590820152815201930192614170565b6020918151811015614233575b60051b010190565b61423b613917565b61422b565b90815180825260208080930193019160005b828110614260575050505090565b835185529381019392810192600101614252565b9190808252602080920192916000905b828210614292575050505090565b9091929360019060ff86356142a6816103a0565b1681528583013583820152604080870135908201526060908101950193920190614284565b9391959492606096808887018988525260809081870193916000905b82821061431857505050506139079596509061430a918582036020870152614240565b926040818503910152614274565b909192946001908635815261433c6020614333818a016103ba565b60ff1690830152565b61435b604061434c818a016103f0565b6001600160a01b031690830152565b6143786143698d89016103f0565b6001600160a01b0316828e0152565b61438e614386848901610f6a565b151582850152565b6143a660a061439e818a01610f6a565b151590830152565b60c0878101359082015260e08088013590820152610100808801359082015261012080880135908201526101409081019601939201906142e7565b6020906001600160401b0381116143fe575b601f01601f19160190565b614406611505565b6143f3565b604051906144188261153c565b604082527f30303030303030303030303030303030303030303030303030303030303030306040837f307830303030303030303030303030303030303030303030303030303030303060208201520152565b9081606091031261000e578051614480816103c5565b9160406020830151614491816103c5565b920151613907816103c5565b919082604091031261000e576020825192015190565b92936101e0926139079560a093600180861b0392838092168852166020870152604086015260a435606086015260c435608086015260e435838601526101043560c08601526101243560e0860152610120806101008701528183511690860152602082015161014086015280604083015116610160860152806060830151166101808601526080820151166101a085015201519160c06101c08201520190610aba565b939796929461459460e096614586600099949560018060a01b0380971689526101008060208b0152890190614240565b908782036040890152614240565b98606086015260808501521660a083015260c08201520152565b60015460405163057453a760e31b81526103b8939290916001600160a01b0391906020908490600490829086165afa928315614614575b6000936145f4575b5016615505565b61460d91935060203d8111610702576106f48183611572565b91386145ed565b61461c61390a565b6145e5565b60015460405163057453a760e31b81526146a9929161468f916001600160a01b03916020908290600490829086165afa9081156146d7575b6000916146b9575b506001600160a01b038416600090815260046020526040902086600052602052604060002054918416615505565b6001600160a01b0316600090815260046020526040902090565b9060005260205260006040812055565b6146d1915060203d8111610702576106f48183611572565b38614661565b6146df61390a565b614659565b60019060001981146146f4570190565b6146fc6139aa565b0190565b9190811015614742575b60051b81013590601e198136030182121561000e5701908135916001600160401b03831161000e57602001823603811361000e579190565b61474a613917565b61470a565b908092918237016000815290565b3d15614788573d9061476e826143e1565b9161477c6040519384611572565b82523d6000602084013e565b606090565b906020613907928181520190610aba565b9081602091031261000e57516139078161188b565b6040516370a0823160e01b8082526001600160a01b0386811660048401526148f697929660209690959382169492939192918789602481895afa988915614a06575b6000996149c8575b506148a4846148db989694614849600095858d9b998c97169487614820846150ec565b6040516304f9ef2360e21b81526001600160801b03909116600482015294859081906024820190565b0381895afa9384156149bb575b899461499a575b50614869939416615505565b60405163bcc1694f60e01b81526001600160a01b03891660048201526001600160801b03909116602482015293849283919082906044820190565b03925af1801561498d575b614960575b506040519081526001600160a01b0390921660048301529092839190829081906024820190565b03915afa918215614953575b600092614936575b5050613a34565b90808210614902575090565b604051636d4c6c8960e01b8152600b600482015260248101929092526044820152600060648201819052608482015260a490fd5b61494c9250803d10610e7b57610e6d8183611572565b38806148ef565b61495b61390a565b6148e7565b61497f90853d8711614986575b6149778183611572565b81019061479e565b50386148b4565b503d61496d565b61499561390a565b6148af565b61486994506149b590893d8b11614986576149778183611572565b9361485d565b6149c361390a565b614856565b88969487929a506148db9896946148496000956149f46148a495873d8911610e7b57610e6d8183611572565b9d9550955050949698509496506147fd565b614a0e61390a565b6147f5565b906000805b83518214614a4d57600190614a2d838661421e565b518101809111614a40575b910190614a18565b614a486139aa565b614a38565b925050565b90600091825b81518114614abe576001600160a01b03806060614a75848661421e565b51015116908416818103614a8d575050600101614a58565b60a491869160405192636d4c6c8960e01b845260036004850152806024850152604484015260648301526084820152fd5b50505050565b9092614b69614b3260a060c09598979660e08652805160e087015260208101516002811015614b72575b610100870152600180831b038060408301511661012088015260608201511661014087015260808101516101608701520151856101808601526101a0850190610aba565b9660208401906060809160018060a01b03808251168552602082015115156020860152604082015116604085015201511515910152565b60a08201520152565b614b7a61397e565b614aee565b9193614c3b9394614ba2610498610498610498606087015160018060a01b031690565b6040516370a0823160e01b808252306004830152909560209592949193919289929087896024818a5afa988915614ca0575b600099614c6f575b5091879594939160008794614c07604051978896879586946352bbbe2960e01b865260048601614ac4565b03926001600160a01b03165af18015614c62575b614c45575b506040519081523060048201529182908180602481016148db565b908110611c8c5790565b614c5b90833d8511610e7b57610e6d8183611572565b5038614c20565b614c6a61390a565b614c1b565b8896959492908794929a50614c92600091863d8811610e7b57610e6d8183611572565b9a9294505091939495614bdc565b614ca861390a565b614bd4565b604051614cb98161153c565b600281526001602082016040368237825115614cf3575b52805160011015614ce6575b6000604082015290565b614cee613917565b614cdc565b614cfb613917565b614cd0565b604051614d0c81611557565b600181526000602082016020368237825115614d26575290565b614d2e613917565b5290565b602090606460018060a01b039160008360015416604051968795869463125cf47f60e01b865216600485015260248401528160448401525af1908115614d96575b600091614d7e575090565b613907915060203d8111610702576106f48183611572565b614d9e61390a565b614d73565b9081602091031261000e5751613907816103a0565b60ff16601b039060ff82116139ee57565b9060ff8091169116039060ff82116139ee57565b60ff16604d8111614def575b600a0a90565b614df76139aa565b614de9565b8060001904821181151516614e0f570290565b614e176139aa565b0290565b60405163313ce56760e01b8082529093926020926001600160a01b03929084908790600490829087165afa958615614f19575b600096614ef3575b50906004849260405194859384928352165afa918215614ee6575b600092614eb9575b505060ff811660ff841611614ea357614e9861390793614e9d92614dc9565b614ddd565b906139c1565b614e98614eb39161390794614dc9565b90614dfc565b614ed89250803d10614edf575b614ed08183611572565b810190614da3565b3880614e79565b503d614ec6565b614eee61390a565b614e71565b8492919650614f10600491843d8611614edf57614ed08183611572565b96919250614e56565b614f2161390a565b614e4e565b91614f86916001600160a01b03168173ae7ab96520de3a18e5e111b5eaab095312d7fe8482146150c9575b50614e9860046020614eb3936040519283809263313ce56760e01b82525afa9081156150bc575b60009161509e575b50614db8565b600a5480821161508657614fab82611a7f8560ff16600052600b602052604060002090565b80614fc38560ff16600052600b602052604060002090565b5562015180614fe9614fe28660ff16600052600c602052604060002090565b5442613a34565b11156150275750506150088260ff16600052600b602052604060002090565b55615021429160ff16600052600c602052604060002090565b55600190565b1190506150345750600190565b61504b8160ff16600052600b602052604060002090565b54604051636d4c6c8960e01b8152601f6004820152602481019190915260ff919091166044820152600060648201819052608482015260a490fd5b8261504b8160ff16600052600b602052604060002090565b6150b6915060203d8111614edf57614ed08183611572565b38614f80565b6150c461390a565b614f78565b614eb3919250600460206150e2614e9893600d54614dfc565b9493505050614f51565b6001600160801b039081811161000e571690565b60405163204f83f960e01b815290602090829060049082906001600160a01b03165afa90811561514e575b600091615136575090565b613907915060203d8111610e7b57610e6d8183611572565b61515661390a565b61512b565b60405163aa082a9d60e01b815290602090829060049082906001600160a01b03165afa90811561514e57600091615136575090565b6040516370c264df60e11b815290602090829060049082906001600160a01b03165afa90811561514e57600091615136575090565b6040516316f0115b60e01b81526020916001600160a01b0391839182908290600490829087165afa908115615268575b60009161524b575b50600460405180948193632745fed560e11b8352165afa91821561523e575b60009261522857505090565b6139079250803d10610e7b57610e6d8183611572565b61524661390a565b61521c565b6152629150823d8411610702576106f48183611572565b386151fd565b61527061390a565b6151f5565b60405163247c579160e21b81526020916001600160a01b0391839161532591849184908290600490829086165afa9081156153b1575b600091615394575b50166040516362b9c05160e11b8152838082600481865afa918215615387575b600092615367575b50604051636558954f60e01b81529192829060049082905afa90811561535a575b60009161533d575b506040519485809481936220e10960e51b8352600483019190602083019252565b0392165afa91821561523e5760009261522857505090565b6153549150843d8611610e7b57610e6d8183611572565b38615304565b61536261390a565b6152fc565b6004925061538190823d8411610702576106f48183611572565b916152db565b61538f61390a565b6152d3565b6153ab9150843d8611610702576106f48183611572565b386152b3565b6153b961390a565b6152ab565b60405163e16695b560e01b815290602090829060049082906001600160a01b03165afa908115615444575b6000916153fc575b5064ffffffffff1690565b6020813d821161543c575b8161541460209383611572565b8101031261543857519064ffffffffff821682036137e3575064ffffffffff6153f1565b5080fd5b3d9150615407565b61544c61390a565b6153e9565b60448151106154cb5760048101518101906020816024840193031261000e576024810151906001600160401b03821161000e57018160438201121561000e57602481015161549e816143e1565b926154ac6040519485611572565b8184526044828401011161000e57613907916044602085019101610a97565b506040516154d881611557565b601d81527f5472616e73616374696f6e2072657665727465642073696c656e746c79000000602082015290565b6044600092838093615538966040519363a9059cbb60e01b855260018060a01b0316600485015260248401525af16155f5565b1561553f57565b60405162461bcd60e51b815260206004820152600f60248201526e1d1c985b9cd9995c8819985a5b1959608a1b6044820152606490fd5b600092836064926155b2968295604051946323b872dd60e01b865260018060a01b03809216600487015216602485015260448401525af16155f5565b156155b957565b60405162461bcd60e51b81526020600482015260146024820152731d1c985b9cd9995c88199c9bdb4819985a5b195960621b6044820152606490fd5b6000903d901561562557908160201461561857501561561357600090565b600190565b9050602081803e51151590565b908181803efd5b60405163095ea7b360e01b81526001600160a01b039092166004830152600019602483015261566691600091829160449183905af16155f5565b1561566d57565b60405162461bcd60e51b815260206004820152600e60248201526d1054141493d59157d1905253115160921b6044820152606490fdfe4dcca373512f0049f0e5732bf8deedaf846a121102837227c421cb8d8fc83522a2646970667358221220fc2f8cfc294ef1d52a654ce80df42688b2f5b2b9c4808b13bd14eaa3b6e5907f64736f6c63430008100033

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

000000000000000000000000373a06bd3067f8da90239a47f316f09312b7800f00000000000000000000000041fad93f225b5c1c95f2445a5d7fcb85ba46713f000000000000000000000000f5ba2e5dded276fc0f7a7637a61157a4be79c626

-----Decoded View---------------
Arg [0] : s (address): 0x373a06bD3067f8DA90239a47f316F09312b7800F
Arg [1] : p (address): 0x41FAD93F225b5C1C95f2445A5d7fcB85bA46713f
Arg [2] : a (address): 0xf5ba2E5DdED276fc0f7a7637A61157a4be79C626

-----Encoded View---------------
3 Constructor Arguments found :
Arg [0] : 000000000000000000000000373a06bd3067f8da90239a47f316f09312b7800f
Arg [1] : 00000000000000000000000041fad93f225b5c1c95f2445a5d7fcb85ba46713f
Arg [2] : 000000000000000000000000f5ba2e5dded276fc0f7a7637a61157a4be79c626


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

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

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