false
true
0

Contract Address Details

0x007FEA7A23da99F3Ce7eA34F976f32BF79A09C43

Contract Name
BondFixedExpirySDA
Creator
0x4e59b4–b4956c at 0xe883a2–c00ea0
Balance
0 PLS ( )
Tokens
Fetching tokens...
Transactions
Fetching transactions...
Transfers
Fetching transfers...
Gas Used
Fetching gas used...
Last Balance Update
27553096
Warning! Contract bytecode has been changed and doesn't match the verified one. Therefore, interaction with this smart contract may be risky.
This contract has been verified via Sourcify. View contract in Sourcify repository
Contract name:
BondFixedExpirySDA




Optimization enabled
true
Compiler version
v0.8.15+commit.e14f2714




Optimization runs
100000
EVM Version
london




Verified at
2026-04-22T03:12:39.234854Z

Constructor Arguments

000000000000000000000000007fe7c498a2cf30971ad8f2cbc36bd14ac51156000000000000000000000000007a66b9e719b3abb2f3917eb47d4231a17f5a0d000000000000000000000000007bd11fca0daaeadd455b51826f9a015f2f0969000000000000000000000000007a0f3b057945db86408197daa7c04373b5a94a

Arg [0] (address) : 0x007fe7c498a2cf30971ad8f2cbc36bd14ac51156
Arg [1] (address) : 0x007a66b9e719b3abb2f3917eb47d4231a17f5a0d
Arg [2] (address) : 0x007bd11fca0daaeadd455b51826f9a015f2f0969
Arg [3] (address) : 0x007a0f3b057945db86408197daa7c04373b5a94a

              

src/BondFixedExpirySDA.sol

// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity 0.8.15;

import {BondBaseSDA, IBondAggregator, Authority} from "./bases/BondBaseSDA.sol";
import {IBondTeller} from "./interfaces/IBondTeller.sol";
import {IBondFixedExpiryTeller} from "./interfaces/IBondFixedExpiryTeller.sol";

/// @title Bond Fixed-Expiry Sequential Dutch Auctioneer
/// @notice Bond Fixed-Expiry Sequential Dutch Auctioneer Contract
/// @dev Bond Protocol is a permissionless system to create Olympus-style bond markets
///      for any token pair. The markets do not require maintenance and will manage
///      bond prices based on activity. Bond issuers create BondMarkets that pay out
///      a Payout Token in exchange for deposited Quote Tokens. Users can purchase
///      future-dated Payout Tokens with Quote Tokens at the current market price and
///      receive Bond Tokens to represent their position while their bond vests.
///      Once the Bond Tokens vest, they can redeem it for the Quote Tokens.
///
/// @dev The Fixed-Expiry Auctioneer is an implementation of the
///      Bond Base Sequential Dutch Auctioneer contract specific to creating bond markets where
///      all purchases on that market vest at a certain timestamp.
///
/// @author Oighty, Zeus, Potted Meat, indigo
contract BondFixedExpirySDA is BondBaseSDA {
    /* ========== CONSTRUCTOR ========== */
    constructor(
        IBondTeller teller_,
        IBondAggregator aggregator_,
        address guardian_,
        Authority authority_
    ) BondBaseSDA(teller_, aggregator_, guardian_, authority_) {}

    /// @inheritdoc BondBaseSDA
    function createMarket(bytes calldata params_) external override returns (uint256) {
        MarketParams memory params = abi.decode(params_, (MarketParams));
        uint256 marketId = _createMarket(params);

        // Create bond token (ERC20 for fixed expiry)
        IBondFixedExpiryTeller(address(_teller)).deploy(params.payoutToken, params.vesting);

        return marketId;
    }
}
        

/

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

/// @title Clone
/// @author zefram.eth
/// @notice Provides helper functions for reading immutable args from calldata
contract Clone {
    /// @notice Reads an immutable arg with type address
    /// @param argOffset The offset of the arg in the packed data
    /// @return arg The arg value
    function _getArgAddress(uint256 argOffset)
        internal
        pure
        returns (address arg)
    {
        uint256 offset = _getImmutableArgsOffset();
        assembly {
            arg := shr(0x60, calldataload(add(offset, argOffset)))
        }
    }

    /// @notice Reads an immutable arg with type uint256
    /// @param argOffset The offset of the arg in the packed data
    /// @return arg The arg value
    function _getArgUint256(uint256 argOffset)
        internal
        pure
        returns (uint256 arg)
    {
        uint256 offset = _getImmutableArgsOffset();
        // solhint-disable-next-line no-inline-assembly
        assembly {
            arg := calldataload(add(offset, argOffset))
        }
    }

    /// @notice Reads an immutable arg with type uint64
    /// @param argOffset The offset of the arg in the packed data
    /// @return arg The arg value
    function _getArgUint64(uint256 argOffset)
        internal
        pure
        returns (uint64 arg)
    {
        uint256 offset = _getImmutableArgsOffset();
        // solhint-disable-next-line no-inline-assembly
        assembly {
            arg := shr(0xc0, calldataload(add(offset, argOffset)))
        }
    }

    /// @notice Reads an immutable arg with type uint8
    /// @param argOffset The offset of the arg in the packed data
    /// @return arg The arg value
    function _getArgUint8(uint256 argOffset) internal pure returns (uint8 arg) {
        uint256 offset = _getImmutableArgsOffset();
        // solhint-disable-next-line no-inline-assembly
        assembly {
            arg := shr(0xf8, calldataload(add(offset, argOffset)))
        }
    }

    /// @return offset The offset of the packed immutable args in calldata
    function _getImmutableArgsOffset() internal pure returns (uint256 offset) {
        // solhint-disable-next-line no-inline-assembly
        assembly {
            offset := sub(
                calldatasize(),
                add(shr(240, calldataload(sub(calldatasize(), 2))), 2)
            )
        }
    }
}
          

/ERC20.sol

// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

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

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

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

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

    string public name;

    string public symbol;

    uint8 public immutable decimals;

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

    uint256 public totalSupply;

    mapping(address => uint256) public balanceOf;

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

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

    bytes32 public constant PERMIT_TYPEHASH =
        keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");

    uint256 internal immutable INITIAL_CHAIN_ID;

    bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;

    mapping(address => uint256) public nonces;

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

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

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

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

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

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

        return true;
    }

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

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

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

        return true;
    }

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

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

        balanceOf[from] -= amount;

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

        emit Transfer(from, to, amount);

        return true;
    }

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

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

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

            address recoveredAddress = ecrecover(digest, v, r, s);

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

            allowance[recoveredAddress][spender] = value;
        }

        emit Approval(owner, spender, value);
    }

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

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

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

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

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

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

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

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

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

/

// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

import {Clone} from "clones/Clone.sol";

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

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

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

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

    uint256 public totalSupply;

    mapping(address => uint256) public balanceOf;

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

    /*///////////////////////////////////////////////////////////////
                               METADATA
    //////////////////////////////////////////////////////////////*/

    function name() external pure returns (string memory) {
        return string(abi.encodePacked(_getArgUint256(0)));
    }

    function symbol() external pure returns (string memory) {
        return string(abi.encodePacked(_getArgUint256(0x20)));
    }

    function decimals() external pure returns (uint8) {
        return _getArgUint8(0x40);
    }

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

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

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

        return true;
    }

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

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

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

        return true;
    }

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

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

        balanceOf[from] -= amount;

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

        emit Transfer(from, to, amount);

        return true;
    }

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

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

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

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

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

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

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

    function _getImmutableVariablesOffset() internal pure returns (uint256 offset) {
        assembly {
            offset := sub(calldatasize(), add(shr(240, calldataload(sub(calldatasize(), 2))), 2))
        }
    }
}
          

/

// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity 0.8.15;

import {ERC20} from "solmate/tokens/ERC20.sol";
import {CloneERC20} from "./lib/CloneERC20.sol";

/// @title ERC20 Bond Token
/// @notice ERC20 Bond Token Contract
/// @dev Bond Protocol is a permissionless system to create Olympus-style bond markets
///      for any token pair. The markets do not require maintenance and will manage
///      bond prices based on activity. Bond issuers create BondMarkets that pay out
///      a Payout Token in exchange for deposited Quote Tokens. Users can purchase
///      future-dated Payout Tokens with Quote Tokens at the current market price and
///      receive Bond Tokens to represent their position while their bond vests.
///      Once the Bond Tokens vest, they can redeem it for the Quote Tokens.
///
/// @dev The ERC20 Bond Token contract is issued by a Fixed Expiry Teller to
///      represent bond positions until they vest. Bond tokens can be redeemed for
//       the underlying token 1:1 at or after expiry.
///
/// @dev This contract uses Clones (https://github.com/wighawag/clones-with-immutable-args)
///      to save gas on deployment and is based on VestedERC20 (https://github.com/ZeframLou/vested-erc20)
///
/// @author Oighty, Zeus, Potted Meat, indigo
contract ERC20BondToken is CloneERC20 {
    /* ========== ERRORS ========== */
    error BondToken_OnlyTeller();

    /* ========== IMMUTABLE PARAMETERS ========== */

    /// @notice The token to be redeemed when the bond vests
    /// @return _underlying The address of the underlying token
    function underlying() external pure returns (ERC20 _underlying) {
        return ERC20(_getArgAddress(0x41));
    }

    /// @notice Timestamp at which the BondToken can be redeemed for the underlying
    /// @return _expiry The vest start timestamp
    function expiry() external pure returns (uint48 _expiry) {
        return uint48(_getArgUint256(0x55));
    }

    /// @notice Address of the Teller that created the token
    function teller() internal pure returns (address _teller) {
        return _getArgAddress(0x75);
    }

    /* ========== MINT/BURN ========== */

    function mint(address to, uint256 amount) external {
        if (msg.sender != teller()) revert BondToken_OnlyTeller();
        _mint(to, amount);
    }

    function burn(address from, uint256 amount) external {
        if (msg.sender != teller()) revert BondToken_OnlyTeller();
        _burn(from, amount);
    }
}
          

/

// SPDX-License-Identifier: AGPL-3.0
pragma solidity >=0.8.0;

import {ERC20} from "solmate/tokens/ERC20.sol";
import {IBondAuctioneer} from "../interfaces/IBondAuctioneer.sol";

interface IBondSDA is IBondAuctioneer {
    /// @notice Main information pertaining to bond market
    struct BondMarket {
        address owner; // market owner. sends payout tokens, receives quote tokens (defaults to creator)
        ERC20 payoutToken; // token to pay depositors with
        ERC20 quoteToken; // token to accept as payment
        address callbackAddr; // address to call for any operations on bond purchase. Must inherit to IBondCallback.
        bool capacityInQuote; // capacity limit is in payment token (true) or in payout (false, default)
        uint256 capacity; // capacity remaining
        uint256 totalDebt; // total payout token debt from market
        uint256 minPrice; // minimum price (debt will stop decaying to maintain this)
        uint256 maxPayout; // max payout tokens out in one order
        uint256 sold; // payout tokens out
        uint256 purchased; // quote tokens in
        uint256 scale; // scaling factor for the market (see MarketParams struct)
    }

    /// @notice Information used to control how a bond market changes
    struct BondTerms {
        uint256 controlVariable; // scaling variable for price
        uint256 maxDebt; // max payout token debt accrued
        uint48 vesting; // length of time from deposit to expiry if fixed-term, vesting timestamp if fixed-expiry
        uint48 conclusion; // timestamp when market no longer offered
    }

    /// @notice Data needed for tuning bond market
    /// @dev Has timestamps in uint32 (not int32), so is not subject to Y2K38 overflow
    struct BondMetadata {
        uint48 lastTune; // last timestamp when control variable was tuned
        uint48 lastDecay; // last timestamp when market was created and debt was decayed
        uint32 length; // time from creation to conclusion.
        uint32 depositInterval; // target frequency of deposits
        uint32 tuneInterval; // frequency of tuning
        uint32 tuneAdjustmentDelay; // time to implement downward tuning adjustments
        uint32 debtDecayInterval; // interval over which debt should decay completely
        uint256 tuneIntervalCapacity; // capacity expected to be used during a tuning interval
        uint256 tuneBelowCapacity; // capacity that the next tuning will occur at
        uint256 lastTuneDebt; // target debt calculated at last tuning
    }

    /// @notice Control variable adjustment data
    struct Adjustment {
        uint256 change;
        uint48 lastAdjustment;
        uint48 timeToAdjusted; // how long until adjustment happens
        bool active;
    }

    /// @notice             Parameters to create a new bond market
    /// @dev                Note price should be passed in a specific format:
    ///                     formatted price = (payoutPriceCoefficient / quotePriceCoefficient)
    ///                             * 10**(36 + scaleAdjustment + quoteDecimals - payoutDecimals + payoutPriceDecimals - quotePriceDecimals)
    ///                     where:
    ///                         payoutDecimals - Number of decimals defined for the payoutToken in its ERC20 contract
    ///                         quoteDecimals - Number of decimals defined for the quoteToken in its ERC20 contract
    ///                         payoutPriceCoefficient - The coefficient of the payoutToken price in scientific notation (also known as the significant digits)
    ///                         payoutPriceDecimals - The significand of the payoutToken price in scientific notation (also known as the base ten exponent)
    ///                         quotePriceCoefficient - The coefficient of the quoteToken price in scientific notation (also known as the significant digits)
    ///                         quotePriceDecimals - The significand of the quoteToken price in scientific notation (also known as the base ten exponent)
    ///                         scaleAdjustment - see below
    ///                         * In the above definitions, the "prices" need to have the same unit of account (i.e. both in OHM, $, ETH, etc.)
    ///                         If price is not provided in this format, the market will not behave as intended.
    /// @param params_      Encoded bytes array, with the following elements
    /// @dev                    0. Payout Token (token paid out)
    /// @dev                    1. Quote Token (token to be received)
    /// @dev                    2. Callback contract address, should conform to IBondCallback. If 0x00, tokens will be transferred from market.owner
    /// @dev                    3. Is Capacity in Quote Token?
    /// @dev                    4. Capacity (amount in quoteDecimals or amount in payoutDecimals)
    /// @dev                    5. Formatted initial price (see note above)
    /// @dev                    6. Formatted minimum price (see note above)
    /// @dev                    7. Debt buffer. Percent with 3 decimals. Percentage over the initial debt to allow the market to accumulate at anyone time.
    /// @dev                       Works as a circuit breaker for the market in case external conditions incentivize massive buying (e.g. stablecoin depeg).
    /// @dev                       Minimum is the greater of 10% or initial max payout as a percentage of capacity.
    /// @dev                       If the value is too small, the market will not be able function normally and close prematurely.
    /// @dev                       If the value is too large, the market will not circuit break when intended. The value must be > 10% but can exceed 100% if desired.
    /// @dev                    8. Is fixed term ? Vesting length (seconds) : Vesting expiry (timestamp).
    /// @dev                        A 'vesting' param longer than 50 years is considered a timestamp for fixed expiry.
    /// @dev                    9. Conclusion (timestamp)
    /// @dev                    10. Deposit interval (seconds)
    /// @dev                    11. Market scaling factor adjustment, ranges from -24 to +24 within the configured market bounds.
    /// @dev                        Should be calculated as: (payoutDecimals - quoteDecimals) - ((payoutPriceDecimals - quotePriceDecimals) / 2)
    /// @dev                        Providing a scaling factor adjustment that doesn't follow this formula could lead to under or overflow errors in the market.
    /// @return                 ID of new bond market
    struct MarketParams {
        ERC20 payoutToken;
        ERC20 quoteToken;
        address callbackAddr;
        bool capacityInQuote;
        uint256 capacity;
        uint256 formattedInitialPrice;
        uint256 formattedMinimumPrice;
        uint32 debtBuffer;
        uint48 vesting;
        uint48 conclusion;
        uint32 depositInterval;
        int8 scaleAdjustment;
    }

    /* ========== VIEW FUNCTIONS ========== */

    /// @notice             Calculate current market price of payout token in quote tokens
    /// @dev                Accounts for debt and control variable decay since last deposit (vs _marketPrice())
    /// @param id_          ID of market
    /// @return             Price for market in configured decimals (see MarketParams)
    //
    // price is derived from the equation
    //
    // p = c * d
    //
    // where
    // p = price
    // c = control variable
    // d = debt
    //
    // d -= ( d * (dt / l) )
    //
    // where
    // dt = change in time
    // l = length of program
    //
    // if price is below minimum price, minimum price is returned
    // this is enforced on deposits by manipulating total debt (see _decay())
    function marketPrice(uint256 id_) external view override returns (uint256);

    /// @notice             Calculate debt factoring in decay
    /// @dev                Accounts for debt decay since last deposit
    /// @param id_          ID of market
    /// @return             Current debt for market in payout token decimals
    function currentDebt(uint256 id_) external view returns (uint256);

    /// @notice             Up to date control variable
    /// @dev                Accounts for control variable adjustment
    /// @param id_          ID of market
    /// @return             Control variable for market in payout token decimals
    function currentControlVariable(uint256 id_) external view returns (uint256);
}
          

/Auth.sol

// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

