Contract 0xfe05da9fffc72027c26e2327a9e6339670cd1b90 3

Contract Overview

Balance:
0 ETH
Txn Hash Method
Block
From
To
Value [Txn Fee]
0xfbf8dff15ac9bd991132b5bfae1fb66cd5790f51420a9e3430152c6f954f77b3Create Market199024682023-05-15 14:14:49202 days 23 hrs ago0xda8b43d5da504a3a418aeedce1ece868536807fa IN  0xfe05da9fffc72027c26e2327a9e6339670cd1b900 ETH0.00006244 0.1
[ Download CSV Export 
Latest 8 internal transactions
Parent Txn Hash Block From To Value
0xfbf8dff15ac9bd991132b5bfae1fb66cd5790f51420a9e3430152c6f954f77b3199024682023-05-15 14:14:49202 days 23 hrs ago 0xfe05da9fffc72027c26e2327a9e6339670cd1b90 0x007fe70dc9797c4198528ae43d8195fff82bdc950 ETH
0xfbf8dff15ac9bd991132b5bfae1fb66cd5790f51420a9e3430152c6f954f77b3199024682023-05-15 14:14:49202 days 23 hrs ago 0xfe05da9fffc72027c26e2327a9e6339670cd1b90 0x91d20a4d57944618500bdce4d94d14c15ef127200 ETH
0xfbf8dff15ac9bd991132b5bfae1fb66cd5790f51420a9e3430152c6f954f77b3199024682023-05-15 14:14:49202 days 23 hrs ago 0xfe05da9fffc72027c26e2327a9e6339670cd1b90 0x91d20a4d57944618500bdce4d94d14c15ef127200 ETH
0xfbf8dff15ac9bd991132b5bfae1fb66cd5790f51420a9e3430152c6f954f77b3199024682023-05-15 14:14:49202 days 23 hrs ago 0xfe05da9fffc72027c26e2327a9e6339670cd1b90 0x91d20a4d57944618500bdce4d94d14c15ef127200 ETH
0xfbf8dff15ac9bd991132b5bfae1fb66cd5790f51420a9e3430152c6f954f77b3199024682023-05-15 14:14:49202 days 23 hrs ago 0xfe05da9fffc72027c26e2327a9e6339670cd1b90 0xca93c9bfac39efc5b069066a0970c3036c3029c90 ETH
0xfbf8dff15ac9bd991132b5bfae1fb66cd5790f51420a9e3430152c6f954f77b3199024682023-05-15 14:14:49202 days 23 hrs ago 0xfe05da9fffc72027c26e2327a9e6339670cd1b90 0xd518614c390e02741f3b0ee24486b09ce53e6d390 ETH
0xfbf8dff15ac9bd991132b5bfae1fb66cd5790f51420a9e3430152c6f954f77b3199024682023-05-15 14:14:49202 days 23 hrs ago 0xfe05da9fffc72027c26e2327a9e6339670cd1b90 0x007a66a2a13415db3613c1a4dd1c942a285902d10 ETH
0x6a5795834b5f497c73f52b64096adfcd57801c3325d628d3be7d1c4539225741189284822023-05-05 14:43:07212 days 23 hrs ago 0x4e59b44847b379578588920ca78fbf26c0b4956c  Contract Creation0 ETH
[ Download CSV Export 
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
BondFixedExpiryOSDA

Compiler Version
v0.8.15+commit.e14f2714

Optimization Enabled:
Yes with 100000 runs

Other Settings:
default evmVersion
File 1 of 17 : BondFixedExpiryOSDA.sol
// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity ^0.8.15;

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

/// @title Bond Fixed-Expiry Oracle-based Sequential Dutch Auctioneer
/// @notice Bond Fixed-Expiry Oracle-based 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 Oracle-based SDA is an implementation of the
///      Bond Base Oracle-based SDA contract specific to creating bond markets where
///      all purchases on that market vest at a certain timestamp.
///
/// @author Oighty, Zeus, Potted Meat, indigo
contract BondFixedExpiryOSDA is BondBaseOSDA {
    /* ========== CONSTRUCTOR ========== */
    constructor(
        IBondTeller teller_,
        IBondAggregator aggregator_,
        address guardian_,
        Authority authority_
    ) BondBaseOSDA(teller_, aggregator_, guardian_, authority_) {}

    /// @inheritdoc BondBaseOSDA
    function createMarket(bytes calldata params_) external override returns (uint256) {
        // Decode params into the struct type expected by this auctioneer
        MarketParams memory params = abi.decode(params_, (MarketParams));

        // Vesting is rounded to the nearest day at 0000 UTC (in seconds) since bond tokens
        // are only unique to a day, not a specific timestamp.
        params.vesting = (params.vesting / 1 days) * 1 days;

        // Get conclusion from start time and duration
        // Don't need to check valid start time or duration here since it will be checked in _createMarket
        uint48 start = params.start == 0 ? uint48(block.timestamp) : params.start;
        uint48 conclusion = start + params.duration;

        // Check that the vesting parameter is valid for a fixed-expiry market
        if (params.vesting != 0 && params.vesting < conclusion) revert Auctioneer_InvalidParams();

        // Create market with provided params
        uint256 marketId = _createMarket(params);

        // Create bond token (ERC20 for fixed expiry) if not instant swap
        if (params.vesting != 0) {
            IBondFixedExpiryTeller(address(_teller)).deploy(params.payoutToken, params.vesting);
        }

        // Return market ID
        return marketId;
    }
}

File 2 of 17 : BondBaseOSDA.sol
// 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 {IBondOSDA, IBondAuctioneer} from "../interfaces/IBondOSDA.sol";
import {IBondOracle} from "../interfaces/IBondOracle.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 Oracle-based Sequential Dutch Auctioneer (OSDA)
/// @notice Bond Oracle-based Sequential Dutch Auctioneer Base Contract
/// @dev Bond Protocol is a system to create 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 Oracle-based Sequential Dutch Auctioneer contract allows users to create
///      and manage bond markets. All bond market data is stored in the Auctioneer.
///      The market price is based on an outside Oracle and varies based on whether the
///      market is under- or oversold with the goal of selling a target amount of
///      payout tokens or buying a target amount of quote tokens over the duration of
///      a market. An Auctioneer is dependent on a Teller to serve external users and
///      an Aggregator to register new markets.
///
/// @author Oighty
abstract contract BondBaseOSDA is IBondOSDA, Auth {
    using TransferHelper for ERC20;
    using FullMath for uint256;

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

    error Auctioneer_OnlyMarketOwner();
    error Auctioneer_InitialPriceLessThanMin();
    error Auctioneer_MarketNotActive();
    error Auctioneer_MaxPayoutExceeded();
    error Auctioneer_AmountLessThanMinimum();
    error Auctioneer_NotEnoughCapacity();
    error Auctioneer_InvalidCallback();
    error Auctioneer_BadExpiry();
    error Auctioneer_InvalidParams();
    error Auctioneer_NotAuthorized();
    error Auctioneer_NewMarketsNotAllowed();
    error Auctioneer_OraclePriceZero();

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

    event MarketCreated(
        uint256 indexed id,
        address indexed payoutToken,
        address indexed quoteToken,
        uint48 vesting
    );
    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 New address to designate as market owner. They must accept ownership to transfer permissions.
    mapping(uint256 => address) public newOwners;

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

    /// @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;

    // Minimum time parameter values. Can be updated by admin.
    /// @notice Minimum deposit interval for a market
    uint48 public minDepositInterval;

    /// @notice Minimum duration for a market
    uint48 public minMarketDuration;

    // 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 ONE_HUNDRED_PERCENT = 100e3; // 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_;

        minDepositInterval = 1 hours;
        minMarketDuration = 1 days;

        allowNewMarkets = true;
    }

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

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

    /// @notice core market creation logic, see IBondOSDA.MarketParams documentation
    function _createMarket(MarketParams memory params_) internal returns (uint256) {
        // Upfront permission and timing checks
        {
            // Check that the auctioneer is allowing new markets to be created
            if (!allowNewMarkets) revert Auctioneer_NewMarketsNotAllowed();
            // Restrict the use of a callback address unless allowed
            if (!callbackAuthorized[msg.sender] && params_.callbackAddr != address(0))
                revert Auctioneer_NotAuthorized();
            // Start time must be zero or in the future
            if (params_.start > 0 && params_.start < block.timestamp)
                revert Auctioneer_InvalidParams();
        }
        // Register new market on aggregator and get marketId
        uint256 marketId = _aggregator.registerMarket(params_.payoutToken, params_.quoteToken);

        // Set basic market data
        BondMarket storage market = markets[marketId];
        market.owner = msg.sender;
        market.quoteToken = params_.quoteToken;
        market.payoutToken = params_.payoutToken;
        market.callbackAddr = params_.callbackAddr;
        market.capacityInQuote = params_.capacityInQuote;
        market.capacity = params_.capacity;

        // Check that the base discount is in bounds (cannot be 100% or greater)
        BondTerms storage term = terms[marketId];
        if (
            params_.baseDiscount >= ONE_HUNDRED_PERCENT ||
            params_.baseDiscount > params_.maxDiscountFromCurrent
        ) revert Auctioneer_InvalidParams();
        term.baseDiscount = params_.baseDiscount;

        // Validate oracle and get price variables
        (uint256 price, uint256 oracleConversion, uint256 scale) = _validateOracle(
            marketId,
            params_.oracle,
            params_.quoteToken,
            params_.payoutToken,
            params_.baseDiscount
        );
        term.oracle = params_.oracle;
        term.oracleConversion = oracleConversion;
        term.scale = scale;

        // Check that the max discount from current price is in bounds (cannot be greater than 100%)
        if (params_.maxDiscountFromCurrent > ONE_HUNDRED_PERCENT) revert Auctioneer_InvalidParams();

        // Calculate the minimum price for the market
        term.minPrice = price.mulDivUp(
            uint256(ONE_HUNDRED_PERCENT - params_.maxDiscountFromCurrent),
            uint256(ONE_HUNDRED_PERCENT)
        );

        // Check time bounds
        if (
            params_.duration < minMarketDuration ||
            params_.depositInterval < minDepositInterval ||
            params_.depositInterval > params_.duration
        ) revert Auctioneer_InvalidParams();

        // Calculate the maximum payout amount for this market, determined by deposit interval
        uint256 capacity = params_.capacityInQuote
            ? params_.capacity.mulDiv(
                scale,
                price.mulDivUp(
                    uint256(ONE_HUNDRED_PERCENT - params_.baseDiscount),
                    uint256(ONE_HUNDRED_PERCENT)
                )
            )
            : params_.capacity;
        market.maxPayout = capacity.mulDiv(
            uint256(params_.depositInterval),
            uint256(params_.duration)
        );

        // Check target interval discount in bounds
        if (params_.targetIntervalDiscount > ONE_HUNDRED_PERCENT) revert Auctioneer_InvalidParams();

        // Calculate decay speed
        term.decaySpeed =
            (params_.duration * params_.targetIntervalDiscount) /
            params_.depositInterval;

        // Store bond time terms
        term.vesting = params_.vesting;
        uint48 start = params_.start == 0 ? uint48(block.timestamp) : params_.start;
        term.start = start;
        term.conclusion = start + params_.duration;

        // Emit market created event
        emit MarketCreated(
            marketId,
            address(params_.payoutToken),
            address(params_.quoteToken),
            params_.vesting
        );

        return marketId;
    }

    function _validateOracle(
        uint256 id_,
        IBondOracle oracle_,
        ERC20 quoteToken_,
        ERC20 payoutToken_,
        uint48 baseDiscount_
    )
        internal
        returns (
            uint256,
            uint256,
            uint256
        )
    {
        // Ensure token decimals are in bounds
        uint8 payoutTokenDecimals = payoutToken_.decimals();
        uint8 quoteTokenDecimals = quoteToken_.decimals();

        if (payoutTokenDecimals < 6 || payoutTokenDecimals > 18) revert Auctioneer_InvalidParams();
        if (quoteTokenDecimals < 6 || quoteTokenDecimals > 18) revert Auctioneer_InvalidParams();

        // Check that oracle is valid. It should:
        // 1. Be a contract
        if (address(oracle_) == address(0) || address(oracle_).code.length == 0)
            revert Auctioneer_InvalidParams();

        // 2. Allow registering markets
        oracle_.registerMarket(id_, quoteToken_, payoutToken_);

        // 3. Return a valid price for the quote token : payout token pair
        uint256 currentPrice = oracle_.currentPrice(id_);
        if (currentPrice == 0) revert Auctioneer_OraclePriceZero();

        // 4. Return a valid decimal value for the quote token : payout token pair price
        uint8 oracleDecimals = oracle_.decimals(id_);
        if (oracleDecimals < 6 || oracleDecimals > 18) revert Auctioneer_InvalidParams();

        // Calculate scaling values for market:
        // 1. We need a value to convert between the oracle decimals to the bond market decimals
        // 2. We need the bond scaling value to convert between quote and payout tokens using the market price

        // Get the price decimals for the current oracle price
        // Oracle price is in quote tokens per payout token
        // E.g. if quote token is $10 and payout token is $2000,
        // then the oracle price is 200 quote tokens per payout token.
        // If the oracle has 18 decimals, then it would return 200 * 10^18.
        // In this case, the price decimals would be 2 since 200 = 2 * 10^2.
        // We apply the base discount to the oracle price before calculating
        // since this will be the initial equilibrium price of the market.
        int8 priceDecimals = _getPriceDecimals(
            currentPrice.mulDivUp(
                uint256(ONE_HUNDRED_PERCENT - baseDiscount_),
                uint256(ONE_HUNDRED_PERCENT)
            ),
            oracleDecimals
        );
        // Check price decimals in reasonable range
        // These bounds are quite large and it is unlikely any combination of tokens
        // will have a price difference larger than 10^24 in either direction.
        // Check that oracle decimals are large enough to avoid precision loss from negative price decimals
        if (int8(oracleDecimals) <= -priceDecimals || priceDecimals > 24)
            revert Auctioneer_InvalidParams();

        // Calculate the oracle price conversion factor
        // oraclePriceFactor = int8(oracleDecimals) + priceDecimals;
        // bondPriceFactor = 36 - priceDecimals / 2 + priceDecimals;
        // oracleConversion = 10^(bondPriceFactor - oraclePriceFactor);
        uint256 oracleConversion = 10**uint8(36 - priceDecimals / 2 - int8(oracleDecimals));

        // Unit to scale calculation for this market by to ensure reasonable values
        // for price, debt, and control variable without under/overflows.
        //
        // scaleAdjustment should be equal to (payoutDecimals - quoteDecimals) - ((payoutPriceDecimals - quotePriceDecimals) / 2)
        // scale = 10^(36 + scaleAdjustment);
        uint256 scale = 10 **
            uint8(36 + int8(payoutTokenDecimals) - int8(quoteTokenDecimals) - priceDecimals / 2);

        return (currentPrice * oracleConversion, oracleConversion, scale);
    }

    /// @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 IBondOSDA
    function setMinMarketDuration(uint48 duration_) external override requiresAuth {
        // Restricted to authorized addresses

        // Require duration to be greater than minimum deposit interval and at least 1 day
        if (duration_ < minDepositInterval || duration_ < 1 days) revert Auctioneer_InvalidParams();

        minMarketDuration = duration_;
    }

    /// @inheritdoc IBondOSDA
    function setMinDepositInterval(uint48 depositInterval_) external override requiresAuth {
        // Restricted to authorized addresses

        // Require min deposit interval to be less than minimum market duration and at least 1 hour
        if (depositInterval_ > minMarketDuration || depositInterval_ < 1 hours)
            revert Auctioneer_InvalidParams();

        minDepositInterval = depositInterval_;
    }

    // Unused, but required by interface
    function setIntervals(uint256 id_, uint32[3] calldata intervals_) external override {}

    // Unused, but required by interface
    function setDefaults(uint32[6] memory defaults_) external override {}

    /// @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_];

        // If market uses a callback, check that owner is still callback authorized
        if (market.callbackAddr != address(0) && !callbackAuthorized[market.owner])
            revert Auctioneer_NotAuthorized();

        // Check if market is live, if not revert
        if (!isLive(id_)) revert Auctioneer_MarketNotActive();

        // Retrieve price and calculate payout
        uint256 price = marketPrice(id_);

        // 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(term.scale, price);

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

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

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

    /* ========== 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_);
    }

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

    /// @notice             Calculate current market price of payout token in quote tokens
    /// @dev                See marketPrice() in IBondOSDA for explanation of price computation
    /// @param id_          Market ID
    /// @return             Price for market as a ratio of quote tokens to payout tokens with 36 decimals
    function _currentMarketPrice(uint256 id_) internal view returns (uint256) {
        BondMarket memory market = markets[id_];
        BondTerms memory term = terms[id_];

        // Get price from oracle, apply oracle conversion factor, and apply target discount
        uint256 price = (term.oracle.currentPrice(id_) * term.oracleConversion).mulDivUp(
            (ONE_HUNDRED_PERCENT - term.baseDiscount),
            ONE_HUNDRED_PERCENT
        );

        // Revert if price is 0
        if (price == 0) revert Auctioneer_OraclePriceZero();

        // Calculate initial capacity based on remaining capacity and amount sold/purchased up to this point
        uint256 initialCapacity = market.capacity +
            (market.capacityInQuote ? market.purchased : market.sold);

        // Compute seconds remaining until market will conclude
        uint256 conclusion = uint256(term.conclusion);
        uint256 timeRemaining = conclusion - block.timestamp;

        // Calculate expectedCapacity as the capacity expected to be bought or sold up to this point
        // Higher than current capacity means the market is undersold, lower than current capacity means the market is oversold
        uint256 expectedCapacity = initialCapacity.mulDiv(
            timeRemaining,
            conclusion - uint256(term.start)
        );

        // Price is increased or decreased based on how far the market is ahead or behind
        // Intuition:
        // If the time neutral capacity is higher than the initial capacity, then the market is undersold and price should be discounted
        // If the time neutral capacity is lower than the initial capacity, then the market is oversold and price should be increased
        //
        // This implementation uses a linear price decay
        // P(t) = P(0) * (1 + k * (X(t) - C(t) / C(0)))
        // P(t): price at time t
        // P(0): initial/target price of the market provided by oracle + base discount (see IOSDA.MarketParams)
        // k: decay speed of the market
        // k = L / I * d, where L is the duration/length of the market, I is the deposit interval, and d is the target interval discount.
        // X(t): expected capacity of the market at time t.
        // X(t) = C(0) * t / L.
        // C(t): actual capacity of the market at time t.
        // C(0): initial capacity of the market provided by the user (see IOSDA.MarketParams).
        uint256 adjustment;
        if (expectedCapacity > market.capacity) {
            adjustment =
                ONE_HUNDRED_PERCENT +
                (term.decaySpeed * (expectedCapacity - market.capacity)) /
                initialCapacity;
        } else {
            // If actual capacity is greater than expected capacity, we need to check for underflows
            // The adjustment has a minimum value of 0 since that will reduce the price to 0 as well.
            uint256 factor = (term.decaySpeed * (market.capacity - expectedCapacity)) /
                initialCapacity;
            adjustment = ONE_HUNDRED_PERCENT > factor ? ONE_HUNDRED_PERCENT - factor : 0;
        }

        return price.mulDivUp(adjustment, ONE_HUNDRED_PERCENT);
    }

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

    /// @notice         Helper function to calculate number of price decimals based on the value returned from the price feed.
    /// @param price_   The price to calculate the number of decimals for
    /// @return         The number of decimals
    function _getPriceDecimals(uint256 price_, uint8 feedDecimals_) internal pure returns (int8) {
        int8 decimals;
        while (price_ >= 10) {
            price_ = price_ / 10;
            decimals++;
        }

        // Subtract the stated decimals from the calculated decimals to get the relative price decimals.
        // Required to do it this way vs. normalizing at the beginning since price decimals can be negative.
        return decimals - int8(feedDecimals_);
    }

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

    /// @inheritdoc IBondAuctioneer
    function getMarketInfoForPurchase(uint256 id_)
        external
        view
        override
        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,
            maxPayout(id_)
        );
    }

    /// @inheritdoc IBondOSDA
    function marketPrice(uint256 id_) public view override returns (uint256) {
        uint256 price = _currentMarketPrice(id_);

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

    /// @inheritdoc IBondAuctioneer
    function marketScale(uint256 id_) external view override returns (uint256) {
        return terms[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(terms[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 > maxPayout(id_)) {
            revert Auctioneer_MaxPayoutExceeded();
        } else {
            return payout;
        }
    }

    /// @inheritdoc IBondOSDA
    function maxPayout(uint256 id_) public view override returns (uint256) {
        // Get current price
        uint256 price = marketPrice(id_);

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

        // Convert capacity to payout token units for comparison with max payout
        uint256 capacity = market.capacityInQuote
            ? market.capacity.mulDiv(term.scale, price)
            : market.capacity;

        // Cap max payout at the remaining capacity
        return market.maxPayout > capacity ? capacity : market.maxPayout;
    }

    /// @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_];
        BondTerms memory term = terms[id_];
        uint256 price = marketPrice(id_);
        uint256 quoteCapacity = market.capacityInQuote
            ? market.capacity
            : market.capacity.mulDiv(price, term.scale);
        uint256 maxQuote = market.maxPayout.mulDiv(price, term.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_),
            ONE_HUNDRED_PERCENT
        );

        return amountAccepted + estimatedFee;
    }

    /// @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 > uint48(block.timestamp) &&
            terms[id_].start <= uint48(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;
    }
}

File 3 of 17 : IBondTeller.sol
// 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 the discount for creating bond tokens from the base protocol fee
    /// @dev             The discount is subtracted from the protocol fee to determine the fee
    ///                  when using create() to mint bond tokens without using an Auctioneer
    /// @param discount_ Create Fee Discount in basis points (3 decimal places)
    function setCreateFeeDiscount(uint48 discount_) 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 by sender in the input tokens and sends them to the provided address
    /// @param tokens_  Array of tokens to claim fees for
    /// @param to_      Address to send fees to
    function claimFees(ERC20[] memory tokens_, address to_) external;
}

File 4 of 17 : IBondFixedExpiryTeller.sol
// 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 ERC20BondToken contract corresponding to a market
    /// @param id_      ID of the market
    /// @return         ERC20BondToken contract address
    function getBondTokenForMarket(uint256 id_) external view returns (ERC20BondToken);

    /// @notice             Get the ERC20BondToken contract corresponding to an (underlying, expiry) pair, reverts if no token exists
    /// @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 (this is rounded to the nearest day)
    /// @return             ERC20BondToken contract address
    function getBondToken(ERC20 underlying_, uint48 expiry_) external view returns (ERC20BondToken);
}

File 5 of 17 : 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);
    }
}

File 6 of 17 : 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;
    }
}

File 7 of 17 : 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);
}

File 8 of 17 : IBondOSDA.sol
// 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 {IBondOracle} from "../interfaces/IBondOracle.sol";

interface IBondOSDA is IBondAuctioneer {
    /// @notice Basic token and capacity information for a 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 implement IBondCallback.
        bool capacityInQuote; // capacity limit is in payment token (true) or in payout (false, default)
        uint256 capacity; // capacity remaining
        uint256 maxPayout; // max payout tokens out in one order
        uint256 sold; // payout tokens out
        uint256 purchased; // quote tokens in
    }

    /// @notice Information pertaining to pricing and time parameters for a bond market
    struct BondTerms {
        IBondOracle oracle; // address to call for reference price. Must implement IBondOracle.
        uint48 start; // timestamp when market starts
        uint48 conclusion; // timestamp when market no longer offered
        uint48 vesting; // length of time from deposit to expiry if fixed-term, vesting timestamp if fixed-expiry
        uint48 baseDiscount; // base discount from oracle price, with 3 decimals of precision. E.g. 10_000 = 10%
        uint48 decaySpeed; // market price decay speed (discount achieved over a target deposit interval)
        uint256 minPrice; // minimum price (hard floor for the market)
        uint256 scale; // scaling factor for the market
        uint256 oracleConversion; // conversion factor for oracle -> market price
    }

    /// @notice             Parameters to create a new bond market
    /// @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. Oracle contract address, should conform to IBondOracle.
    /// @dev                    4. Base discount with 3 decimals of precision, e.g. 10_000 = 10%. Sets a base discount against the oracle price before time-related decay is applied.
    /// @dev                    5. Maximum discount from current oracle price with 3 decimals of precision, sets absolute minimum price for market
    /// @dev                    6. Target interval discount with 3 decimals of precision. The discount to be achieved over a deposit interval (in addition to base discount).
    /// @dev                    7. Is Capacity in Quote Token?
    /// @dev                    8. Capacity (amount in the decimals of the token chosen to provided capacity in).
    /// @dev                    9. Deposit interval (seconds). Desired frequency of bonds. Used to calculate max payout of market (maxPayout = duration / depositInterval * capacity)
    /// @dev                    10. 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                    11. Start Time of the Market (timestamp) - Allows starting a market in the future.
    /// @dev                        If a start time is provided, the txn must be sent prior to the start time (functions as a deadline).
    /// @dev                        If start time is not provided (i.e. 0), the market will start immediately.
    /// @dev                    12. Market Duration (seconds) - Duration of the market in seconds.
    struct MarketParams {
        ERC20 payoutToken;
        ERC20 quoteToken;
        address callbackAddr;
        IBondOracle oracle;
        uint48 baseDiscount;
        uint48 maxDiscountFromCurrent;
        uint48 targetIntervalDiscount;
        bool capacityInQuote;
        uint256 capacity;
        uint48 depositInterval;
        uint48 vesting;
        uint48 start;
        uint48 duration;
    }

    /// @notice Set the minimum market duration
    /// @notice Access controlled
    /// @param duration_ Minimum market duration in seconds
    function setMinMarketDuration(uint48 duration_) external;

    /// @notice Set the minimum deposit interval
    /// @notice Access controlled
    /// @param depositInterval_ Minimum deposit interval in seconds
    function setMinDepositInterval(uint48 depositInterval_) external;

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

    /// @notice             Calculate current market price of payout token in quote tokens
    /// @param id_          ID of market
    /// @return             Price for market in configured decimals (see MarketParams)
    /// @dev price is derived from the equation
    //
    // p(t) = max(min_p, o_p * (1 - d) * (1 + k * r(t)))
    //
    // where
    // p: price
    // min_p: minimum price
    // o_p: oracle price
    // d: base discount
    //
    // k: decay speed
    // k = l / i_d * t_d
    // where
    // l: market length
    // i_d: deposit interval
    // t_d: target interval discount
    //
    // r(t): percent difference of expected capacity and actual capacity at time t
    // r(t) = (ec(t) - c(t)) / ic
    // where
    // ec(t): expected capacity at time t (assumes capacity is expended linearly over the duration)
    // ec(t) = ic * (l - t) / l
    // c(t): capacity remaining at time t
    // ic = initial capacity
    //
    // if price is below minimum price, minimum price is returned
    function marketPrice(uint256 id_) external view override returns (uint256);

    /// @notice             Calculate max payout of the market in payout tokens
    /// @dev                Returns a dynamically calculated payout or the maximum set by the creator, whichever is less.
    /// @param id_          ID of market
    /// @return             Current max payout for the market in payout tokens
    function maxPayout(uint256 id_) external view returns (uint256);
}

File 9 of 17 : IBondOracle.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity >=0.8.0;

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

interface IBondOracle {
    /// @notice Register a new bond market on the oracle
    function registerMarket(
        uint256 id_,
        ERC20 quoteToken_,
        ERC20 payoutToken_
    ) external;

    /// @notice Returns the price as a ratio of quote tokens to base tokens for the provided market id scaled by 10^decimals
    function currentPrice(uint256 id_) external view returns (uint256);

    /// @notice Returns the price as a ratio of quote tokens to base tokens for the provided token pair scaled by 10^decimals
    function currentPrice(ERC20 quoteToken_, ERC20 payoutToken_) external view returns (uint256);

    /// @notice Returns the number of configured decimals of the price value for the provided market id
    function decimals(uint256 id_) external view returns (uint8);

    /// @notice Returns the number of configured decimals of the price value for the provided token pair
    function decimals(ERC20 quoteToken_, ERC20 payoutToken_) external view returns (uint8);
}

File 10 of 17 : IBondCallback.sol
// 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 Remove a market ID on a teller from the whitelist
    /// @dev    Shutdown function in case there's an issue with the teller
    /// @param  teller_ Address of the Teller contract which serves the market
    /// @param  id_     ID of the market to remove from whitelist
    function blacklist(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;
}

File 11 of 17 : IBondAggregator.sol
// 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
    /// @param firstIndex_  Market ID to start at
    /// @param lastIndex_   Market ID to end at (non-inclusive)
    function liveMarketsBy(
        address owner_,
        uint256 firstIndex_,
        uint256 lastIndex_
    ) 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);
}

File 12 of 17 : TransferHelper.sol
// 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))) &&
                address(token).code.length > 0,
            "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))) &&
                address(token).code.length > 0,
            "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");
    // }
}

File 13 of 17 : FullMath.sol
// 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++;
            }
        }
    }
}

File 14 of 17 : ERC20BondToken.sol
// 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() public 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);
    }
}

File 15 of 17 : IBondAuctioneer.sol
// 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
    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);
}

File 16 of 17 : CloneERC20.sol
// 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 increaseAllowance(address spender, uint256 amount) public virtual returns (bool) {
        allowance[msg.sender][spender] += amount;

        emit Approval(msg.sender, spender, allowance[msg.sender][spender]);

        return true;
    }

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

        emit Approval(msg.sender, spender, allowance[msg.sender][spender]);

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

File 17 of 17 : Clone.sol
// 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)
            )
        }
    }
}

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/",
    "solidity-code-metrics/=node_modules/solidity-code-metrics/",
    "solmate/=lib/solmate/src/",
    "weird-erc20/=lib/solmate/lib/weird-erc20/src/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 100000
  },
  "metadata": {
    "bytecodeHash": "ipfs"
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "london",
  "libraries": {}
}

Contract ABI

[{"inputs":[{"internalType":"contract IBondTeller","name":"teller_","type":"address"},{"internalType":"contract IBondAggregator","name":"aggregator_","type":"address"},{"internalType":"address","name":"guardian_","type":"address"},{"internalType":"contract Authority","name":"authority_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"Auctioneer_AmountLessThanMinimum","type":"error"},{"inputs":[],"name":"Auctioneer_BadExpiry","type":"error"},{"inputs":[],"name":"Auctioneer_InitialPriceLessThanMin","type":"error"},{"inputs":[],"name":"Auctioneer_InvalidCallback","type":"error"},{"inputs":[],"name":"Auctioneer_InvalidParams","type":"error"},{"inputs":[],"name":"Auctioneer_MarketNotActive","type":"error"},{"inputs":[],"name":"Auctioneer_MaxPayoutExceeded","type":"error"},{"inputs":[],"name":"Auctioneer_NewMarketsNotAllowed","type":"error"},{"inputs":[],"name":"Auctioneer_NotAuthorized","type":"error"},{"inputs":[],"name":"Auctioneer_NotEnoughCapacity","type":"error"},{"inputs":[],"name":"Auctioneer_OnlyMarketOwner","type":"error"},{"inputs":[],"name":"Auctioneer_OraclePriceZero","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"contract Authority","name":"newAuthority","type":"address"}],"name":"AuthorityUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"}],"name":"MarketClosed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":true,"internalType":"address","name":"payoutToken","type":"address"},{"indexed":true,"internalType":"address","name":"quoteToken","type":"address"},{"indexed":false,"internalType":"uint48","name":"vesting","type":"uint48"}],"name":"MarketCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnerUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"oldControlVariable","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newControlVariable","type":"uint256"}],"name":"Tuned","type":"event"},{"inputs":[],"name":"allowNewMarkets","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"authority","outputs":[{"internalType":"contract Authority","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"callbackAuthorized","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id_","type":"uint256"}],"name":"closeMarket","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"params_","type":"bytes"}],"name":"createMarket","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id_","type":"uint256"}],"name":"currentCapacity","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getAggregator","outputs":[{"internalType":"contract IBondAggregator","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id_","type":"uint256"}],"name":"getMarketInfoForPurchase","outputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"callbackAddr","type":"address"},{"internalType":"contract ERC20","name":"payoutToken","type":"address"},{"internalType":"contract ERC20","name":"quoteToken","type":"address"},{"internalType":"uint48","name":"vesting","type":"uint48"},{"internalType":"uint256","name":"maxPayout_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTeller","outputs":[{"internalType":"contract IBondTeller","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id_","type":"uint256"}],"name":"isInstantSwap","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id_","type":"uint256"}],"name":"isLive","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id_","type":"uint256"}],"name":"marketPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id_","type":"uint256"}],"name":"marketScale","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"markets","outputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"contract ERC20","name":"payoutToken","type":"address"},{"internalType":"contract ERC20","name":"quoteToken","type":"address"},{"internalType":"address","name":"callbackAddr","type":"address"},{"internalType":"bool","name":"capacityInQuote","type":"bool"},{"internalType":"uint256","name":"capacity","type":"uint256"},{"internalType":"uint256","name":"maxPayout","type":"uint256"},{"internalType":"uint256","name":"sold","type":"uint256"},{"internalType":"uint256","name":"purchased","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id_","type":"uint256"},{"internalType":"address","name":"referrer_","type":"address"}],"name":"maxAmountAccepted","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id_","type":"uint256"}],"name":"maxPayout","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minDepositInterval","outputs":[{"internalType":"uint48","name":"","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minMarketDuration","outputs":[{"internalType":"uint48","name":"","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"newOwners","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id_","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount_","type":"uint256"},{"internalType":"uint256","name":"id_","type":"uint256"},{"internalType":"address","name":"referrer_","type":"address"}],"name":"payoutFor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id_","type":"uint256"}],"name":"pullOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id_","type":"uint256"},{"internalType":"uint256","name":"amount_","type":"uint256"},{"internalType":"uint256","name":"minAmountOut_","type":"uint256"}],"name":"purchaseBond","outputs":[{"internalType":"uint256","name":"payout","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id_","type":"uint256"},{"internalType":"address","name":"newOwner_","type":"address"}],"name":"pushOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"status_","type":"bool"}],"name":"setAllowNewMarkets","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract Authority","name":"newAuthority","type":"address"}],"name":"setAuthority","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"creator_","type":"address"},{"internalType":"bool","name":"status_","type":"bool"}],"name":"setCallbackAuthStatus","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32[6]","name":"defaults_","type":"uint32[6]"}],"name":"setDefaults","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id_","type":"uint256"},{"internalType":"uint32[3]","name":"intervals_","type":"uint32[3]"}],"name":"setIntervals","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint48","name":"depositInterval_","type":"uint48"}],"name":"setMinDepositInterval","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint48","name":"duration_","type":"uint48"}],"name":"setMinMarketDuration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"setOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"terms","outputs":[{"internalType":"contract IBondOracle","name":"oracle","type":"address"},{"internalType":"uint48","name":"start","type":"uint48"},{"internalType":"uint48","name":"conclusion","type":"uint48"},{"internalType":"uint48","name":"vesting","type":"uint48"},{"internalType":"uint48","name":"baseDiscount","type":"uint48"},{"internalType":"uint48","name":"decaySpeed","type":"uint48"},{"internalType":"uint256","name":"minPrice","type":"uint256"},{"internalType":"uint256","name":"scale","type":"uint256"},{"internalType":"uint256","name":"oracleConversion","type":"uint256"}],"stateMutability":"view","type":"function"}]

60c06040523480156200001157600080fd5b506040516200415e3803806200415e833981016040819052620000349162000125565b600080546001600160a01b03199081166001600160a01b0385811691821784556001805490931690851617909155604051869286928692869284928492909133917f8292fce18fa69edf4db7b94ea2e58241df0ae57f97e0a6c9b29067028bf92d769190a36040516001600160a01b0382169033907fa3396fd7f6e0a21b50e5089d2da70d5ac0a3bbbd1f617a93f134b7638998019890600090a3505050506001600160a01b039081166080521660a0525050600680546001600160681b03191669015180000000000e1001179055506200018d9050565b6001600160a01b03811681146200012257600080fd5b50565b600080600080608085870312156200013c57600080fd5b845162000149816200010c565b60208601519094506200015c816200010c565b60408601519093506200016f816200010c565b606086015190925062000182816200010c565b939692955090935050565b60805160a051613f88620001d66000396000818161045f01528181610d34015281816116fd01528181611beb0152611c9f0152600081816102ae01526129060152613f886000f3fe608060405234801561001057600080fd5b50600436106102265760003560e01c8063946824cd1161012a578063bd1f3a5e116100bd578063c7bf8ca01161008c578063d2bee32311610071578063d2bee323146107ce578063e007fa97146107eb578063f61338f6146107fe57600080fd5b8063c7bf8ca01461078d578063d2040687146107a057600080fd5b8063bd1f3a5e1461061f578063bf48582b14610630578063bf7e214f14610643578063c0aa0e8a1461066357600080fd5b8063afa9d3b0116100f9578063afa9d3b0146104ff578063b1283e771461050c578063bbbdd95a146105f9578063bcf6cde81461060c57600080fd5b8063946824cd146104395780639787d1071461045d578063acc5570c14610483578063ae418095146104ec57600080fd5b806357e333ba116101bd5780636729a41e1161018c5780637a9e5e4b116101715780637a9e5e4b146103f35780638b098db3146104065780638da5cb5b1461041957600080fd5b80636729a41e146103ab578063699e17d9146103e157600080fd5b806357e333ba1461032c5780635dc4d16b1461033f5780635f77274e146103625780636352211e1461037557600080fd5b80633ad59dbc116101f95780633ad59dbc146102ac5780633adec5a7146102f357806344ee01721461030657806353c7f8e01461031957600080fd5b806310b053171461022b57806313af4035146102405780631c063a6c146102535780632750745814610289575b600080fd5b61023e610239366004613657565b610811565b005b61023e61024e3660046136a2565b6108cc565b610276610261366004613657565b60009081526002602052604090206004015490565b6040519081526020015b60405180910390f35b61029c610297366004613657565b6109d5565b6040519015158152602001610280565b7f00000000000000000000000000000000000000000000000000000000000000005b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610280565b610276610301366004613657565b610a6e565b61023e6103143660046136de565b610ab5565b6102766103273660046136fb565b610bef565b61027661033a366004613657565b610ddf565b61029c61034d3660046136a2565b60056020526000908152604090205460ff1681565b61023e610370366004613786565b611167565b6102ce610383366004613657565b60009081526002602052604090205473ffffffffffffffffffffffffffffffffffffffff1690565b6102ce6103b9366004613657565b60046020526000908152604090205473ffffffffffffffffffffffffffffffffffffffff1681565b61023e6103ef3660046137a3565b5050565b61023e6104013660046136a2565b61122c565b61029c610414366004613657565b611389565b6000546102ce9073ffffffffffffffffffffffffffffffffffffffff1681565b610276610447366004613657565b6000908152600360208190526040909120015490565b7f00000000000000000000000000000000000000000000000000000000000000006102ce565b610496610491366004613657565b6113bf565b6040805173ffffffffffffffffffffffffffffffffffffffff97881681529587166020870152938616938501939093529316606083015265ffffffffffff909216608082015260a081019190915260c001610280565b61023e6104fa366004613657565b6114af565b60065461029c9060ff1681565b61059461051a366004613657565b60026020819052600091825260409091208054600182015492820154600383015460048401546005850154600686015460079096015473ffffffffffffffffffffffffffffffffffffffff95861697861696948616958416947401000000000000000000000000000000000000000090940460ff16939089565b6040805173ffffffffffffffffffffffffffffffffffffffff9a8b168152988a1660208a015296891696880196909652969093166060860152901515608085015260a084015260c083015260e082019290925261010081019190915261012001610280565b61023e6106073660046137d6565b611515565b61023e61061a36600461380f565b6115ff565b61023e61062d36600461388d565b50565b61027661063e36600461391e565b6116af565b6001546102ce9073ffffffffffffffffffffffffffffffffffffffff1681565b61071e610671366004613657565b60036020819052600091825260409091208054600182015460028301549383015460049093015473ffffffffffffffffffffffffffffffffffffffff83169465ffffffffffff7401000000000000000000000000000000000000000085048116957a01000000000000000000000000000000000000000000000000000090950481169484821694660100000000000081048316946c01000000000000000000000000909104909216929189565b6040805173ffffffffffffffffffffffffffffffffffffffff909a168a5265ffffffffffff98891660208b015296881696890196909652938616606088015291851660808701529390931660a085015260c084019290925260e083019190915261010082015261012001610280565b61027661079b36600461380f565b6117fe565b6006546107b790610100900465ffffffffffff1681565b60405165ffffffffffff9091168152602001610280565b6006546107b790670100000000000000900465ffffffffffff1681565b6102766107f9366004613957565b611c85565b61023e61080c3660046136de565b611fec565b60008181526004602052604090205473ffffffffffffffffffffffffffffffffffffffff16331461086e576040517f2c47703200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600090815260046020908152604080832054600290925290912080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff909216919091179055565b6108fa336000357fffffffff0000000000000000000000000000000000000000000000000000000016612125565b610965576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f554e415554484f52495a4544000000000000000000000000000000000000000060448201526064015b60405180910390fd5b600080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83169081178255604051909133917f8292fce18fa69edf4db7b94ea2e58241df0ae57f97e0a6c9b29067028bf92d769190a350565b60008181526002602052604081206004015415801590610a2b575060008281526003602052604090205465ffffffffffff4281167a01000000000000000000000000000000000000000000000000000090920416115b8015610a68575060008281526003602052604090205465ffffffffffff428116740100000000000000000000000000000000000000009092041611155b92915050565b600080610a7a83612236565b6000848152600360205260409020600201549091508111610aac57600083815260036020526040902060020154610aae565b805b9392505050565b610ae3336000357fffffffff0000000000000000000000000000000000000000000000000000000016612125565b610b49576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f554e415554484f52495a45440000000000000000000000000000000000000000604482015260640161095c565b60065465ffffffffffff61010090910481169082161080610b745750620151808165ffffffffffff16105b15610bab576040517f3b596f5f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6006805465ffffffffffff909216670100000000000000027fffffffffffffffffffffffffffffffffffffff000000000000ffffffffffffff909216919091179055565b600080610bfe83850185613983565b905062015180816101400151610c149190613ad8565b610c219062015180613afd565b65ffffffffffff9081166101408301526101608201516000911615610c4b57816101600151610c4d565b425b9050600082610180015182610c629190613b2b565b905082610140015165ffffffffffff16600014158015610c9657508065ffffffffffff1683610140015165ffffffffffff16105b15610ccd576040517f3b596f5f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000610cd8846127a0565b905083610140015165ffffffffffff16600014610dd55783516101408501516040517fc6e38a4b00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000169263c6e38a4b92610d909260040173ffffffffffffffffffffffffffffffffffffffff92909216825265ffffffffffff16602082015260400190565b6020604051808303816000875af1158015610daf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610dd39190613b55565b505b9695505050505050565b600080610deb83610a6e565b9050600060026000858152602001908152602001600020604051806101200160405290816000820160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020016001820160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020016002820160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020016003820160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020016003820160149054906101000a900460ff161515151581526020016004820154815260200160058201548152602001600682015481526020016007820154815250509050600060036000868152602001908152602001600020604051806101200160405290816000820160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020016000820160149054906101000a900465ffffffffffff1665ffffffffffff1665ffffffffffff16815260200160008201601a9054906101000a900465ffffffffffff1665ffffffffffff1665ffffffffffff1681526020016001820160009054906101000a900465ffffffffffff1665ffffffffffff1665ffffffffffff1681526020016001820160069054906101000a900465ffffffffffff1665ffffffffffff1665ffffffffffff16815260200160018201600c9054906101000a900465ffffffffffff1665ffffffffffff1665ffffffffffff1681526020016002820154815260200160038201548152602001600482015481525050905060008260800151611133578260a00151611147565b60e082015160a08401516111479186612f2b565b9050808360c001511161115e578260c00151610dd5565b95945050505050565b611195336000357fffffffff0000000000000000000000000000000000000000000000000000000016612125565b6111fb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f554e415554484f52495a45440000000000000000000000000000000000000000604482015260640161095c565b600680547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016911515919091179055565b60005473ffffffffffffffffffffffffffffffffffffffff1633148061130f57506001546040517fb70096130000000000000000000000000000000000000000000000000000000081523360048201523060248201526000357fffffffff0000000000000000000000000000000000000000000000000000000016604482015273ffffffffffffffffffffffffffffffffffffffff9091169063b700961390606401602060405180830381865afa1580156112eb573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061130f9190613b72565b61131857600080fd5b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff831690811790915560405133907fa3396fd7f6e0a21b50e5089d2da70d5ac0a3bbbd1f617a93f134b7638998019890600090a350565b60008181526003602052604081206001015465ffffffffffff16635dba24008111156113b85742811115610aae565b1592915050565b6000818152600260208181526040808420815161012081018352815473ffffffffffffffffffffffffffffffffffffffff9081168083526001808501548316848801819052978501548316848701819052600380870154948516606087018190527401000000000000000000000000000000000000000090950460ff1615156080870152600487015460a0870152600587015460c0870152600687015460e08701526007909601546101008601528a8a529490965293872090940154869586958695869586959491939165ffffffffffff1661149a8d610ddf565b949d939c50919a509850965090945092505050565b60008181526002602052604090205473ffffffffffffffffffffffffffffffffffffffff16331461150c576040517f4e1c8b5d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61062d81612ff7565b611543336000357fffffffff0000000000000000000000000000000000000000000000000000000016612125565b6115a9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f554e415554484f52495a45440000000000000000000000000000000000000000604482015260640161095c565b73ffffffffffffffffffffffffffffffffffffffff91909116600090815260056020526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016911515919091179055565b60008281526002602052604090205473ffffffffffffffffffffffffffffffffffffffff16331461165c576040517f4e1c8b5d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60009182526004602052604090912080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff909216919091179055565b6040517fb88c914800000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8281166004830152600091829161177d917f00000000000000000000000000000000000000000000000000000000000000009091169063b88c914890602401602060405180830381865afa158015611746573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061176a9190613b8f565b869065ffffffffffff16620186a0612f2b565b600085815260036020819052604082200154919250906117b1906117a087610a6e565b6117aa858a613bac565b9190612f2b565b90506117bc85610ddf565b8111156117f5576040517f5c430eae00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b9150610aae9050565b60008060026000858152602001908152602001600020604051806101200160405290816000820160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020016001820160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020016002820160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020016003820160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020016003820160149054906101000a900460ff161515151581526020016004820154815260200160058201548152602001600682015481526020016007820154815250509050600060036000868152602001908152602001600020604051806101200160405290816000820160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020016000820160149054906101000a900465ffffffffffff1665ffffffffffff1665ffffffffffff16815260200160008201601a9054906101000a900465ffffffffffff1665ffffffffffff1665ffffffffffff1681526020016001820160009054906101000a900465ffffffffffff1665ffffffffffff1665ffffffffffff1681526020016001820160069054906101000a900465ffffffffffff1665ffffffffffff1665ffffffffffff16815260200160018201600c9054906101000a900465ffffffffffff1665ffffffffffff1665ffffffffffff168152602001600282015481526020016003820154815260200160048201548152505090506000611b3b86610a6e565b905060008360800151611b625760e083015160a0850151611b5d918490612f2b565b611b68565b8360a001515b90506000611b89838560e001518760c00151612f2b9092919063ffffffff16565b90506000818310611b9a5781611b9c565b825b6040517fb88c914800000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8a81166004830152919250600091611c6b917f00000000000000000000000000000000000000000000000000000000000000009091169063b88c914890602401602060405180830381865afa158015611c34573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c589190613b8f565b839065ffffffffffff16620186a0612f2b565b9050611c778183613bc3565b9a9950505050505050505050565b60003373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614611cf6576040517f2c47703200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000848152600260208181526040808420600380845294829020825161012081018452815473ffffffffffffffffffffffffffffffffffffffff808216835265ffffffffffff7401000000000000000000000000000000000000000083048116978401979097527a0100000000000000000000000000000000000000000000000000009091048616948201949094526001820154808616606083015266010000000000008104861660808301526c01000000000000000000000000900490941660a08501529384015460c08401528385015460e084015260049093015461010083015292820154919290911615801590611e175750815473ffffffffffffffffffffffffffffffffffffffff1660009081526005602052604090205460ff16155b15611e4e576040517f2c47703200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611e57866109d5565b611e8d576040517fa24c407400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000611e9887610a6e565b60e0830151909150611eac90879083612f2b565b935084841015611ee8576040517f74ec9d5b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8260050154841115611f26576040517f5c430eae00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600383015474010000000000000000000000000000000000000000900460ff16611f565782600401548411611f5e565b826004015486115b15611f95576040517ff3383dc900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600383015474010000000000000000000000000000000000000000900460ff16611fbf5783611fc1565b855b6004840180549190910390555050600781018054909401909355600690920180548301905550919050565b61201a336000357fffffffff0000000000000000000000000000000000000000000000000000000016612125565b612080576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f554e415554484f52495a45440000000000000000000000000000000000000000604482015260640161095c565b60065465ffffffffffff670100000000000000909104811690821611806120b05750610e108165ffffffffffff16105b156120e7576040517f3b596f5f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6006805465ffffffffffff909216610100027fffffffffffffffffffffffffffffffffffffffffffffffffff000000000000ff909216919091179055565b60015460009073ffffffffffffffffffffffffffffffffffffffff16801580159061220957506040517fb700961300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85811660048301523060248301527fffffffff000000000000000000000000000000000000000000000000000000008516604483015282169063b700961390606401602060405180830381865afa1580156121e5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122099190613b72565b8061222e575060005473ffffffffffffffffffffffffffffffffffffffff8581169116145b949350505050565b60008060026000848152602001908152602001600020604051806101200160405290816000820160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020016001820160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020016002820160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020016003820160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020016003820160149054906101000a900460ff161515151581526020016004820154815260200160058201548152602001600682015481526020016007820154815250509050600060036000858152602001908152602001600020604051806101200160405290816000820160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020016000820160149054906101000a900465ffffffffffff1665ffffffffffff1665ffffffffffff16815260200160008201601a9054906101000a900465ffffffffffff1665ffffffffffff1665ffffffffffff1681526020016001820160009054906101000a900465ffffffffffff1665ffffffffffff1665ffffffffffff1681526020016001820160069054906101000a900465ffffffffffff1665ffffffffffff1665ffffffffffff16815260200160018201600c9054906101000a900465ffffffffffff1665ffffffffffff1665ffffffffffff1681526020016002820154815260200160038201548152602001600482015481525050905060006126398260800151620186a06125809190613bdb565b61010084015184516040517f7a3c4c17000000000000000000000000000000000000000000000000000000008152600481018a905265ffffffffffff9390931692620186a0929173ffffffffffffffffffffffffffffffffffffffff1690637a3c4c1790602401602060405180830381865afa158015612604573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126289190613c02565b6126329190613c1b565b9190613086565b905080600003612675576040517fe67b3f4400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000836080015161268a578360e00151612691565b8361010001515b8460a001516126a09190613bc3565b604084015190915065ffffffffffff1660006126bc4283613bac565b905060006126e482876020015165ffffffffffff16856126dc9190613bac565b869190612f2b565b905060008760a0015182111561273a57848860a00151836127059190613bac565b8860a0015165ffffffffffff1661271c9190613c1b565b6127269190613c58565b61273390620186a0613bc3565b9050612792565b600085838a60a0015161274d9190613bac565b8960a0015165ffffffffffff166127649190613c1b565b61276e9190613c58565b9050620186a0811061278157600061278e565b61278e81620186a0613bac565b9150505b611c778682620186a0613086565b60065460009060ff166127df576040517f64be3ffa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3360009081526005602052604090205460ff161580156128185750604082015173ffffffffffffffffffffffffffffffffffffffff1615155b1561284f576040517f2c47703200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600082610160015165ffffffffffff1611801561287857504282610160015165ffffffffffff16105b156128af576040517f3b596f5f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b815160208301516040517fb435914300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff928316600482015290821660248201526000917f0000000000000000000000000000000000000000000000000000000000000000169063b4359143906044016020604051808303816000875af115801561294f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129739190613c02565b600081815260026020818152604080842080547fffffffffffffffffffffffff00000000000000000000000000000000000000009081163317825589840151948201805473ffffffffffffffffffffffffffffffffffffffff968716908316179055895160018301805491871691909216179055888201516003808301805460e08d0151151574010000000000000000000000000000000000000000027fffffffffffffffffffffff000000000000000000000000000000000000000000909116939097169290921795909517905561010089015160048201559290915290912060808601519293509091620186a065ffffffffffff91909116101580612a9157508460a0015165ffffffffffff16856080015165ffffffffffff16115b15612ac8576040517f3b596f5f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60808501516001820180547fffffffffffffffffffffffffffffffffffffffff000000000000ffffffffffff16660100000000000065ffffffffffff84160217905560608601516020870151875160009384938493612b28938a936130e4565b60608b015187547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff909116178755600487018290556003870181905560a08b01519295509093509150620186a065ffffffffffff9091161115612bcd576040517f3b596f5f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612bf68860a00151620186a0612be39190613bdb565b849065ffffffffffff16620186a0613086565b600285015560065461018089015165ffffffffffff670100000000000000909204821691161080612c3e575060065461012089015165ffffffffffff61010090920482169116105b80612c62575087610180015165ffffffffffff1688610120015165ffffffffffff16115b15612c99576040517f3b596f5f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008860e00151612caf57886101000151612ce9565b612ce982612cdc8b60800151620186a0612cc99190613bdb565b879065ffffffffffff16620186a0613086565b6101008c01519190612f2b565b9050612d1a89610120015165ffffffffffff168a610180015165ffffffffffff1683612f2b9092919063ffffffff16565b600587015560c0890151620186a065ffffffffffff9091161115612d6a576040517f3b596f5f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8861012001518960c001518a6101800151612d859190613afd565b612d8f9190613ad8565b6001860180546101408c01517fffffffffffffffffffffffffffff000000000000ffffffffffff0000000000009091166c0100000000000000000000000065ffffffffffff948516027fffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000016179083161790556101608a01516000911615612e1b57896101600151612e1d565b425b86547fffffffffffff000000000000ffffffffffffffffffffffffffffffffffffffff167401000000000000000000000000000000000000000065ffffffffffff8316021787556101808b0151909150612e779082613b2b565b865479ffffffffffffffffffffffffffffffffffffffffffffffffffff167a01000000000000000000000000000000000000000000000000000065ffffffffffff9283160217875560208b8101518c516101408e01516040519416845273ffffffffffffffffffffffffffffffffffffffff918216939116918b917f9cc08aa705173351ee44462d57949faf65814067be71f3271ec1f5636f9efa2d910160405180910390a4509598975050505050505050565b600080807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff85870985870292508281108382030391505080600003612f825760008411612f7757600080fd5b508290049050610aae565b808411612f8e57600080fd5b60008486880960026001871981018816978890046003810283188082028403028082028403028082028403028082028403028082028403029081029092039091026000889003889004909101858311909403939093029303949094049190911702949350505050565b6000818152600360209081526040808320805479ffffffffffffffffffffffffffffffffffffffffffffffffffff167a0100000000000000000000000000000000000000000000000000004265ffffffffffff160217905560029091528082206004018290555182917f9dc30b8eda31a6a144e092e5de600955523a6a925cc15cc1d1b9b4872cfa615591a250565b6000613093848484612f2b565b9050600082806130a5576130a5613a7a565b8486091115610aae577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81106130da57600080fd5b6001019392505050565b6000806000808573ffffffffffffffffffffffffffffffffffffffff1663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015613135573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131599190613c6c565b905060008773ffffffffffffffffffffffffffffffffffffffff1663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa1580156131a8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131cc9190613c6c565b905060068260ff1610806131e3575060128260ff16115b1561321a576040517f3b596f5f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60068160ff16108061322f575060128160ff16115b15613266576040517f3b596f5f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8916158061329e575073ffffffffffffffffffffffffffffffffffffffff89163b155b156132d5576040517f3b596f5f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f5538adb7000000000000000000000000000000000000000000000000000000008152600481018b905273ffffffffffffffffffffffffffffffffffffffff898116602483015288811660448301528a1690635538adb790606401600060405180830381600087803b15801561334d57600080fd5b505af1158015613361573d6000803e3d6000fd5b50506040517f7a3c4c17000000000000000000000000000000000000000000000000000000008152600481018d90526000925073ffffffffffffffffffffffffffffffffffffffff8c169150637a3c4c1790602401602060405180830381865afa1580156133d3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133f79190613c02565b905080600003613433576040517fe67b3f4400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f3f47e662000000000000000000000000000000000000000000000000000000008152600481018c905260009073ffffffffffffffffffffffffffffffffffffffff8c1690633f47e66290602401602060405180830381865afa1580156134a1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906134c59190613c6c565b905060068160ff1610806134dc575060128160ff16115b15613513576040517f3b596f5f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600061354161353b6135288b620186a0613bdb565b859065ffffffffffff16620186a0613086565b83613622565b905061354c81613c8f565b60000b8260000b131580613563575060188160000b135b1561359a576040517f3b596f5f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000826135a8600284613ccd565b6135b3906024613d41565b6135bd9190613d41565b6135c890600a613ec3565b905060006135d7600284613ccd565b866135e3896024613ed2565b6135ed9190613d41565b6135f79190613d41565b61360290600a613ec3565b905061360e8286613c1b565b9f919e509c509a5050505050505050505050565b6000805b600a841061364d57613639600a85613c58565b93508061364581613f33565b915050613626565b61222e8382613d41565b60006020828403121561366957600080fd5b5035919050565b73ffffffffffffffffffffffffffffffffffffffff8116811461062d57600080fd5b803561369d81613670565b919050565b6000602082840312156136b457600080fd5b8135610aae81613670565b65ffffffffffff8116811461062d57600080fd5b803561369d816136bf565b6000602082840312156136f057600080fd5b8135610aae816136bf565b6000806020838503121561370e57600080fd5b823567ffffffffffffffff8082111561372657600080fd5b818501915085601f83011261373a57600080fd5b81358181111561374957600080fd5b86602082850101111561375b57600080fd5b60209290920196919550909350505050565b801515811461062d57600080fd5b803561369d8161376d565b60006020828403121561379857600080fd5b8135610aae8161376d565b600080608083850312156137b657600080fd5b82359150836080840111156137ca57600080fd5b50926020919091019150565b600080604083850312156137e957600080fd5b82356137f481613670565b915060208301356138048161376d565b809150509250929050565b6000806040838503121561382257600080fd5b82359150602083013561380481613670565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040516101a0810167ffffffffffffffff8111828210171561388757613887613834565b60405290565b600060c0828403121561389f57600080fd5b82601f8301126138ae57600080fd5b60405160c0810181811067ffffffffffffffff821117156138d1576138d1613834565b6040528060c08401858111156138e657600080fd5b845b8181101561391357803563ffffffff811681146139055760008081fd5b8352602092830192016138e8565b509195945050505050565b60008060006060848603121561393357600080fd5b8335925060208401359150604084013561394c81613670565b809150509250925092565b60008060006060848603121561396c57600080fd5b505081359360208301359350604090920135919050565b60006101a0828403121561399657600080fd5b61399e613863565b6139a783613692565b81526139b560208401613692565b60208201526139c660408401613692565b60408201526139d760608401613692565b60608201526139e8608084016136d3565b60808201526139f960a084016136d3565b60a0820152613a0a60c084016136d3565b60c0820152613a1b60e0840161377b565b60e08201526101008381013590820152610120613a398185016136d3565b90820152610140613a4b8482016136d3565b90820152610160613a5d8482016136d3565b90820152610180613a6f8482016136d3565b908201529392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600065ffffffffffff80841680613af157613af1613a7a565b92169190910492915050565b600065ffffffffffff80831681851681830481118215151615613b2257613b22613aa9565b02949350505050565b600065ffffffffffff808316818516808303821115613b4c57613b4c613aa9565b01949350505050565b600060208284031215613b6757600080fd5b8151610aae81613670565b600060208284031215613b8457600080fd5b8151610aae8161376d565b600060208284031215613ba157600080fd5b8151610aae816136bf565b600082821015613bbe57613bbe613aa9565b500390565b60008219821115613bd657613bd6613aa9565b500190565b600065ffffffffffff83811690831681811015613bfa57613bfa613aa9565b039392505050565b600060208284031215613c1457600080fd5b5051919050565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0483118215151615613c5357613c53613aa9565b500290565b600082613c6757613c67613a7a565b500490565b600060208284031215613c7e57600080fd5b815160ff81168114610aae57600080fd5b60008160000b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff808103613cc457613cc4613aa9565b60000392915050565b60008160000b8360000b80613ce457613ce4613a7a565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81147fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8083141615613d3857613d38613aa9565b90059392505050565b60008160000b8360000b60008112817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8001831281151615613d8457613d84613aa9565b81607f018313811615613d9957613d99613aa9565b5090039392505050565b600181815b80851115613dfc57817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04821115613de257613de2613aa9565b80851615613def57918102915b93841c9390800290613da8565b509250929050565b600082613e1357506001610a68565b81613e2057506000610a68565b8160018114613e365760028114613e4057613e5c565b6001915050610a68565b60ff841115613e5157613e51613aa9565b50506001821b610a68565b5060208310610133831016604e8410600b8410161715613e7f575081810a610a68565b613e898383613da3565b807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04821115613ebb57613ebb613aa9565b029392505050565b6000610aae60ff841683613e04565b60008160000b8360000b6000821282607f03821381151615613ef657613ef6613aa9565b827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80038212811615613f2a57613f2a613aa9565b50019392505050565b60008160000b607f8103613f4957613f49613aa9565b6001019291505056fea26469706673582212205a0e041088a99d6031c9ad8158727d96984382ff94c1bfd6d80623a3066aafba64736f6c634300080f0033000000000000000000000000007fe70dc9797c4198528ae43d8195fff82bdc95000000000000000000000000007a66a2a13415db3613c1a4dd1c942a285902d1000000000000000000000000007bd11fca0daaeadd455b51826f9a015f2f0969000000000000000000000000007a0f48a4e3d74ab4234adf9ea9eb32f87b4b14

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

000000000000000000000000007fe70dc9797c4198528ae43d8195fff82bdc95000000000000000000000000007a66a2a13415db3613c1a4dd1c942a285902d1000000000000000000000000007bd11fca0daaeadd455b51826f9a015f2f0969000000000000000000000000007a0f48a4e3d74ab4234adf9ea9eb32f87b4b14

-----Decoded View---------------
Arg [0] : teller_ (address): 0x007fe70dc9797c4198528ae43d8195fff82bdc95
Arg [1] : aggregator_ (address): 0x007a66a2a13415db3613c1a4dd1c942a285902d1
Arg [2] : guardian_ (address): 0x007bd11fca0daaeadd455b51826f9a015f2f0969
Arg [3] : authority_ (address): 0x007a0f48a4e3d74ab4234adf9ea9eb32f87b4b14

-----Encoded View---------------
4 Constructor Arguments found :
Arg [0] : 000000000000000000000000007fe70dc9797c4198528ae43d8195fff82bdc95
Arg [1] : 000000000000000000000000007a66a2a13415db3613c1a4dd1c942a285902d1
Arg [2] : 000000000000000000000000007bd11fca0daaeadd455b51826f9a015f2f0969
Arg [3] : 000000000000000000000000007a0f48a4e3d74ab4234adf9ea9eb32f87b4b14


Block Transaction Gas Used Reward
Age Block Fee Address BC Fee Address Voting Power Jailed Incoming
Block Uncle Number Difficulty Gas Used Reward
Loading