/// @notice Provides a flexible and updatable auth pattern which is completely separate from application logic.
/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/auth/Auth.sol)
/// @author Modified from Dappsys (https://github.com/dapphub/ds-auth/blob/master/src/auth.sol)
abstract contract Auth {
    event OwnerUpdated(address indexed user, address indexed newOwner);

    event AuthorityUpdated(address indexed user, Authority indexed newAuthority);

    address public owner;

    Authority public authority;

    constructor(address _owner, Authority _authority) {
        owner = _owner;
        authority = _authority;

        emit OwnerUpdated(msg.sender, _owner);
        emit AuthorityUpdated(msg.sender, _authority);
    }

    modifier requiresAuth() {
        require(isAuthorized(msg.sender, msg.sig), "UNAUTHORIZED");

        _;
    }

    function isAuthorized(address user, bytes4 functionSig) internal view virtual returns (bool) {
        Authority auth = authority; // Memoizing authority saves us a warm SLOAD, around 100 gas.

        // Checking if the caller is the owner only after calling the authority saves gas in most cases, but be
        // aware that this makes protected functions uncallable even to the owner if the authority is out of order.
        return (address(auth) != address(0) && auth.canCall(user, address(this), functionSig)) || user == owner;
    }

    function setAuthority(Authority newAuthority) public virtual {
        // We check if the caller is the owner first because we want to ensure they can
        // always swap out the authority even if it's reverting or using up a lot of gas.
        require(msg.sender == owner || authority.canCall(msg.sender, address(this), msg.sig));

        authority = newAuthority;

        emit AuthorityUpdated(msg.sender, newAuthority);
    }

    function setOwner(address newOwner) public virtual requiresAuth {
        owner = newOwner;

        emit OwnerUpdated(msg.sender, newOwner);
    }
}

/// @notice A generic interface for a contract which provides authorization data to an Auth instance.
/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/auth/Auth.sol)
/// @author Modified from Dappsys (https://github.com/dapphub/ds-auth/blob/master/src/auth.sol)
interface Authority {
    function canCall(
        address user,
        address target,
        bytes4 functionSig
    ) external view returns (bool);
}
          

/

// SPDX-License-Identifier: AGPL-3.0
pragma solidity >=0.8.0;

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

interface IBondFixedExpiryTeller {
    /// @notice          Redeem a fixed-expiry bond token for the underlying token (bond token must have matured)
    /// @param token_    Token to redeem
    /// @param amount_   Amount to redeem
    function redeem(ERC20BondToken token_, uint256 amount_) external;

    /// @notice              Deposit an ERC20 token and mint a future-dated ERC20 bond token
    /// @param underlying_   ERC20 token redeemable when the bond token vests
    /// @param expiry_       Timestamp at which the bond token can be redeemed for the underlying token
    /// @param amount_       Amount of underlying tokens to deposit
    /// @return              Address of the ERC20 bond token received
    /// @return              Amount of the ERC20 bond token received
    function create(
        ERC20 underlying_,
        uint48 expiry_,
        uint256 amount_
    ) external returns (ERC20BondToken, uint256);

    /// @notice             Deploy a new ERC20 bond token for an (underlying, expiry) pair and return its address
    /// @dev                ERC20 used for fixed-expiry
    /// @dev                If a bond token exists for the (underlying, expiry) pair, it returns that address
    /// @param underlying_  ERC20 token redeemable when the bond token vests
    /// @param expiry_      Timestamp at which the bond token can be redeemed for the underlying token
    /// @return             Address of the ERC20 bond token being created
    function deploy(ERC20 underlying_, uint48 expiry_) external returns (ERC20BondToken);

    /// @notice         Get the OlympusERC20BondToken contract corresponding to a market
    /// @param id_      ID of the market
    /// @return         ERC20BondToken contract address
    function getBondTokenForMarket(uint256 id_) external view returns (ERC20BondToken);
}
          

/

// SPDX-License-Identifier: AGPL-3.0
pragma solidity >=0.8.0;

import {ERC20} from "solmate/tokens/ERC20.sol";
import {IBondTeller} from "../interfaces/IBondTeller.sol";
import {IBondAggregator} from "../interfaces/IBondAggregator.sol";

interface IBondAuctioneer {
    /// @notice                 Creates a new bond market
    /// @param params_          Configuration data needed for market creation, encoded in a bytes array
    /// @dev                    See specific auctioneer implementations for details on encoding the parameters.
    /// @return id              ID of new bond market
    function createMarket(bytes memory params_) external returns (uint256);

    /// @notice                 Disable existing bond market
    /// @notice                 Must be market owner
    /// @param id_              ID of market to close
    function closeMarket(uint256 id_) external;

    /// @notice                 Exchange quote tokens for a bond in a specified market
    /// @notice                 Must be teller
    /// @param id_              ID of the Market the bond is being purchased from
    /// @param amount_          Amount to deposit in exchange for bond (after fee has been deducted)
    /// @param minAmountOut_    Minimum acceptable amount of bond to receive. Prevents frontrunning
    /// @return payout          Amount of payout token to be received from the bond
    function purchaseBond(
        uint256 id_,
        uint256 amount_,
        uint256 minAmountOut_
    ) external returns (uint256 payout);

    /// @notice                         Set market intervals to different values than the defaults
    /// @notice                         Must be market owner
    /// @dev                            Changing the intervals could cause markets to behave in unexpected way
    ///                                 tuneInterval should be greater than tuneAdjustmentDelay
    /// @param id_                      Market ID
    /// @param intervals_               Array of intervals (3)
    ///                                 1. Tune interval - Frequency of tuning
    ///                                 2. Tune adjustment delay - Time to implement downward tuning adjustments
    ///                                 3. Debt decay interval - Interval over which debt should decay completely
    function setIntervals(uint256 id_, uint32[3] calldata intervals_) external;

    /// @notice                      Designate a new owner of a market
    /// @notice                      Must be market owner
    /// @dev                         Doesn't change permissions until newOwner calls pullOwnership
    /// @param id_                   Market ID
    /// @param newOwner_             New address to give ownership to
    function pushOwnership(uint256 id_, address newOwner_) external;

    /// @notice                      Accept ownership of a market
    /// @notice                      Must be market newOwner
    /// @dev                         The existing owner must call pushOwnership prior to the newOwner calling this function
    /// @param id_                   Market ID
    function pullOwnership(uint256 id_) external;

    /// @notice             Set the auctioneer defaults
    /// @notice             Must be policy
    /// @param defaults_    Array of default values
    ///                     1. Tune interval - amount of time between tuning adjustments
    ///                     2. Tune adjustment delay - amount of time to apply downward tuning adjustments
    ///                     3. Minimum debt decay interval - minimum amount of time to let debt decay to zero
    ///                     4. Minimum deposit interval - minimum amount of time to wait between deposits
    ///                     5. Minimum market duration - minimum amount of time a market can be created for
    ///                     6. Minimum debt buffer - the minimum amount of debt over the initial debt to trigger a market shutdown
    /// @dev                The defaults set here are important to avoid edge cases in market behavior, e.g. a very short market reacts doesn't tune well
    /// @dev                Only applies to new markets that are created after the change
    function setDefaults(uint32[6] memory defaults_) external;

    /// @notice             Change the status of the auctioneer to allow creation of new markets
    /// @dev                Setting to false and allowing active markets to end will sunset the auctioneer
    /// @param status_      Allow market creation (true) : Disallow market creation (false)
    function setAllowNewMarkets(bool status_) external;

    /// @notice             Change whether a market creator is allowed to use a callback address in their markets or not
    /// @notice             Must be guardian
    /// @dev                Callback is believed to be safe, but a whitelist is implemented to prevent abuse
    /// @param creator_     Address of market creator
    /// @param status_      Allow callback (true) : Disallow callback (false)
    function setCallbackAuthStatus(address creator_, bool status_) external;

    /* ========== VIEW FUNCTIONS ========== */

    /// @notice                 Provides information for the Teller to execute purchases on a Market
    /// @param id_              Market ID
    /// @return owner           Address of the market owner (tokens transferred from this address if no callback)
    /// @return callbackAddr    Address of the callback contract to get tokens for payouts
    /// @return payoutToken     Payout Token (token paid out) for the Market
    /// @return quoteToken      Quote Token (token received) for the Market
    /// @return vesting         Timestamp or duration for vesting, implementation-dependent
    /// @return maxPayout       Maximum amount of payout tokens you can purchase in one transaction
    function getMarketInfoForPurchase(uint256 id_)
        external
        view
        returns (
            address owner,
            address callbackAddr,
            ERC20 payoutToken,
            ERC20 quoteToken,
            uint48 vesting,
            uint256 maxPayout
        );

    /// @notice             Calculate current market price of payout token in quote tokens
    /// @param id_          ID of market
    /// @return             Price for market in configured decimals
    //
    // if price is below minimum price, minimum price is returned
    // this is enforced on deposits by manipulating total debt (see _decay())
    function marketPrice(uint256 id_) external view returns (uint256);

    /// @notice             Scale value to use when converting between quote token and payout token amounts with marketPrice()
    /// @param id_          ID of market
    /// @return             Scaling factor for market in configured decimals
    function marketScale(uint256 id_) external view returns (uint256);

    /// @notice             Payout due for amount of quote tokens
    /// @dev                Accounts for debt and control variable decay so it is up to date
    /// @param amount_      Amount of quote tokens to spend
    /// @param id_          ID of market
    /// @param referrer_    Address of referrer, used to get fees to calculate accurate payout amount.
    ///                     Inputting the zero address will take into account just the protocol fee.
    /// @return             amount of payout tokens to be paid
    function payoutFor(
        uint256 amount_,
        uint256 id_,
        address referrer_
    ) external view returns (uint256);

    /// @notice             Returns maximum amount of quote token accepted by the market
    /// @param id_          ID of market
    /// @param referrer_    Address of referrer, used to get fees to calculate accurate payout amount.
    ///                     Inputting the zero address will take into account just the protocol fee.
    function maxAmountAccepted(uint256 id_, address referrer_) external view returns (uint256);

    /// @notice             Does market send payout immediately
    /// @param id_          Market ID to search for
    function isInstantSwap(uint256 id_) external view returns (bool);

    /// @notice             Is a given market accepting deposits
    /// @param id_          ID of market
    function isLive(uint256 id_) external view returns (bool);

    /// @notice             Returns the address of the market owner
    /// @param id_          ID of market
    function ownerOf(uint256 id_) external view returns (address);

    /// @notice             Returns the Teller that services the Auctioneer
    function getTeller() external view returns (IBondTeller);

    /// @notice             Returns the Aggregator that services the Auctioneer
    function getAggregator() external view returns (IBondAggregator);

    /// @notice             Returns current capacity of a market
    function currentCapacity(uint256 id_) external view returns (uint256);
}
          

/

// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;

/// @title Contains 512-bit math functions
/// @notice Facilitates multiplication and division that can have overflow of an intermediate value without any loss of precision
/// @dev Handles "phantom overflow" i.e., allows multiplication and division where an intermediate value overflows 256 bits
library FullMath {
    /// @notice Calculates floor(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
    /// @param a The multiplicand
    /// @param b The multiplier
    /// @param denominator The divisor
    /// @return result The 256-bit result
    /// @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv
    function mulDiv(
        uint256 a,
        uint256 b,
        uint256 denominator
    ) internal pure returns (uint256 result) {
        unchecked {
            // 512-bit multiply [prod1 prod0] = a * b
            // Compute the product mod 2**256 and mod 2**256 - 1
            // then use the Chinese Remainder Theorem to reconstruct
            // the 512 bit result. The result is stored in two 256
            // variables such that product = prod1 * 2**256 + prod0
            uint256 prod0; // Least significant 256 bits of the product
            uint256 prod1; // Most significant 256 bits of the product
            assembly {
                let mm := mulmod(a, b, not(0))
                prod0 := mul(a, b)
                prod1 := sub(sub(mm, prod0), lt(mm, prod0))
            }

            // Handle non-overflow cases, 256 by 256 division
            if (prod1 == 0) {
                require(denominator > 0);
                assembly {
                    result := div(prod0, denominator)
                }
                return result;
            }

            // Make sure the result is less than 2**256.
            // Also prevents denominator == 0
            require(denominator > prod1);

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

            // Make division exact by subtracting the remainder from [prod1 prod0]
            // Compute remainder using mulmod
            uint256 remainder;
            assembly {
                remainder := mulmod(a, b, denominator)
            }
            // Subtract 256 bit number from 512 bit number
            assembly {
                prod1 := sub(prod1, gt(remainder, prod0))
                prod0 := sub(prod0, remainder)
            }

            // Factor powers of two out of denominator
            // Compute largest power of two divisor of denominator.
            // Always >= 1.
            uint256 twos = (type(uint256).max - denominator + 1) & denominator;
            // Divide denominator by power of two
            assembly {
                denominator := div(denominator, twos)
            }

            // Divide [prod1 prod0] by the factors of two
            assembly {
                prod0 := div(prod0, twos)
            }
            // Shift in bits from prod1 into prod0. For this we need
            // to flip `twos` such that it is 2**256 / twos.
            // If twos is zero, then it becomes one
            assembly {
                twos := add(div(sub(0, twos), twos), 1)
            }
            prod0 |= prod1 * twos;

            // Invert denominator mod 2**256
            // Now that denominator is an odd number, it has an inverse
            // modulo 2**256 such that denominator * inv = 1 mod 2**256.
            // Compute the inverse by starting with a seed that is correct
            // correct for four bits. That is, denominator * inv = 1 mod 2**4
            uint256 inv = (3 * denominator) ^ 2;
            // Now use Newton-Raphson iteration to improve the precision.
            // Thanks to Hensel's lifting lemma, this also works in modular
            // arithmetic, doubling the correct bits in each step.
            inv *= 2 - denominator * inv; // inverse mod 2**8
            inv *= 2 - denominator * inv; // inverse mod 2**16
            inv *= 2 - denominator * inv; // inverse mod 2**32
            inv *= 2 - denominator * inv; // inverse mod 2**64
            inv *= 2 - denominator * inv; // inverse mod 2**128
            inv *= 2 - denominator * inv; // inverse mod 2**256

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

    /// @notice Calculates ceil(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
    /// @param a The multiplicand
    /// @param b The multiplier
    /// @param denominator The divisor
    /// @return result The 256-bit result
    function mulDivUp(
        uint256 a,
        uint256 b,
        uint256 denominator
    ) internal pure returns (uint256 result) {
        result = mulDiv(a, b, denominator);
        unchecked {
            if (mulmod(a, b, denominator) > 0) {
                require(result < type(uint256).max);
                result++;
            }
        }
    }
}
          

/

// SPDX-License-Identifier: AGPL-3.0
pragma solidity >=0.8.0;

import {ERC20} from "solmate/tokens/ERC20.sol";
import {IBondAuctioneer} from "../interfaces/IBondAuctioneer.sol";
import {IBondTeller} from "../interfaces/IBondTeller.sol";

interface IBondAggregator {
    /// @notice             Register a auctioneer with the aggregator
    /// @notice             Only Guardian
    /// @param auctioneer_  Address of the Auctioneer to register
    /// @dev                A auctioneer must be registered with an aggregator to create markets
    function registerAuctioneer(IBondAuctioneer auctioneer_) external;

    /// @notice             Register a new market with the aggregator
    /// @notice             Only registered depositories
    /// @param payoutToken_ Token to be paid out by the market
    /// @param quoteToken_  Token to be accepted by the market
    /// @param marketId     ID of the market being created
    function registerMarket(ERC20 payoutToken_, ERC20 quoteToken_)
        external
        returns (uint256 marketId);

    /// @notice     Get the auctioneer for the provided market ID
    /// @param id_  ID of Market
    function getAuctioneer(uint256 id_) external view returns (IBondAuctioneer);

    /// @notice             Calculate current market price of payout token in quote tokens
    /// @dev                Accounts for debt and control variable decay since last deposit (vs _marketPrice())
    /// @param id_          ID of market
    /// @return             Price for market (see the specific auctioneer for units)
    //
    // if price is below minimum price, minimum price is returned
    // this is enforced on deposits by manipulating total debt (see _decay())
    function marketPrice(uint256 id_) external view returns (uint256);

    /// @notice             Scale value to use when converting between quote token and payout token amounts with marketPrice()
    /// @param id_          ID of market
    /// @return             Scaling factor for market in configured decimals
    function marketScale(uint256 id_) external view returns (uint256);

    /// @notice             Payout due for amount of quote tokens
    /// @dev                Accounts for debt and control variable decay so it is up to date
    /// @param amount_      Amount of quote tokens to spend
    /// @param id_          ID of market
    /// @param referrer_    Address of referrer, used to get fees to calculate accurate payout amount.
    ///                     Inputting the zero address will take into account just the protocol fee.
    /// @return             amount of payout tokens to be paid
    function payoutFor(
        uint256 amount_,
        uint256 id_,
        address referrer_
    ) external view returns (uint256);

    /// @notice             Returns maximum amount of quote token accepted by the market
    /// @param id_          ID of market
    /// @param referrer_    Address of referrer, used to get fees to calculate accurate payout amount.
    ///                     Inputting the zero address will take into account just the protocol fee.
    function maxAmountAccepted(uint256 id_, address referrer_) external view returns (uint256);

    /// @notice             Does market send payout immediately
    /// @param id_          Market ID to search for
    function isInstantSwap(uint256 id_) external view returns (bool);

    /// @notice             Is a given market accepting deposits
    /// @param id_          ID of market
    function isLive(uint256 id_) external view returns (bool);

    /// @notice             Returns array of active market IDs within a range
    /// @dev                Should be used if length exceeds max to query entire array
    function liveMarketsBetween(uint256 firstIndex_, uint256 lastIndex_)
        external
        view
        returns (uint256[] memory);

    /// @notice             Returns an array of all active market IDs for a given quote token
    /// @param token_       Address of token to query by
    /// @param isPayout_    If true, search by payout token, else search for quote token
    function liveMarketsFor(address token_, bool isPayout_)
        external
        view
        returns (uint256[] memory);

    /// @notice             Returns an array of all active market IDs for a given owner
    /// @param owner_       Address of owner to query by
    function liveMarketsBy(address owner_) external view returns (uint256[] memory);

    /// @notice             Returns an array of all active market IDs for a given payout and quote token
    /// @param payout_      Address of payout token
    /// @param quote_       Address of quote token
    function marketsFor(address payout_, address quote_) external view returns (uint256[] memory);

    /// @notice                 Returns the market ID with the highest current payoutToken payout for depositing quoteToken
    /// @param payout_          Address of payout token
    /// @param quote_           Address of quote token
    /// @param amountIn_        Amount of quote tokens to deposit
    /// @param minAmountOut_    Minimum amount of payout tokens to receive as payout
    /// @param maxExpiry_       Latest acceptable vesting timestamp for bond
    ///                         Inputting the zero address will take into account just the protocol fee.
    function findMarketFor(
        address payout_,
        address quote_,
        uint256 amountIn_,
        uint256 minAmountOut_,
        uint256 maxExpiry_
    ) external view returns (uint256 id);

    /// @notice             Returns the Teller that services the market ID
    function getTeller(uint256 id_) external view returns (IBondTeller);

    /// @notice             Returns current capacity of a market
    function currentCapacity(uint256 id_) external view returns (uint256);
}
          

/ReentrancyGuard.sol

// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

/// @notice Gas optimized reentrancy protection for smart contracts.
/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/utils/ReentrancyGuard.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/security/ReentrancyGuard.sol)
abstract contract ReentrancyGuard {
    uint256 private locked = 1;

    modifier nonReentrant() {
        require(locked == 1, "REENTRANCY");

        locked = 2;

        _;

        locked = 1;
    }
}
          

/

// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

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

/// @notice Safe ERC20 and ETH transfer library that safely handles missing return values.
/// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v3-periphery/blob/main/contracts/libraries/TransferHelper.sol)
/// @author Taken from Solmate.
library TransferHelper {
    function safeTransferFrom(
        ERC20 token,
        address from,
        address to,
        uint256 amount
    ) internal {
        (bool success, bytes memory data) = address(token).call(
            abi.encodeWithSelector(ERC20.transferFrom.selector, from, to, amount)
        );

        require(success && (data.length == 0 || abi.decode(data, (bool))), "TRANSFER_FROM_FAILED");
    }

    function safeTransfer(
        ERC20 token,
        address to,
        uint256 amount
    ) internal {
        (bool success, bytes memory data) = address(token).call(
            abi.encodeWithSelector(ERC20.transfer.selector, to, amount)
        );

        require(success && (data.length == 0 || abi.decode(data, (bool))), "TRANSFER_FAILED");
    }

    // function safeApprove(
    //     ERC20 token,
    //     address to,
    //     uint256 amount
    // ) internal {
    //     (bool success, bytes memory data) = address(token).call(
    //         abi.encodeWithSelector(ERC20.approve.selector, to, amount)
    //     );

    //     require(success && (data.length == 0 || abi.decode(data, (bool))), "APPROVE_FAILED");
    // }

    // function safeTransferETH(address to, uint256 amount) internal {
    //     (bool success, ) = to.call{value: amount}(new bytes(0));

    //     require(success, "ETH_TRANSFER_FAILED");
    // }
}
          

/

// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity 0.8.15;

import {ERC20} from "solmate/tokens/ERC20.sol";
import {ReentrancyGuard} from "solmate/utils/ReentrancyGuard.sol";
import {Auth, Authority} from "solmate/auth/Auth.sol";

import {IBondSDA, IBondAuctioneer} from "../interfaces/IBondSDA.sol";
import {IBondTeller} from "../interfaces/IBondTeller.sol";
import {IBondCallback} from "../interfaces/IBondCallback.sol";
import {IBondAggregator} from "../interfaces/IBondAggregator.sol";

import {TransferHelper} from "../lib/TransferHelper.sol";
import {FullMath} from "../lib/FullMath.sol";

/// @title Bond Sequential Dutch Auctioneer (SDA)
/// @notice Bond Sequential Dutch Auctioneer Base Contract
/// @dev Bond Protocol is a system to create Olympus-style bond markets
///      for any token pair. The markets do not require maintenance and will manage
///      bond prices based on activity. Bond issuers create BondMarkets that pay out
///      a Payout Token in exchange for deposited Quote Tokens. Users can purchase
///      future-dated Payout Tokens with Quote Tokens at the current market price and
///      receive Bond Tokens to represent their position while their bond vests.
///      Once the Bond Tokens vest, they can redeem it for the Quote Tokens.
///
/// @dev The Auctioneer contract allows users to create and manage bond markets.
///      All bond pricing logic and market data is stored in the Auctioneer.
///      A Auctioneer is dependent on a Teller to serve external users and
///      an Aggregator to register new markets. This implementation of the Auctioneer
///      uses a Sequential Dutch Auction pricing system to buy a target amount of quote
///      tokens or sell a target amount of payout tokens over the duration of a market.
///
/// @author Oighty, Zeus, Potted Meat, indigo
abstract contract BondBaseSDA is IBondSDA, Auth {
    using TransferHelper for ERC20;
    using FullMath for uint256;

    /* ========== ERRORS ========== */

    error Auctioneer_OnlyMarketOwner();
    error Auctioneer_InitialPriceLessThanMin();
    error Auctioneer_MarketConcluded(uint256 conclusion_);
    error Auctioneer_MaxPayoutExceeded();
    error Auctioneer_AmountLessThanMinimum();
    error Auctioneer_NotEnoughCapacity();
    error Auctioneer_InvalidCallback();
    error Auctioneer_BadExpiry();
    error Auctioneer_InvalidParams();
    error Auctioneer_NotAuthorized();
    error Auctioneer_NewMarketsNotAllowed();

    /* ========== EVENTS ========== */

    event MarketCreated(
        uint256 indexed id,
        address indexed payoutToken,
        address indexed quoteToken,
        uint48 vesting,
        uint256 initialPrice
    );
    event MarketClosed(uint256 indexed id);
    event Tuned(uint256 indexed id, uint256 oldControlVariable, uint256 newControlVariable);

    /* ========== STATE VARIABLES ========== */

    /// @notice Main information pertaining to bond market
    mapping(uint256 => BondMarket) public markets;

    /// @notice Information used to control how a bond market changes
    mapping(uint256 => BondTerms) public terms;

    /// @notice Data needed for tuning bond market
    mapping(uint256 => BondMetadata) public metadata;

    /// @notice Control variable changes
    mapping(uint256 => Adjustment) public adjustments;

    /// @notice New address to designate as market owner. They must accept ownership to transfer permissions.
    mapping(uint256 => address) public newOwners;

    /// @notice Whether or not the auctioneer allows new markets to be created
    /// @dev    Changing to false will sunset the auctioneer after all active markets end
    bool public allowNewMarkets;

    /// @notice Whether or not the market creator is authorized to use a callback address
    mapping(address => bool) public callbackAuthorized;

    /// Sane defaults for tuning. Can be adjusted for a specific market via setters.
    uint32 public defaultTuneInterval;
    uint32 public defaultTuneAdjustment;
    /// Minimum values for decay, deposit interval, market duration and debt buffer.
    uint32 public minDebtDecayInterval;
    uint32 public minDepositInterval;
    uint32 public minMarketDuration;
    uint32 public minDebtBuffer;

    // A 'vesting' param longer than 50 years is considered a timestamp for fixed expiry.
    uint48 internal constant MAX_FIXED_TERM = 52 weeks * 50;
    uint48 internal constant FEE_DECIMALS = 1e5; // one percent equals 1000.

    // BondAggregator contract with utility functions
    IBondAggregator internal immutable _aggregator;

    // BondTeller contract that handles interactions with users and issues tokens
    IBondTeller internal immutable _teller;

    constructor(
        IBondTeller teller_,
        IBondAggregator aggregator_,
        address guardian_,
        Authority authority_
    ) Auth(guardian_, authority_) {
        _aggregator = aggregator_;
        _teller = teller_;

        defaultTuneInterval = 24 hours;
        defaultTuneAdjustment = 1 hours;
        minDebtDecayInterval = 3 days;
        minDepositInterval = 1 hours;
        minMarketDuration = 1 days;
        minDebtBuffer = 10000; // 10%

        allowNewMarkets = true;
    }

    /* ========== MARKET FUNCTIONS ========== */

    /// @inheritdoc IBondAuctioneer
    function createMarket(bytes calldata params_) external virtual returns (uint256);

    /// @notice core market creation logic, see IBondAuctioneer.createMarket documentation
    function _createMarket(MarketParams memory params_) internal returns (uint256) {
        {
            // Check that the auctioneer is allowing new markets to be created
            if (!allowNewMarkets) revert Auctioneer_NewMarketsNotAllowed();

            // Ensure params are in bounds
            uint8 payoutTokenDecimals = params_.payoutToken.decimals();
            uint8 quoteTokenDecimals = params_.quoteToken.decimals();

            if (payoutTokenDecimals < 6 || payoutTokenDecimals > 18)
                revert Auctioneer_InvalidParams();
            if (quoteTokenDecimals < 6 || quoteTokenDecimals > 18)
                revert Auctioneer_InvalidParams();
            if (params_.scaleAdjustment < -24 || params_.scaleAdjustment > 24)
                revert Auctioneer_InvalidParams();

            // Restrict the use of a callback address unless allowed
            if (!callbackAuthorized[msg.sender] && params_.callbackAddr != address(0))
                revert Auctioneer_NotAuthorized();
        }

        // Unit to scale calculation for this market by to ensure reasonable values
        // for price, debt, and control variable without under/overflows.
        // See IBondAuctioneer for more details.
        //
        // scaleAdjustment should be equal to (payoutDecimals - quoteDecimals) - ((payoutPriceDecimals - quotePriceDecimals) / 2)
        uint256 scale;
        unchecked {
            scale = 10**uint8(36 + params_.scaleAdjustment);
        }

        if (params_.formattedInitialPrice < params_.formattedMinimumPrice)
            revert Auctioneer_InitialPriceLessThanMin();

        // Record new market into array for later retrieval and get marketId
        uint256 marketId = _aggregator.registerMarket(params_.payoutToken, params_.quoteToken);

        uint32 secondsToConclusion;
        uint32 debtDecayInterval;
        {
            secondsToConclusion = uint32(params_.conclusion - block.timestamp);
            if (
                secondsToConclusion < minMarketDuration ||
                params_.depositInterval < minDepositInterval
            ) revert Auctioneer_InvalidParams();

            // At minimum, the interval is how long it takes for price to drop to 0. In reality, a 50% drop is likely a guaranteed
            // bond sale. So debt decay interval needs to be long enough to allow a bond to adjust if oversold.
            // Needs to be some multiple of deposit interval because you don't want to go from 100 to 0 during the time frame
            // you expected to sell a single bond. 5 is a sane default observed from running OP v1 bond markets.
            uint32 userDebtDecay = params_.depositInterval * 5;
            debtDecayInterval = minDebtDecayInterval > userDebtDecay
                ? minDebtDecayInterval
                : userDebtDecay;

            uint256 tuneIntervalCapacity = params_.capacity.mulDiv(
                uint256(
                    params_.depositInterval > defaultTuneInterval
                        ? params_.depositInterval
                        : defaultTuneInterval
                ),
                uint256(secondsToConclusion)
            );

            metadata[marketId] = BondMetadata({
                lastTune: uint48(block.timestamp),
                lastDecay: uint48(block.timestamp),
                length: secondsToConclusion,
                depositInterval: params_.depositInterval,
                tuneInterval: params_.depositInterval > defaultTuneInterval
                    ? params_.depositInterval
                    : defaultTuneInterval,
                tuneAdjustmentDelay: defaultTuneAdjustment,
                debtDecayInterval: debtDecayInterval,
                tuneIntervalCapacity: tuneIntervalCapacity,
                tuneBelowCapacity: params_.capacity - tuneIntervalCapacity,
                lastTuneDebt: (
                    params_.capacityInQuote
                        ? params_.capacity.mulDiv(scale, params_.formattedInitialPrice)
                        : params_.capacity
                ).mulDiv(uint256(debtDecayInterval), uint256(secondsToConclusion))
            });
        }

        // Initial target debt is equal to capacity scaled by the ratio of the debt decay interval and the length of the market.
        // This is the amount of debt that should be decayed over the decay interval if no purchases are made.
        // Note price should be passed in a specific format:
        // price = (payoutPriceCoefficient / quotePriceCoefficient)
        //         * 10**(36 + scaleAdjustment + quoteDecimals - payoutDecimals + payoutPriceDecimals - quotePriceDecimals)
        // See IBondAuctioneer for more details and variable definitions.
        uint256 targetDebt;
        uint256 maxPayout;
        {
            uint256 capacity = params_.capacityInQuote
                ? params_.capacity.mulDiv(scale, params_.formattedInitialPrice)
                : params_.capacity;

            targetDebt = capacity.mulDiv(uint256(debtDecayInterval), uint256(secondsToConclusion));

            // Max payout is the amount of capacity that should be utilized in a deposit
            // interval. for example, if capacity is 1,000 TOKEN, there are 10 days to conclusion,
            // and the preferred deposit interval is 1 day, max payout would be 100 TOKEN.
            maxPayout = capacity.mulDiv(
                uint256(params_.depositInterval),
                uint256(secondsToConclusion)
            );
        }

        markets[marketId] = BondMarket({
            owner: msg.sender,
            payoutToken: params_.payoutToken,
            quoteToken: params_.quoteToken,
            callbackAddr: params_.callbackAddr,
            capacityInQuote: params_.capacityInQuote,
            capacity: params_.capacity,
            totalDebt: targetDebt,
            minPrice: params_.formattedMinimumPrice,
            maxPayout: maxPayout,
            purchased: 0,
            sold: 0,
            scale: scale
        });

        // Max debt serves as a circuit breaker for the market. let's say the quote token is a stablecoin,
        // and that stablecoin depegs. without max debt, the market would continue to buy until it runs
        // out of capacity. this is configurable with a 3 decimal buffer (1000 = 1% above initial price).
        // Note that its likely advisable to keep this buffer wide.
        // Note that the buffer is above 100%. i.e. 10% buffer = initial debt * 1.1
        // 1e5 = 100,000. 10,000 / 100,000 = 10%.
        uint256 minDebtBuffer_ = maxPayout.mulDiv(FEE_DECIMALS, targetDebt) > minDebtBuffer
            ? maxPayout.mulDiv(FEE_DECIMALS, targetDebt)
            : minDebtBuffer;
        uint256 maxDebt = targetDebt +
            targetDebt.mulDiv(
                uint256(params_.debtBuffer > minDebtBuffer_ ? params_.debtBuffer : minDebtBuffer_),
                1e5
            );

        // The control variable is set so that initial price equals the desired initial price. the control
        // variable is the ultimate determinant of price, so we compute this last.
        //
        // price = control variable * debt / scale
        // therefore, control variable = price * scale / debt
        uint256 controlVariable = params_.formattedInitialPrice.mulDiv(scale, targetDebt);

        terms[marketId] = BondTerms({
            controlVariable: controlVariable,
            maxDebt: maxDebt,
            vesting: params_.vesting,
            conclusion: params_.conclusion
        });

        emit MarketCreated(
            marketId,
            address(params_.payoutToken),
            address(params_.quoteToken),
            params_.vesting,
            params_.formattedInitialPrice
        );

        return marketId;
    }

    /// @inheritdoc IBondAuctioneer
    function setIntervals(uint256 id_, uint32[3] calldata intervals_) external override {
        // Check that the intervals are non-zero
        if (intervals_[0] == 0 || intervals_[1] == 0 || intervals_[2] == 0)
            revert Auctioneer_InvalidParams();

        // Check that tuneInterval >= tuneAdjustmentDelay
        if (intervals_[0] < intervals_[1]) revert Auctioneer_InvalidParams();

        BondMetadata storage meta = metadata[id_];
        // Check that tuneInterval >= depositInterval
        if (intervals_[0] < meta.depositInterval) revert Auctioneer_InvalidParams();

        // Check that debtDecayInterval >= minDebtDecayInterval
        if (intervals_[2] < minDebtDecayInterval) revert Auctioneer_InvalidParams();

        // Check that sender is market owner
        BondMarket memory market = markets[id_];
        if (msg.sender != market.owner) revert Auctioneer_OnlyMarketOwner();

        // Update intervals
        meta.tuneInterval = intervals_[0];
        meta.tuneIntervalCapacity = market.capacity.mulDiv(
            uint256(intervals_[0]),
            uint256(terms[id_].conclusion) - block.timestamp
        ); // don't have a stored value for market duration, this will update tuneIntervalCapacity based on time remaining
        meta.tuneAdjustmentDelay = intervals_[1];
        meta.debtDecayInterval = intervals_[2];
    }

    /// @inheritdoc IBondAuctioneer
    function pushOwnership(uint256 id_, address newOwner_) external override {
        if (msg.sender != markets[id_].owner) revert Auctioneer_OnlyMarketOwner();
        newOwners[id_] = newOwner_;
    }

    /// @inheritdoc IBondAuctioneer
    function pullOwnership(uint256 id_) external override {
        if (msg.sender != newOwners[id_]) revert Auctioneer_NotAuthorized();
        markets[id_].owner = newOwners[id_];
    }

    /// @inheritdoc IBondAuctioneer
    function setDefaults(uint32[6] memory defaults_) external override requiresAuth {
        // Restricted to authorized addresses, initially restricted to policy
        defaultTuneInterval = defaults_[0];
        defaultTuneAdjustment = defaults_[1];
        minDebtDecayInterval = defaults_[2];
        minDepositInterval = defaults_[3];
        minMarketDuration = defaults_[4];
        minDebtBuffer = defaults_[5];
    }

    /// @inheritdoc IBondAuctioneer
    function setAllowNewMarkets(bool status_) external override requiresAuth {
        // Restricted to authorized addresses, initially restricted to guardian
        allowNewMarkets = status_;
    }

    /// @inheritdoc IBondAuctioneer
    function setCallbackAuthStatus(address creator_, bool status_) external override requiresAuth {
        // Restricted to authorized addresses, initially restricted to guardian
        callbackAuthorized[creator_] = status_;
    }

    /// @inheritdoc IBondAuctioneer
    function closeMarket(uint256 id_) external override {
        if (msg.sender != markets[id_].owner) revert Auctioneer_OnlyMarketOwner();
        _close(id_);
    }

    /* ========== TELLER FUNCTIONS ========== */

    /// @inheritdoc IBondAuctioneer
    function purchaseBond(
        uint256 id_,
        uint256 amount_,
        uint256 minAmountOut_
    ) external override returns (uint256 payout) {
        if (msg.sender != address(_teller)) revert Auctioneer_NotAuthorized();

        BondMarket storage market = markets[id_];
        BondTerms memory term = terms[id_];

        // Markets end at a defined timestamp
        uint48 currentTime = uint48(block.timestamp);
        if (currentTime >= term.conclusion) revert Auctioneer_MarketConcluded(term.conclusion);

        uint256 price;
        (price, payout) = _decayAndGetPrice(id_, amount_, uint48(block.timestamp)); // Debt and the control variable decay over time

        // Payout must be greater than user inputted minimum
        if (payout < minAmountOut_) revert Auctioneer_AmountLessThanMinimum();

        // Markets have a max payout amount, capping size because deposits
        // do not experience slippage. max payout is recalculated upon tuning
        if (payout > market.maxPayout) revert Auctioneer_MaxPayoutExceeded();

        // Update Capacity and Debt values

        // Capacity is either the number of payout tokens that the market can sell
        // (if capacity in quote is false),
        //
        // or the number of quote tokens that the market can buy
        // (if capacity in quote is true)

        // If amount/payout is greater than capacity remaining, revert
        if (market.capacityInQuote ? amount_ > market.capacity : payout > market.capacity)
            revert Auctioneer_NotEnoughCapacity();
        unchecked {
            // Capacity is decreased by the deposited or paid amount
            market.capacity -= market.capacityInQuote ? amount_ : payout;

            // Incrementing total debt raises the price of the next bond
            market.totalDebt += payout + 1; // add 1 to satisfy price inequality

            // Markets keep track of how many quote tokens have been
            // purchased, and how many payout tokens have been sold
            market.purchased += amount_;
            market.sold += payout;
        }

        // Circuit breaker. If max debt is breached, the market is closed
        if (term.maxDebt < market.totalDebt) {
            _close(id_);
        } else {
            // If market will continue, the control variable is tuned to hit targets on time
            _tune(id_, currentTime, price);
        }
    }

    /* ========== INTERNAL DEPO FUNCTIONS ========== */

    /// @notice          Close a market
    /// @dev             Closing a market sets capacity to 0 and immediately stops bonding
    function _close(uint256 id_) internal {
        terms[id_].conclusion = uint48(block.timestamp);
        markets[id_].capacity = 0;

        emit MarketClosed(id_);
    }

    /// @notice                 Decay debt, and adjust control variable if there is an active change
    /// @param id_              ID of market
    /// @param amount_          Amount of quote tokens being purchased
    /// @param time_            Current timestamp (saves gas when passed in)
    /// @return marketPrice_    Current market price of bond, accounting for decay
    /// @return payout_         Amount of payout tokens received at current price
    function _decayAndGetPrice(
        uint256 id_,
        uint256 amount_,
        uint48 time_
    ) internal returns (uint256 marketPrice_, uint256 payout_) {
        BondMarket memory market = markets[id_];

        // Debt is a time-decayed sum of tokens spent in a market
        // Debt is added when deposits occur and removed over time
        // |
        // |    debt falls with
        // |   / \  inactivity        / \
        // | /     \              /\ /   \
        // |         \           /        \ / \
        // |           \      /\/
        // |             \  /  and rises
        // |                with deposits
        // |
        // |------------------------------------| t
        if (uint256(metadata[id_].lastDecay) <= block.timestamp)
            markets[id_].totalDebt -= _debtDecay(id_);

        // Control variable decay

        // The bond control variable is continually tuned. When it is lowered (which
        // lowers the market price), the change is carried out smoothly over time.
        if (adjustments[id_].active) {
            Adjustment storage adjustment = adjustments[id_];

            (uint256 adjustBy, uint48 secondsSince, bool stillActive) = _controlDecay(id_);
            terms[id_].controlVariable -= adjustBy;

            if (stillActive) {
                adjustment.change -= adjustBy;
                adjustment.timeToAdjusted -= secondsSince;
                adjustment.lastAdjustment = time_;
            } else {
                adjustment.active = false;
            }
        }

        // Price is not allowed to be lower than the minimum price
        marketPrice_ = _currentMarketPrice(id_);
        uint256 minPrice = market.minPrice;
        if (marketPrice_ < minPrice) marketPrice_ = minPrice;

        // Payout for the deposit = amount / price
        //
        // where:
        // payout = payout tokens out
        // amount = quote tokens in
        // price = quote tokens : payout token (i.e. 200 QUOTE : BASE), adjusted for scaling
        payout_ = amount_.mulDiv(market.scale, marketPrice_);

        // Set last decay timestamp based on size of purchase to linearize decay
        // 1e9 is used a scaling constant since the decay interval is unlikely to exceed 1e9 seconds (> 30 years)
        // It is also low enough to avoid overflows from high debt values
        uint256 debtPerSecond = metadata[id_].lastTuneDebt.mulDiv(
            1e9,
            uint256(metadata[id_].debtDecayInterval)
        );

        metadata[id_].lastDecay += uint48(payout_.mulDivUp(1e9, debtPerSecond));
    }

    /// @notice             Auto-adjust control variable to hit capacity/spend target
    /// @param id_          ID of market
    /// @param time_        Timestamp (saves gas when passed in)
    /// @param price_       Current price of the market
    function _tune(
        uint256 id_,
        uint48 time_,
        uint256 price_
    ) internal {
        BondMetadata memory meta = metadata[id_];
        BondMarket memory market = markets[id_];

        // Market tunes in 2 situations:
        // 1. If capacity has exceeded target since last tune adjustment and the market is oversold
        // 2. If a tune interval has passed since last tune adjustment and the market is undersold
        //
        // Intuition:
        // Markets are created with a target capacity with the expectation that capacity will
        // be utilized evenly over the duration of the market.
        // The intuition with tuning is:
        // - When the market is ahead of target capacity, we should tune based on capacity.
        // - When the market is behind target capacity, we should tune based on time.

        // Compute seconds remaining until market will conclude
        uint256 timeRemaining = uint256(terms[id_].conclusion - time_);

        // Standardize capacity into an payout token amount
        uint256 capacity = market.capacityInQuote
            ? market.capacity.mulDiv(market.scale, price_)
            : market.capacity;
        // Calculate initial capacity based on remaining capacity and amount sold/purchased up to this point
        uint256 initialCapacity = capacity +
            (market.capacityInQuote ? market.purchased.mulDiv(market.scale, price_) : market.sold);

        // Calculate timeNeutralCapacity as the capacity expected to be sold up to this point and the current capacity
        // Higher than initial capacity means the market is undersold, lower than initial capacity means the market is oversold
        uint256 timeNeutralCapacity = initialCapacity.mulDiv(
            uint256(meta.length) - timeRemaining,
            uint256(meta.length)
        ) + capacity;

        if (
            (market.capacity < meta.tuneBelowCapacity && timeNeutralCapacity < initialCapacity) ||
            (time_ >= meta.lastTune + meta.tuneInterval && timeNeutralCapacity > initialCapacity)
        ) {
            // Calculate the correct payout to complete on time assuming each bond
            // will be max size in the desired deposit interval for the remaining time
            //
            // i.e. market has 10 days remaining. deposit interval is 1 day. capacity
            // is 10,000 TOKEN. max payout would be 1,000 TOKEN (10,000 * 1 / 10).
            markets[id_].maxPayout = capacity.mulDiv(uint256(meta.depositInterval), timeRemaining);

            // Calculate ideal target debt to satisty capacity in the remaining time
            // The target debt is based on whether the market is under or oversold at this point in time
            // This target debt will ensure price is reactive while ensuring the magnitude of being over/undersold
            // doesn't cause larger fluctuations towards the end of the market.
            //

            // Calculate target debt from the timeNeutralCapacity and the ratio of debt decay interval and the length of the market
            uint256 targetDebt = timeNeutralCapacity.mulDiv(
                uint256(meta.debtDecayInterval),
                uint256(meta.length)
            );

            // Derive a new control variable from the target debt
            uint256 controlVariable = terms[id_].controlVariable;
            uint256 newControlVariable = price_.mulDivUp(market.scale, targetDebt);

            emit Tuned(id_, controlVariable, newControlVariable);

            if (newControlVariable < controlVariable) {
                // If decrease, control variable change will be carried out over the tune interval
                // this is because price will be lowered
                uint256 change = controlVariable - newControlVariable;
                adjustments[id_] = Adjustment(change, time_, meta.tuneAdjustmentDelay, true);
            } else {
                // Tune up immediately
                terms[id_].controlVariable = newControlVariable;
                // Set current adjustment to inactive (e.g. if we are re-tuning early)
                adjustments[id_].active = false;
            }

            metadata[id_].lastTune = time_;
            metadata[id_].tuneBelowCapacity = market.capacity > meta.tuneIntervalCapacity
                ? market.capacity - meta.tuneIntervalCapacity
                : 0;
            metadata[id_].lastTuneDebt = targetDebt;
        }
    }

    /* ========== INTERNAL VIEW FUNCTIONS ========== */

    /// @notice             Calculate current market price of payout token in quote tokens
    /// @dev                See marketPrice() in IBondAuctioneer for explanation of price computation
    /// @dev                Uses info from storage because data has been updated before call (vs marketPrice())
    /// @param id_          Market ID
    /// @return             Price for market in payout token decimals
    function _currentMarketPrice(uint256 id_) internal view returns (uint256) {
        BondMarket memory market = markets[id_];
        return terms[id_].controlVariable.mulDiv(market.totalDebt, market.scale);
    }

    /// @notice             Amount of debt to decay from total debt for market ID
    /// @param id_          ID of market
    /// @return             Amount of debt to decay
    function _debtDecay(uint256 id_) internal view returns (uint256) {
        BondMetadata memory meta = metadata[id_];
        uint256 lastDecay = uint256(meta.lastDecay);
        uint256 currentTime = block.timestamp;
        uint256 secondsSince;
        unchecked {
            secondsSince = currentTime > lastDecay ? currentTime - lastDecay : 0;
        }
        return
            secondsSince > meta.debtDecayInterval
                ? markets[id_].totalDebt
                : markets[id_].totalDebt.mulDiv(secondsSince, uint256(meta.debtDecayInterval));
    }

    /// @notice                 Amount to decay control variable by
    /// @param id_              ID of market
    /// @return decay           change in control variable
    /// @return secondsSince    seconds since last change in control variable
    /// @return active          whether or not change remains active
    function _controlDecay(uint256 id_)
        internal
        view
        returns (
            uint256 decay,
            uint48 secondsSince,
            bool active
        )
    {
        Adjustment memory info = adjustments[id_];
        if (!info.active) return (0, 0, false);

        secondsSince = uint48(block.timestamp) - info.lastAdjustment;
        active = secondsSince < info.timeToAdjusted;
        decay = active
            ? info.change.mulDiv(uint256(secondsSince), uint256(info.timeToAdjusted))
            : info.change;
    }

    /* ========== EXTERNAL VIEW FUNCTIONS ========== */

    /// @inheritdoc IBondAuctioneer
    function getMarketInfoForPurchase(uint256 id_)
        external
        view
        returns (
            address owner,
            address callbackAddr,
            ERC20 payoutToken,
            ERC20 quoteToken,
            uint48 vesting,
            uint256 maxPayout
        )
    {
        BondMarket memory market = markets[id_];
        return (
            market.owner,
            market.callbackAddr,
            market.payoutToken,
            market.quoteToken,
            terms[id_].vesting,
            market.maxPayout
        );
    }

    /// @inheritdoc IBondSDA
    function marketPrice(uint256 id_) public view override returns (uint256) {
        uint256 price = currentControlVariable(id_).mulDivUp(currentDebt(id_), markets[id_].scale);

        return (price > markets[id_].minPrice) ? price : markets[id_].minPrice;
    }

    /// @inheritdoc IBondAuctioneer
    function marketScale(uint256 id_) external view override returns (uint256) {
        return markets[id_].scale;
    }

    /// @inheritdoc IBondAuctioneer
    function payoutFor(
        uint256 amount_,
        uint256 id_,
        address referrer_
    ) public view override returns (uint256) {
        // Calculate the payout for the given amount of tokens
        uint256 fee = amount_.mulDiv(_teller.getFee(referrer_), 1e5);
        uint256 payout = (amount_ - fee).mulDiv(markets[id_].scale, marketPrice(id_));

        // Check that the payout is less than or equal to the maximum payout,
        // Revert if not, otherwise return the payout
        if (payout > markets[id_].maxPayout) {
            revert Auctioneer_MaxPayoutExceeded();
        } else {
            return payout;
        }
    }

    /// @inheritdoc IBondAuctioneer
    function maxAmountAccepted(uint256 id_, address referrer_) external view returns (uint256) {
        // Calculate maximum amount of quote tokens that correspond to max bond size
        // Maximum of the maxPayout and the remaining capacity converted to quote tokens
        BondMarket memory market = markets[id_];
        uint256 price = marketPrice(id_);
        uint256 quoteCapacity = market.capacityInQuote
            ? market.capacity
            : market.capacity.mulDiv(price, market.scale);
        uint256 maxQuote = market.maxPayout.mulDiv(price, market.scale);
        uint256 amountAccepted = quoteCapacity < maxQuote ? quoteCapacity : maxQuote;

        // Take into account teller fees and return
        // Estimate fee based on amountAccepted. Fee taken will be slightly larger than
        // this given it will be taken off the larger amount, but this avoids rounding
        // errors with trying to calculate the exact amount.
        // Therefore, the maxAmountAccepted is slightly conservative.
        uint256 estimatedFee = amountAccepted.mulDiv(_teller.getFee(referrer_), 1e5);

        return amountAccepted + estimatedFee;
    }

    /// @inheritdoc IBondSDA
    function currentDebt(uint256 id_) public view override returns (uint256) {
        return markets[id_].totalDebt - _debtDecay(id_);
    }

    /// @inheritdoc IBondSDA
    function currentControlVariable(uint256 id_) public view override returns (uint256) {
        (uint256 decay, , ) = _controlDecay(id_);
        return terms[id_].controlVariable - decay;
    }

    /// @inheritdoc IBondAuctioneer
    function isInstantSwap(uint256 id_) public view returns (bool) {
        uint256 vesting = terms[id_].vesting;
        return (vesting <= MAX_FIXED_TERM) ? vesting == 0 : vesting <= block.timestamp;
    }

    /// @inheritdoc IBondAuctioneer
    function isLive(uint256 id_) public view override returns (bool) {
        return (markets[id_].capacity != 0 && terms[id_].conclusion > block.timestamp);
    }

    /// @inheritdoc IBondAuctioneer
    function ownerOf(uint256 id_) external view override returns (address) {
        return markets[id_].owner;
    }

    /// @inheritdoc IBondAuctioneer
    function getTeller() external view override returns (IBondTeller) {
        return _teller;
    }

    /// @inheritdoc IBondAuctioneer
    function getAggregator() external view override returns (IBondAggregator) {
        return _aggregator;
    }

    /// @inheritdoc IBondAuctioneer
    function currentCapacity(uint256 id_) external view override returns (uint256) {
        return markets[id_].capacity;
    }
}
          

/

// SPDX-License-Identifier: AGPL-3.0
pragma solidity >=0.8.0;

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

interface IBondCallback {
    /// @notice                 Send payout tokens to Teller while allowing market owners to perform custom logic on received or paid out tokens
    /// @notice                 Market ID on Teller must be whitelisted
    /// @param id_              ID of the market
    /// @param inputAmount_     Amount of quote tokens bonded to the market
    /// @param outputAmount_    Amount of payout tokens to be paid out to the market
    /// @dev Must transfer the output amount of payout tokens back to the Teller
    /// @dev Should check that the quote tokens have been transferred to the contract in the _callback function
    function callback(
        uint256 id_,
        uint256 inputAmount_,
        uint256 outputAmount_
    ) external;

    /// @notice         Returns the number of quote tokens received and payout tokens paid out for a market
    /// @param id_      ID of the market
    /// @return in_     Amount of quote tokens bonded to the market
    /// @return out_    Amount of payout tokens paid out to the market
    function amountsForMarket(uint256 id_) external view returns (uint256 in_, uint256 out_);

    /// @notice         Whitelist a teller and market ID combination
    /// @notice         Must be callback owner
    /// @param teller_  Address of the Teller contract which serves the market
    /// @param id_      ID of the market
    function whitelist(address teller_, uint256 id_) external;

    /// @notice         Withdraw tokens from the callback and update balances
    /// @notice         Only callback owner
    /// @param to_      Address of the recipient
    /// @param token_   Address of the token to withdraw
    /// @param amount_  Amount of tokens to withdraw
    function withdraw(
        address to_,
        ERC20 token_,
        uint256 amount_
    ) external;

    /// @notice         Deposit tokens to the callback and update balances
    /// @notice         Only callback owner
    /// @param token_   Address of the token to deposit
    /// @param amount_  Amount of tokens to deposit
    function deposit(ERC20 token_, uint256 amount_) external;
}
          

/

// SPDX-License-Identifier: AGPL-3.0
pragma solidity >=0.8.0;

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

interface IBondTeller {
    /// @notice                 Exchange quote tokens for a bond in a specified market
    /// @param recipient_       Address of recipient of bond. Allows deposits for other addresses
    /// @param referrer_        Address of referrer who will receive referral fee. For frontends to fill.
    ///                         Direct calls can use the zero address for no referrer fee.
    /// @param id_              ID of the Market the bond is being purchased from
    /// @param amount_          Amount to deposit in exchange for bond
    /// @param minAmountOut_    Minimum acceptable amount of bond to receive. Prevents frontrunning
    /// @return                 Amount of payout token to be received from the bond
    /// @return                 Timestamp at which the bond token can be redeemed for the underlying token
    function purchase(
        address recipient_,
        address referrer_,
        uint256 id_,
        uint256 amount_,
        uint256 minAmountOut_
    ) external returns (uint256, uint48);

    /// @notice          Get current fee charged by the teller based on the combined protocol and referrer fee
    /// @param referrer_ Address of the referrer
    /// @return          Fee in basis points (3 decimal places)
    function getFee(address referrer_) external view returns (uint48);

    /// @notice         Set protocol fee
    /// @notice         Must be guardian
    /// @param fee_     Protocol fee in basis points (3 decimal places)
    function setProtocolFee(uint48 fee_) external;

    /// @notice         Set your fee as a referrer to the protocol
    /// @notice         Fee is set for sending address
    /// @param fee_     Referrer fee in basis points (3 decimal places)
    function setReferrerFee(uint48 fee_) external;

    /// @notice         Claim fees accrued for input tokens and sends to protocol
    /// @notice         Must be guardian
    /// @param tokens_  Array of tokens to claim fees for
    /// @param to_      Address to send fees to
    function claimFees(ERC20[] memory tokens_, address to_) external;
}
          

Compiler Settings

{"remappings":[":clones-with-immutable-args/=lib/clones-with-immutable-args/src/",":clones/=lib/clones-with-immutable-args/src/",":ds-test/=lib/ds-test/src/",":forge-std/=lib/forge-std/src/",":hardhat/=node_modules/hardhat/",":openzeppelin-contracts/=lib/openzeppelin-contracts/",":openzeppelin/=lib/openzeppelin-contracts/contracts/",":solmate/=lib/solmate/src/",":weird-erc20/=lib/solmate/lib/weird-erc20/src/"],"optimizer":{"runs":100000,"enabled":true},"metadata":{"bytecodeHash":"ipfs"},"libraries":{},"evmVersion":"london","compilationTarget":{"src/BondFixedExpirySDA.sol":"BondFixedExpirySDA"}}
              

Contract ABI

[{"type":"constructor","stateMutability":"nonpayable","inputs":[{"type":"address","name":"teller_","internalType":"contract IBondTeller"},{"type":"address","name":"aggregator_","internalType":"contract IBondAggregator"},{"type":"address","name":"guardian_","internalType":"address"},{"type":"address","name":"authority_","internalType":"contract Authority"}]},{"type":"error","name":"Auctioneer_AmountLessThanMinimum","inputs":[]},{"type":"error","name":"Auctioneer_BadExpiry","inputs":[]},{"type":"error","name":"Auctioneer_InitialPriceLessThanMin","inputs":[]},{"type":"error","name":"Auctioneer_InvalidCallback","inputs":[]},{"type":"error","name":"Auctioneer_InvalidParams","inputs":[]},{"type":"error","name":"Auctioneer_MarketConcluded","inputs":[{"type":"uint256","name":"conclusion_","internalType":"uint256"}]},{"type":"error","name":"Auctioneer_MaxPayoutExceeded","inputs":[]},{"type":"error","name":"Auctioneer_NewMarketsNotAllowed","inputs":[]},{"type":"error","name":"Auctioneer_NotAuthorized","inputs":[]},{"type":"error","name":"Auctioneer_NotEnoughCapacity","inputs":[]},{"type":"error","name":"Auctioneer_OnlyMarketOwner","inputs":[]},{"type":"event","name":"AuthorityUpdated","inputs":[{"type":"address","name":"user","internalType":"address","indexed":true},{"type":"address","name":"newAuthority","internalType":"contract Authority","indexed":true}],"anonymous":false},{"type":"event","name":"MarketClosed","inputs":[{"type":"uint256","name":"id","internalType":"uint256","indexed":true}],"anonymous":false},{"type":"event","name":"MarketCreated","inputs":[{"type":"uint256","name":"id","internalType":"uint256","indexed":true},{"type":"address","name":"payoutToken","internalType":"address","indexed":true},{"type":"address","name":"quoteToken","internalType":"address","indexed":true},{"type":"uint48","name":"vesting","internalType":"uint48","indexed":false},{"type":"uint256","name":"initialPrice","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"OwnerUpdated","inputs":[{"type":"address","name":"user","internalType":"address","indexed":true},{"type":"address","name":"newOwner","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"Tuned","inputs":[{"type":"uint256","name":"id","internalType":"uint256","indexed":true},{"type":"uint256","name":"oldControlVariable","internalType":"uint256","indexed":false},{"type":"uint256","name":"newControlVariable","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"change","internalType":"uint256"},{"type":"uint48","name":"lastAdjustment","internalType":"uint48"},{"type":"uint48","name":"timeToAdjusted","internalType":"uint48"},{"type":"bool","name":"active","internalType":"bool"}],"name":"adjustments","inputs":[{"type":"uint256","name":"","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"allowNewMarkets","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract Authority"}],"name":"authority","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"callbackAuthorized","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"closeMarket","inputs":[{"type":"uint256","name":"id_","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"createMarket","inputs":[{"type":"bytes","name":"params_","internalType":"bytes"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"currentCapacity","inputs":[{"type":"uint256","name":"id_","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"currentControlVariable","inputs":[{"type":"uint256","name":"id_","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"currentDebt","inputs":[{"type":"uint256","name":"id_","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint32","name":"","internalType":"uint32"}],"name":"defaultTuneAdjustment","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint32","name":"","internalType":"uint32"}],"name":"defaultTuneInterval","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IBondAggregator"}],"name":"getAggregator","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"owner","internalType":"address"},{"type":"address","name":"callbackAddr","internalType":"address"},{"type":"address","name":"payoutToken","internalType":"contract ERC20"},{"type":"address","name":"quoteToken","internalType":"contract ERC20"},{"type":"uint48","name":"vesting","internalType":"uint48"},{"type":"uint256","name":"maxPayout","internalType":"uint256"}],"name":"getMarketInfoForPurchase","inputs":[{"type":"uint256","name":"id_","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IBondTeller"}],"name":"getTeller","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"isInstantSwap","inputs":[{"type":"uint256","name":"id_","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"isLive","inputs":[{"type":"uint256","name":"id_","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"marketPrice","inputs":[{"type":"uint256","name":"id_","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"marketScale","inputs":[{"type":"uint256","name":"id_","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"owner","internalType":"address"},{"type":"address","name":"payoutToken","internalType":"contract ERC20"},{"type":"address","name":"quoteToken","internalType":"contract ERC20"},{"type":"address","name":"callbackAddr","internalType":"address"},{"type":"bool","name":"capacityInQuote","internalType":"bool"},{"type":"uint256","name":"capacity","internalType":"uint256"},{"type":"uint256","name":"totalDebt","internalType":"uint256"},{"type":"uint256","name":"minPrice","internalType":"uint256"},{"type":"uint256","name":"maxPayout","internalType":"uint256"},{"type":"uint256","name":"sold","internalType":"uint256"},{"type":"uint256","name":"purchased","internalType":"uint256"},{"type":"uint256","name":"scale","internalType":"uint256"}],"name":"markets","inputs":[{"type":"uint256","name":"","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"maxAmountAccepted","inputs":[{"type":"uint256","name":"id_","internalType":"uint256"},{"type":"address","name":"referrer_","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint48","name":"lastTune","internalType":"uint48"},{"type":"uint48","name":"lastDecay","internalType":"uint48"},{"type":"uint32","name":"length","internalType":"uint32"},{"type":"uint32","name":"depositInterval","internalType":"uint32"},{"type":"uint32","name":"tuneInterval","internalType":"uint32"},{"type":"uint32","name":"tuneAdjustmentDelay","internalType":"uint32"},{"type":"uint32","name":"debtDecayInterval","internalType":"uint32"},{"type":"uint256","name":"tuneIntervalCapacity","internalType":"uint256"},{"type":"uint256","name":"tuneBelowCapacity","internalType":"uint256"},{"type":"uint256","name":"lastTuneDebt","internalType":"uint256"}],"name":"metadata","inputs":[{"type":"uint256","name":"","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint32","name":"","internalType":"uint32"}],"name":"minDebtBuffer","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint32","name":"","internalType":"uint32"}],"name":"minDebtDecayInterval","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint32","name":"","internalType":"uint32"}],"name":"minDepositInterval","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint32","name":"","internalType":"uint32"}],"name":"minMarketDuration","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"newOwners","inputs":[{"type":"uint256","name":"","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"owner","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"ownerOf","inputs":[{"type":"uint256","name":"id_","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"payoutFor","inputs":[{"type":"uint256","name":"amount_","internalType":"uint256"},{"type":"uint256","name":"id_","internalType":"uint256"},{"type":"address","name":"referrer_","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"pullOwnership","inputs":[{"type":"uint256","name":"id_","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256","name":"payout","internalType":"uint256"}],"name":"purchaseBond","inputs":[{"type":"uint256","name":"id_","internalType":"uint256"},{"type":"uint256","name":"amount_","internalType":"uint256"},{"type":"uint256","name":"minAmountOut_","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"pushOwnership","inputs":[{"type":"uint256","name":"id_","internalType":"uint256"},{"type":"address","name":"newOwner_","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setAllowNewMarkets","inputs":[{"type":"bool","name":"status_","internalType":"bool"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setAuthority","inputs":[{"type":"address","name":"newAuthority","internalType":"contract Authority"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setCallbackAuthStatus","inputs":[{"type":"address","name":"creator_","internalType":"address"},{"type":"bool","name":"status_","internalType":"bool"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setDefaults","inputs":[{"type":"uint32[6]","name":"defaults_","internalType":"uint32[6]"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setIntervals","inputs":[{"type":"uint256","name":"id_","internalType":"uint256"},{"type":"uint32[3]","name":"intervals_","internalType":"uint32[3]"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setOwner","inputs":[{"type":"address","name":"newOwner","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"controlVariable","internalType":"uint256"},{"type":"uint256","name":"maxDebt","internalType":"uint256"},{"type":"uint48","name":"vesting","internalType":"uint48"},{"type":"uint48","name":"conclusion","internalType":"uint48"}],"name":"terms","inputs":[{"type":"uint256","name":"","internalType":"uint256"}]}]
              

Contract Creation Code

0x60c06040523480156200001157600080fd5b50604051620044273803806200442783398101604081905262000034916200013d565b600080546001600160a01b03199081166001600160a01b0385811691821784556001805490931690851617909155604051869286928692869284928492909133917f8292fce18fa69edf4db7b94ea2e58241df0ae57f97e0a6c9b29067028bf92d769190a36040516001600160a01b0382169033907fa3396fd7f6e0a21b50e5089d2da70d5ac0a3bbbd1f617a93f134b7638998019890600090a3505050506001600160a01b039081166080521660a0525050600980546001600160c01b0319167527100001518000000e100003f48000000e100001518017905550506007805460ff19166001179055620001a5565b6001600160a01b03811681146200013a57600080fd5b50565b600080600080608085870312156200015457600080fd5b8451620001618162000124565b6020860151909450620001748162000124565b6040860151909350620001878162000124565b60608601519092506200019a8162000124565b939692955090935050565b60805160a051614239620001ee6000396000818161051001528181610e3e0152818161199401528181611c260152611cd901526000818161034801526124fb01526142396000f3fe608060405234801561001057600080fd5b50600436106102ad5760003560e01c8063acc5570c1161017b578063bf7e214f116100d8578063d9ccdc931161008c578063e3684e3911610071578063e3684e39146109af578063e922067314610aed578063ea0aca3314610b0957600080fd5b8063d9ccdc931461098c578063e007fa971461099c57600080fd5b8063c7bf8ca0116100bd578063c7bf8ca014610935578063d204068714610948578063d2bee3231461096857600080fd5b8063bf7e214f146108a2578063c0aa0e8a146108c257600080fd5b8063bc3b2b121161012f578063bcf6cde811610114578063bcf6cde814610869578063bd1f3a5e1461087c578063bf48582b1461088f57600080fd5b8063bc3b2b12146107d2578063bcb296671461085657600080fd5b8063afa9d3b011610160578063afa9d3b0146106a1578063b1283e77146106ae578063bbbdd95a146107bf57600080fd5b8063acc5570c14610534578063ae4180951461068e57600080fd5b80635f77274e116102295780638973082c116101dd5780638da5cb5b116101c25780638da5cb5b146104cb578063946824cd146104eb5780639787d1071461050e57600080fd5b80638973082c1461047b5780638b098db3146104b857600080fd5b80636729a41e1161020e5780636729a41e1461041f578063699e17d9146104555780637a9e5e4b1461046857600080fd5b80635f77274e146103d65780636352211e146103e957600080fd5b806327507458116102805780633adec5a7116102655780633adec5a71461038d57806353c7f8e0146103a05780635dc4d16b146103b357600080fd5b806327507458146103235780633ad59dbc1461034657600080fd5b80630a9d85eb146102b257806310b05317146102d857806313af4035146102ed5780631c063a6c14610300575b600080fd5b6102c56102c0366004613c4b565b610b21565b6040519081526020015b60405180910390f35b6102eb6102e6366004613c4b565b610b53565b005b6102eb6102fb366004613c96565b610c0e565b6102c561030e366004613c4b565b60009081526002602052604090206004015490565b610336610331366004613c4b565b610d17565b60405190151581526020016102cf565b7f00000000000000000000000000000000000000000000000000000000000000005b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016102cf565b6102c561039b366004613c4b565b610d60565b6102c56103ae366004613cb3565b610dc3565b6103366103c1366004613c96565b60086020526000908152604090205460ff1681565b6102eb6103e4366004613d3e565b610eb4565b6103686103f7366004613c4b565b60009081526002602052604090205473ffffffffffffffffffffffffffffffffffffffff1690565b61036861042d366004613c4b565b60066020526000908152604090205473ffffffffffffffffffffffffffffffffffffffff1681565b6102eb610463366004613d5b565b610f79565b6102eb610476366004613c96565b6113b5565b6009546104a39074010000000000000000000000000000000000000000900463ffffffff1681565b60405163ffffffff90911681526020016102cf565b6103366104c6366004613c4b565b611512565b6000546103689073ffffffffffffffffffffffffffffffffffffffff1681565b6102c56104f9366004613c4b565b6000908152600260205260409020600a015490565b7f0000000000000000000000000000000000000000000000000000000000000000610368565b610638610542366004613c4b565b6000818152600260208181526040808420815161018081018352815473ffffffffffffffffffffffffffffffffffffffff90811680835260018401548216838701819052848801548316848701819052600380870154948516606087018190527401000000000000000000000000000000000000000090950460ff1615156080870152600487015460a0870152600587015460c0870152600687015460e08701526007870154610100870190815260088801546101208801526009880154610140880152600a90970154610160909601959095529989529290955292909520909301549251919590949265ffffffffffff169190565b6040805173ffffffffffffffffffffffffffffffffffffffff97881681529587166020870152938616938501939093529316606083015265ffffffffffff909216608082015260a081019190915260c0016102cf565b6102eb61069c366004613c4b565b611548565b6007546103369060ff1681565b6107456106bc366004613c4b565b600260208190526000918252604090912080546001820154928201546003830154600484015460058501546006860154600787015460088801546009890154600a9099015473ffffffffffffffffffffffffffffffffffffffff9889169a891699978916988716977401000000000000000000000000000000000000000090970460ff1696908c565b6040805173ffffffffffffffffffffffffffffffffffffffff9d8e1681529b8d1660208d0152998c16998b0199909952999096166060890152931515608088015260a087019290925260c086015260e0850152610100840152610120830152610140820192909252610160810191909152610180016102cf565b6102eb6107cd366004613d8e565b6115b1565b6108256107e0366004613c4b565b6005602052600090815260409020805460019091015465ffffffffffff8082169166010000000000008104909116906c01000000000000000000000000900460ff1684565b6040516102cf949392919093845265ffffffffffff9283166020850152911660408301521515606082015260800190565b6102c5610864366004613c4b565b61169b565b6102eb610877366004613dc7565b6116c2565b6102eb61088a366004613e59565b611772565b6102c561089d366004613ede565b611946565b6001546103689073ffffffffffffffffffffffffffffffffffffffff1681565b6109076108d0366004613c4b565b60036020526000908152604090208054600182015460029092015490919065ffffffffffff80821691660100000000000090041684565b60408051948552602085019390935265ffffffffffff918216928401929092521660608201526080016102cf565b6102c5610943366004613dc7565b611a9f565b6009546104a3906c01000000000000000000000000900463ffffffff1681565b6009546104a390700100000000000000000000000000000000900463ffffffff1681565b6009546104a39063ffffffff1681565b6102c56109aa366004613f17565b611cbf565b610a876109bd366004613c4b565b600460205260009081526040902080546001820154600283015460039093015465ffffffffffff80841694660100000000000085049091169363ffffffff6c0100000000000000000000000082048116947001000000000000000000000000000000008304821694740100000000000000000000000000000000000000008404831694780100000000000000000000000000000000000000000000000085048416947c0100000000000000000000000000000000000000000000000000000000900490931692908a565b6040805165ffffffffffff9b8c1681529a90991660208b015263ffffffff978816988a01989098529486166060890152928516608088015290841660a08701529290921660c085015260e0840191909152610100830152610120820152610140016102cf565b6009546104a39068010000000000000000900463ffffffff1681565b6009546104a390640100000000900463ffffffff1681565b600080610b2d83611f59565b5050600084815260036020526040902054909150610b4c908290613f72565b9392505050565b60008181526006602052604090205473ffffffffffffffffffffffffffffffffffffffff163314610bb0576040517f2c47703200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600090815260066020908152604080832054600290925290912080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff909216919091179055565b610c3c336000357fffffffff0000000000000000000000000000000000000000000000000000000016612034565b610ca7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f554e415554484f52495a4544000000000000000000000000000000000000000060448201526064015b60405180910390fd5b600080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83169081178255604051909133917f8292fce18fa69edf4db7b94ea2e58241df0ae57f97e0a6c9b29067028bf92d769190a350565b60008181526002602052604081206004015415801590610d5a575060008281526003602052604090206002015442660100000000000090910465ffffffffffff16115b92915050565b600080610d91610d6f8461169b565b6000858152600260205260409020600a0154610d8a86610b21565b9190612145565b6000848152600260205260409020600601549091508111610d5a57600083815260026020526040902060060154610b4c565b600080610dd283850185613fba565b90506000610ddf826121a3565b82516101008401516040517fc6e38a4b00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff928316600482015265ffffffffffff90911660248201529192507f0000000000000000000000000000000000000000000000000000000000000000169063c6e38a4b906044016020604051808303816000875af1158015610e87573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610eab9190614091565b50949350505050565b610ee2336000357fffffffff0000000000000000000000000000000000000000000000000000000016612034565b610f48576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f554e415554484f52495a454400000000000000000000000000000000000000006044820152606401610c9e565b600780547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016911515919091179055565b610f8660208201826140ae565b63ffffffff161580610fab5750610fa360408201602083016140ae565b63ffffffff16155b80610fc95750610fc160608201604083016140ae565b63ffffffff16155b15611000576040517f3b596f5f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61101060408201602083016140ae565b63ffffffff1661102360208301836140ae565b63ffffffff161015611061576040517f3b596f5f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008281526004602090815260409091208054909170010000000000000000000000000000000090910463ffffffff169061109e908401846140ae565b63ffffffff1610156110dc576040517f3b596f5f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60095468010000000000000000900463ffffffff1661110160608401604085016140ae565b63ffffffff16101561113f576040517f3b596f5f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600083815260026020818152604092839020835161018081018552815473ffffffffffffffffffffffffffffffffffffffff908116808352600184015482169483019490945293820154841694810194909452600381015492831660608501527401000000000000000000000000000000000000000090920460ff1615156080840152600482015460a0840152600582015460c0840152600682015460e0840152600782015461010084015260088201546101208401526009820154610140840152600a909101546101608301523314611245576040517f4e1c8b5d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61125260208401846140ae565b825463ffffffff9190911674010000000000000000000000000000000000000000027fffffffffffffffff00000000ffffffffffffffffffffffffffffffffffffffff9091161782556112f06112ab60208501856140ae565b60008681526003602052604090206002015463ffffffff91909116906112e49042906601000000000000900465ffffffffffff16613f72565b60a08401519190612e71565b600183015561130560408401602085016140ae565b825463ffffffff919091167801000000000000000000000000000000000000000000000000027fffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffffff90911617825561136260608401604085016140ae565b825463ffffffff919091167c0100000000000000000000000000000000000000000000000000000000027bffffffffffffffffffffffffffffffffffffffffffffffffffffffff90911617909155505050565b60005473ffffffffffffffffffffffffffffffffffffffff1633148061149857506001546040517fb70096130000000000000000000000000000000000000000000000000000000081523360048201523060248201526000357fffffffff0000000000000000000000000000000000000000000000000000000016604482015273ffffffffffffffffffffffffffffffffffffffff9091169063b700961390606401602060405180830381865afa158015611474573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061149891906140c9565b6114a157600080fd5b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff831690811790915560405133907fa3396fd7f6e0a21b50e5089d2da70d5ac0a3bbbd1f617a93f134b7638998019890600090a350565b60008181526003602052604081206002015465ffffffffffff16635dba24008111156115415742811115610b4c565b1592915050565b60008181526002602052604090205473ffffffffffffffffffffffffffffffffffffffff1633146115a5576040517f4e1c8b5d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6115ae81612f3d565b50565b6115df336000357fffffffff0000000000000000000000000000000000000000000000000000000016612034565b611645576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f554e415554484f52495a454400000000000000000000000000000000000000006044820152606401610c9e565b73ffffffffffffffffffffffffffffffffffffffff91909116600090815260086020526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016911515919091179055565b60006116a682612fc1565b600083815260026020526040902060050154610d5a9190613f72565b60008281526002602052604090205473ffffffffffffffffffffffffffffffffffffffff16331461171f576040517f4e1c8b5d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60009182526006602052604090912080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff909216919091179055565b6117a0336000357fffffffff0000000000000000000000000000000000000000000000000000000016612034565b611806576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f554e415554484f52495a454400000000000000000000000000000000000000006044820152606401610c9e565b805160098054602084015160408501516060860151608087015160a09097015163ffffffff90811674010000000000000000000000000000000000000000029181166c0100000000000000000000000002938116640100000000029681167fffffffffffffffffffffffffffffffffffffffffffffffff000000000000000090951694909417959095177fffffffffffffffffffffffffffffffff0000000000000000ffffffffffffffff1690831668010000000000000000027fffffffffffffffffffffffffffffffff00000000ffffffffffffffffffffffff1617177fffffffffffffffff0000000000000000ffffffffffffffffffffffffffffffff169316700100000000000000000000000000000000027fffffffffffffffff00000000ffffffffffffffffffffffffffffffffffffffff1692909217179055565b6040517fb88c914800000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82811660048301526000918291611a14917f00000000000000000000000000000000000000000000000000000000000000009091169063b88c914890602401602060405180830381865afa1580156119dd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a0191906140e6565b869065ffffffffffff16620186a0612e71565b6000858152600260205260408120600a015491925090611a4890611a3787610d60565b611a41858a613f72565b9190612e71565b600086815260026020526040902060070154909150811115611a96576040517f5c430eae00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b9150610b4c9050565b6000828152600260208181526040808420815161018081018352815473ffffffffffffffffffffffffffffffffffffffff9081168252600183015481169482019490945293810154831691840191909152600381015491821660608401527401000000000000000000000000000000000000000090910460ff1615156080830152600481015460a0830152600581015460c0830152600681015460e0830152600781015461010083015260088101546101208301526009810154610140830152600a015461016082015281611b7385610d60565b905060008260800151611b9b5761016083015160a0840151611b96918490612e71565b611ba1565b8260a001515b90506000611bc483856101600151866101000151612e719092919063ffffffff16565b90506000818310611bd55781611bd7565b825b6040517fb88c914800000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8981166004830152919250600091611ca6917f00000000000000000000000000000000000000000000000000000000000000009091169063b88c914890602401602060405180830381865afa158015611c6f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c9391906140e6565b839065ffffffffffff16620186a0612e71565b9050611cb28183614103565b9998505050505050505050565b60003373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614611d30576040517f2c47703200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600084815260026020818152604080842060038352938190208151608081018352815481526001820154938101939093529092015465ffffffffffff80821693830193909352660100000000000090048216606082018190529091429190821610611dd75760608201516040517f07fc4a7000000000000000000000000000000000000000000000000000000000815265ffffffffffff9091166004820152602401610c9e565b6000611de4888842613132565b9550905085851015611e22576040517f74ec9d5b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8360070154851115611e60576040517f5c430eae00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600384015474010000000000000000000000000000000000000000900460ff16611e905783600401548511611e98565b836004015487115b15611ecf576040517ff3383dc900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600384015474010000000000000000000000000000000000000000900460ff16611ef95784611efb565b865b600485018054919091039055600584018054860160010190819055600985018054890190556008850180548701905560208401511015611f4357611f3e88612f3d565b611f4e565b611f4e888383613474565b505050509392505050565b600081815260056020908152604080832081516080810183528154815260019091015465ffffffffffff8082169483019490945266010000000000008104909316918101919091526c0100000000000000000000000090910460ff1615156060820181905282918291611fd75760008060009350935093505061202d565b6020810151611fe6904261411b565b9250806040015165ffffffffffff168365ffffffffffff161091508161200d578051612029565b604081015181516120299165ffffffffffff8087169116612e71565b9350505b9193909250565b60015460009073ffffffffffffffffffffffffffffffffffffffff16801580159061211857506040517fb700961300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85811660048301523060248301527fffffffff000000000000000000000000000000000000000000000000000000008516604483015282169063b700961390606401602060405180830381865afa1580156120f4573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061211891906140c9565b8061213d575060005473ffffffffffffffffffffffffffffffffffffffff8581169116145b949350505050565b6000612152848484612e71565b90506000828061216457612164614142565b8486091115610b4c577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff811061219957600080fd5b6001019392505050565b60075460009060ff166121e2576040517f64be3ffa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000826000015173ffffffffffffffffffffffffffffffffffffffff1663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015612233573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122579190614171565b90506000836020015173ffffffffffffffffffffffffffffffffffffffff1663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa1580156122aa573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122ce9190614171565b905060068260ff1610806122e5575060128260ff16115b1561231c576040517f3b596f5f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60068160ff161080612331575060128160ff16115b15612368576040517f3b596f5f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe884610160015160000b12806123a65750601884610160015160000b135b156123dd576040517f3b596f5f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3360009081526008602052604090205460ff161580156124165750604084015173ffffffffffffffffffffffffffffffffffffffff1615155b1561244d576040517f2c47703200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050600082610160015160240160ff16600a0a90508260c001518360a0015110156124a4576040517f4496547d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b825160208401516040517fb435914300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff928316600482015290821660248201526000917f0000000000000000000000000000000000000000000000000000000000000000169063b4359143906044016020604051808303816000875af1158015612544573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125689190614194565b90506000804286610120015165ffffffffffff166125869190613f72565b60095490925063ffffffff700100000000000000000000000000000000909104811690831610806125d7575060095461014087015163ffffffff6c0100000000000000000000000090920482169116105b1561260e576040517f3b596f5f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000866101400151600561262291906141ad565b60095490915063ffffffff808316680100000000000000009092041611612649578061265f565b60095468010000000000000000900463ffffffff165b6009546101408901519193506000916126b79163ffffffff90811691161161268f5760095463ffffffff16612696565b8861014001515b63ffffffff168563ffffffff168a60800151612e719092919063ffffffff16565b60408051610140808201835265ffffffffffff4216808352602083015263ffffffff808916938301939093528b018051831660608301526009549051939450909260808401929182169116116127155760095463ffffffff1661271c565b8961014001515b63ffffffff9081168252600954640100000000900481166020830152851660408201526060810183905260808a81015191019061275a908490613f72565b81526020016127988563ffffffff168763ffffffff168c60600151612783578c60800151611a41565b60a08d015160808e0151611a41918d90612e71565b90526000868152600460209081526040808320845181549386015192860151606080880151608089015160a08a015160c08b015165ffffffffffff9687167fffffffffffffffffffffffffffffffffffffffff000000000000000000000000909a169990991766010000000000009690981695909502969096177fffffffffffffffffffffffff0000000000000000ffffffffffffffffffffffff166c0100000000000000000000000063ffffffff948516027fffffffffffffffffffffffff00000000ffffffffffffffffffffffffffffffff161770010000000000000000000000000000000091841691909102177fffffffff0000000000000000ffffffffffffffffffffffffffffffffffffffff1674010000000000000000000000000000000000000000958316959095027fffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffffff1694909417780100000000000000000000000000000000000000000000000092821692909202919091177bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167c0100000000000000000000000000000000000000000000000000000000919094160292909217825560e084015160018301556101008401516002830155610120909301516003909101559089015190925082915081906129955788608001516129aa565b60a089015160808a01516129aa918990612e71565b90506129c38163ffffffff8087169088811690612e7116565b92506129eb89610140015163ffffffff168663ffffffff1683612e719092919063ffffffff16565b9150506040518061018001604052803373ffffffffffffffffffffffffffffffffffffffff168152602001896000015173ffffffffffffffffffffffffffffffffffffffff168152602001896020015173ffffffffffffffffffffffffffffffffffffffff168152602001896040015173ffffffffffffffffffffffffffffffffffffffff168152602001896060015115158152602001896080015181526020018381526020018960c0015181526020018281526020016000815260200160008152602001878152506002600087815260200190815260200160002060008201518160000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060208201518160010160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060408201518160020160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060608201518160030160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060808201518160030160146101000a81548160ff02191690831515021790555060a0820151816004015560c0820151816005015560e0820151816006015561010082015181600701556101208201518160080155610140820151816009015561016082015181600a01559050506000600960149054906101000a900463ffffffff1663ffffffff16612c8b620186a065ffffffffffff168585612e719092919063ffffffff16565b11612cb65760095474010000000000000000000000000000000000000000900463ffffffff16612cc4565b612cc482620186a085612e71565b90506000612cf9828b60e0015163ffffffff1611612ce25782612cee565b8a60e0015163ffffffff165b8590620186a0612e71565b612d039085614103565b60a08b0151909150600090612d19908a87612e71565b905060405180608001604052808281526020018381526020018c610100015165ffffffffffff1681526020018c610120015165ffffffffffff16815250600360008a8152602001908152602001600020600082015181600001556020820151816001015560408201518160020160006101000a81548165ffffffffffff021916908365ffffffffffff16021790555060608201518160020160066101000a81548165ffffffffffff021916908365ffffffffffff1602179055509050508a6020015173ffffffffffffffffffffffffffffffffffffffff168b6000015173ffffffffffffffffffffffffffffffffffffffff16897f8235b14cd272b4e791960fe1118559bb7fed86934fcffeeae9b1175103b0756d8e61010001518f60a00151604051612e5a92919065ffffffffffff929092168252602082015260400190565b60405180910390a450959998505050505050505050565b600080807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff85870985870292508281108382030391505080600003612ec85760008411612ebd57600080fd5b508290049050610b4c565b808411612ed457600080fd5b60008486880960026001871981018816978890046003810283188082028403028082028403028082028403028082028403028082028403029081029092039091026000889003889004909101858311909403939093029303949094049190911702949350505050565b6000818152600360209081526040808320600290810180547fffffffffffffffffffffffffffffffffffffffff000000000000ffffffffffff1666010000000000004265ffffffffffff16021790559091528082206004018290555182917f9dc30b8eda31a6a144e092e5de600955523a6a925cc15cc1d1b9b4872cfa615591a250565b6000818152600460209081526040808320815161014081018352815465ffffffffffff8082168352660100000000000082041694820185905263ffffffff6c0100000000000000000000000082048116948301949094527001000000000000000000000000000000008104841660608301527401000000000000000000000000000000000000000081048416608083015278010000000000000000000000000000000000000000000000008104841660a08301527c0100000000000000000000000000000000000000000000000000000000900490921660c0830152600181015460e08301526002810154610100830152600301546101208201529042838282116130cd5760006130d1565b8282035b90508360c0015163ffffffff1681116131155760c084015160008781526002602052604090206005015461311091839063ffffffff90811690612e7116565b613128565b6000868152600260205260409020600501545b9695505050505050565b6000838152600260208181526040808420815161018081018352815473ffffffffffffffffffffffffffffffffffffffff9081168252600183015481168286015294820154851681840152600382015494851660608201527401000000000000000000000000000000000000000090940460ff161515608085015260048082015460a0860152600582015460c0860152600682015460e0860152600782015461010086015260088201546101208601526009820154610140860152600a9091015461016085015287855290915282205482919042660100000000000090910465ffffffffffff161161324e5761322786612fc1565b60008781526002602052604081206005018054909190613248908490613f72565b90915550505b6000868152600560205260409020600101546c01000000000000000000000000900460ff16156133835760008681526005602052604081209080806132928a611f59565b60008d8152600360205260408120805494975092955090935085926132b8908490613f72565b9091555050801561335357828460000160008282546132d79190613f72565b90915550506001840180548391906006906133059084906601000000000000900465ffffffffffff1661411b565b92506101000a81548165ffffffffffff021916908365ffffffffffff160217905550878460010160006101000a81548165ffffffffffff021916908365ffffffffffff16021790555061337e565b6001840180547fffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffff1690555b505050505b61338c86613b68565b60e0820151909350808410156133a0578093505b6101608201516133b290879086612e71565b60008881526004602052604081208054600390910154929550909161340691633b9aca009063ffffffff7c0100000000000000000000000000000000000000000000000000000000909104811690612e7116565b905061341784633b9aca0083612145565b600089815260046020526040902080546006906134479084906601000000000000900465ffffffffffff166141d9565b92506101000a81548165ffffffffffff021916908365ffffffffffff160217905550505050935093915050565b600060046000858152602001908152602001600020604051806101400160405290816000820160009054906101000a900465ffffffffffff1665ffffffffffff1665ffffffffffff1681526020016000820160069054906101000a900465ffffffffffff1665ffffffffffff1665ffffffffffff16815260200160008201600c9054906101000a900463ffffffff1663ffffffff1663ffffffff1681526020016000820160109054906101000a900463ffffffff1663ffffffff1663ffffffff1681526020016000820160149054906101000a900463ffffffff1663ffffffff1663ffffffff1681526020016000820160189054906101000a900463ffffffff1663ffffffff1663ffffffff16815260200160008201601c9054906101000a900463ffffffff1663ffffffff1663ffffffff16815260200160018201548152602001600282015481526020016003820154815250509050600060026000868152602001908152602001600020604051806101800160405290816000820160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020016001820160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020016002820160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020016003820160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020016003820160149054906101000a900460ff16151515158152602001600482015481526020016005820154815260200160068201548152602001600782015481526020016008820154815260200160098201548152602001600a8201548152505090506000846003600088815260200190815260200160002060020160069054906101000a900465ffffffffffff166137dd919061411b565b65ffffffffffff169050600082608001516137fc578260a00151613811565b61016083015160a08401516138119187612e71565b9050600083608001516138295783610120015161383f565b61016084015161014085015161383f9188612e71565b6138499083614103565b905060008261388285886040015163ffffffff166138679190613f72565b886040015163ffffffff1685612e719092919063ffffffff16565b61388c9190614103565b90508561010001518560a001511080156138a557508181105b806138e05750608086015186516138c29163ffffffff16906141d9565b65ffffffffffff168865ffffffffffff16101580156138e057508181115b15613b5d57613904866060015163ffffffff168585612e719092919063ffffffff16565b60008a8152600260205260408082206007019290925560c088015191880151909161393d91849163ffffffff90811691811690612e7116565b60008b81526003602052604081205461016089015192935091613962908b9085612145565b60408051848152602081018390529192508d917f78f9c01d72705dba80d6ce051d36a1f987bf2a3800fee938c111a2ae741e57d1910160405180910390a281811015613a905760006139b48284613f72565b905060405180608001604052808281526020018d65ffffffffffff1681526020018b60a0015163ffffffff1665ffffffffffff16815260200160011515815250600560008f81526020019081526020016000206000820151816000015560208201518160010160006101000a81548165ffffffffffff021916908365ffffffffffff16021790555060408201518160010160066101000a81548165ffffffffffff021916908365ffffffffffff160217905550606082015181600101600c6101000a81548160ff02191690831515021790555090505050613ad5565b60008c81526003602090815260408083208490556005909152902060010180547fffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffff1690555b60008c815260046020526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000001665ffffffffffff8d1617905560e089015160a089015111613b29576000613b3d565b8860e001518860a00151613b3d9190613f72565b60008d815260046020526040902060028101919091556003019290925550505b505050505050505050565b6000818152600260208181526040808420815161018081018352815473ffffffffffffffffffffffffffffffffffffffff908116825260018301548116828601529482015485168184015260038083015495861660608301527401000000000000000000000000000000000000000090950460ff1615156080820152600482015460a0820152600582015460c08201819052600683015460e0830152600783015461010083015260088301546101208301526009830154610140830152600a90920154610160820181905287875294909352908420549192610b4c929190612e71565b600060208284031215613c5d57600080fd5b5035919050565b73ffffffffffffffffffffffffffffffffffffffff811681146115ae57600080fd5b8035613c9181613c64565b919050565b600060208284031215613ca857600080fd5b8135610b4c81613c64565b60008060208385031215613cc657600080fd5b823567ffffffffffffffff80821115613cde57600080fd5b818501915085601f830112613cf257600080fd5b813581811115613d0157600080fd5b866020828501011115613d1357600080fd5b60209290920196919550909350505050565b80151581146115ae57600080fd5b8035613c9181613d25565b600060208284031215613d5057600080fd5b8135610b4c81613d25565b60008060808385031215613d6e57600080fd5b8235915083608084011115613d8257600080fd5b50926020919091019150565b60008060408385031215613da157600080fd5b8235613dac81613c64565b91506020830135613dbc81613d25565b809150509250929050565b60008060408385031215613dda57600080fd5b823591506020830135613dbc81613c64565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051610180810167ffffffffffffffff81118282101715613e3f57613e3f613dec565b60405290565b803563ffffffff81168114613c9157600080fd5b600060c08284031215613e6b57600080fd5b82601f830112613e7a57600080fd5b60405160c0810181811067ffffffffffffffff82111715613e9d57613e9d613dec565b6040528060c0840185811115613eb257600080fd5b845b81811015613ed357613ec581613e45565b835260209283019201613eb4565b509195945050505050565b600080600060608486031215613ef357600080fd5b83359250602084013591506040840135613f0c81613c64565b809150509250925092565b600080600060608486031215613f2c57600080fd5b505081359360208301359350604090920135919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600082821015613f8457613f84613f43565b500390565b65ffffffffffff811681146115ae57600080fd5b8035613c9181613f89565b8035600081900b8114613c9157600080fd5b60006101808284031215613fcd57600080fd5b613fd5613e1b565b613fde83613c86565b8152613fec60208401613c86565b6020820152613ffd60408401613c86565b604082015261400e60608401613d33565b60608201526080830135608082015260a083013560a082015260c083013560c082015261403d60e08401613e45565b60e0820152610100614050818501613f9d565b90820152610120614062848201613f9d565b90820152610140614074848201613e45565b90820152610160614086848201613fa8565b908201529392505050565b6000602082840312156140a357600080fd5b8151610b4c81613c64565b6000602082840312156140c057600080fd5b610b4c82613e45565b6000602082840312156140db57600080fd5b8151610b4c81613d25565b6000602082840312156140f857600080fd5b8151610b4c81613f89565b6000821982111561411657614116613f43565b500190565b600065ffffffffffff8381169083168181101561413a5761413a613f43565b039392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b60006020828403121561418357600080fd5b815160ff81168114610b4c57600080fd5b6000602082840312156141a657600080fd5b5051919050565b600063ffffffff808316818516818304811182151516156141d0576141d0613f43565b02949350505050565b600065ffffffffffff8083168185168083038211156141fa576141fa613f43565b0194935050505056fea26469706673582212204ebe048e16250c60f03f21d2557e0fe1a8ddc2725ee7c865ff636e241b955d9e64736f6c634300080f0033000000000000000000000000007fe7c498a2cf30971ad8f2cbc36bd14ac51156000000000000000000000000007a66b9e719b3abb2f3917eb47d4231a17f5a0d000000000000000000000000007bd11fca0daaeadd455b51826f9a015f2f0969000000000000000000000000007a0f3b057945db86408197daa7c04373b5a94a

Deployed ByteCode

0x608060405234801561001057600080fd5b50600436106102ad5760003560e01c8063acc5570c1161017b578063bf7e214f116100d8578063d9ccdc931161008c578063e3684e3911610071578063e3684e39146109af578063e922067314610aed578063ea0aca3314610b0957600080fd5b8063d9ccdc931461098c578063e007fa971461099c57600080fd5b8063c7bf8ca0116100bd578063c7bf8ca014610935578063d204068714610948578063d2bee3231461096857600080fd5b8063bf7e214f146108a2578063c0aa0e8a146108c257600080fd5b8063bc3b2b121161012f578063bcf6cde811610114578063bcf6cde814610869578063bd1f3a5e1461087c578063bf48582b1461088f57600080fd5b8063bc3b2b12146107d2578063bcb296671461085657600080fd5b8063afa9d3b011610160578063afa9d3b0146106a1578063b1283e77146106ae578063bbbdd95a146107bf57600080fd5b8063acc5570c14610534578063ae4180951461068e57600080fd5b80635f77274e116102295780638973082c116101dd5780638da5cb5b116101c25780638da5cb5b146104cb578063946824cd146104eb5780639787d1071461050e57600080fd5b80638973082c1461047b5780638b098db3146104b857600080fd5b80636729a41e1161020e5780636729a41e1461041f578063699e17d9146104555780637a9e5e4b1461046857600080fd5b80635f77274e146103d65780636352211e146103e957600080fd5b806327507458116102805780633adec5a7116102655780633adec5a71461038d57806353c7f8e0146103a05780635dc4d16b146103b357600080fd5b806327507458146103235780633ad59dbc1461034657600080fd5b80630a9d85eb146102b257806310b05317146102d857806313af4035146102ed5780631c063a6c14610300575b600080fd5b6102c56102c0366004613c4b565b610b21565b6040519081526020015b60405180910390f35b6102eb6102e6366004613c4b565b610b53565b005b6102eb6102fb366004613c96565b610c0e565b6102c561030e366004613c4b565b60009081526002602052604090206004015490565b610336610331366004613c4b565b610d17565b60405190151581526020016102cf565b7f000000000000000000000000007a66b9e719b3abb2f3917eb47d4231a17f5a0d5b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016102cf565b6102c561039b366004613c4b565b610d60565b6102c56103ae366004613cb3565b610dc3565b6103366103c1366004613c96565b60086020526000908152604090205460ff1681565b6102eb6103e4366004613d3e565b610eb4565b6103686103f7366004613c4b565b60009081526002602052604090205473ffffffffffffffffffffffffffffffffffffffff1690565b61036861042d366004613c4b565b60066020526000908152604090205473ffffffffffffffffffffffffffffffffffffffff1681565b6102eb610463366004613d5b565b610f79565b6102eb610476366004613c96565b6113b5565b6009546104a39074010000000000000000000000000000000000000000900463ffffffff1681565b60405163ffffffff90911681526020016102cf565b6103366104c6366004613c4b565b611512565b6000546103689073ffffffffffffffffffffffffffffffffffffffff1681565b6102c56104f9366004613c4b565b6000908152600260205260409020600a015490565b7f000000000000000000000000007fe7c498a2cf30971ad8f2cbc36bd14ac51156610368565b610638610542366004613c4b565b6000818152600260208181526040808420815161018081018352815473ffffffffffffffffffffffffffffffffffffffff90811680835260018401548216838701819052848801548316848701819052600380870154948516606087018190527401000000000000000000000000000000000000000090950460ff1615156080870152600487015460a0870152600587015460c0870152600687015460e08701526007870154610100870190815260088801546101208801526009880154610140880152600a90970154610160909601959095529989529290955292909520909301549251919590949265ffffffffffff169190565b6040805173ffffffffffffffffffffffffffffffffffffffff97881681529587166020870152938616938501939093529316606083015265ffffffffffff909216608082015260a081019190915260c0016102cf565b6102eb61069c366004613c4b565b611548565b6007546103369060ff1681565b6107456106bc366004613c4b565b600260208190526000918252604090912080546001820154928201546003830154600484015460058501546006860154600787015460088801546009890154600a9099015473ffffffffffffffffffffffffffffffffffffffff9889169a891699978916988716977401000000000000000000000000000000000000000090970460ff1696908c565b6040805173ffffffffffffffffffffffffffffffffffffffff9d8e1681529b8d1660208d0152998c16998b0199909952999096166060890152931515608088015260a087019290925260c086015260e0850152610100840152610120830152610140820192909252610160810191909152610180016102cf565b6102eb6107cd366004613d8e565b6115b1565b6108256107e0366004613c4b565b6005602052600090815260409020805460019091015465ffffffffffff8082169166010000000000008104909116906c01000000000000000000000000900460ff1684565b6040516102cf949392919093845265ffffffffffff9283166020850152911660408301521515606082015260800190565b6102c5610864366004613c4b565b61169b565b6102eb610877366004613dc7565b6116c2565b6102eb61088a366004613e59565b611772565b6102c561089d366004613ede565b611946565b6001546103689073ffffffffffffffffffffffffffffffffffffffff1681565b6109076108d0366004613c4b565b60036020526000908152604090208054600182015460029092015490919065ffffffffffff80821691660100000000000090041684565b60408051948552602085019390935265ffffffffffff918216928401929092521660608201526080016102cf565b6102c5610943366004613dc7565b611a9f565b6009546104a3906c01000000000000000000000000900463ffffffff1681565b6009546104a390700100000000000000000000000000000000900463ffffffff1681565b6009546104a39063ffffffff1681565b6102c56109aa366004613f17565b611cbf565b610a876109bd366004613c4b565b600460205260009081526040902080546001820154600283015460039093015465ffffffffffff80841694660100000000000085049091169363ffffffff6c0100000000000000000000000082048116947001000000000000000000000000000000008304821694740100000000000000000000000000000000000000008404831694780100000000000000000000000000000000000000000000000085048416947c0100000000000000000000000000000000000000000000000000000000900490931692908a565b6040805165ffffffffffff9b8c1681529a90991660208b015263ffffffff978816988a01989098529486166060890152928516608088015290841660a08701529290921660c085015260e0840191909152610100830152610120820152610140016102cf565b6009546104a39068010000000000000000900463ffffffff1681565b6009546104a390640100000000900463ffffffff1681565b600080610b2d83611f59565b5050600084815260036020526040902054909150610b4c908290613f72565b9392505050565b60008181526006602052604090205473ffffffffffffffffffffffffffffffffffffffff163314610bb0576040517f2c47703200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600090815260066020908152604080832054600290925290912080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff909216919091179055565b610c3c336000357fffffffff0000000000000000000000000000000000000000000000000000000016612034565b610ca7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f554e415554484f52495a4544000000000000000000000000000000000000000060448201526064015b60405180910390fd5b600080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83169081178255604051909133917f8292fce18fa69edf4db7b94ea2e58241df0ae57f97e0a6c9b29067028bf92d769190a350565b60008181526002602052604081206004015415801590610d5a575060008281526003602052604090206002015442660100000000000090910465ffffffffffff16115b92915050565b600080610d91610d6f8461169b565b6000858152600260205260409020600a0154610d8a86610b21565b9190612145565b6000848152600260205260409020600601549091508111610d5a57600083815260026020526040902060060154610b4c565b600080610dd283850185613fba565b90506000610ddf826121a3565b82516101008401516040517fc6e38a4b00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff928316600482015265ffffffffffff90911660248201529192507f000000000000000000000000007fe7c498a2cf30971ad8f2cbc36bd14ac51156169063c6e38a4b906044016020604051808303816000875af1158015610e87573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610eab9190614091565b50949350505050565b610ee2336000357fffffffff0000000000000000000000000000000000000000000000000000000016612034565b610f48576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f554e415554484f52495a454400000000000000000000000000000000000000006044820152606401610c9e565b600780547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016911515919091179055565b610f8660208201826140ae565b63ffffffff161580610fab5750610fa360408201602083016140ae565b63ffffffff16155b80610fc95750610fc160608201604083016140ae565b63ffffffff16155b15611000576040517f3b596f5f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61101060408201602083016140ae565b63ffffffff1661102360208301836140ae565b63ffffffff161015611061576040517f3b596f5f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008281526004602090815260409091208054909170010000000000000000000000000000000090910463ffffffff169061109e908401846140ae565b63ffffffff1610156110dc576040517f3b596f5f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60095468010000000000000000900463ffffffff1661110160608401604085016140ae565b63ffffffff16101561113f576040517f3b596f5f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600083815260026020818152604092839020835161018081018552815473ffffffffffffffffffffffffffffffffffffffff908116808352600184015482169483019490945293820154841694810194909452600381015492831660608501527401000000000000000000000000000000000000000090920460ff1615156080840152600482015460a0840152600582015460c0840152600682015460e0840152600782015461010084015260088201546101208401526009820154610140840152600a909101546101608301523314611245576040517f4e1c8b5d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61125260208401846140ae565b825463ffffffff9190911674010000000000000000000000000000000000000000027fffffffffffffffff00000000ffffffffffffffffffffffffffffffffffffffff9091161782556112f06112ab60208501856140ae565b60008681526003602052604090206002015463ffffffff91909116906112e49042906601000000000000900465ffffffffffff16613f72565b60a08401519190612e71565b600183015561130560408401602085016140ae565b825463ffffffff919091167801000000000000000000000000000000000000000000000000027fffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffffff90911617825561136260608401604085016140ae565b825463ffffffff919091167c0100000000000000000000000000000000000000000000000000000000027bffffffffffffffffffffffffffffffffffffffffffffffffffffffff90911617909155505050565b60005473ffffffffffffffffffffffffffffffffffffffff1633148061149857506001546040517fb70096130000000000000000000000000000000000000000000000000000000081523360048201523060248201526000357fffffffff0000000000000000000000000000000000000000000000000000000016604482015273ffffffffffffffffffffffffffffffffffffffff9091169063b700961390606401602060405180830381865afa158015611474573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061149891906140c9565b6114a157600080fd5b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff831690811790915560405133907fa3396fd7f6e0a21b50e5089d2da70d5ac0a3bbbd1f617a93f134b7638998019890600090a350565b60008181526003602052604081206002015465ffffffffffff16635dba24008111156115415742811115610b4c565b1592915050565b60008181526002602052604090205473ffffffffffffffffffffffffffffffffffffffff1633146115a5576040517f4e1c8b5d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6115ae81612f3d565b50565b6115df336000357fffffffff0000000000000000000000000000000000000000000000000000000016612034565b611645576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f554e415554484f52495a454400000000000000000000000000000000000000006044820152606401610c9e565b73ffffffffffffffffffffffffffffffffffffffff91909116600090815260086020526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016911515919091179055565b60006116a682612fc1565b600083815260026020526040902060050154610d5a9190613f72565b60008281526002602052604090205473ffffffffffffffffffffffffffffffffffffffff16331461171f576040517f4e1c8b5d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60009182526006602052604090912080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff909216919091179055565b6117a0336000357fffffffff0000000000000000000000000000000000000000000000000000000016612034565b611806576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f554e415554484f52495a454400000000000000000000000000000000000000006044820152606401610c9e565b805160098054602084015160408501516060860151608087015160a09097015163ffffffff90811674010000000000000000000000000000000000000000029181166c0100000000000000000000000002938116640100000000029681167fffffffffffffffffffffffffffffffffffffffffffffffff000000000000000090951694909417959095177fffffffffffffffffffffffffffffffff0000000000000000ffffffffffffffff1690831668010000000000000000027fffffffffffffffffffffffffffffffff00000000ffffffffffffffffffffffff1617177fffffffffffffffff0000000000000000ffffffffffffffffffffffffffffffff169316700100000000000000000000000000000000027fffffffffffffffff00000000ffffffffffffffffffffffffffffffffffffffff1692909217179055565b6040517fb88c914800000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82811660048301526000918291611a14917f000000000000000000000000007fe7c498a2cf30971ad8f2cbc36bd14ac511569091169063b88c914890602401602060405180830381865afa1580156119dd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a0191906140e6565b869065ffffffffffff16620186a0612e71565b6000858152600260205260408120600a015491925090611a4890611a3787610d60565b611a41858a613f72565b9190612e71565b600086815260026020526040902060070154909150811115611a96576040517f5c430eae00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b9150610b4c9050565b6000828152600260208181526040808420815161018081018352815473ffffffffffffffffffffffffffffffffffffffff9081168252600183015481169482019490945293810154831691840191909152600381015491821660608401527401000000000000000000000000000000000000000090910460ff1615156080830152600481015460a0830152600581015460c0830152600681015460e0830152600781015461010083015260088101546101208301526009810154610140830152600a015461016082015281611b7385610d60565b905060008260800151611b9b5761016083015160a0840151611b96918490612e71565b611ba1565b8260a001515b90506000611bc483856101600151866101000151612e719092919063ffffffff16565b90506000818310611bd55781611bd7565b825b6040517fb88c914800000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8981166004830152919250600091611ca6917f000000000000000000000000007fe7c498a2cf30971ad8f2cbc36bd14ac511569091169063b88c914890602401602060405180830381865afa158015611c6f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c9391906140e6565b839065ffffffffffff16620186a0612e71565b9050611cb28183614103565b9998505050505050505050565b60003373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000007fe7c498a2cf30971ad8f2cbc36bd14ac511561614611d30576040517f2c47703200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600084815260026020818152604080842060038352938190208151608081018352815481526001820154938101939093529092015465ffffffffffff80821693830193909352660100000000000090048216606082018190529091429190821610611dd75760608201516040517f07fc4a7000000000000000000000000000000000000000000000000000000000815265ffffffffffff9091166004820152602401610c9e565b6000611de4888842613132565b9550905085851015611e22576040517f74ec9d5b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8360070154851115611e60576040517f5c430eae00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600384015474010000000000000000000000000000000000000000900460ff16611e905783600401548511611e98565b836004015487115b15611ecf576040517ff3383dc900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600384015474010000000000000000000000000000000000000000900460ff16611ef95784611efb565b865b600485018054919091039055600584018054860160010190819055600985018054890190556008850180548701905560208401511015611f4357611f3e88612f3d565b611f4e565b611f4e888383613474565b505050509392505050565b600081815260056020908152604080832081516080810183528154815260019091015465ffffffffffff8082169483019490945266010000000000008104909316918101919091526c0100000000000000000000000090910460ff1615156060820181905282918291611fd75760008060009350935093505061202d565b6020810151611fe6904261411b565b9250806040015165ffffffffffff168365ffffffffffff161091508161200d578051612029565b604081015181516120299165ffffffffffff8087169116612e71565b9350505b9193909250565b60015460009073ffffffffffffffffffffffffffffffffffffffff16801580159061211857506040517fb700961300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85811660048301523060248301527fffffffff000000000000000000000000000000000000000000000000000000008516604483015282169063b700961390606401602060405180830381865afa1580156120f4573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061211891906140c9565b8061213d575060005473ffffffffffffffffffffffffffffffffffffffff8581169116145b949350505050565b6000612152848484612e71565b90506000828061216457612164614142565b8486091115610b4c577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff811061219957600080fd5b6001019392505050565b60075460009060ff166121e2576040517f64be3ffa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000826000015173ffffffffffffffffffffffffffffffffffffffff1663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015612233573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122579190614171565b90506000836020015173ffffffffffffffffffffffffffffffffffffffff1663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa1580156122aa573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122ce9190614171565b905060068260ff1610806122e5575060128260ff16115b1561231c576040517f3b596f5f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60068160ff161080612331575060128160ff16115b15612368576040517f3b596f5f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe884610160015160000b12806123a65750601884610160015160000b135b156123dd576040517f3b596f5f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3360009081526008602052604090205460ff161580156124165750604084015173ffffffffffffffffffffffffffffffffffffffff1615155b1561244d576040517f2c47703200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050600082610160015160240160ff16600a0a90508260c001518360a0015110156124a4576040517f4496547d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b825160208401516040517fb435914300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff928316600482015290821660248201526000917f000000000000000000000000007a66b9e719b3abb2f3917eb47d4231a17f5a0d169063b4359143906044016020604051808303816000875af1158015612544573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125689190614194565b90506000804286610120015165ffffffffffff166125869190613f72565b60095490925063ffffffff700100000000000000000000000000000000909104811690831610806125d7575060095461014087015163ffffffff6c0100000000000000000000000090920482169116105b1561260e576040517f3b596f5f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000866101400151600561262291906141ad565b60095490915063ffffffff808316680100000000000000009092041611612649578061265f565b60095468010000000000000000900463ffffffff165b6009546101408901519193506000916126b79163ffffffff90811691161161268f5760095463ffffffff16612696565b8861014001515b63ffffffff168563ffffffff168a60800151612e719092919063ffffffff16565b60408051610140808201835265ffffffffffff4216808352602083015263ffffffff808916938301939093528b018051831660608301526009549051939450909260808401929182169116116127155760095463ffffffff1661271c565b8961014001515b63ffffffff9081168252600954640100000000900481166020830152851660408201526060810183905260808a81015191019061275a908490613f72565b81526020016127988563ffffffff168763ffffffff168c60600151612783578c60800151611a41565b60a08d015160808e0151611a41918d90612e71565b90526000868152600460209081526040808320845181549386015192860151606080880151608089015160a08a015160c08b015165ffffffffffff9687167fffffffffffffffffffffffffffffffffffffffff000000000000000000000000909a169990991766010000000000009690981695909502969096177fffffffffffffffffffffffff0000000000000000ffffffffffffffffffffffff166c0100000000000000000000000063ffffffff948516027fffffffffffffffffffffffff00000000ffffffffffffffffffffffffffffffff161770010000000000000000000000000000000091841691909102177fffffffff0000000000000000ffffffffffffffffffffffffffffffffffffffff1674010000000000000000000000000000000000000000958316959095027fffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffffff1694909417780100000000000000000000000000000000000000000000000092821692909202919091177bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167c0100000000000000000000000000000000000000000000000000000000919094160292909217825560e084015160018301556101008401516002830155610120909301516003909101559089015190925082915081906129955788608001516129aa565b60a089015160808a01516129aa918990612e71565b90506129c38163ffffffff8087169088811690612e7116565b92506129eb89610140015163ffffffff168663ffffffff1683612e719092919063ffffffff16565b9150506040518061018001604052803373ffffffffffffffffffffffffffffffffffffffff168152602001896000015173ffffffffffffffffffffffffffffffffffffffff168152602001896020015173ffffffffffffffffffffffffffffffffffffffff168152602001896040015173ffffffffffffffffffffffffffffffffffffffff168152602001896060015115158152602001896080015181526020018381526020018960c0015181526020018281526020016000815260200160008152602001878152506002600087815260200190815260200160002060008201518160000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060208201518160010160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060408201518160020160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060608201518160030160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060808201518160030160146101000a81548160ff02191690831515021790555060a0820151816004015560c0820151816005015560e0820151816006015561010082015181600701556101208201518160080155610140820151816009015561016082015181600a01559050506000600960149054906101000a900463ffffffff1663ffffffff16612c8b620186a065ffffffffffff168585612e719092919063ffffffff16565b11612cb65760095474010000000000000000000000000000000000000000900463ffffffff16612cc4565b612cc482620186a085612e71565b90506000612cf9828b60e0015163ffffffff1611612ce25782612cee565b8a60e0015163ffffffff165b8590620186a0612e71565b612d039085614103565b60a08b0151909150600090612d19908a87612e71565b905060405180608001604052808281526020018381526020018c610100015165ffffffffffff1681526020018c610120015165ffffffffffff16815250600360008a8152602001908152602001600020600082015181600001556020820151816001015560408201518160020160006101000a81548165ffffffffffff021916908365ffffffffffff16021790555060608201518160020160066101000a81548165ffffffffffff021916908365ffffffffffff1602179055509050508a6020015173ffffffffffffffffffffffffffffffffffffffff168b6000015173ffffffffffffffffffffffffffffffffffffffff16897f8235b14cd272b4e791960fe1118559bb7fed86934fcffeeae9b1175103b0756d8e61010001518f60a00151604051612e5a92919065ffffffffffff929092168252602082015260400190565b60405180910390a450959998505050505050505050565b600080807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff85870985870292508281108382030391505080600003612ec85760008411612ebd57600080fd5b508290049050610b4c565b808411612ed457600080fd5b60008486880960026001871981018816978890046003810283188082028403028082028403028082028403028082028403028082028403029081029092039091026000889003889004909101858311909403939093029303949094049190911702949350505050565b6000818152600360209081526040808320600290810180547fffffffffffffffffffffffffffffffffffffffff000000000000ffffffffffff1666010000000000004265ffffffffffff16021790559091528082206004018290555182917f9dc30b8eda31a6a144e092e5de600955523a6a925cc15cc1d1b9b4872cfa615591a250565b6000818152600460209081526040808320815161014081018352815465ffffffffffff8082168352660100000000000082041694820185905263ffffffff6c0100000000000000000000000082048116948301949094527001000000000000000000000000000000008104841660608301527401000000000000000000000000000000000000000081048416608083015278010000000000000000000000000000000000000000000000008104841660a08301527c0100000000000000000000000000000000000000000000000000000000900490921660c0830152600181015460e08301526002810154610100830152600301546101208201529042838282116130cd5760006130d1565b8282035b90508360c0015163ffffffff1681116131155760c084015160008781526002602052604090206005015461311091839063ffffffff90811690612e7116565b613128565b6000868152600260205260409020600501545b9695505050505050565b6000838152600260208181526040808420815161018081018352815473ffffffffffffffffffffffffffffffffffffffff9081168252600183015481168286015294820154851681840152600382015494851660608201527401000000000000000000000000000000000000000090940460ff161515608085015260048082015460a0860152600582015460c0860152600682015460e0860152600782015461010086015260088201546101208601526009820154610140860152600a9091015461016085015287855290915282205482919042660100000000000090910465ffffffffffff161161324e5761322786612fc1565b60008781526002602052604081206005018054909190613248908490613f72565b90915550505b6000868152600560205260409020600101546c01000000000000000000000000900460ff16156133835760008681526005602052604081209080806132928a611f59565b60008d8152600360205260408120805494975092955090935085926132b8908490613f72565b9091555050801561335357828460000160008282546132d79190613f72565b90915550506001840180548391906006906133059084906601000000000000900465ffffffffffff1661411b565b92506101000a81548165ffffffffffff021916908365ffffffffffff160217905550878460010160006101000a81548165ffffffffffff021916908365ffffffffffff16021790555061337e565b6001840180547fffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffff1690555b505050505b61338c86613b68565b60e0820151909350808410156133a0578093505b6101608201516133b290879086612e71565b60008881526004602052604081208054600390910154929550909161340691633b9aca009063ffffffff7c0100000000000000000000000000000000000000000000000000000000909104811690612e7116565b905061341784633b9aca0083612145565b600089815260046020526040902080546006906134479084906601000000000000900465ffffffffffff166141d9565b92506101000a81548165ffffffffffff021916908365ffffffffffff160217905550505050935093915050565b600060046000858152602001908152602001600020604051806101400160405290816000820160009054906101000a900465ffffffffffff1665ffffffffffff1665ffffffffffff1681526020016000820160069054906101000a900465ffffffffffff1665ffffffffffff1665ffffffffffff16815260200160008201600c9054906101000a900463ffffffff1663ffffffff1663ffffffff1681526020016000820160109054906101000a900463ffffffff1663ffffffff1663ffffffff1681526020016000820160149054906101000a900463ffffffff1663ffffffff1663ffffffff1681526020016000820160189054906101000a900463ffffffff1663ffffffff1663ffffffff16815260200160008201601c9054906101000a900463ffffffff1663ffffffff1663ffffffff16815260200160018201548152602001600282015481526020016003820154815250509050600060026000868152602001908152602001600020604051806101800160405290816000820160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020016001820160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020016002820160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020016003820160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020016003820160149054906101000a900460ff16151515158152602001600482015481526020016005820154815260200160068201548152602001600782015481526020016008820154815260200160098201548152602001600a8201548152505090506000846003600088815260200190815260200160002060020160069054906101000a900465ffffffffffff166137dd919061411b565b65ffffffffffff169050600082608001516137fc578260a00151613811565b61016083015160a08401516138119187612e71565b9050600083608001516138295783610120015161383f565b61016084015161014085015161383f9188612e71565b6138499083614103565b905060008261388285886040015163ffffffff166138679190613f72565b886040015163ffffffff1685612e719092919063ffffffff16565b61388c9190614103565b90508561010001518560a001511080156138a557508181105b806138e05750608086015186516138c29163ffffffff16906141d9565b65ffffffffffff168865ffffffffffff16101580156138e057508181115b15613b5d57613904866060015163ffffffff168585612e719092919063ffffffff16565b60008a8152600260205260408082206007019290925560c088015191880151909161393d91849163ffffffff90811691811690612e7116565b60008b81526003602052604081205461016089015192935091613962908b9085612145565b60408051848152602081018390529192508d917f78f9c01d72705dba80d6ce051d36a1f987bf2a3800fee938c111a2ae741e57d1910160405180910390a281811015613a905760006139b48284613f72565b905060405180608001604052808281526020018d65ffffffffffff1681526020018b60a0015163ffffffff1665ffffffffffff16815260200160011515815250600560008f81526020019081526020016000206000820151816000015560208201518160010160006101000a81548165ffffffffffff021916908365ffffffffffff16021790555060408201518160010160066101000a81548165ffffffffffff021916908365ffffffffffff160217905550606082015181600101600c6101000a81548160ff02191690831515021790555090505050613ad5565b60008c81526003602090815260408083208490556005909152902060010180547fffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffff1690555b60008c815260046020526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000001665ffffffffffff8d1617905560e089015160a089015111613b29576000613b3d565b8860e001518860a00151613b3d9190613f72565b60008d815260046020526040902060028101919091556003019290925550505b505050505050505050565b6000818152600260208181526040808420815161018081018352815473ffffffffffffffffffffffffffffffffffffffff908116825260018301548116828601529482015485168184015260038083015495861660608301527401000000000000000000000000000000000000000090950460ff1615156080820152600482015460a0820152600582015460c08201819052600683015460e0830152600783015461010083015260088301546101208301526009830154610140830152600a90920154610160820181905287875294909352908420549192610b4c929190612e71565b600060208284031215613c5d57600080fd5b5035919050565b73ffffffffffffffffffffffffffffffffffffffff811681146115ae57600080fd5b8035613c9181613c64565b919050565b600060208284031215613ca857600080fd5b8135610b4c81613c64565b60008060208385031215613cc657600080fd5b823567ffffffffffffffff80821115613cde57600080fd5b818501915085601f830112613cf257600080fd5b813581811115613d0157600080fd5b866020828501011115613d1357600080fd5b60209290920196919550909350505050565b80151581146115ae57600080fd5b8035613c9181613d25565b600060208284031215613d5057600080fd5b8135610b4c81613d25565b60008060808385031215613d6e57600080fd5b8235915083608084011115613d8257600080fd5b50926020919091019150565b60008060408385031215613da157600080fd5b8235613dac81613c64565b91506020830135613dbc81613d25565b809150509250929050565b60008060408385031215613dda57600080fd5b823591506020830135613dbc81613c64565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051610180810167ffffffffffffffff81118282101715613e3f57613e3f613dec565b60405290565b803563ffffffff81168114613c9157600080fd5b600060c08284031215613e6b57600080fd5b82601f830112613e7a57600080fd5b60405160c0810181811067ffffffffffffffff82111715613e9d57613e9d613dec565b6040528060c0840185811115613eb257600080fd5b845b81811015613ed357613ec581613e45565b835260209283019201613eb4565b509195945050505050565b600080600060608486031215613ef357600080fd5b83359250602084013591506040840135613f0c81613c64565b809150509250925092565b600080600060608486031215613f2c57600080fd5b505081359360208301359350604090920135919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600082821015613f8457613f84613f43565b500390565b65ffffffffffff811681146115ae57600080fd5b8035613c9181613f89565b8035600081900b8114613c9157600080fd5b60006101808284031215613fcd57600080fd5b613fd5613e1b565b613fde83613c86565b8152613fec60208401613c86565b6020820152613ffd60408401613c86565b604082015261400e60608401613d33565b60608201526080830135608082015260a083013560a082015260c083013560c082015261403d60e08401613e45565b60e0820152610100614050818501613f9d565b90820152610120614062848201613f9d565b90820152610140614074848201613e45565b90820152610160614086848201613fa8565b908201529392505050565b6000602082840312156140a357600080fd5b8151610b4c81613c64565b6000602082840312156140c057600080fd5b610b4c82613e45565b6000602082840312156140db57600080fd5b8151610b4c81613d25565b6000602082840312156140f857600080fd5b8151610b4c81613f89565b6000821982111561411657614116613f43565b500190565b600065ffffffffffff8381169083168181101561413a5761413a613f43565b039392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b60006020828403121561418357600080fd5b815160ff81168114610b4c57600080fd5b6000602082840312156141a657600080fd5b5051919050565b600063ffffffff808316818516818304811182151516156141d0576141d0613f43565b02949350505050565b600065ffffffffffff8083168185168083038211156141fa576141fa613f43565b0194935050505056fea26469706673582212204ebe048e16250c60f03f21d2557e0fe1a8ddc2725ee7c865ff636e241b955d9e64736f6c634300080f0033