Warning! Contract bytecode has been changed and doesn't match the verified one. Therefore, interaction with this smart contract may be risky.
This contract has been verified via Sourcify.
View contract in Sourcify repository
- Contract name:
- BondFixedExpirySDA
- Optimization enabled
- true
- Compiler version
- v0.8.15+commit.e14f2714
- Optimization runs
- 100000
- EVM Version
- london
- Verified at
- 2026-04-22T03:12:39.234854Z
Constructor Arguments
000000000000000000000000007fe7c498a2cf30971ad8f2cbc36bd14ac51156000000000000000000000000007a66b9e719b3abb2f3917eb47d4231a17f5a0d000000000000000000000000007bd11fca0daaeadd455b51826f9a015f2f0969000000000000000000000000007a0f3b057945db86408197daa7c04373b5a94a
Arg [0] (address) : 0x007fe7c498a2cf30971ad8f2cbc36bd14ac51156
Arg [1] (address) : 0x007a66b9e719b3abb2f3917eb47d4231a17f5a0d
Arg [2] (address) : 0x007bd11fca0daaeadd455b51826f9a015f2f0969
Arg [3] (address) : 0x007a0f3b057945db86408197daa7c04373b5a94a
src/BondFixedExpirySDA.sol
// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity 0.8.15;
import {BondBaseSDA, IBondAggregator, Authority} from "./bases/BondBaseSDA.sol";
import {IBondTeller} from "./interfaces/IBondTeller.sol";
import {IBondFixedExpiryTeller} from "./interfaces/IBondFixedExpiryTeller.sol";
/// @title Bond Fixed-Expiry Sequential Dutch Auctioneer
/// @notice Bond Fixed-Expiry Sequential Dutch Auctioneer Contract
/// @dev Bond Protocol is a permissionless system to create Olympus-style bond markets
/// for any token pair. The markets do not require maintenance and will manage
/// bond prices based on activity. Bond issuers create BondMarkets that pay out
/// a Payout Token in exchange for deposited Quote Tokens. Users can purchase
/// future-dated Payout Tokens with Quote Tokens at the current market price and
/// receive Bond Tokens to represent their position while their bond vests.
/// Once the Bond Tokens vest, they can redeem it for the Quote Tokens.
///
/// @dev The Fixed-Expiry Auctioneer is an implementation of the
/// Bond Base Sequential Dutch Auctioneer contract specific to creating bond markets where
/// all purchases on that market vest at a certain timestamp.
///
/// @author Oighty, Zeus, Potted Meat, indigo
contract BondFixedExpirySDA is BondBaseSDA {
/* ========== CONSTRUCTOR ========== */
constructor(
IBondTeller teller_,
IBondAggregator aggregator_,
address guardian_,
Authority authority_
) BondBaseSDA(teller_, aggregator_, guardian_, authority_) {}
/// @inheritdoc BondBaseSDA
function createMarket(bytes calldata params_) external override returns (uint256) {
MarketParams memory params = abi.decode(params_, (MarketParams));
uint256 marketId = _createMarket(params);
// Create bond token (ERC20 for fixed expiry)
IBondFixedExpiryTeller(address(_teller)).deploy(params.payoutToken, params.vesting);
return marketId;
}
}
/
// SPDX-License-Identifier: BSD
pragma solidity ^0.8.4;
/// @title Clone
/// @author zefram.eth
/// @notice Provides helper functions for reading immutable args from calldata
contract Clone {
/// @notice Reads an immutable arg with type address
/// @param argOffset The offset of the arg in the packed data
/// @return arg The arg value
function _getArgAddress(uint256 argOffset)
internal
pure
returns (address arg)
{
uint256 offset = _getImmutableArgsOffset();
assembly {
arg := shr(0x60, calldataload(add(offset, argOffset)))
}
}
/// @notice Reads an immutable arg with type uint256
/// @param argOffset The offset of the arg in the packed data
/// @return arg The arg value
function _getArgUint256(uint256 argOffset)
internal
pure
returns (uint256 arg)
{
uint256 offset = _getImmutableArgsOffset();
// solhint-disable-next-line no-inline-assembly
assembly {
arg := calldataload(add(offset, argOffset))
}
}
/// @notice Reads an immutable arg with type uint64
/// @param argOffset The offset of the arg in the packed data
/// @return arg The arg value
function _getArgUint64(uint256 argOffset)
internal
pure
returns (uint64 arg)
{
uint256 offset = _getImmutableArgsOffset();
// solhint-disable-next-line no-inline-assembly
assembly {
arg := shr(0xc0, calldataload(add(offset, argOffset)))
}
}
/// @notice Reads an immutable arg with type uint8
/// @param argOffset The offset of the arg in the packed data
/// @return arg The arg value
function _getArgUint8(uint256 argOffset) internal pure returns (uint8 arg) {
uint256 offset = _getImmutableArgsOffset();
// solhint-disable-next-line no-inline-assembly
assembly {
arg := shr(0xf8, calldataload(add(offset, argOffset)))
}
}
/// @return offset The offset of the packed immutable args in calldata
function _getImmutableArgsOffset() internal pure returns (uint256 offset) {
// solhint-disable-next-line no-inline-assembly
assembly {
offset := sub(
calldatasize(),
add(shr(240, calldataload(sub(calldatasize(), 2))), 2)
)
}
}
}
/ERC20.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
/// @notice Modern and gas efficient ERC20 + EIP-2612 implementation.
/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC20.sol)
/// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)
/// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.
abstract contract ERC20 {
/*///////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/
event Transfer(address indexed from, address indexed to, uint256 amount);
event Approval(address indexed owner, address indexed spender, uint256 amount);
/*///////////////////////////////////////////////////////////////
METADATA STORAGE
//////////////////////////////////////////////////////////////*/
string public name;
string public symbol;
uint8 public immutable decimals;
/*///////////////////////////////////////////////////////////////
ERC20 STORAGE
//////////////////////////////////////////////////////////////*/
uint256 public totalSupply;
mapping(address => uint256) public balanceOf;
mapping(address => mapping(address => uint256)) public allowance;
/*///////////////////////////////////////////////////////////////
EIP-2612 STORAGE
//////////////////////////////////////////////////////////////*/
bytes32 public constant PERMIT_TYPEHASH =
keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
uint256 internal immutable INITIAL_CHAIN_ID;
bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;
mapping(address => uint256) public nonces;
/*///////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////*/
constructor(
string memory _name,
string memory _symbol,
uint8 _decimals
) {
name = _name;
symbol = _symbol;
decimals = _decimals;
INITIAL_CHAIN_ID = block.chainid;
INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
}
/*///////////////////////////////////////////////////////////////
ERC20 LOGIC
//////////////////////////////////////////////////////////////*/
function approve(address spender, uint256 amount) public virtual returns (bool) {
allowance[msg.sender][spender] = amount;
emit Approval(msg.sender, spender, amount);
return true;
}
function transfer(address to, uint256 amount) public virtual returns (bool) {
balanceOf[msg.sender] -= amount;
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
balanceOf[to] += amount;
}
emit Transfer(msg.sender, to, amount);
return true;
}
function transferFrom(
address from,
address to,
uint256 amount
) public virtual returns (bool) {
uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.
if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;
balanceOf[from] -= amount;
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
balanceOf[to] += amount;
}
emit Transfer(from, to, amount);
return true;
}
/*///////////////////////////////////////////////////////////////
EIP-2612 LOGIC
//////////////////////////////////////////////////////////////*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) public virtual {
require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");
// Unchecked because the only math done is incrementing
// the owner's nonce which cannot realistically overflow.
unchecked {
bytes32 digest = keccak256(
abi.encodePacked(
"\x19\x01",
DOMAIN_SEPARATOR(),
keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, nonces[owner]++, deadline))
)
);
address recoveredAddress = ecrecover(digest, v, r, s);
require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");
allowance[recoveredAddress][spender] = value;
}
emit Approval(owner, spender, value);
}
function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
}
function computeDomainSeparator() internal view virtual returns (bytes32) {
return
keccak256(
abi.encode(
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
keccak256(bytes(name)),
keccak256("1"),
block.chainid,
address(this)
)
);
}
/*///////////////////////////////////////////////////////////////
INTERNAL MINT/BURN LOGIC
//////////////////////////////////////////////////////////////*/
function _mint(address to, uint256 amount) internal virtual {
totalSupply += amount;
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
balanceOf[to] += amount;
}
emit Transfer(address(0), to, amount);
}
function _burn(address from, uint256 amount) internal virtual {
balanceOf[from] -= amount;
// Cannot underflow because a user's balance
// will never be larger than the total supply.
unchecked {
totalSupply -= amount;
}
emit Transfer(from, address(0), amount);
}
}
/
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
import {Clone} from "clones/Clone.sol";
/// @notice Modern and gas efficient ERC20 implementation.
/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC20.sol)
/// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)
/// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.
abstract contract CloneERC20 is Clone {
/*///////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/
event Transfer(address indexed from, address indexed to, uint256 amount);
event Approval(address indexed owner, address indexed spender, uint256 amount);
/*///////////////////////////////////////////////////////////////
ERC20 STORAGE
//////////////////////////////////////////////////////////////*/
uint256 public totalSupply;
mapping(address => uint256) public balanceOf;
mapping(address => mapping(address => uint256)) public allowance;
/*///////////////////////////////////////////////////////////////
METADATA
//////////////////////////////////////////////////////////////*/
function name() external pure returns (string memory) {
return string(abi.encodePacked(_getArgUint256(0)));
}
function symbol() external pure returns (string memory) {
return string(abi.encodePacked(_getArgUint256(0x20)));
}
function decimals() external pure returns (uint8) {
return _getArgUint8(0x40);
}
/*///////////////////////////////////////////////////////////////
ERC20 LOGIC
//////////////////////////////////////////////////////////////*/
function approve(address spender, uint256 amount) public virtual returns (bool) {
allowance[msg.sender][spender] = amount;
emit Approval(msg.sender, spender, amount);
return true;
}
function transfer(address to, uint256 amount) public virtual returns (bool) {
balanceOf[msg.sender] -= amount;
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
balanceOf[to] += amount;
}
emit Transfer(msg.sender, to, amount);
return true;
}
function transferFrom(
address from,
address to,
uint256 amount
) public virtual returns (bool) {
uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.
if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;
balanceOf[from] -= amount;
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
balanceOf[to] += amount;
}
emit Transfer(from, to, amount);
return true;
}
/*///////////////////////////////////////////////////////////////
INTERNAL LOGIC
//////////////////////////////////////////////////////////////*/
function _mint(address to, uint256 amount) internal virtual {
totalSupply += amount;
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
balanceOf[to] += amount;
}
emit Transfer(address(0), to, amount);
}
function _burn(address from, uint256 amount) internal virtual {
balanceOf[from] -= amount;
// Cannot underflow because a user's balance
// will never be larger than the total supply.
unchecked {
totalSupply -= amount;
}
emit Transfer(from, address(0), amount);
}
function _getImmutableVariablesOffset() internal pure returns (uint256 offset) {
assembly {
offset := sub(calldatasize(), add(shr(240, calldataload(sub(calldatasize(), 2))), 2))
}
}
}
/
// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity 0.8.15;
import {ERC20} from "solmate/tokens/ERC20.sol";
import {CloneERC20} from "./lib/CloneERC20.sol";
/// @title ERC20 Bond Token
/// @notice ERC20 Bond Token Contract
/// @dev Bond Protocol is a permissionless system to create Olympus-style bond markets
/// for any token pair. The markets do not require maintenance and will manage
/// bond prices based on activity. Bond issuers create BondMarkets that pay out
/// a Payout Token in exchange for deposited Quote Tokens. Users can purchase
/// future-dated Payout Tokens with Quote Tokens at the current market price and
/// receive Bond Tokens to represent their position while their bond vests.
/// Once the Bond Tokens vest, they can redeem it for the Quote Tokens.
///
/// @dev The ERC20 Bond Token contract is issued by a Fixed Expiry Teller to
/// represent bond positions until they vest. Bond tokens can be redeemed for
// the underlying token 1:1 at or after expiry.
///
/// @dev This contract uses Clones (https://github.com/wighawag/clones-with-immutable-args)
/// to save gas on deployment and is based on VestedERC20 (https://github.com/ZeframLou/vested-erc20)
///
/// @author Oighty, Zeus, Potted Meat, indigo
contract ERC20BondToken is CloneERC20 {
/* ========== ERRORS ========== */
error BondToken_OnlyTeller();
/* ========== IMMUTABLE PARAMETERS ========== */
/// @notice The token to be redeemed when the bond vests
/// @return _underlying The address of the underlying token
function underlying() external pure returns (ERC20 _underlying) {
return ERC20(_getArgAddress(0x41));
}
/// @notice Timestamp at which the BondToken can be redeemed for the underlying
/// @return _expiry The vest start timestamp
function expiry() external pure returns (uint48 _expiry) {
return uint48(_getArgUint256(0x55));
}
/// @notice Address of the Teller that created the token
function teller() internal pure returns (address _teller) {
return _getArgAddress(0x75);
}
/* ========== MINT/BURN ========== */
function mint(address to, uint256 amount) external {
if (msg.sender != teller()) revert BondToken_OnlyTeller();
_mint(to, amount);
}
function burn(address from, uint256 amount) external {
if (msg.sender != teller()) revert BondToken_OnlyTeller();
_burn(from, amount);
}
}
/
// SPDX-License-Identifier: AGPL-3.0
pragma solidity >=0.8.0;
import {ERC20} from "solmate/tokens/ERC20.sol";
import {IBondAuctioneer} from "../interfaces/IBondAuctioneer.sol";
interface IBondSDA is IBondAuctioneer {
/// @notice Main information pertaining to bond market
struct BondMarket {
address owner; // market owner. sends payout tokens, receives quote tokens (defaults to creator)
ERC20 payoutToken; // token to pay depositors with
ERC20 quoteToken; // token to accept as payment
address callbackAddr; // address to call for any operations on bond purchase. Must inherit to IBondCallback.
bool capacityInQuote; // capacity limit is in payment token (true) or in payout (false, default)
uint256 capacity; // capacity remaining
uint256 totalDebt; // total payout token debt from market
uint256 minPrice; // minimum price (debt will stop decaying to maintain this)
uint256 maxPayout; // max payout tokens out in one order
uint256 sold; // payout tokens out
uint256 purchased; // quote tokens in
uint256 scale; // scaling factor for the market (see MarketParams struct)
}
/// @notice Information used to control how a bond market changes
struct BondTerms {
uint256 controlVariable; // scaling variable for price
uint256 maxDebt; // max payout token debt accrued
uint48 vesting; // length of time from deposit to expiry if fixed-term, vesting timestamp if fixed-expiry
uint48 conclusion; // timestamp when market no longer offered
}
/// @notice Data needed for tuning bond market
/// @dev Has timestamps in uint32 (not int32), so is not subject to Y2K38 overflow
struct BondMetadata {
uint48 lastTune; // last timestamp when control variable was tuned
uint48 lastDecay; // last timestamp when market was created and debt was decayed
uint32 length; // time from creation to conclusion.
uint32 depositInterval; // target frequency of deposits
uint32 tuneInterval; // frequency of tuning
uint32 tuneAdjustmentDelay; // time to implement downward tuning adjustments
uint32 debtDecayInterval; // interval over which debt should decay completely
uint256 tuneIntervalCapacity; // capacity expected to be used during a tuning interval
uint256 tuneBelowCapacity; // capacity that the next tuning will occur at
uint256 lastTuneDebt; // target debt calculated at last tuning
}
/// @notice Control variable adjustment data
struct Adjustment {
uint256 change;
uint48 lastAdjustment;
uint48 timeToAdjusted; // how long until adjustment happens
bool active;
}
/// @notice Parameters to create a new bond market
/// @dev Note price should be passed in a specific format:
/// formatted price = (payoutPriceCoefficient / quotePriceCoefficient)
/// * 10**(36 + scaleAdjustment + quoteDecimals - payoutDecimals + payoutPriceDecimals - quotePriceDecimals)
/// where:
/// payoutDecimals - Number of decimals defined for the payoutToken in its ERC20 contract
/// quoteDecimals - Number of decimals defined for the quoteToken in its ERC20 contract
/// payoutPriceCoefficient - The coefficient of the payoutToken price in scientific notation (also known as the significant digits)
/// payoutPriceDecimals - The significand of the payoutToken price in scientific notation (also known as the base ten exponent)
/// quotePriceCoefficient - The coefficient of the quoteToken price in scientific notation (also known as the significant digits)
/// quotePriceDecimals - The significand of the quoteToken price in scientific notation (also known as the base ten exponent)
/// scaleAdjustment - see below
/// * In the above definitions, the "prices" need to have the same unit of account (i.e. both in OHM, $, ETH, etc.)
/// If price is not provided in this format, the market will not behave as intended.
/// @param params_ Encoded bytes array, with the following elements
/// @dev 0. Payout Token (token paid out)
/// @dev 1. Quote Token (token to be received)
/// @dev 2. Callback contract address, should conform to IBondCallback. If 0x00, tokens will be transferred from market.owner
/// @dev 3. Is Capacity in Quote Token?
/// @dev 4. Capacity (amount in quoteDecimals or amount in payoutDecimals)
/// @dev 5. Formatted initial price (see note above)
/// @dev 6. Formatted minimum price (see note above)
/// @dev 7. Debt buffer. Percent with 3 decimals. Percentage over the initial debt to allow the market to accumulate at anyone time.
/// @dev Works as a circuit breaker for the market in case external conditions incentivize massive buying (e.g. stablecoin depeg).
/// @dev Minimum is the greater of 10% or initial max payout as a percentage of capacity.
/// @dev If the value is too small, the market will not be able function normally and close prematurely.
/// @dev If the value is too large, the market will not circuit break when intended. The value must be > 10% but can exceed 100% if desired.
/// @dev 8. Is fixed term ? Vesting length (seconds) : Vesting expiry (timestamp).
/// @dev A 'vesting' param longer than 50 years is considered a timestamp for fixed expiry.
/// @dev 9. Conclusion (timestamp)
/// @dev 10. Deposit interval (seconds)
/// @dev 11. Market scaling factor adjustment, ranges from -24 to +24 within the configured market bounds.
/// @dev Should be calculated as: (payoutDecimals - quoteDecimals) - ((payoutPriceDecimals - quotePriceDecimals) / 2)
/// @dev Providing a scaling factor adjustment that doesn't follow this formula could lead to under or overflow errors in the market.
/// @return ID of new bond market
struct MarketParams {
ERC20 payoutToken;
ERC20 quoteToken;
address callbackAddr;
bool capacityInQuote;
uint256 capacity;
uint256 formattedInitialPrice;
uint256 formattedMinimumPrice;
uint32 debtBuffer;
uint48 vesting;
uint48 conclusion;
uint32 depositInterval;
int8 scaleAdjustment;
}
/* ========== VIEW FUNCTIONS ========== */
/// @notice Calculate current market price of payout token in quote tokens
/// @dev Accounts for debt and control variable decay since last deposit (vs _marketPrice())
/// @param id_ ID of market
/// @return Price for market in configured decimals (see MarketParams)
//
// price is derived from the equation
//
// p = c * d
//
// where
// p = price
// c = control variable
// d = debt
//
// d -= ( d * (dt / l) )
//
// where
// dt = change in time
// l = length of program
//
// if price is below minimum price, minimum price is returned
// this is enforced on deposits by manipulating total debt (see _decay())
function marketPrice(uint256 id_) external view override returns (uint256);
/// @notice Calculate debt factoring in decay
/// @dev Accounts for debt decay since last deposit
/// @param id_ ID of market
/// @return Current debt for market in payout token decimals
function currentDebt(uint256 id_) external view returns (uint256);
/// @notice Up to date control variable
/// @dev Accounts for control variable adjustment
/// @param id_ ID of market
/// @return Control variable for market in payout token decimals
function currentControlVariable(uint256 id_) external view returns (uint256);
}
/Auth.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
/// @notice Provides a flexible and updatable auth pattern which is completely separate from application logic.
/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/auth/Auth.sol)
/// @author Modified from Dappsys (https://github.com/dapphub/ds-auth/blob/master/src/auth.sol)
abstract contract Auth {
event OwnerUpdated(address indexed user, address indexed newOwner);
event AuthorityUpdated(address indexed user, Authority indexed newAuthority);
address public owner;
Authority public authority;
constructor(address _owner, Authority _authority) {
owner = _owner;
authority = _authority;
emit OwnerUpdated(msg.sender, _owner);
emit AuthorityUpdated(msg.sender, _authority);
}
modifier requiresAuth() {
require(isAuthorized(msg.sender, msg.sig), "UNAUTHORIZED");
_;
}
function isAuthorized(address user, bytes4 functionSig) internal view virtual returns (bool) {
Authority auth = authority; // Memoizing authority saves us a warm SLOAD, around 100 gas.
// Checking if the caller is the owner only after calling the authority saves gas in most cases, but be
// aware that this makes protected functions uncallable even to the owner if the authority is out of order.
return (address(auth) != address(0) && auth.canCall(user, address(this), functionSig)) || user == owner;
}
function setAuthority(Authority newAuthority) public virtual {
// We check if the caller is the owner first because we want to ensure they can
// always swap out the authority even if it's reverting or using up a lot of gas.
require(msg.sender == owner || authority.canCall(msg.sender, address(this), msg.sig));
authority = newAuthority;
emit AuthorityUpdated(msg.sender, newAuthority);
}
function setOwner(address newOwner) public virtual requiresAuth {
owner = newOwner;
emit OwnerUpdated(msg.sender, newOwner);
}
}
/// @notice A generic interface for a contract which provides authorization data to an Auth instance.
/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/auth/Auth.sol)
/// @author Modified from Dappsys (https://github.com/dapphub/ds-auth/blob/master/src/auth.sol)
interface Authority {
function canCall(
address user,
address target,
bytes4 functionSig
) external view returns (bool);
}
/
// SPDX-License-Identifier: AGPL-3.0
pragma solidity >=0.8.0;
import {ERC20BondToken} from "../ERC20BondToken.sol";
import {ERC20} from "solmate/tokens/ERC20.sol";
interface IBondFixedExpiryTeller {
/// @notice Redeem a fixed-expiry bond token for the underlying token (bond token must have matured)
/// @param token_ Token to redeem
/// @param amount_ Amount to redeem
function redeem(ERC20BondToken token_, uint256 amount_) external;
/// @notice Deposit an ERC20 token and mint a future-dated ERC20 bond token
/// @param underlying_ ERC20 token redeemable when the bond token vests
/// @param expiry_ Timestamp at which the bond token can be redeemed for the underlying token
/// @param amount_ Amount of underlying tokens to deposit
/// @return Address of the ERC20 bond token received
/// @return Amount of the ERC20 bond token received
function create(
ERC20 underlying_,
uint48 expiry_,
uint256 amount_
) external returns (ERC20BondToken, uint256);
/// @notice Deploy a new ERC20 bond token for an (underlying, expiry) pair and return its address
/// @dev ERC20 used for fixed-expiry
/// @dev If a bond token exists for the (underlying, expiry) pair, it returns that address
/// @param underlying_ ERC20 token redeemable when the bond token vests
/// @param expiry_ Timestamp at which the bond token can be redeemed for the underlying token
/// @return Address of the ERC20 bond token being created
function deploy(ERC20 underlying_, uint48 expiry_) external returns (ERC20BondToken);
/// @notice Get the OlympusERC20BondToken contract corresponding to a market
/// @param id_ ID of the market
/// @return ERC20BondToken contract address
function getBondTokenForMarket(uint256 id_) external view returns (ERC20BondToken);
}
/
// SPDX-License-Identifier: AGPL-3.0
pragma solidity >=0.8.0;
import {ERC20} from "solmate/tokens/ERC20.sol";
import {IBondTeller} from "../interfaces/IBondTeller.sol";
import {IBondAggregator} from "../interfaces/IBondAggregator.sol";
interface IBondAuctioneer {
/// @notice Creates a new bond market
/// @param params_ Configuration data needed for market creation, encoded in a bytes array
/// @dev See specific auctioneer implementations for details on encoding the parameters.
/// @return id ID of new bond market
function createMarket(bytes memory params_) external returns (uint256);
/// @notice Disable existing bond market
/// @notice Must be market owner
/// @param id_ ID of market to close
function closeMarket(uint256 id_) external;
/// @notice Exchange quote tokens for a bond in a specified market
/// @notice Must be teller
/// @param id_ ID of the Market the bond is being purchased from
/// @param amount_ Amount to deposit in exchange for bond (after fee has been deducted)
/// @param minAmountOut_ Minimum acceptable amount of bond to receive. Prevents frontrunning
/// @return payout Amount of payout token to be received from the bond
function purchaseBond(
uint256 id_,
uint256 amount_,
uint256 minAmountOut_
) external returns (uint256 payout);
/// @notice Set market intervals to different values than the defaults
/// @notice Must be market owner
/// @dev Changing the intervals could cause markets to behave in unexpected way
/// tuneInterval should be greater than tuneAdjustmentDelay
/// @param id_ Market ID
/// @param intervals_ Array of intervals (3)
/// 1. Tune interval - Frequency of tuning
/// 2. Tune adjustment delay - Time to implement downward tuning adjustments
/// 3. Debt decay interval - Interval over which debt should decay completely
function setIntervals(uint256 id_, uint32[3] calldata intervals_) external;
/// @notice Designate a new owner of a market
/// @notice Must be market owner
/// @dev Doesn't change permissions until newOwner calls pullOwnership
/// @param id_ Market ID
/// @param newOwner_ New address to give ownership to
function pushOwnership(uint256 id_, address newOwner_) external;
/// @notice Accept ownership of a market
/// @notice Must be market newOwner
/// @dev The existing owner must call pushOwnership prior to the newOwner calling this function
/// @param id_ Market ID
function pullOwnership(uint256 id_) external;
/// @notice Set the auctioneer defaults
/// @notice Must be policy
/// @param defaults_ Array of default values
/// 1. Tune interval - amount of time between tuning adjustments
/// 2. Tune adjustment delay - amount of time to apply downward tuning adjustments
/// 3. Minimum debt decay interval - minimum amount of time to let debt decay to zero
/// 4. Minimum deposit interval - minimum amount of time to wait between deposits
/// 5. Minimum market duration - minimum amount of time a market can be created for
/// 6. Minimum debt buffer - the minimum amount of debt over the initial debt to trigger a market shutdown
/// @dev The defaults set here are important to avoid edge cases in market behavior, e.g. a very short market reacts doesn't tune well
/// @dev Only applies to new markets that are created after the change
function setDefaults(uint32[6] memory defaults_) external;
/// @notice Change the status of the auctioneer to allow creation of new markets
/// @dev Setting to false and allowing active markets to end will sunset the auctioneer
/// @param status_ Allow market creation (true) : Disallow market creation (false)
function setAllowNewMarkets(bool status_) external;
/// @notice Change whether a market creator is allowed to use a callback address in their markets or not
/// @notice Must be guardian
/// @dev Callback is believed to be safe, but a whitelist is implemented to prevent abuse
/// @param creator_ Address of market creator
/// @param status_ Allow callback (true) : Disallow callback (false)
function setCallbackAuthStatus(address creator_, bool status_) external;
/* ========== VIEW FUNCTIONS ========== */
/// @notice Provides information for the Teller to execute purchases on a Market
/// @param id_ Market ID
/// @return owner Address of the market owner (tokens transferred from this address if no callback)
/// @return callbackAddr Address of the callback contract to get tokens for payouts
/// @return payoutToken Payout Token (token paid out) for the Market
/// @return quoteToken Quote Token (token received) for the Market
/// @return vesting Timestamp or duration for vesting, implementation-dependent
/// @return maxPayout Maximum amount of payout tokens you can purchase in one transaction
function getMarketInfoForPurchase(uint256 id_)
external
view
returns (
address owner,
address callbackAddr,
ERC20 payoutToken,
ERC20 quoteToken,
uint48 vesting,
uint256 maxPayout
);
/// @notice Calculate current market price of payout token in quote tokens
/// @param id_ ID of market
/// @return Price for market in configured decimals
//
// if price is below minimum price, minimum price is returned
// this is enforced on deposits by manipulating total debt (see _decay())
function marketPrice(uint256 id_) external view returns (uint256);
/// @notice Scale value to use when converting between quote token and payout token amounts with marketPrice()
/// @param id_ ID of market
/// @return Scaling factor for market in configured decimals
function marketScale(uint256 id_) external view returns (uint256);
/// @notice Payout due for amount of quote tokens
/// @dev Accounts for debt and control variable decay so it is up to date
/// @param amount_ Amount of quote tokens to spend
/// @param id_ ID of market
/// @param referrer_ Address of referrer, used to get fees to calculate accurate payout amount.
/// Inputting the zero address will take into account just the protocol fee.
/// @return amount of payout tokens to be paid
function payoutFor(
uint256 amount_,
uint256 id_,
address referrer_
) external view returns (uint256);
/// @notice Returns maximum amount of quote token accepted by the market
/// @param id_ ID of market
/// @param referrer_ Address of referrer, used to get fees to calculate accurate payout amount.
/// Inputting the zero address will take into account just the protocol fee.
function maxAmountAccepted(uint256 id_, address referrer_) external view returns (uint256);
/// @notice Does market send payout immediately
/// @param id_ Market ID to search for
function isInstantSwap(uint256 id_) external view returns (bool);
/// @notice Is a given market accepting deposits
/// @param id_ ID of market
function isLive(uint256 id_) external view returns (bool);
/// @notice Returns the address of the market owner
/// @param id_ ID of market
function ownerOf(uint256 id_) external view returns (address);
/// @notice Returns the Teller that services the Auctioneer
function getTeller() external view returns (IBondTeller);
/// @notice Returns the Aggregator that services the Auctioneer
function getAggregator() external view returns (IBondAggregator);
/// @notice Returns current capacity of a market
function currentCapacity(uint256 id_) external view returns (uint256);
}
/
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
/// @title Contains 512-bit math functions
/// @notice Facilitates multiplication and division that can have overflow of an intermediate value without any loss of precision
/// @dev Handles "phantom overflow" i.e., allows multiplication and division where an intermediate value overflows 256 bits
library FullMath {
/// @notice Calculates floor(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
/// @param a The multiplicand
/// @param b The multiplier
/// @param denominator The divisor
/// @return result The 256-bit result
/// @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv
function mulDiv(
uint256 a,
uint256 b,
uint256 denominator
) internal pure returns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = a * b
// Compute the product mod 2**256 and mod 2**256 - 1
// then use the Chinese Remainder Theorem to reconstruct
// the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2**256 + prod0
uint256 prod0; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(a, b, not(0))
prod0 := mul(a, b)
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division
if (prod1 == 0) {
require(denominator > 0);
assembly {
result := div(prod0, denominator)
}
return result;
}
// Make sure the result is less than 2**256.
// Also prevents denominator == 0
require(denominator > prod1);
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0]
// Compute remainder using mulmod
uint256 remainder;
assembly {
remainder := mulmod(a, b, denominator)
}
// Subtract 256 bit number from 512 bit number
assembly {
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator
// Compute largest power of two divisor of denominator.
// Always >= 1.
uint256 twos = (type(uint256).max - denominator + 1) & denominator;
// Divide denominator by power of two
assembly {
denominator := div(denominator, twos)
}
// Divide [prod1 prod0] by the factors of two
assembly {
prod0 := div(prod0, twos)
}
// Shift in bits from prod1 into prod0. For this we need
// to flip `twos` such that it is 2**256 / twos.
// If twos is zero, then it becomes one
assembly {
twos := add(div(sub(0, twos), twos), 1)
}
prod0 |= prod1 * twos;
// Invert denominator mod 2**256
// Now that denominator is an odd number, it has an inverse
// modulo 2**256 such that denominator * inv = 1 mod 2**256.
// Compute the inverse by starting with a seed that is correct
// correct for four bits. That is, denominator * inv = 1 mod 2**4
uint256 inv = (3 * denominator) ^ 2;
// Now use Newton-Raphson iteration to improve the precision.
// Thanks to Hensel's lifting lemma, this also works in modular
// arithmetic, doubling the correct bits in each step.
inv *= 2 - denominator * inv; // inverse mod 2**8
inv *= 2 - denominator * inv; // inverse mod 2**16
inv *= 2 - denominator * inv; // inverse mod 2**32
inv *= 2 - denominator * inv; // inverse mod 2**64
inv *= 2 - denominator * inv; // inverse mod 2**128
inv *= 2 - denominator * inv; // inverse mod 2**256
// Because the division is now exact we can divide by multiplying
// with the modular inverse of denominator. This will give us the
// correct result modulo 2**256. Since the precoditions guarantee
// that the outcome is less than 2**256, this is the final result.
// We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inv;
return result;
}
}
/// @notice Calculates ceil(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
/// @param a The multiplicand
/// @param b The multiplier
/// @param denominator The divisor
/// @return result The 256-bit result
function mulDivUp(
uint256 a,
uint256 b,
uint256 denominator
) internal pure returns (uint256 result) {
result = mulDiv(a, b, denominator);
unchecked {
if (mulmod(a, b, denominator) > 0) {
require(result < type(uint256).max);
result++;
}
}
}
}
/
// SPDX-License-Identifier: AGPL-3.0
pragma solidity >=0.8.0;
import {ERC20} from "solmate/tokens/ERC20.sol";
import {IBondAuctioneer} from "../interfaces/IBondAuctioneer.sol";
import {IBondTeller} from "../interfaces/IBondTeller.sol";
interface IBondAggregator {
/// @notice Register a auctioneer with the aggregator
/// @notice Only Guardian
/// @param auctioneer_ Address of the Auctioneer to register
/// @dev A auctioneer must be registered with an aggregator to create markets
function registerAuctioneer(IBondAuctioneer auctioneer_) external;
/// @notice Register a new market with the aggregator
/// @notice Only registered depositories
/// @param payoutToken_ Token to be paid out by the market
/// @param quoteToken_ Token to be accepted by the market
/// @param marketId ID of the market being created
function registerMarket(ERC20 payoutToken_, ERC20 quoteToken_)
external
returns (uint256 marketId);
/// @notice Get the auctioneer for the provided market ID
/// @param id_ ID of Market
function getAuctioneer(uint256 id_) external view returns (IBondAuctioneer);
/// @notice Calculate current market price of payout token in quote tokens
/// @dev Accounts for debt and control variable decay since last deposit (vs _marketPrice())
/// @param id_ ID of market
/// @return Price for market (see the specific auctioneer for units)
//
// if price is below minimum price, minimum price is returned
// this is enforced on deposits by manipulating total debt (see _decay())
function marketPrice(uint256 id_) external view returns (uint256);
/// @notice Scale value to use when converting between quote token and payout token amounts with marketPrice()
/// @param id_ ID of market
/// @return Scaling factor for market in configured decimals
function marketScale(uint256 id_) external view returns (uint256);
/// @notice Payout due for amount of quote tokens
/// @dev Accounts for debt and control variable decay so it is up to date
/// @param amount_ Amount of quote tokens to spend
/// @param id_ ID of market
/// @param referrer_ Address of referrer, used to get fees to calculate accurate payout amount.
/// Inputting the zero address will take into account just the protocol fee.
/// @return amount of payout tokens to be paid
function payoutFor(
uint256 amount_,
uint256 id_,
address referrer_
) external view returns (uint256);
/// @notice Returns maximum amount of quote token accepted by the market
/// @param id_ ID of market
/// @param referrer_ Address of referrer, used to get fees to calculate accurate payout amount.
/// Inputting the zero address will take into account just the protocol fee.
function maxAmountAccepted(uint256 id_, address referrer_) external view returns (uint256);
/// @notice Does market send payout immediately
/// @param id_ Market ID to search for
function isInstantSwap(uint256 id_) external view returns (bool);
/// @notice Is a given market accepting deposits
/// @param id_ ID of market
function isLive(uint256 id_) external view returns (bool);
/// @notice Returns array of active market IDs within a range
/// @dev Should be used if length exceeds max to query entire array
function liveMarketsBetween(uint256 firstIndex_, uint256 lastIndex_)
external
view
returns (uint256[] memory);
/// @notice Returns an array of all active market IDs for a given quote token
/// @param token_ Address of token to query by
/// @param isPayout_ If true, search by payout token, else search for quote token
function liveMarketsFor(address token_, bool isPayout_)
external
view
returns (uint256[] memory);
/// @notice Returns an array of all active market IDs for a given owner
/// @param owner_ Address of owner to query by
function liveMarketsBy(address owner_) external view returns (uint256[] memory);
/// @notice Returns an array of all active market IDs for a given payout and quote token
/// @param payout_ Address of payout token
/// @param quote_ Address of quote token
function marketsFor(address payout_, address quote_) external view returns (uint256[] memory);
/// @notice Returns the market ID with the highest current payoutToken payout for depositing quoteToken
/// @param payout_ Address of payout token
/// @param quote_ Address of quote token
/// @param amountIn_ Amount of quote tokens to deposit
/// @param minAmountOut_ Minimum amount of payout tokens to receive as payout
/// @param maxExpiry_ Latest acceptable vesting timestamp for bond
/// Inputting the zero address will take into account just the protocol fee.
function findMarketFor(
address payout_,
address quote_,
uint256 amountIn_,
uint256 minAmountOut_,
uint256 maxExpiry_
) external view returns (uint256 id);
/// @notice Returns the Teller that services the market ID
function getTeller(uint256 id_) external view returns (IBondTeller);
/// @notice Returns current capacity of a market
function currentCapacity(uint256 id_) external view returns (uint256);
}
/ReentrancyGuard.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
/// @notice Gas optimized reentrancy protection for smart contracts.
/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/utils/ReentrancyGuard.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/security/ReentrancyGuard.sol)
abstract contract ReentrancyGuard {
uint256 private locked = 1;
modifier nonReentrant() {
require(locked == 1, "REENTRANCY");
locked = 2;
_;
locked = 1;
}
}
/
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
import {ERC20} from "solmate/tokens/ERC20.sol";
/// @notice Safe ERC20 and ETH transfer library that safely handles missing return values.
/// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v3-periphery/blob/main/contracts/libraries/TransferHelper.sol)
/// @author Taken from Solmate.
library TransferHelper {
function safeTransferFrom(
ERC20 token,
address from,
address to,
uint256 amount
) internal {
(bool success, bytes memory data) = address(token).call(
abi.encodeWithSelector(ERC20.transferFrom.selector, from, to, amount)
);
require(success && (data.length == 0 || abi.decode(data, (bool))), "TRANSFER_FROM_FAILED");
}
function safeTransfer(
ERC20 token,
address to,
uint256 amount
) internal {
(bool success, bytes memory data) = address(token).call(
abi.encodeWithSelector(ERC20.transfer.selector, to, amount)
);
require(success && (data.length == 0 || abi.decode(data, (bool))), "TRANSFER_FAILED");
}
// function safeApprove(
// ERC20 token,
// address to,
// uint256 amount
// ) internal {
// (bool success, bytes memory data) = address(token).call(
// abi.encodeWithSelector(ERC20.approve.selector, to, amount)
// );
// require(success && (data.length == 0 || abi.decode(data, (bool))), "APPROVE_FAILED");
// }
// function safeTransferETH(address to, uint256 amount) internal {
// (bool success, ) = to.call{value: amount}(new bytes(0));
// require(success, "ETH_TRANSFER_FAILED");
// }
}
/
// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity 0.8.15;
import {ERC20} from "solmate/tokens/ERC20.sol";
import {ReentrancyGuard} from "solmate/utils/ReentrancyGuard.sol";
import {Auth, Authority} from "solmate/auth/Auth.sol";
import {IBondSDA, IBondAuctioneer} from "../interfaces/IBondSDA.sol";
import {IBondTeller} from "../interfaces/IBondTeller.sol";
import {IBondCallback} from "../interfaces/IBondCallback.sol";
import {IBondAggregator} from "../interfaces/IBondAggregator.sol";
import {TransferHelper} from "../lib/TransferHelper.sol";
import {FullMath} from "../lib/FullMath.sol";
/// @title Bond Sequential Dutch Auctioneer (SDA)
/// @notice Bond Sequential Dutch Auctioneer Base Contract
/// @dev Bond Protocol is a system to create Olympus-style bond markets
/// for any token pair. The markets do not require maintenance and will manage
/// bond prices based on activity. Bond issuers create BondMarkets that pay out
/// a Payout Token in exchange for deposited Quote Tokens. Users can purchase
/// future-dated Payout Tokens with Quote Tokens at the current market price and
/// receive Bond Tokens to represent their position while their bond vests.
/// Once the Bond Tokens vest, they can redeem it for the Quote Tokens.
///
/// @dev The Auctioneer contract allows users to create and manage bond markets.
/// All bond pricing logic and market data is stored in the Auctioneer.
/// A Auctioneer is dependent on a Teller to serve external users and
/// an Aggregator to register new markets. This implementation of the Auctioneer
/// uses a Sequential Dutch Auction pricing system to buy a target amount of quote
/// tokens or sell a target amount of payout tokens over the duration of a market.
///
/// @author Oighty, Zeus, Potted Meat, indigo
abstract contract BondBaseSDA is IBondSDA, Auth {
using TransferHelper for ERC20;
using FullMath for uint256;
/* ========== ERRORS ========== */
error Auctioneer_OnlyMarketOwner();
error Auctioneer_InitialPriceLessThanMin();
error Auctioneer_MarketConcluded(uint256 conclusion_);
error Auctioneer_MaxPayoutExceeded();
error Auctioneer_AmountLessThanMinimum();
error Auctioneer_NotEnoughCapacity();
error Auctioneer_InvalidCallback();
error Auctioneer_BadExpiry();
error Auctioneer_InvalidParams();
error Auctioneer_NotAuthorized();
error Auctioneer_NewMarketsNotAllowed();
/* ========== EVENTS ========== */
event MarketCreated(
uint256 indexed id,
address indexed payoutToken,
address indexed quoteToken,
uint48 vesting,
uint256 initialPrice
);
event MarketClosed(uint256 indexed id);
event Tuned(uint256 indexed id, uint256 oldControlVariable, uint256 newControlVariable);
/* ========== STATE VARIABLES ========== */
/// @notice Main information pertaining to bond market
mapping(uint256 => BondMarket) public markets;
/// @notice Information used to control how a bond market changes
mapping(uint256 => BondTerms) public terms;
/// @notice Data needed for tuning bond market
mapping(uint256 => BondMetadata) public metadata;
/// @notice Control variable changes
mapping(uint256 => Adjustment) public adjustments;
/// @notice New address to designate as market owner. They must accept ownership to transfer permissions.
mapping(uint256 => address) public newOwners;
/// @notice Whether or not the auctioneer allows new markets to be created
/// @dev Changing to false will sunset the auctioneer after all active markets end
bool public allowNewMarkets;
/// @notice Whether or not the market creator is authorized to use a callback address
mapping(address => bool) public callbackAuthorized;
/// Sane defaults for tuning. Can be adjusted for a specific market via setters.
uint32 public defaultTuneInterval;
uint32 public defaultTuneAdjustment;
/// Minimum values for decay, deposit interval, market duration and debt buffer.
uint32 public minDebtDecayInterval;
uint32 public minDepositInterval;
uint32 public minMarketDuration;
uint32 public minDebtBuffer;
// A 'vesting' param longer than 50 years is considered a timestamp for fixed expiry.
uint48 internal constant MAX_FIXED_TERM = 52 weeks * 50;
uint48 internal constant FEE_DECIMALS = 1e5; // one percent equals 1000.
// BondAggregator contract with utility functions
IBondAggregator internal immutable _aggregator;
// BondTeller contract that handles interactions with users and issues tokens
IBondTeller internal immutable _teller;
constructor(
IBondTeller teller_,
IBondAggregator aggregator_,
address guardian_,
Authority authority_
) Auth(guardian_, authority_) {
_aggregator = aggregator_;
_teller = teller_;
defaultTuneInterval = 24 hours;
defaultTuneAdjustment = 1 hours;
minDebtDecayInterval = 3 days;
minDepositInterval = 1 hours;
minMarketDuration = 1 days;
minDebtBuffer = 10000; // 10%
allowNewMarkets = true;
}
/* ========== MARKET FUNCTIONS ========== */
/// @inheritdoc IBondAuctioneer
function createMarket(bytes calldata params_) external virtual returns (uint256);
/// @notice core market creation logic, see IBondAuctioneer.createMarket documentation
function _createMarket(MarketParams memory params_) internal returns (uint256) {
{
// Check that the auctioneer is allowing new markets to be created
if (!allowNewMarkets) revert Auctioneer_NewMarketsNotAllowed();
// Ensure params are in bounds
uint8 payoutTokenDecimals = params_.payoutToken.decimals();
uint8 quoteTokenDecimals = params_.quoteToken.decimals();
if (payoutTokenDecimals < 6 || payoutTokenDecimals > 18)
revert Auctioneer_InvalidParams();
if (quoteTokenDecimals < 6 || quoteTokenDecimals > 18)
revert Auctioneer_InvalidParams();
if (params_.scaleAdjustment < -24 || params_.scaleAdjustment > 24)
revert Auctioneer_InvalidParams();
// Restrict the use of a callback address unless allowed
if (!callbackAuthorized[msg.sender] && params_.callbackAddr != address(0))
revert Auctioneer_NotAuthorized();
}
// Unit to scale calculation for this market by to ensure reasonable values
// for price, debt, and control variable without under/overflows.
// See IBondAuctioneer for more details.
//
// scaleAdjustment should be equal to (payoutDecimals - quoteDecimals) - ((payoutPriceDecimals - quotePriceDecimals) / 2)
uint256 scale;
unchecked {
scale = 10**uint8(36 + params_.scaleAdjustment);
}
if (params_.formattedInitialPrice < params_.formattedMinimumPrice)
revert Auctioneer_InitialPriceLessThanMin();
// Record new market into array for later retrieval and get marketId
uint256 marketId = _aggregator.registerMarket(params_.payoutToken, params_.quoteToken);
uint32 secondsToConclusion;
uint32 debtDecayInterval;
{
secondsToConclusion = uint32(params_.conclusion - block.timestamp);
if (
secondsToConclusion < minMarketDuration ||
params_.depositInterval < minDepositInterval
) revert Auctioneer_InvalidParams();
// At minimum, the interval is how long it takes for price to drop to 0. In reality, a 50% drop is likely a guaranteed
// bond sale. So debt decay interval needs to be long enough to allow a bond to adjust if oversold.
// Needs to be some multiple of deposit interval because you don't want to go from 100 to 0 during the time frame
// you expected to sell a single bond. 5 is a sane default observed from running OP v1 bond markets.
uint32 userDebtDecay = params_.depositInterval * 5;
debtDecayInterval = minDebtDecayInterval > userDebtDecay
? minDebtDecayInterval
: userDebtDecay;
uint256 tuneIntervalCapacity = params_.capacity.mulDiv(
uint256(
params_.depositInterval > defaultTuneInterval
? params_.depositInterval
: defaultTuneInterval
),
uint256(secondsToConclusion)
);
metadata[marketId] = BondMetadata({
lastTune: uint48(block.timestamp),
lastDecay: uint48(block.timestamp),
length: secondsToConclusion,
depositInterval: params_.depositInterval,
tuneInterval: params_.depositInterval > defaultTuneInterval
? params_.depositInterval
: defaultTuneInterval,
tuneAdjustmentDelay: defaultTuneAdjustment,
debtDecayInterval: debtDecayInterval,
tuneIntervalCapacity: tuneIntervalCapacity,
tuneBelowCapacity: params_.capacity - tuneIntervalCapacity,
lastTuneDebt: (
params_.capacityInQuote
? params_.capacity.mulDiv(scale, params_.formattedInitialPrice)
: params_.capacity
).mulDiv(uint256(debtDecayInterval), uint256(secondsToConclusion))
});
}
// Initial target debt is equal to capacity scaled by the ratio of the debt decay interval and the length of the market.
// This is the amount of debt that should be decayed over the decay interval if no purchases are made.
// Note price should be passed in a specific format:
// price = (payoutPriceCoefficient / quotePriceCoefficient)
// * 10**(36 + scaleAdjustment + quoteDecimals - payoutDecimals + payoutPriceDecimals - quotePriceDecimals)
// See IBondAuctioneer for more details and variable definitions.
uint256 targetDebt;
uint256 maxPayout;
{
uint256 capacity = params_.capacityInQuote
? params_.capacity.mulDiv(scale, params_.formattedInitialPrice)
: params_.capacity;
targetDebt = capacity.mulDiv(uint256(debtDecayInterval), uint256(secondsToConclusion));
// Max payout is the amount of capacity that should be utilized in a deposit
// interval. for example, if capacity is 1,000 TOKEN, there are 10 days to conclusion,
// and the preferred deposit interval is 1 day, max payout would be 100 TOKEN.
maxPayout = capacity.mulDiv(
uint256(params_.depositInterval),
uint256(secondsToConclusion)
);
}
markets[marketId] = BondMarket({
owner: msg.sender,
payoutToken: params_.payoutToken,
quoteToken: params_.quoteToken,
callbackAddr: params_.callbackAddr,
capacityInQuote: params_.capacityInQuote,
capacity: params_.capacity,
totalDebt: targetDebt,
minPrice: params_.formattedMinimumPrice,
maxPayout: maxPayout,
purchased: 0,
sold: 0,
scale: scale
});
// Max debt serves as a circuit breaker for the market. let's say the quote token is a stablecoin,
// and that stablecoin depegs. without max debt, the market would continue to buy until it runs
// out of capacity. this is configurable with a 3 decimal buffer (1000 = 1% above initial price).
// Note that its likely advisable to keep this buffer wide.
// Note that the buffer is above 100%. i.e. 10% buffer = initial debt * 1.1
// 1e5 = 100,000. 10,000 / 100,000 = 10%.
uint256 minDebtBuffer_ = maxPayout.mulDiv(FEE_DECIMALS, targetDebt) > minDebtBuffer
? maxPayout.mulDiv(FEE_DECIMALS, targetDebt)
: minDebtBuffer;
uint256 maxDebt = targetDebt +
targetDebt.mulDiv(
uint256(params_.debtBuffer > minDebtBuffer_ ? params_.debtBuffer : minDebtBuffer_),
1e5
);
// The control variable is set so that initial price equals the desired initial price. the control
// variable is the ultimate determinant of price, so we compute this last.
//
// price = control variable * debt / scale
// therefore, control variable = price * scale / debt
uint256 controlVariable = params_.formattedInitialPrice.mulDiv(scale, targetDebt);
terms[marketId] = BondTerms({
controlVariable: controlVariable,
maxDebt: maxDebt,
vesting: params_.vesting,
conclusion: params_.conclusion
});
emit MarketCreated(
marketId,
address(params_.payoutToken),
address(params_.quoteToken),
params_.vesting,
params_.formattedInitialPrice
);
return marketId;
}
/// @inheritdoc IBondAuctioneer
function setIntervals(uint256 id_, uint32[3] calldata intervals_) external override {
// Check that the intervals are non-zero
if (intervals_[0] == 0 || intervals_[1] == 0 || intervals_[2] == 0)
revert Auctioneer_InvalidParams();
// Check that tuneInterval >= tuneAdjustmentDelay
if (intervals_[0] < intervals_[1]) revert Auctioneer_InvalidParams();
BondMetadata storage meta = metadata[id_];
// Check that tuneInterval >= depositInterval
if (intervals_[0] < meta.depositInterval) revert Auctioneer_InvalidParams();
// Check that debtDecayInterval >= minDebtDecayInterval
if (intervals_[2] < minDebtDecayInterval) revert Auctioneer_InvalidParams();
// Check that sender is market owner
BondMarket memory market = markets[id_];
if (msg.sender != market.owner) revert Auctioneer_OnlyMarketOwner();
// Update intervals
meta.tuneInterval = intervals_[0];
meta.tuneIntervalCapacity = market.capacity.mulDiv(
uint256(intervals_[0]),
uint256(terms[id_].conclusion) - block.timestamp
); // don't have a stored value for market duration, this will update tuneIntervalCapacity based on time remaining
meta.tuneAdjustmentDelay = intervals_[1];
meta.debtDecayInterval = intervals_[2];
}
/// @inheritdoc IBondAuctioneer
function pushOwnership(uint256 id_, address newOwner_) external override {
if (msg.sender != markets[id_].owner) revert Auctioneer_OnlyMarketOwner();
newOwners[id_] = newOwner_;
}
/// @inheritdoc IBondAuctioneer
function pullOwnership(uint256 id_) external override {
if (msg.sender != newOwners[id_]) revert Auctioneer_NotAuthorized();
markets[id_].owner = newOwners[id_];
}
/// @inheritdoc IBondAuctioneer
function setDefaults(uint32[6] memory defaults_) external override requiresAuth {
// Restricted to authorized addresses, initially restricted to policy
defaultTuneInterval = defaults_[0];
defaultTuneAdjustment = defaults_[1];
minDebtDecayInterval = defaults_[2];
minDepositInterval = defaults_[3];
minMarketDuration = defaults_[4];
minDebtBuffer = defaults_[5];
}
/// @inheritdoc IBondAuctioneer
function setAllowNewMarkets(bool status_) external override requiresAuth {
// Restricted to authorized addresses, initially restricted to guardian
allowNewMarkets = status_;
}
/// @inheritdoc IBondAuctioneer
function setCallbackAuthStatus(address creator_, bool status_) external override requiresAuth {
// Restricted to authorized addresses, initially restricted to guardian
callbackAuthorized[creator_] = status_;
}
/// @inheritdoc IBondAuctioneer
function closeMarket(uint256 id_) external override {
if (msg.sender != markets[id_].owner) revert Auctioneer_OnlyMarketOwner();
_close(id_);
}
/* ========== TELLER FUNCTIONS ========== */
/// @inheritdoc IBondAuctioneer
function purchaseBond(
uint256 id_,
uint256 amount_,
uint256 minAmountOut_
) external override returns (uint256 payout) {
if (msg.sender != address(_teller)) revert Auctioneer_NotAuthorized();
BondMarket storage market = markets[id_];
BondTerms memory term = terms[id_];
// Markets end at a defined timestamp
uint48 currentTime = uint48(block.timestamp);
if (currentTime >= term.conclusion) revert Auctioneer_MarketConcluded(term.conclusion);
uint256 price;
(price, payout) = _decayAndGetPrice(id_, amount_, uint48(block.timestamp)); // Debt and the control variable decay over time
// Payout must be greater than user inputted minimum
if (payout < minAmountOut_) revert Auctioneer_AmountLessThanMinimum();
// Markets have a max payout amount, capping size because deposits
// do not experience slippage. max payout is recalculated upon tuning
if (payout > market.maxPayout) revert Auctioneer_MaxPayoutExceeded();
// Update Capacity and Debt values
// Capacity is either the number of payout tokens that the market can sell
// (if capacity in quote is false),
//
// or the number of quote tokens that the market can buy
// (if capacity in quote is true)
// If amount/payout is greater than capacity remaining, revert
if (market.capacityInQuote ? amount_ > market.capacity : payout > market.capacity)
revert Auctioneer_NotEnoughCapacity();
unchecked {
// Capacity is decreased by the deposited or paid amount
market.capacity -= market.capacityInQuote ? amount_ : payout;
// Incrementing total debt raises the price of the next bond
market.totalDebt += payout + 1; // add 1 to satisfy price inequality
// Markets keep track of how many quote tokens have been
// purchased, and how many payout tokens have been sold
market.purchased += amount_;
market.sold += payout;
}
// Circuit breaker. If max debt is breached, the market is closed
if (term.maxDebt < market.totalDebt) {
_close(id_);
} else {
// If market will continue, the control variable is tuned to hit targets on time
_tune(id_, currentTime, price);
}
}
/* ========== INTERNAL DEPO FUNCTIONS ========== */
/// @notice Close a market
/// @dev Closing a market sets capacity to 0 and immediately stops bonding
function _close(uint256 id_) internal {
terms[id_].conclusion = uint48(block.timestamp);
markets[id_].capacity = 0;
emit MarketClosed(id_);
}
/// @notice Decay debt, and adjust control variable if there is an active change
/// @param id_ ID of market
/// @param amount_ Amount of quote tokens being purchased
/// @param time_ Current timestamp (saves gas when passed in)
/// @return marketPrice_ Current market price of bond, accounting for decay
/// @return payout_ Amount of payout tokens received at current price
function _decayAndGetPrice(
uint256 id_,
uint256 amount_,
uint48 time_
) internal returns (uint256 marketPrice_, uint256 payout_) {
BondMarket memory market = markets[id_];
// Debt is a time-decayed sum of tokens spent in a market
// Debt is added when deposits occur and removed over time
// |
// | debt falls with
// | / \ inactivity / \
// | / \ /\ / \
// | \ / \ / \
// | \ /\/
// | \ / and rises
// | with deposits
// |
// |------------------------------------| t
if (uint256(metadata[id_].lastDecay) <= block.timestamp)
markets[id_].totalDebt -= _debtDecay(id_);
// Control variable decay
// The bond control variable is continually tuned. When it is lowered (which
// lowers the market price), the change is carried out smoothly over time.
if (adjustments[id_].active) {
Adjustment storage adjustment = adjustments[id_];
(uint256 adjustBy, uint48 secondsSince, bool stillActive) = _controlDecay(id_);
terms[id_].controlVariable -= adjustBy;
if (stillActive) {
adjustment.change -= adjustBy;
adjustment.timeToAdjusted -= secondsSince;
adjustment.lastAdjustment = time_;
} else {
adjustment.active = false;
}
}
// Price is not allowed to be lower than the minimum price
marketPrice_ = _currentMarketPrice(id_);
uint256 minPrice = market.minPrice;
if (marketPrice_ < minPrice) marketPrice_ = minPrice;
// Payout for the deposit = amount / price
//
// where:
// payout = payout tokens out
// amount = quote tokens in
// price = quote tokens : payout token (i.e. 200 QUOTE : BASE), adjusted for scaling
payout_ = amount_.mulDiv(market.scale, marketPrice_);
// Set last decay timestamp based on size of purchase to linearize decay
// 1e9 is used a scaling constant since the decay interval is unlikely to exceed 1e9 seconds (> 30 years)
// It is also low enough to avoid overflows from high debt values
uint256 debtPerSecond = metadata[id_].lastTuneDebt.mulDiv(
1e9,
uint256(metadata[id_].debtDecayInterval)
);
metadata[id_].lastDecay += uint48(payout_.mulDivUp(1e9, debtPerSecond));
}
/// @notice Auto-adjust control variable to hit capacity/spend target
/// @param id_ ID of market
/// @param time_ Timestamp (saves gas when passed in)
/// @param price_ Current price of the market
function _tune(
uint256 id_,
uint48 time_,
uint256 price_
) internal {
BondMetadata memory meta = metadata[id_];
BondMarket memory market = markets[id_];
// Market tunes in 2 situations:
// 1. If capacity has exceeded target since last tune adjustment and the market is oversold
// 2. If a tune interval has passed since last tune adjustment and the market is undersold
//
// Intuition:
// Markets are created with a target capacity with the expectation that capacity will
// be utilized evenly over the duration of the market.
// The intuition with tuning is:
// - When the market is ahead of target capacity, we should tune based on capacity.
// - When the market is behind target capacity, we should tune based on time.
// Compute seconds remaining until market will conclude
uint256 timeRemaining = uint256(terms[id_].conclusion - time_);
// Standardize capacity into an payout token amount
uint256 capacity = market.capacityInQuote
? market.capacity.mulDiv(market.scale, price_)
: market.capacity;
// Calculate initial capacity based on remaining capacity and amount sold/purchased up to this point
uint256 initialCapacity = capacity +
(market.capacityInQuote ? market.purchased.mulDiv(market.scale, price_) : market.sold);
// Calculate timeNeutralCapacity as the capacity expected to be sold up to this point and the current capacity
// Higher than initial capacity means the market is undersold, lower than initial capacity means the market is oversold
uint256 timeNeutralCapacity = initialCapacity.mulDiv(
uint256(meta.length) - timeRemaining,
uint256(meta.length)
) + capacity;
if (
(market.capacity < meta.tuneBelowCapacity && timeNeutralCapacity < initialCapacity) ||
(time_ >= meta.lastTune + meta.tuneInterval && timeNeutralCapacity > initialCapacity)
) {
// Calculate the correct payout to complete on time assuming each bond
// will be max size in the desired deposit interval for the remaining time
//
// i.e. market has 10 days remaining. deposit interval is 1 day. capacity
// is 10,000 TOKEN. max payout would be 1,000 TOKEN (10,000 * 1 / 10).
markets[id_].maxPayout = capacity.mulDiv(uint256(meta.depositInterval), timeRemaining);
// Calculate ideal target debt to satisty capacity in the remaining time
// The target debt is based on whether the market is under or oversold at this point in time
// This target debt will ensure price is reactive while ensuring the magnitude of being over/undersold
// doesn't cause larger fluctuations towards the end of the market.
//
// Calculate target debt from the timeNeutralCapacity and the ratio of debt decay interval and the length of the market
uint256 targetDebt = timeNeutralCapacity.mulDiv(
uint256(meta.debtDecayInterval),
uint256(meta.length)
);
// Derive a new control variable from the target debt
uint256 controlVariable = terms[id_].controlVariable;
uint256 newControlVariable = price_.mulDivUp(market.scale, targetDebt);
emit Tuned(id_, controlVariable, newControlVariable);
if (newControlVariable < controlVariable) {
// If decrease, control variable change will be carried out over the tune interval
// this is because price will be lowered
uint256 change = controlVariable - newControlVariable;
adjustments[id_] = Adjustment(change, time_, meta.tuneAdjustmentDelay, true);
} else {
// Tune up immediately
terms[id_].controlVariable = newControlVariable;
// Set current adjustment to inactive (e.g. if we are re-tuning early)
adjustments[id_].active = false;
}
metadata[id_].lastTune = time_;
metadata[id_].tuneBelowCapacity = market.capacity > meta.tuneIntervalCapacity
? market.capacity - meta.tuneIntervalCapacity
: 0;
metadata[id_].lastTuneDebt = targetDebt;
}
}
/* ========== INTERNAL VIEW FUNCTIONS ========== */
/// @notice Calculate current market price of payout token in quote tokens
/// @dev See marketPrice() in IBondAuctioneer for explanation of price computation
/// @dev Uses info from storage because data has been updated before call (vs marketPrice())
/// @param id_ Market ID
/// @return Price for market in payout token decimals
function _currentMarketPrice(uint256 id_) internal view returns (uint256) {
BondMarket memory market = markets[id_];
return terms[id_].controlVariable.mulDiv(market.totalDebt, market.scale);
}
/// @notice Amount of debt to decay from total debt for market ID
/// @param id_ ID of market
/// @return Amount of debt to decay
function _debtDecay(uint256 id_) internal view returns (uint256) {
BondMetadata memory meta = metadata[id_];
uint256 lastDecay = uint256(meta.lastDecay);
uint256 currentTime = block.timestamp;
uint256 secondsSince;
unchecked {
secondsSince = currentTime > lastDecay ? currentTime - lastDecay : 0;
}
return
secondsSince > meta.debtDecayInterval
? markets[id_].totalDebt
: markets[id_].totalDebt.mulDiv(secondsSince, uint256(meta.debtDecayInterval));
}
/// @notice Amount to decay control variable by
/// @param id_ ID of market
/// @return decay change in control variable
/// @return secondsSince seconds since last change in control variable
/// @return active whether or not change remains active
function _controlDecay(uint256 id_)
internal
view
returns (
uint256 decay,
uint48 secondsSince,
bool active
)
{
Adjustment memory info = adjustments[id_];
if (!info.active) return (0, 0, false);
secondsSince = uint48(block.timestamp) - info.lastAdjustment;
active = secondsSince < info.timeToAdjusted;
decay = active
? info.change.mulDiv(uint256(secondsSince), uint256(info.timeToAdjusted))
: info.change;
}
/* ========== EXTERNAL VIEW FUNCTIONS ========== */
/// @inheritdoc IBondAuctioneer
function getMarketInfoForPurchase(uint256 id_)
external
view
returns (
address owner,
address callbackAddr,
ERC20 payoutToken,
ERC20 quoteToken,
uint48 vesting,
uint256 maxPayout
)
{
BondMarket memory market = markets[id_];
return (
market.owner,
market.callbackAddr,
market.payoutToken,
market.quoteToken,
terms[id_].vesting,
market.maxPayout
);
}
/// @inheritdoc IBondSDA
function marketPrice(uint256 id_) public view override returns (uint256) {
uint256 price = currentControlVariable(id_).mulDivUp(currentDebt(id_), markets[id_].scale);
return (price > markets[id_].minPrice) ? price : markets[id_].minPrice;
}
/// @inheritdoc IBondAuctioneer
function marketScale(uint256 id_) external view override returns (uint256) {
return markets[id_].scale;
}
/// @inheritdoc IBondAuctioneer
function payoutFor(
uint256 amount_,
uint256 id_,
address referrer_
) public view override returns (uint256) {
// Calculate the payout for the given amount of tokens
uint256 fee = amount_.mulDiv(_teller.getFee(referrer_), 1e5);
uint256 payout = (amount_ - fee).mulDiv(markets[id_].scale, marketPrice(id_));
// Check that the payout is less than or equal to the maximum payout,
// Revert if not, otherwise return the payout
if (payout > markets[id_].maxPayout) {
revert Auctioneer_MaxPayoutExceeded();
} else {
return payout;
}
}
/// @inheritdoc IBondAuctioneer
function maxAmountAccepted(uint256 id_, address referrer_) external view returns (uint256) {
// Calculate maximum amount of quote tokens that correspond to max bond size
// Maximum of the maxPayout and the remaining capacity converted to quote tokens
BondMarket memory market = markets[id_];
uint256 price = marketPrice(id_);
uint256 quoteCapacity = market.capacityInQuote
? market.capacity
: market.capacity.mulDiv(price, market.scale);
uint256 maxQuote = market.maxPayout.mulDiv(price, market.scale);
uint256 amountAccepted = quoteCapacity < maxQuote ? quoteCapacity : maxQuote;
// Take into account teller fees and return
// Estimate fee based on amountAccepted. Fee taken will be slightly larger than
// this given it will be taken off the larger amount, but this avoids rounding
// errors with trying to calculate the exact amount.
// Therefore, the maxAmountAccepted is slightly conservative.
uint256 estimatedFee = amountAccepted.mulDiv(_teller.getFee(referrer_), 1e5);
return amountAccepted + estimatedFee;
}
/// @inheritdoc IBondSDA
function currentDebt(uint256 id_) public view override returns (uint256) {
return markets[id_].totalDebt - _debtDecay(id_);
}
/// @inheritdoc IBondSDA
function currentControlVariable(uint256 id_) public view override returns (uint256) {
(uint256 decay, , ) = _controlDecay(id_);
return terms[id_].controlVariable - decay;
}
/// @inheritdoc IBondAuctioneer
function isInstantSwap(uint256 id_) public view returns (bool) {
uint256 vesting = terms[id_].vesting;
return (vesting <= MAX_FIXED_TERM) ? vesting == 0 : vesting <= block.timestamp;
}
/// @inheritdoc IBondAuctioneer
function isLive(uint256 id_) public view override returns (bool) {
return (markets[id_].capacity != 0 && terms[id_].conclusion > block.timestamp);
}
/// @inheritdoc IBondAuctioneer
function ownerOf(uint256 id_) external view override returns (address) {
return markets[id_].owner;
}
/// @inheritdoc IBondAuctioneer
function getTeller() external view override returns (IBondTeller) {
return _teller;
}
/// @inheritdoc IBondAuctioneer
function getAggregator() external view override returns (IBondAggregator) {
return _aggregator;
}
/// @inheritdoc IBondAuctioneer
function currentCapacity(uint256 id_) external view override returns (uint256) {
return markets[id_].capacity;
}
}
/
// SPDX-License-Identifier: AGPL-3.0
pragma solidity >=0.8.0;
import {ERC20} from "solmate/tokens/ERC20.sol";
interface IBondCallback {
/// @notice Send payout tokens to Teller while allowing market owners to perform custom logic on received or paid out tokens
/// @notice Market ID on Teller must be whitelisted
/// @param id_ ID of the market
/// @param inputAmount_ Amount of quote tokens bonded to the market
/// @param outputAmount_ Amount of payout tokens to be paid out to the market
/// @dev Must transfer the output amount of payout tokens back to the Teller
/// @dev Should check that the quote tokens have been transferred to the contract in the _callback function
function callback(
uint256 id_,
uint256 inputAmount_,
uint256 outputAmount_
) external;
/// @notice Returns the number of quote tokens received and payout tokens paid out for a market
/// @param id_ ID of the market
/// @return in_ Amount of quote tokens bonded to the market
/// @return out_ Amount of payout tokens paid out to the market
function amountsForMarket(uint256 id_) external view returns (uint256 in_, uint256 out_);
/// @notice Whitelist a teller and market ID combination
/// @notice Must be callback owner
/// @param teller_ Address of the Teller contract which serves the market
/// @param id_ ID of the market
function whitelist(address teller_, uint256 id_) external;
/// @notice Withdraw tokens from the callback and update balances
/// @notice Only callback owner
/// @param to_ Address of the recipient
/// @param token_ Address of the token to withdraw
/// @param amount_ Amount of tokens to withdraw
function withdraw(
address to_,
ERC20 token_,
uint256 amount_
) external;
/// @notice Deposit tokens to the callback and update balances
/// @notice Only callback owner
/// @param token_ Address of the token to deposit
/// @param amount_ Amount of tokens to deposit
function deposit(ERC20 token_, uint256 amount_) external;
}
/
// SPDX-License-Identifier: AGPL-3.0
pragma solidity >=0.8.0;
import {ERC20} from "solmate/tokens/ERC20.sol";
interface IBondTeller {
/// @notice Exchange quote tokens for a bond in a specified market
/// @param recipient_ Address of recipient of bond. Allows deposits for other addresses
/// @param referrer_ Address of referrer who will receive referral fee. For frontends to fill.
/// Direct calls can use the zero address for no referrer fee.
/// @param id_ ID of the Market the bond is being purchased from
/// @param amount_ Amount to deposit in exchange for bond
/// @param minAmountOut_ Minimum acceptable amount of bond to receive. Prevents frontrunning
/// @return Amount of payout token to be received from the bond
/// @return Timestamp at which the bond token can be redeemed for the underlying token
function purchase(
address recipient_,
address referrer_,
uint256 id_,
uint256 amount_,
uint256 minAmountOut_
) external returns (uint256, uint48);
/// @notice Get current fee charged by the teller based on the combined protocol and referrer fee
/// @param referrer_ Address of the referrer
/// @return Fee in basis points (3 decimal places)
function getFee(address referrer_) external view returns (uint48);
/// @notice Set protocol fee
/// @notice Must be guardian
/// @param fee_ Protocol fee in basis points (3 decimal places)
function setProtocolFee(uint48 fee_) external;
/// @notice Set your fee as a referrer to the protocol
/// @notice Fee is set for sending address
/// @param fee_ Referrer fee in basis points (3 decimal places)
function setReferrerFee(uint48 fee_) external;
/// @notice Claim fees accrued for input tokens and sends to protocol
/// @notice Must be guardian
/// @param tokens_ Array of tokens to claim fees for
/// @param to_ Address to send fees to
function claimFees(ERC20[] memory tokens_, address to_) external;
}
Compiler Settings
{"remappings":[":clones-with-immutable-args/=lib/clones-with-immutable-args/src/",":clones/=lib/clones-with-immutable-args/src/",":ds-test/=lib/ds-test/src/",":forge-std/=lib/forge-std/src/",":hardhat/=node_modules/hardhat/",":openzeppelin-contracts/=lib/openzeppelin-contracts/",":openzeppelin/=lib/openzeppelin-contracts/contracts/",":solmate/=lib/solmate/src/",":weird-erc20/=lib/solmate/lib/weird-erc20/src/"],"optimizer":{"runs":100000,"enabled":true},"metadata":{"bytecodeHash":"ipfs"},"libraries":{},"evmVersion":"london","compilationTarget":{"src/BondFixedExpirySDA.sol":"BondFixedExpirySDA"}}
Contract ABI
[{"type":"constructor","stateMutability":"nonpayable","inputs":[{"type":"address","name":"teller_","internalType":"contract IBondTeller"},{"type":"address","name":"aggregator_","internalType":"contract IBondAggregator"},{"type":"address","name":"guardian_","internalType":"address"},{"type":"address","name":"authority_","internalType":"contract Authority"}]},{"type":"error","name":"Auctioneer_AmountLessThanMinimum","inputs":[]},{"type":"error","name":"Auctioneer_BadExpiry","inputs":[]},{"type":"error","name":"Auctioneer_InitialPriceLessThanMin","inputs":[]},{"type":"error","name":"Auctioneer_InvalidCallback","inputs":[]},{"type":"error","name":"Auctioneer_InvalidParams","inputs":[]},{"type":"error","name":"Auctioneer_MarketConcluded","inputs":[{"type":"uint256","name":"conclusion_","internalType":"uint256"}]},{"type":"error","name":"Auctioneer_MaxPayoutExceeded","inputs":[]},{"type":"error","name":"Auctioneer_NewMarketsNotAllowed","inputs":[]},{"type":"error","name":"Auctioneer_NotAuthorized","inputs":[]},{"type":"error","name":"Auctioneer_NotEnoughCapacity","inputs":[]},{"type":"error","name":"Auctioneer_OnlyMarketOwner","inputs":[]},{"type":"event","name":"AuthorityUpdated","inputs":[{"type":"address","name":"user","internalType":"address","indexed":true},{"type":"address","name":"newAuthority","internalType":"contract Authority","indexed":true}],"anonymous":false},{"type":"event","name":"MarketClosed","inputs":[{"type":"uint256","name":"id","internalType":"uint256","indexed":true}],"anonymous":false},{"type":"event","name":"MarketCreated","inputs":[{"type":"uint256","name":"id","internalType":"uint256","indexed":true},{"type":"address","name":"payoutToken","internalType":"address","indexed":true},{"type":"address","name":"quoteToken","internalType":"address","indexed":true},{"type":"uint48","name":"vesting","internalType":"uint48","indexed":false},{"type":"uint256","name":"initialPrice","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"OwnerUpdated","inputs":[{"type":"address","name":"user","internalType":"address","indexed":true},{"type":"address","name":"newOwner","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"Tuned","inputs":[{"type":"uint256","name":"id","internalType":"uint256","indexed":true},{"type":"uint256","name":"oldControlVariable","internalType":"uint256","indexed":false},{"type":"uint256","name":"newControlVariable","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"change","internalType":"uint256"},{"type":"uint48","name":"lastAdjustment","internalType":"uint48"},{"type":"uint48","name":"timeToAdjusted","internalType":"uint48"},{"type":"bool","name":"active","internalType":"bool"}],"name":"adjustments","inputs":[{"type":"uint256","name":"","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"allowNewMarkets","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract Authority"}],"name":"authority","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"callbackAuthorized","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"closeMarket","inputs":[{"type":"uint256","name":"id_","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"createMarket","inputs":[{"type":"bytes","name":"params_","internalType":"bytes"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"currentCapacity","inputs":[{"type":"uint256","name":"id_","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"currentControlVariable","inputs":[{"type":"uint256","name":"id_","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"currentDebt","inputs":[{"type":"uint256","name":"id_","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint32","name":"","internalType":"uint32"}],"name":"defaultTuneAdjustment","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint32","name":"","internalType":"uint32"}],"name":"defaultTuneInterval","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IBondAggregator"}],"name":"getAggregator","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"owner","internalType":"address"},{"type":"address","name":"callbackAddr","internalType":"address"},{"type":"address","name":"payoutToken","internalType":"contract ERC20"},{"type":"address","name":"quoteToken","internalType":"contract ERC20"},{"type":"uint48","name":"vesting","internalType":"uint48"},{"type":"uint256","name":"maxPayout","internalType":"uint256"}],"name":"getMarketInfoForPurchase","inputs":[{"type":"uint256","name":"id_","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IBondTeller"}],"name":"getTeller","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"isInstantSwap","inputs":[{"type":"uint256","name":"id_","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"isLive","inputs":[{"type":"uint256","name":"id_","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"marketPrice","inputs":[{"type":"uint256","name":"id_","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"marketScale","inputs":[{"type":"uint256","name":"id_","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"owner","internalType":"address"},{"type":"address","name":"payoutToken","internalType":"contract ERC20"},{"type":"address","name":"quoteToken","internalType":"contract ERC20"},{"type":"address","name":"callbackAddr","internalType":"address"},{"type":"bool","name":"capacityInQuote","internalType":"bool"},{"type":"uint256","name":"capacity","internalType":"uint256"},{"type":"uint256","name":"totalDebt","internalType":"uint256"},{"type":"uint256","name":"minPrice","internalType":"uint256"},{"type":"uint256","name":"maxPayout","internalType":"uint256"},{"type":"uint256","name":"sold","internalType":"uint256"},{"type":"uint256","name":"purchased","internalType":"uint256"},{"type":"uint256","name":"scale","internalType":"uint256"}],"name":"markets","inputs":[{"type":"uint256","name":"","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"maxAmountAccepted","inputs":[{"type":"uint256","name":"id_","internalType":"uint256"},{"type":"address","name":"referrer_","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint48","name":"lastTune","internalType":"uint48"},{"type":"uint48","name":"lastDecay","internalType":"uint48"},{"type":"uint32","name":"length","internalType":"uint32"},{"type":"uint32","name":"depositInterval","internalType":"uint32"},{"type":"uint32","name":"tuneInterval","internalType":"uint32"},{"type":"uint32","name":"tuneAdjustmentDelay","internalType":"uint32"},{"type":"uint32","name":"debtDecayInterval","internalType":"uint32"},{"type":"uint256","name":"tuneIntervalCapacity","internalType":"uint256"},{"type":"uint256","name":"tuneBelowCapacity","internalType":"uint256"},{"type":"uint256","name":"lastTuneDebt","internalType":"uint256"}],"name":"metadata","inputs":[{"type":"uint256","name":"","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint32","name":"","internalType":"uint32"}],"name":"minDebtBuffer","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint32","name":"","internalType":"uint32"}],"name":"minDebtDecayInterval","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint32","name":"","internalType":"uint32"}],"name":"minDepositInterval","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint32","name":"","internalType":"uint32"}],"name":"minMarketDuration","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"newOwners","inputs":[{"type":"uint256","name":"","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"owner","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"ownerOf","inputs":[{"type":"uint256","name":"id_","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"payoutFor","inputs":[{"type":"uint256","name":"amount_","internalType":"uint256"},{"type":"uint256","name":"id_","internalType":"uint256"},{"type":"address","name":"referrer_","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"pullOwnership","inputs":[{"type":"uint256","name":"id_","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256","name":"payout","internalType":"uint256"}],"name":"purchaseBond","inputs":[{"type":"uint256","name":"id_","internalType":"uint256"},{"type":"uint256","name":"amount_","internalType":"uint256"},{"type":"uint256","name":"minAmountOut_","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"pushOwnership","inputs":[{"type":"uint256","name":"id_","internalType":"uint256"},{"type":"address","name":"newOwner_","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setAllowNewMarkets","inputs":[{"type":"bool","name":"status_","internalType":"bool"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setAuthority","inputs":[{"type":"address","name":"newAuthority","internalType":"contract Authority"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setCallbackAuthStatus","inputs":[{"type":"address","name":"creator_","internalType":"address"},{"type":"bool","name":"status_","internalType":"bool"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setDefaults","inputs":[{"type":"uint32[6]","name":"defaults_","internalType":"uint32[6]"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setIntervals","inputs":[{"type":"uint256","name":"id_","internalType":"uint256"},{"type":"uint32[3]","name":"intervals_","internalType":"uint32[3]"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setOwner","inputs":[{"type":"address","name":"newOwner","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"controlVariable","internalType":"uint256"},{"type":"uint256","name":"maxDebt","internalType":"uint256"},{"type":"uint48","name":"vesting","internalType":"uint48"},{"type":"uint48","name":"conclusion","internalType":"uint48"}],"name":"terms","inputs":[{"type":"uint256","name":"","internalType":"uint256"}]}]
Contract Creation Code
0x60c06040523480156200001157600080fd5b50604051620044273803806200442783398101604081905262000034916200013d565b600080546001600160a01b03199081166001600160a01b0385811691821784556001805490931690851617909155604051869286928692869284928492909133917f8292fce18fa69edf4db7b94ea2e58241df0ae57f97e0a6c9b29067028bf92d769190a36040516001600160a01b0382169033907fa3396fd7f6e0a21b50e5089d2da70d5ac0a3bbbd1f617a93f134b7638998019890600090a3505050506001600160a01b039081166080521660a0525050600980546001600160c01b0319167527100001518000000e100003f48000000e100001518017905550506007805460ff19166001179055620001a5565b6001600160a01b03811681146200013a57600080fd5b50565b600080600080608085870312156200015457600080fd5b8451620001618162000124565b6020860151909450620001748162000124565b6040860151909350620001878162000124565b60608601519092506200019a8162000124565b939692955090935050565b60805160a051614239620001ee6000396000818161051001528181610e3e0152818161199401528181611c260152611cd901526000818161034801526124fb01526142396000f3fe608060405234801561001057600080fd5b50600436106102ad5760003560e01c8063acc5570c1161017b578063bf7e214f116100d8578063d9ccdc931161008c578063e3684e3911610071578063e3684e39146109af578063e922067314610aed578063ea0aca3314610b0957600080fd5b8063d9ccdc931461098c578063e007fa971461099c57600080fd5b8063c7bf8ca0116100bd578063c7bf8ca014610935578063d204068714610948578063d2bee3231461096857600080fd5b8063bf7e214f146108a2578063c0aa0e8a146108c257600080fd5b8063bc3b2b121161012f578063bcf6cde811610114578063bcf6cde814610869578063bd1f3a5e1461087c578063bf48582b1461088f57600080fd5b8063bc3b2b12146107d2578063bcb296671461085657600080fd5b8063afa9d3b011610160578063afa9d3b0146106a1578063b1283e77146106ae578063bbbdd95a146107bf57600080fd5b8063acc5570c14610534578063ae4180951461068e57600080fd5b80635f77274e116102295780638973082c116101dd5780638da5cb5b116101c25780638da5cb5b146104cb578063946824cd146104eb5780639787d1071461050e57600080fd5b80638973082c1461047b5780638b098db3146104b857600080fd5b80636729a41e1161020e5780636729a41e1461041f578063699e17d9146104555780637a9e5e4b1461046857600080fd5b80635f77274e146103d65780636352211e146103e957600080fd5b806327507458116102805780633adec5a7116102655780633adec5a71461038d57806353c7f8e0146103a05780635dc4d16b146103b357600080fd5b806327507458146103235780633ad59dbc1461034657600080fd5b80630a9d85eb146102b257806310b05317146102d857806313af4035146102ed5780631c063a6c14610300575b600080fd5b6102c56102c0366004613c4b565b610b21565b6040519081526020015b60405180910390f35b6102eb6102e6366004613c4b565b610b53565b005b6102eb6102fb366004613c96565b610c0e565b6102c561030e366004613c4b565b60009081526002602052604090206004015490565b610336610331366004613c4b565b610d17565b60405190151581526020016102cf565b7f00000000000000000000000000000000000000000000000000000000000000005b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016102cf565b6102c561039b366004613c4b565b610d60565b6102c56103ae366004613cb3565b610dc3565b6103366103c1366004613c96565b60086020526000908152604090205460ff1681565b6102eb6103e4366004613d3e565b610eb4565b6103686103f7366004613c4b565b60009081526002602052604090205473ffffffffffffffffffffffffffffffffffffffff1690565b61036861042d366004613c4b565b60066020526000908152604090205473ffffffffffffffffffffffffffffffffffffffff1681565b6102eb610463366004613d5b565b610f79565b6102eb610476366004613c96565b6113b5565b6009546104a39074010000000000000000000000000000000000000000900463ffffffff1681565b60405163ffffffff90911681526020016102cf565b6103366104c6366004613c4b565b611512565b6000546103689073ffffffffffffffffffffffffffffffffffffffff1681565b6102c56104f9366004613c4b565b6000908152600260205260409020600a015490565b7f0000000000000000000000000000000000000000000000000000000000000000610368565b610638610542366004613c4b565b6000818152600260208181526040808420815161018081018352815473ffffffffffffffffffffffffffffffffffffffff90811680835260018401548216838701819052848801548316848701819052600380870154948516606087018190527401000000000000000000000000000000000000000090950460ff1615156080870152600487015460a0870152600587015460c0870152600687015460e08701526007870154610100870190815260088801546101208801526009880154610140880152600a90970154610160909601959095529989529290955292909520909301549251919590949265ffffffffffff169190565b6040805173ffffffffffffffffffffffffffffffffffffffff97881681529587166020870152938616938501939093529316606083015265ffffffffffff909216608082015260a081019190915260c0016102cf565b6102eb61069c366004613c4b565b611548565b6007546103369060ff1681565b6107456106bc366004613c4b565b600260208190526000918252604090912080546001820154928201546003830154600484015460058501546006860154600787015460088801546009890154600a9099015473ffffffffffffffffffffffffffffffffffffffff9889169a891699978916988716977401000000000000000000000000000000000000000090970460ff1696908c565b6040805173ffffffffffffffffffffffffffffffffffffffff9d8e1681529b8d1660208d0152998c16998b0199909952999096166060890152931515608088015260a087019290925260c086015260e0850152610100840152610120830152610140820192909252610160810191909152610180016102cf565b6102eb6107cd366004613d8e565b6115b1565b6108256107e0366004613c4b565b6005602052600090815260409020805460019091015465ffffffffffff8082169166010000000000008104909116906c01000000000000000000000000900460ff1684565b6040516102cf949392919093845265ffffffffffff9283166020850152911660408301521515606082015260800190565b6102c5610864366004613c4b565b61169b565b6102eb610877366004613dc7565b6116c2565b6102eb61088a366004613e59565b611772565b6102c561089d366004613ede565b611946565b6001546103689073ffffffffffffffffffffffffffffffffffffffff1681565b6109076108d0366004613c4b565b60036020526000908152604090208054600182015460029092015490919065ffffffffffff80821691660100000000000090041684565b60408051948552602085019390935265ffffffffffff918216928401929092521660608201526080016102cf565b6102c5610943366004613dc7565b611a9f565b6009546104a3906c01000000000000000000000000900463ffffffff1681565b6009546104a390700100000000000000000000000000000000900463ffffffff1681565b6009546104a39063ffffffff1681565b6102c56109aa366004613f17565b611cbf565b610a876109bd366004613c4b565b600460205260009081526040902080546001820154600283015460039093015465ffffffffffff80841694660100000000000085049091169363ffffffff6c0100000000000000000000000082048116947001000000000000000000000000000000008304821694740100000000000000000000000000000000000000008404831694780100000000000000000000000000000000000000000000000085048416947c0100000000000000000000000000000000000000000000000000000000900490931692908a565b6040805165ffffffffffff9b8c1681529a90991660208b015263ffffffff978816988a01989098529486166060890152928516608088015290841660a08701529290921660c085015260e0840191909152610100830152610120820152610140016102cf565b6009546104a39068010000000000000000900463ffffffff1681565b6009546104a390640100000000900463ffffffff1681565b600080610b2d83611f59565b5050600084815260036020526040902054909150610b4c908290613f72565b9392505050565b60008181526006602052604090205473ffffffffffffffffffffffffffffffffffffffff163314610bb0576040517f2c47703200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600090815260066020908152604080832054600290925290912080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff909216919091179055565b610c3c336000357fffffffff0000000000000000000000000000000000000000000000000000000016612034565b610ca7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f554e415554484f52495a4544000000000000000000000000000000000000000060448201526064015b60405180910390fd5b600080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83169081178255604051909133917f8292fce18fa69edf4db7b94ea2e58241df0ae57f97e0a6c9b29067028bf92d769190a350565b60008181526002602052604081206004015415801590610d5a575060008281526003602052604090206002015442660100000000000090910465ffffffffffff16115b92915050565b600080610d91610d6f8461169b565b6000858152600260205260409020600a0154610d8a86610b21565b9190612145565b6000848152600260205260409020600601549091508111610d5a57600083815260026020526040902060060154610b4c565b600080610dd283850185613fba565b90506000610ddf826121a3565b82516101008401516040517fc6e38a4b00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff928316600482015265ffffffffffff90911660248201529192507f0000000000000000000000000000000000000000000000000000000000000000169063c6e38a4b906044016020604051808303816000875af1158015610e87573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610eab9190614091565b50949350505050565b610ee2336000357fffffffff0000000000000000000000000000000000000000000000000000000016612034565b610f48576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f554e415554484f52495a454400000000000000000000000000000000000000006044820152606401610c9e565b600780547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016911515919091179055565b610f8660208201826140ae565b63ffffffff161580610fab5750610fa360408201602083016140ae565b63ffffffff16155b80610fc95750610fc160608201604083016140ae565b63ffffffff16155b15611000576040517f3b596f5f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61101060408201602083016140ae565b63ffffffff1661102360208301836140ae565b63ffffffff161015611061576040517f3b596f5f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008281526004602090815260409091208054909170010000000000000000000000000000000090910463ffffffff169061109e908401846140ae565b63ffffffff1610156110dc576040517f3b596f5f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60095468010000000000000000900463ffffffff1661110160608401604085016140ae565b63ffffffff16101561113f576040517f3b596f5f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600083815260026020818152604092839020835161018081018552815473ffffffffffffffffffffffffffffffffffffffff908116808352600184015482169483019490945293820154841694810194909452600381015492831660608501527401000000000000000000000000000000000000000090920460ff1615156080840152600482015460a0840152600582015460c0840152600682015460e0840152600782015461010084015260088201546101208401526009820154610140840152600a909101546101608301523314611245576040517f4e1c8b5d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61125260208401846140ae565b825463ffffffff9190911674010000000000000000000000000000000000000000027fffffffffffffffff00000000ffffffffffffffffffffffffffffffffffffffff9091161782556112f06112ab60208501856140ae565b60008681526003602052604090206002015463ffffffff91909116906112e49042906601000000000000900465ffffffffffff16613f72565b60a08401519190612e71565b600183015561130560408401602085016140ae565b825463ffffffff919091167801000000000000000000000000000000000000000000000000027fffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffffff90911617825561136260608401604085016140ae565b825463ffffffff919091167c0100000000000000000000000000000000000000000000000000000000027bffffffffffffffffffffffffffffffffffffffffffffffffffffffff90911617909155505050565b60005473ffffffffffffffffffffffffffffffffffffffff1633148061149857506001546040517fb70096130000000000000000000000000000000000000000000000000000000081523360048201523060248201526000357fffffffff0000000000000000000000000000000000000000000000000000000016604482015273ffffffffffffffffffffffffffffffffffffffff9091169063b700961390606401602060405180830381865afa158015611474573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061149891906140c9565b6114a157600080fd5b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff831690811790915560405133907fa3396fd7f6e0a21b50e5089d2da70d5ac0a3bbbd1f617a93f134b7638998019890600090a350565b60008181526003602052604081206002015465ffffffffffff16635dba24008111156115415742811115610b4c565b1592915050565b60008181526002602052604090205473ffffffffffffffffffffffffffffffffffffffff1633146115a5576040517f4e1c8b5d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6115ae81612f3d565b50565b6115df336000357fffffffff0000000000000000000000000000000000000000000000000000000016612034565b611645576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f554e415554484f52495a454400000000000000000000000000000000000000006044820152606401610c9e565b73ffffffffffffffffffffffffffffffffffffffff91909116600090815260086020526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016911515919091179055565b60006116a682612fc1565b600083815260026020526040902060050154610d5a9190613f72565b60008281526002602052604090205473ffffffffffffffffffffffffffffffffffffffff16331461171f576040517f4e1c8b5d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60009182526006602052604090912080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff909216919091179055565b6117a0336000357fffffffff0000000000000000000000000000000000000000000000000000000016612034565b611806576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f554e415554484f52495a454400000000000000000000000000000000000000006044820152606401610c9e565b805160098054602084015160408501516060860151608087015160a09097015163ffffffff90811674010000000000000000000000000000000000000000029181166c0100000000000000000000000002938116640100000000029681167fffffffffffffffffffffffffffffffffffffffffffffffff000000000000000090951694909417959095177fffffffffffffffffffffffffffffffff0000000000000000ffffffffffffffff1690831668010000000000000000027fffffffffffffffffffffffffffffffff00000000ffffffffffffffffffffffff1617177fffffffffffffffff0000000000000000ffffffffffffffffffffffffffffffff169316700100000000000000000000000000000000027fffffffffffffffff00000000ffffffffffffffffffffffffffffffffffffffff1692909217179055565b6040517fb88c914800000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82811660048301526000918291611a14917f00000000000000000000000000000000000000000000000000000000000000009091169063b88c914890602401602060405180830381865afa1580156119dd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a0191906140e6565b869065ffffffffffff16620186a0612e71565b6000858152600260205260408120600a015491925090611a4890611a3787610d60565b611a41858a613f72565b9190612e71565b600086815260026020526040902060070154909150811115611a96576040517f5c430eae00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b9150610b4c9050565b6000828152600260208181526040808420815161018081018352815473ffffffffffffffffffffffffffffffffffffffff9081168252600183015481169482019490945293810154831691840191909152600381015491821660608401527401000000000000000000000000000000000000000090910460ff1615156080830152600481015460a0830152600581015460c0830152600681015460e0830152600781015461010083015260088101546101208301526009810154610140830152600a015461016082015281611b7385610d60565b905060008260800151611b9b5761016083015160a0840151611b96918490612e71565b611ba1565b8260a001515b90506000611bc483856101600151866101000151612e719092919063ffffffff16565b90506000818310611bd55781611bd7565b825b6040517fb88c914800000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8981166004830152919250600091611ca6917f00000000000000000000000000000000000000000000000000000000000000009091169063b88c914890602401602060405180830381865afa158015611c6f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c9391906140e6565b839065ffffffffffff16620186a0612e71565b9050611cb28183614103565b9998505050505050505050565b60003373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614611d30576040517f2c47703200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600084815260026020818152604080842060038352938190208151608081018352815481526001820154938101939093529092015465ffffffffffff80821693830193909352660100000000000090048216606082018190529091429190821610611dd75760608201516040517f07fc4a7000000000000000000000000000000000000000000000000000000000815265ffffffffffff9091166004820152602401610c9e565b6000611de4888842613132565b9550905085851015611e22576040517f74ec9d5b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8360070154851115611e60576040517f5c430eae00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600384015474010000000000000000000000000000000000000000900460ff16611e905783600401548511611e98565b836004015487115b15611ecf576040517ff3383dc900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600384015474010000000000000000000000000000000000000000900460ff16611ef95784611efb565b865b600485018054919091039055600584018054860160010190819055600985018054890190556008850180548701905560208401511015611f4357611f3e88612f3d565b611f4e565b611f4e888383613474565b505050509392505050565b600081815260056020908152604080832081516080810183528154815260019091015465ffffffffffff8082169483019490945266010000000000008104909316918101919091526c0100000000000000000000000090910460ff1615156060820181905282918291611fd75760008060009350935093505061202d565b6020810151611fe6904261411b565b9250806040015165ffffffffffff168365ffffffffffff161091508161200d578051612029565b604081015181516120299165ffffffffffff8087169116612e71565b9350505b9193909250565b60015460009073ffffffffffffffffffffffffffffffffffffffff16801580159061211857506040517fb700961300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85811660048301523060248301527fffffffff000000000000000000000000000000000000000000000000000000008516604483015282169063b700961390606401602060405180830381865afa1580156120f4573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061211891906140c9565b8061213d575060005473ffffffffffffffffffffffffffffffffffffffff8581169116145b949350505050565b6000612152848484612e71565b90506000828061216457612164614142565b8486091115610b4c577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff811061219957600080fd5b6001019392505050565b60075460009060ff166121e2576040517f64be3ffa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000826000015173ffffffffffffffffffffffffffffffffffffffff1663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015612233573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122579190614171565b90506000836020015173ffffffffffffffffffffffffffffffffffffffff1663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa1580156122aa573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122ce9190614171565b905060068260ff1610806122e5575060128260ff16115b1561231c576040517f3b596f5f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60068160ff161080612331575060128160ff16115b15612368576040517f3b596f5f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe884610160015160000b12806123a65750601884610160015160000b135b156123dd576040517f3b596f5f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3360009081526008602052604090205460ff161580156124165750604084015173ffffffffffffffffffffffffffffffffffffffff1615155b1561244d576040517f2c47703200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050600082610160015160240160ff16600a0a90508260c001518360a0015110156124a4576040517f4496547d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b825160208401516040517fb435914300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff928316600482015290821660248201526000917f0000000000000000000000000000000000000000000000000000000000000000169063b4359143906044016020604051808303816000875af1158015612544573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125689190614194565b90506000804286610120015165ffffffffffff166125869190613f72565b60095490925063ffffffff700100000000000000000000000000000000909104811690831610806125d7575060095461014087015163ffffffff6c0100000000000000000000000090920482169116105b1561260e576040517f3b596f5f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000866101400151600561262291906141ad565b60095490915063ffffffff808316680100000000000000009092041611612649578061265f565b60095468010000000000000000900463ffffffff165b6009546101408901519193506000916126b79163ffffffff90811691161161268f5760095463ffffffff16612696565b8861014001515b63ffffffff168563ffffffff168a60800151612e719092919063ffffffff16565b60408051610140808201835265ffffffffffff4216808352602083015263ffffffff808916938301939093528b018051831660608301526009549051939450909260808401929182169116116127155760095463ffffffff1661271c565b8961014001515b63ffffffff9081168252600954640100000000900481166020830152851660408201526060810183905260808a81015191019061275a908490613f72565b81526020016127988563ffffffff168763ffffffff168c60600151612783578c60800151611a41565b60a08d015160808e0151611a41918d90612e71565b90526000868152600460209081526040808320845181549386015192860151606080880151608089015160a08a015160c08b015165ffffffffffff9687167fffffffffffffffffffffffffffffffffffffffff000000000000000000000000909a169990991766010000000000009690981695909502969096177fffffffffffffffffffffffff0000000000000000ffffffffffffffffffffffff166c0100000000000000000000000063ffffffff948516027fffffffffffffffffffffffff00000000ffffffffffffffffffffffffffffffff161770010000000000000000000000000000000091841691909102177fffffffff0000000000000000ffffffffffffffffffffffffffffffffffffffff1674010000000000000000000000000000000000000000958316959095027fffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffffff1694909417780100000000000000000000000000000000000000000000000092821692909202919091177bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167c0100000000000000000000000000000000000000000000000000000000919094160292909217825560e084015160018301556101008401516002830155610120909301516003909101559089015190925082915081906129955788608001516129aa565b60a089015160808a01516129aa918990612e71565b90506129c38163ffffffff8087169088811690612e7116565b92506129eb89610140015163ffffffff168663ffffffff1683612e719092919063ffffffff16565b9150506040518061018001604052803373ffffffffffffffffffffffffffffffffffffffff168152602001896000015173ffffffffffffffffffffffffffffffffffffffff168152602001896020015173ffffffffffffffffffffffffffffffffffffffff168152602001896040015173ffffffffffffffffffffffffffffffffffffffff168152602001896060015115158152602001896080015181526020018381526020018960c0015181526020018281526020016000815260200160008152602001878152506002600087815260200190815260200160002060008201518160000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060208201518160010160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060408201518160020160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060608201518160030160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060808201518160030160146101000a81548160ff02191690831515021790555060a0820151816004015560c0820151816005015560e0820151816006015561010082015181600701556101208201518160080155610140820151816009015561016082015181600a01559050506000600960149054906101000a900463ffffffff1663ffffffff16612c8b620186a065ffffffffffff168585612e719092919063ffffffff16565b11612cb65760095474010000000000000000000000000000000000000000900463ffffffff16612cc4565b612cc482620186a085612e71565b90506000612cf9828b60e0015163ffffffff1611612ce25782612cee565b8a60e0015163ffffffff165b8590620186a0612e71565b612d039085614103565b60a08b0151909150600090612d19908a87612e71565b905060405180608001604052808281526020018381526020018c610100015165ffffffffffff1681526020018c610120015165ffffffffffff16815250600360008a8152602001908152602001600020600082015181600001556020820151816001015560408201518160020160006101000a81548165ffffffffffff021916908365ffffffffffff16021790555060608201518160020160066101000a81548165ffffffffffff021916908365ffffffffffff1602179055509050508a6020015173ffffffffffffffffffffffffffffffffffffffff168b6000015173ffffffffffffffffffffffffffffffffffffffff16897f8235b14cd272b4e791960fe1118559bb7fed86934fcffeeae9b1175103b0756d8e61010001518f60a00151604051612e5a92919065ffffffffffff929092168252602082015260400190565b60405180910390a450959998505050505050505050565b600080807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff85870985870292508281108382030391505080600003612ec85760008411612ebd57600080fd5b508290049050610b4c565b808411612ed457600080fd5b60008486880960026001871981018816978890046003810283188082028403028082028403028082028403028082028403028082028403029081029092039091026000889003889004909101858311909403939093029303949094049190911702949350505050565b6000818152600360209081526040808320600290810180547fffffffffffffffffffffffffffffffffffffffff000000000000ffffffffffff1666010000000000004265ffffffffffff16021790559091528082206004018290555182917f9dc30b8eda31a6a144e092e5de600955523a6a925cc15cc1d1b9b4872cfa615591a250565b6000818152600460209081526040808320815161014081018352815465ffffffffffff8082168352660100000000000082041694820185905263ffffffff6c0100000000000000000000000082048116948301949094527001000000000000000000000000000000008104841660608301527401000000000000000000000000000000000000000081048416608083015278010000000000000000000000000000000000000000000000008104841660a08301527c0100000000000000000000000000000000000000000000000000000000900490921660c0830152600181015460e08301526002810154610100830152600301546101208201529042838282116130cd5760006130d1565b8282035b90508360c0015163ffffffff1681116131155760c084015160008781526002602052604090206005015461311091839063ffffffff90811690612e7116565b613128565b6000868152600260205260409020600501545b9695505050505050565b6000838152600260208181526040808420815161018081018352815473ffffffffffffffffffffffffffffffffffffffff9081168252600183015481168286015294820154851681840152600382015494851660608201527401000000000000000000000000000000000000000090940460ff161515608085015260048082015460a0860152600582015460c0860152600682015460e0860152600782015461010086015260088201546101208601526009820154610140860152600a9091015461016085015287855290915282205482919042660100000000000090910465ffffffffffff161161324e5761322786612fc1565b60008781526002602052604081206005018054909190613248908490613f72565b90915550505b6000868152600560205260409020600101546c01000000000000000000000000900460ff16156133835760008681526005602052604081209080806132928a611f59565b60008d8152600360205260408120805494975092955090935085926132b8908490613f72565b9091555050801561335357828460000160008282546132d79190613f72565b90915550506001840180548391906006906133059084906601000000000000900465ffffffffffff1661411b565b92506101000a81548165ffffffffffff021916908365ffffffffffff160217905550878460010160006101000a81548165ffffffffffff021916908365ffffffffffff16021790555061337e565b6001840180547fffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffff1690555b505050505b61338c86613b68565b60e0820151909350808410156133a0578093505b6101608201516133b290879086612e71565b60008881526004602052604081208054600390910154929550909161340691633b9aca009063ffffffff7c0100000000000000000000000000000000000000000000000000000000909104811690612e7116565b905061341784633b9aca0083612145565b600089815260046020526040902080546006906134479084906601000000000000900465ffffffffffff166141d9565b92506101000a81548165ffffffffffff021916908365ffffffffffff160217905550505050935093915050565b600060046000858152602001908152602001600020604051806101400160405290816000820160009054906101000a900465ffffffffffff1665ffffffffffff1665ffffffffffff1681526020016000820160069054906101000a900465ffffffffffff1665ffffffffffff1665ffffffffffff16815260200160008201600c9054906101000a900463ffffffff1663ffffffff1663ffffffff1681526020016000820160109054906101000a900463ffffffff1663ffffffff1663ffffffff1681526020016000820160149054906101000a900463ffffffff1663ffffffff1663ffffffff1681526020016000820160189054906101000a900463ffffffff1663ffffffff1663ffffffff16815260200160008201601c9054906101000a900463ffffffff1663ffffffff1663ffffffff16815260200160018201548152602001600282015481526020016003820154815250509050600060026000868152602001908152602001600020604051806101800160405290816000820160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020016001820160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020016002820160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020016003820160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020016003820160149054906101000a900460ff16151515158152602001600482015481526020016005820154815260200160068201548152602001600782015481526020016008820154815260200160098201548152602001600a8201548152505090506000846003600088815260200190815260200160002060020160069054906101000a900465ffffffffffff166137dd919061411b565b65ffffffffffff169050600082608001516137fc578260a00151613811565b61016083015160a08401516138119187612e71565b9050600083608001516138295783610120015161383f565b61016084015161014085015161383f9188612e71565b6138499083614103565b905060008261388285886040015163ffffffff166138679190613f72565b886040015163ffffffff1685612e719092919063ffffffff16565b61388c9190614103565b90508561010001518560a001511080156138a557508181105b806138e05750608086015186516138c29163ffffffff16906141d9565b65ffffffffffff168865ffffffffffff16101580156138e057508181115b15613b5d57613904866060015163ffffffff168585612e719092919063ffffffff16565b60008a8152600260205260408082206007019290925560c088015191880151909161393d91849163ffffffff90811691811690612e7116565b60008b81526003602052604081205461016089015192935091613962908b9085612145565b60408051848152602081018390529192508d917f78f9c01d72705dba80d6ce051d36a1f987bf2a3800fee938c111a2ae741e57d1910160405180910390a281811015613a905760006139b48284613f72565b905060405180608001604052808281526020018d65ffffffffffff1681526020018b60a0015163ffffffff1665ffffffffffff16815260200160011515815250600560008f81526020019081526020016000206000820151816000015560208201518160010160006101000a81548165ffffffffffff021916908365ffffffffffff16021790555060408201518160010160066101000a81548165ffffffffffff021916908365ffffffffffff160217905550606082015181600101600c6101000a81548160ff02191690831515021790555090505050613ad5565b60008c81526003602090815260408083208490556005909152902060010180547fffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffff1690555b60008c815260046020526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000001665ffffffffffff8d1617905560e089015160a089015111613b29576000613b3d565b8860e001518860a00151613b3d9190613f72565b60008d815260046020526040902060028101919091556003019290925550505b505050505050505050565b6000818152600260208181526040808420815161018081018352815473ffffffffffffffffffffffffffffffffffffffff908116825260018301548116828601529482015485168184015260038083015495861660608301527401000000000000000000000000000000000000000090950460ff1615156080820152600482015460a0820152600582015460c08201819052600683015460e0830152600783015461010083015260088301546101208301526009830154610140830152600a90920154610160820181905287875294909352908420549192610b4c929190612e71565b600060208284031215613c5d57600080fd5b5035919050565b73ffffffffffffffffffffffffffffffffffffffff811681146115ae57600080fd5b8035613c9181613c64565b919050565b600060208284031215613ca857600080fd5b8135610b4c81613c64565b60008060208385031215613cc657600080fd5b823567ffffffffffffffff80821115613cde57600080fd5b818501915085601f830112613cf257600080fd5b813581811115613d0157600080fd5b866020828501011115613d1357600080fd5b60209290920196919550909350505050565b80151581146115ae57600080fd5b8035613c9181613d25565b600060208284031215613d5057600080fd5b8135610b4c81613d25565b60008060808385031215613d6e57600080fd5b8235915083608084011115613d8257600080fd5b50926020919091019150565b60008060408385031215613da157600080fd5b8235613dac81613c64565b91506020830135613dbc81613d25565b809150509250929050565b60008060408385031215613dda57600080fd5b823591506020830135613dbc81613c64565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051610180810167ffffffffffffffff81118282101715613e3f57613e3f613dec565b60405290565b803563ffffffff81168114613c9157600080fd5b600060c08284031215613e6b57600080fd5b82601f830112613e7a57600080fd5b60405160c0810181811067ffffffffffffffff82111715613e9d57613e9d613dec565b6040528060c0840185811115613eb257600080fd5b845b81811015613ed357613ec581613e45565b835260209283019201613eb4565b509195945050505050565b600080600060608486031215613ef357600080fd5b83359250602084013591506040840135613f0c81613c64565b809150509250925092565b600080600060608486031215613f2c57600080fd5b505081359360208301359350604090920135919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600082821015613f8457613f84613f43565b500390565b65ffffffffffff811681146115ae57600080fd5b8035613c9181613f89565b8035600081900b8114613c9157600080fd5b60006101808284031215613fcd57600080fd5b613fd5613e1b565b613fde83613c86565b8152613fec60208401613c86565b6020820152613ffd60408401613c86565b604082015261400e60608401613d33565b60608201526080830135608082015260a083013560a082015260c083013560c082015261403d60e08401613e45565b60e0820152610100614050818501613f9d565b90820152610120614062848201613f9d565b90820152610140614074848201613e45565b90820152610160614086848201613fa8565b908201529392505050565b6000602082840312156140a357600080fd5b8151610b4c81613c64565b6000602082840312156140c057600080fd5b610b4c82613e45565b6000602082840312156140db57600080fd5b8151610b4c81613d25565b6000602082840312156140f857600080fd5b8151610b4c81613f89565b6000821982111561411657614116613f43565b500190565b600065ffffffffffff8381169083168181101561413a5761413a613f43565b039392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b60006020828403121561418357600080fd5b815160ff81168114610b4c57600080fd5b6000602082840312156141a657600080fd5b5051919050565b600063ffffffff808316818516818304811182151516156141d0576141d0613f43565b02949350505050565b600065ffffffffffff8083168185168083038211156141fa576141fa613f43565b0194935050505056fea26469706673582212204ebe048e16250c60f03f21d2557e0fe1a8ddc2725ee7c865ff636e241b955d9e64736f6c634300080f0033000000000000000000000000007fe7c498a2cf30971ad8f2cbc36bd14ac51156000000000000000000000000007a66b9e719b3abb2f3917eb47d4231a17f5a0d000000000000000000000000007bd11fca0daaeadd455b51826f9a015f2f0969000000000000000000000000007a0f3b057945db86408197daa7c04373b5a94a
Deployed ByteCode
0x608060405234801561001057600080fd5b50600436106102ad5760003560e01c8063acc5570c1161017b578063bf7e214f116100d8578063d9ccdc931161008c578063e3684e3911610071578063e3684e39146109af578063e922067314610aed578063ea0aca3314610b0957600080fd5b8063d9ccdc931461098c578063e007fa971461099c57600080fd5b8063c7bf8ca0116100bd578063c7bf8ca014610935578063d204068714610948578063d2bee3231461096857600080fd5b8063bf7e214f146108a2578063c0aa0e8a146108c257600080fd5b8063bc3b2b121161012f578063bcf6cde811610114578063bcf6cde814610869578063bd1f3a5e1461087c578063bf48582b1461088f57600080fd5b8063bc3b2b12146107d2578063bcb296671461085657600080fd5b8063afa9d3b011610160578063afa9d3b0146106a1578063b1283e77146106ae578063bbbdd95a146107bf57600080fd5b8063acc5570c14610534578063ae4180951461068e57600080fd5b80635f77274e116102295780638973082c116101dd5780638da5cb5b116101c25780638da5cb5b146104cb578063946824cd146104eb5780639787d1071461050e57600080fd5b80638973082c1461047b5780638b098db3146104b857600080fd5b80636729a41e1161020e5780636729a41e1461041f578063699e17d9146104555780637a9e5e4b1461046857600080fd5b80635f77274e146103d65780636352211e146103e957600080fd5b806327507458116102805780633adec5a7116102655780633adec5a71461038d57806353c7f8e0146103a05780635dc4d16b146103b357600080fd5b806327507458146103235780633ad59dbc1461034657600080fd5b80630a9d85eb146102b257806310b05317146102d857806313af4035146102ed5780631c063a6c14610300575b600080fd5b6102c56102c0366004613c4b565b610b21565b6040519081526020015b60405180910390f35b6102eb6102e6366004613c4b565b610b53565b005b6102eb6102fb366004613c96565b610c0e565b6102c561030e366004613c4b565b60009081526002602052604090206004015490565b610336610331366004613c4b565b610d17565b60405190151581526020016102cf565b7f000000000000000000000000007a66b9e719b3abb2f3917eb47d4231a17f5a0d5b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016102cf565b6102c561039b366004613c4b565b610d60565b6102c56103ae366004613cb3565b610dc3565b6103366103c1366004613c96565b60086020526000908152604090205460ff1681565b6102eb6103e4366004613d3e565b610eb4565b6103686103f7366004613c4b565b60009081526002602052604090205473ffffffffffffffffffffffffffffffffffffffff1690565b61036861042d366004613c4b565b60066020526000908152604090205473ffffffffffffffffffffffffffffffffffffffff1681565b6102eb610463366004613d5b565b610f79565b6102eb610476366004613c96565b6113b5565b6009546104a39074010000000000000000000000000000000000000000900463ffffffff1681565b60405163ffffffff90911681526020016102cf565b6103366104c6366004613c4b565b611512565b6000546103689073ffffffffffffffffffffffffffffffffffffffff1681565b6102c56104f9366004613c4b565b6000908152600260205260409020600a015490565b7f000000000000000000000000007fe7c498a2cf30971ad8f2cbc36bd14ac51156610368565b610638610542366004613c4b565b6000818152600260208181526040808420815161018081018352815473ffffffffffffffffffffffffffffffffffffffff90811680835260018401548216838701819052848801548316848701819052600380870154948516606087018190527401000000000000000000000000000000000000000090950460ff1615156080870152600487015460a0870152600587015460c0870152600687015460e08701526007870154610100870190815260088801546101208801526009880154610140880152600a90970154610160909601959095529989529290955292909520909301549251919590949265ffffffffffff169190565b6040805173ffffffffffffffffffffffffffffffffffffffff97881681529587166020870152938616938501939093529316606083015265ffffffffffff909216608082015260a081019190915260c0016102cf565b6102eb61069c366004613c4b565b611548565b6007546103369060ff1681565b6107456106bc366004613c4b565b600260208190526000918252604090912080546001820154928201546003830154600484015460058501546006860154600787015460088801546009890154600a9099015473ffffffffffffffffffffffffffffffffffffffff9889169a891699978916988716977401000000000000000000000000000000000000000090970460ff1696908c565b6040805173ffffffffffffffffffffffffffffffffffffffff9d8e1681529b8d1660208d0152998c16998b0199909952999096166060890152931515608088015260a087019290925260c086015260e0850152610100840152610120830152610140820192909252610160810191909152610180016102cf565b6102eb6107cd366004613d8e565b6115b1565b6108256107e0366004613c4b565b6005602052600090815260409020805460019091015465ffffffffffff8082169166010000000000008104909116906c01000000000000000000000000900460ff1684565b6040516102cf949392919093845265ffffffffffff9283166020850152911660408301521515606082015260800190565b6102c5610864366004613c4b565b61169b565b6102eb610877366004613dc7565b6116c2565b6102eb61088a366004613e59565b611772565b6102c561089d366004613ede565b611946565b6001546103689073ffffffffffffffffffffffffffffffffffffffff1681565b6109076108d0366004613c4b565b60036020526000908152604090208054600182015460029092015490919065ffffffffffff80821691660100000000000090041684565b60408051948552602085019390935265ffffffffffff918216928401929092521660608201526080016102cf565b6102c5610943366004613dc7565b611a9f565b6009546104a3906c01000000000000000000000000900463ffffffff1681565b6009546104a390700100000000000000000000000000000000900463ffffffff1681565b6009546104a39063ffffffff1681565b6102c56109aa366004613f17565b611cbf565b610a876109bd366004613c4b565b600460205260009081526040902080546001820154600283015460039093015465ffffffffffff80841694660100000000000085049091169363ffffffff6c0100000000000000000000000082048116947001000000000000000000000000000000008304821694740100000000000000000000000000000000000000008404831694780100000000000000000000000000000000000000000000000085048416947c0100000000000000000000000000000000000000000000000000000000900490931692908a565b6040805165ffffffffffff9b8c1681529a90991660208b015263ffffffff978816988a01989098529486166060890152928516608088015290841660a08701529290921660c085015260e0840191909152610100830152610120820152610140016102cf565b6009546104a39068010000000000000000900463ffffffff1681565b6009546104a390640100000000900463ffffffff1681565b600080610b2d83611f59565b5050600084815260036020526040902054909150610b4c908290613f72565b9392505050565b60008181526006602052604090205473ffffffffffffffffffffffffffffffffffffffff163314610bb0576040517f2c47703200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600090815260066020908152604080832054600290925290912080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff909216919091179055565b610c3c336000357fffffffff0000000000000000000000000000000000000000000000000000000016612034565b610ca7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f554e415554484f52495a4544000000000000000000000000000000000000000060448201526064015b60405180910390fd5b600080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83169081178255604051909133917f8292fce18fa69edf4db7b94ea2e58241df0ae57f97e0a6c9b29067028bf92d769190a350565b60008181526002602052604081206004015415801590610d5a575060008281526003602052604090206002015442660100000000000090910465ffffffffffff16115b92915050565b600080610d91610d6f8461169b565b6000858152600260205260409020600a0154610d8a86610b21565b9190612145565b6000848152600260205260409020600601549091508111610d5a57600083815260026020526040902060060154610b4c565b600080610dd283850185613fba565b90506000610ddf826121a3565b82516101008401516040517fc6e38a4b00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff928316600482015265ffffffffffff90911660248201529192507f000000000000000000000000007fe7c498a2cf30971ad8f2cbc36bd14ac51156169063c6e38a4b906044016020604051808303816000875af1158015610e87573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610eab9190614091565b50949350505050565b610ee2336000357fffffffff0000000000000000000000000000000000000000000000000000000016612034565b610f48576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f554e415554484f52495a454400000000000000000000000000000000000000006044820152606401610c9e565b600780547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016911515919091179055565b610f8660208201826140ae565b63ffffffff161580610fab5750610fa360408201602083016140ae565b63ffffffff16155b80610fc95750610fc160608201604083016140ae565b63ffffffff16155b15611000576040517f3b596f5f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61101060408201602083016140ae565b63ffffffff1661102360208301836140ae565b63ffffffff161015611061576040517f3b596f5f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008281526004602090815260409091208054909170010000000000000000000000000000000090910463ffffffff169061109e908401846140ae565b63ffffffff1610156110dc576040517f3b596f5f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60095468010000000000000000900463ffffffff1661110160608401604085016140ae565b63ffffffff16101561113f576040517f3b596f5f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600083815260026020818152604092839020835161018081018552815473ffffffffffffffffffffffffffffffffffffffff908116808352600184015482169483019490945293820154841694810194909452600381015492831660608501527401000000000000000000000000000000000000000090920460ff1615156080840152600482015460a0840152600582015460c0840152600682015460e0840152600782015461010084015260088201546101208401526009820154610140840152600a909101546101608301523314611245576040517f4e1c8b5d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61125260208401846140ae565b825463ffffffff9190911674010000000000000000000000000000000000000000027fffffffffffffffff00000000ffffffffffffffffffffffffffffffffffffffff9091161782556112f06112ab60208501856140ae565b60008681526003602052604090206002015463ffffffff91909116906112e49042906601000000000000900465ffffffffffff16613f72565b60a08401519190612e71565b600183015561130560408401602085016140ae565b825463ffffffff919091167801000000000000000000000000000000000000000000000000027fffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffffff90911617825561136260608401604085016140ae565b825463ffffffff919091167c0100000000000000000000000000000000000000000000000000000000027bffffffffffffffffffffffffffffffffffffffffffffffffffffffff90911617909155505050565b60005473ffffffffffffffffffffffffffffffffffffffff1633148061149857506001546040517fb70096130000000000000000000000000000000000000000000000000000000081523360048201523060248201526000357fffffffff0000000000000000000000000000000000000000000000000000000016604482015273ffffffffffffffffffffffffffffffffffffffff9091169063b700961390606401602060405180830381865afa158015611474573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061149891906140c9565b6114a157600080fd5b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff831690811790915560405133907fa3396fd7f6e0a21b50e5089d2da70d5ac0a3bbbd1f617a93f134b7638998019890600090a350565b60008181526003602052604081206002015465ffffffffffff16635dba24008111156115415742811115610b4c565b1592915050565b60008181526002602052604090205473ffffffffffffffffffffffffffffffffffffffff1633146115a5576040517f4e1c8b5d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6115ae81612f3d565b50565b6115df336000357fffffffff0000000000000000000000000000000000000000000000000000000016612034565b611645576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f554e415554484f52495a454400000000000000000000000000000000000000006044820152606401610c9e565b73ffffffffffffffffffffffffffffffffffffffff91909116600090815260086020526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016911515919091179055565b60006116a682612fc1565b600083815260026020526040902060050154610d5a9190613f72565b60008281526002602052604090205473ffffffffffffffffffffffffffffffffffffffff16331461171f576040517f4e1c8b5d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60009182526006602052604090912080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff909216919091179055565b6117a0336000357fffffffff0000000000000000000000000000000000000000000000000000000016612034565b611806576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f554e415554484f52495a454400000000000000000000000000000000000000006044820152606401610c9e565b805160098054602084015160408501516060860151608087015160a09097015163ffffffff90811674010000000000000000000000000000000000000000029181166c0100000000000000000000000002938116640100000000029681167fffffffffffffffffffffffffffffffffffffffffffffffff000000000000000090951694909417959095177fffffffffffffffffffffffffffffffff0000000000000000ffffffffffffffff1690831668010000000000000000027fffffffffffffffffffffffffffffffff00000000ffffffffffffffffffffffff1617177fffffffffffffffff0000000000000000ffffffffffffffffffffffffffffffff169316700100000000000000000000000000000000027fffffffffffffffff00000000ffffffffffffffffffffffffffffffffffffffff1692909217179055565b6040517fb88c914800000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82811660048301526000918291611a14917f000000000000000000000000007fe7c498a2cf30971ad8f2cbc36bd14ac511569091169063b88c914890602401602060405180830381865afa1580156119dd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a0191906140e6565b869065ffffffffffff16620186a0612e71565b6000858152600260205260408120600a015491925090611a4890611a3787610d60565b611a41858a613f72565b9190612e71565b600086815260026020526040902060070154909150811115611a96576040517f5c430eae00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b9150610b4c9050565b6000828152600260208181526040808420815161018081018352815473ffffffffffffffffffffffffffffffffffffffff9081168252600183015481169482019490945293810154831691840191909152600381015491821660608401527401000000000000000000000000000000000000000090910460ff1615156080830152600481015460a0830152600581015460c0830152600681015460e0830152600781015461010083015260088101546101208301526009810154610140830152600a015461016082015281611b7385610d60565b905060008260800151611b9b5761016083015160a0840151611b96918490612e71565b611ba1565b8260a001515b90506000611bc483856101600151866101000151612e719092919063ffffffff16565b90506000818310611bd55781611bd7565b825b6040517fb88c914800000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8981166004830152919250600091611ca6917f000000000000000000000000007fe7c498a2cf30971ad8f2cbc36bd14ac511569091169063b88c914890602401602060405180830381865afa158015611c6f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c9391906140e6565b839065ffffffffffff16620186a0612e71565b9050611cb28183614103565b9998505050505050505050565b60003373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000007fe7c498a2cf30971ad8f2cbc36bd14ac511561614611d30576040517f2c47703200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600084815260026020818152604080842060038352938190208151608081018352815481526001820154938101939093529092015465ffffffffffff80821693830193909352660100000000000090048216606082018190529091429190821610611dd75760608201516040517f07fc4a7000000000000000000000000000000000000000000000000000000000815265ffffffffffff9091166004820152602401610c9e565b6000611de4888842613132565b9550905085851015611e22576040517f74ec9d5b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8360070154851115611e60576040517f5c430eae00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600384015474010000000000000000000000000000000000000000900460ff16611e905783600401548511611e98565b836004015487115b15611ecf576040517ff3383dc900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600384015474010000000000000000000000000000000000000000900460ff16611ef95784611efb565b865b600485018054919091039055600584018054860160010190819055600985018054890190556008850180548701905560208401511015611f4357611f3e88612f3d565b611f4e565b611f4e888383613474565b505050509392505050565b600081815260056020908152604080832081516080810183528154815260019091015465ffffffffffff8082169483019490945266010000000000008104909316918101919091526c0100000000000000000000000090910460ff1615156060820181905282918291611fd75760008060009350935093505061202d565b6020810151611fe6904261411b565b9250806040015165ffffffffffff168365ffffffffffff161091508161200d578051612029565b604081015181516120299165ffffffffffff8087169116612e71565b9350505b9193909250565b60015460009073ffffffffffffffffffffffffffffffffffffffff16801580159061211857506040517fb700961300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85811660048301523060248301527fffffffff000000000000000000000000000000000000000000000000000000008516604483015282169063b700961390606401602060405180830381865afa1580156120f4573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061211891906140c9565b8061213d575060005473ffffffffffffffffffffffffffffffffffffffff8581169116145b949350505050565b6000612152848484612e71565b90506000828061216457612164614142565b8486091115610b4c577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff811061219957600080fd5b6001019392505050565b60075460009060ff166121e2576040517f64be3ffa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000826000015173ffffffffffffffffffffffffffffffffffffffff1663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015612233573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122579190614171565b90506000836020015173ffffffffffffffffffffffffffffffffffffffff1663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa1580156122aa573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122ce9190614171565b905060068260ff1610806122e5575060128260ff16115b1561231c576040517f3b596f5f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60068160ff161080612331575060128160ff16115b15612368576040517f3b596f5f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe884610160015160000b12806123a65750601884610160015160000b135b156123dd576040517f3b596f5f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3360009081526008602052604090205460ff161580156124165750604084015173ffffffffffffffffffffffffffffffffffffffff1615155b1561244d576040517f2c47703200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050600082610160015160240160ff16600a0a90508260c001518360a0015110156124a4576040517f4496547d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b825160208401516040517fb435914300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff928316600482015290821660248201526000917f000000000000000000000000007a66b9e719b3abb2f3917eb47d4231a17f5a0d169063b4359143906044016020604051808303816000875af1158015612544573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125689190614194565b90506000804286610120015165ffffffffffff166125869190613f72565b60095490925063ffffffff700100000000000000000000000000000000909104811690831610806125d7575060095461014087015163ffffffff6c0100000000000000000000000090920482169116105b1561260e576040517f3b596f5f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000866101400151600561262291906141ad565b60095490915063ffffffff808316680100000000000000009092041611612649578061265f565b60095468010000000000000000900463ffffffff165b6009546101408901519193506000916126b79163ffffffff90811691161161268f5760095463ffffffff16612696565b8861014001515b63ffffffff168563ffffffff168a60800151612e719092919063ffffffff16565b60408051610140808201835265ffffffffffff4216808352602083015263ffffffff808916938301939093528b018051831660608301526009549051939450909260808401929182169116116127155760095463ffffffff1661271c565b8961014001515b63ffffffff9081168252600954640100000000900481166020830152851660408201526060810183905260808a81015191019061275a908490613f72565b81526020016127988563ffffffff168763ffffffff168c60600151612783578c60800151611a41565b60a08d015160808e0151611a41918d90612e71565b90526000868152600460209081526040808320845181549386015192860151606080880151608089015160a08a015160c08b015165ffffffffffff9687167fffffffffffffffffffffffffffffffffffffffff000000000000000000000000909a169990991766010000000000009690981695909502969096177fffffffffffffffffffffffff0000000000000000ffffffffffffffffffffffff166c0100000000000000000000000063ffffffff948516027fffffffffffffffffffffffff00000000ffffffffffffffffffffffffffffffff161770010000000000000000000000000000000091841691909102177fffffffff0000000000000000ffffffffffffffffffffffffffffffffffffffff1674010000000000000000000000000000000000000000958316959095027fffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffffff1694909417780100000000000000000000000000000000000000000000000092821692909202919091177bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167c0100000000000000000000000000000000000000000000000000000000919094160292909217825560e084015160018301556101008401516002830155610120909301516003909101559089015190925082915081906129955788608001516129aa565b60a089015160808a01516129aa918990612e71565b90506129c38163ffffffff8087169088811690612e7116565b92506129eb89610140015163ffffffff168663ffffffff1683612e719092919063ffffffff16565b9150506040518061018001604052803373ffffffffffffffffffffffffffffffffffffffff168152602001896000015173ffffffffffffffffffffffffffffffffffffffff168152602001896020015173ffffffffffffffffffffffffffffffffffffffff168152602001896040015173ffffffffffffffffffffffffffffffffffffffff168152602001896060015115158152602001896080015181526020018381526020018960c0015181526020018281526020016000815260200160008152602001878152506002600087815260200190815260200160002060008201518160000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060208201518160010160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060408201518160020160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060608201518160030160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060808201518160030160146101000a81548160ff02191690831515021790555060a0820151816004015560c0820151816005015560e0820151816006015561010082015181600701556101208201518160080155610140820151816009015561016082015181600a01559050506000600960149054906101000a900463ffffffff1663ffffffff16612c8b620186a065ffffffffffff168585612e719092919063ffffffff16565b11612cb65760095474010000000000000000000000000000000000000000900463ffffffff16612cc4565b612cc482620186a085612e71565b90506000612cf9828b60e0015163ffffffff1611612ce25782612cee565b8a60e0015163ffffffff165b8590620186a0612e71565b612d039085614103565b60a08b0151909150600090612d19908a87612e71565b905060405180608001604052808281526020018381526020018c610100015165ffffffffffff1681526020018c610120015165ffffffffffff16815250600360008a8152602001908152602001600020600082015181600001556020820151816001015560408201518160020160006101000a81548165ffffffffffff021916908365ffffffffffff16021790555060608201518160020160066101000a81548165ffffffffffff021916908365ffffffffffff1602179055509050508a6020015173ffffffffffffffffffffffffffffffffffffffff168b6000015173ffffffffffffffffffffffffffffffffffffffff16897f8235b14cd272b4e791960fe1118559bb7fed86934fcffeeae9b1175103b0756d8e61010001518f60a00151604051612e5a92919065ffffffffffff929092168252602082015260400190565b60405180910390a450959998505050505050505050565b600080807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff85870985870292508281108382030391505080600003612ec85760008411612ebd57600080fd5b508290049050610b4c565b808411612ed457600080fd5b60008486880960026001871981018816978890046003810283188082028403028082028403028082028403028082028403028082028403029081029092039091026000889003889004909101858311909403939093029303949094049190911702949350505050565b6000818152600360209081526040808320600290810180547fffffffffffffffffffffffffffffffffffffffff000000000000ffffffffffff1666010000000000004265ffffffffffff16021790559091528082206004018290555182917f9dc30b8eda31a6a144e092e5de600955523a6a925cc15cc1d1b9b4872cfa615591a250565b6000818152600460209081526040808320815161014081018352815465ffffffffffff8082168352660100000000000082041694820185905263ffffffff6c0100000000000000000000000082048116948301949094527001000000000000000000000000000000008104841660608301527401000000000000000000000000000000000000000081048416608083015278010000000000000000000000000000000000000000000000008104841660a08301527c0100000000000000000000000000000000000000000000000000000000900490921660c0830152600181015460e08301526002810154610100830152600301546101208201529042838282116130cd5760006130d1565b8282035b90508360c0015163ffffffff1681116131155760c084015160008781526002602052604090206005015461311091839063ffffffff90811690612e7116565b613128565b6000868152600260205260409020600501545b9695505050505050565b6000838152600260208181526040808420815161018081018352815473ffffffffffffffffffffffffffffffffffffffff9081168252600183015481168286015294820154851681840152600382015494851660608201527401000000000000000000000000000000000000000090940460ff161515608085015260048082015460a0860152600582015460c0860152600682015460e0860152600782015461010086015260088201546101208601526009820154610140860152600a9091015461016085015287855290915282205482919042660100000000000090910465ffffffffffff161161324e5761322786612fc1565b60008781526002602052604081206005018054909190613248908490613f72565b90915550505b6000868152600560205260409020600101546c01000000000000000000000000900460ff16156133835760008681526005602052604081209080806132928a611f59565b60008d8152600360205260408120805494975092955090935085926132b8908490613f72565b9091555050801561335357828460000160008282546132d79190613f72565b90915550506001840180548391906006906133059084906601000000000000900465ffffffffffff1661411b565b92506101000a81548165ffffffffffff021916908365ffffffffffff160217905550878460010160006101000a81548165ffffffffffff021916908365ffffffffffff16021790555061337e565b6001840180547fffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffff1690555b505050505b61338c86613b68565b60e0820151909350808410156133a0578093505b6101608201516133b290879086612e71565b60008881526004602052604081208054600390910154929550909161340691633b9aca009063ffffffff7c0100000000000000000000000000000000000000000000000000000000909104811690612e7116565b905061341784633b9aca0083612145565b600089815260046020526040902080546006906134479084906601000000000000900465ffffffffffff166141d9565b92506101000a81548165ffffffffffff021916908365ffffffffffff160217905550505050935093915050565b600060046000858152602001908152602001600020604051806101400160405290816000820160009054906101000a900465ffffffffffff1665ffffffffffff1665ffffffffffff1681526020016000820160069054906101000a900465ffffffffffff1665ffffffffffff1665ffffffffffff16815260200160008201600c9054906101000a900463ffffffff1663ffffffff1663ffffffff1681526020016000820160109054906101000a900463ffffffff1663ffffffff1663ffffffff1681526020016000820160149054906101000a900463ffffffff1663ffffffff1663ffffffff1681526020016000820160189054906101000a900463ffffffff1663ffffffff1663ffffffff16815260200160008201601c9054906101000a900463ffffffff1663ffffffff1663ffffffff16815260200160018201548152602001600282015481526020016003820154815250509050600060026000868152602001908152602001600020604051806101800160405290816000820160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020016001820160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020016002820160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020016003820160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020016003820160149054906101000a900460ff16151515158152602001600482015481526020016005820154815260200160068201548152602001600782015481526020016008820154815260200160098201548152602001600a8201548152505090506000846003600088815260200190815260200160002060020160069054906101000a900465ffffffffffff166137dd919061411b565b65ffffffffffff169050600082608001516137fc578260a00151613811565b61016083015160a08401516138119187612e71565b9050600083608001516138295783610120015161383f565b61016084015161014085015161383f9188612e71565b6138499083614103565b905060008261388285886040015163ffffffff166138679190613f72565b886040015163ffffffff1685612e719092919063ffffffff16565b61388c9190614103565b90508561010001518560a001511080156138a557508181105b806138e05750608086015186516138c29163ffffffff16906141d9565b65ffffffffffff168865ffffffffffff16101580156138e057508181115b15613b5d57613904866060015163ffffffff168585612e719092919063ffffffff16565b60008a8152600260205260408082206007019290925560c088015191880151909161393d91849163ffffffff90811691811690612e7116565b60008b81526003602052604081205461016089015192935091613962908b9085612145565b60408051848152602081018390529192508d917f78f9c01d72705dba80d6ce051d36a1f987bf2a3800fee938c111a2ae741e57d1910160405180910390a281811015613a905760006139b48284613f72565b905060405180608001604052808281526020018d65ffffffffffff1681526020018b60a0015163ffffffff1665ffffffffffff16815260200160011515815250600560008f81526020019081526020016000206000820151816000015560208201518160010160006101000a81548165ffffffffffff021916908365ffffffffffff16021790555060408201518160010160066101000a81548165ffffffffffff021916908365ffffffffffff160217905550606082015181600101600c6101000a81548160ff02191690831515021790555090505050613ad5565b60008c81526003602090815260408083208490556005909152902060010180547fffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffff1690555b60008c815260046020526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000001665ffffffffffff8d1617905560e089015160a089015111613b29576000613b3d565b8860e001518860a00151613b3d9190613f72565b60008d815260046020526040902060028101919091556003019290925550505b505050505050505050565b6000818152600260208181526040808420815161018081018352815473ffffffffffffffffffffffffffffffffffffffff908116825260018301548116828601529482015485168184015260038083015495861660608301527401000000000000000000000000000000000000000090950460ff1615156080820152600482015460a0820152600582015460c08201819052600683015460e0830152600783015461010083015260088301546101208301526009830154610140830152600a90920154610160820181905287875294909352908420549192610b4c929190612e71565b600060208284031215613c5d57600080fd5b5035919050565b73ffffffffffffffffffffffffffffffffffffffff811681146115ae57600080fd5b8035613c9181613c64565b919050565b600060208284031215613ca857600080fd5b8135610b4c81613c64565b60008060208385031215613cc657600080fd5b823567ffffffffffffffff80821115613cde57600080fd5b818501915085601f830112613cf257600080fd5b813581811115613d0157600080fd5b866020828501011115613d1357600080fd5b60209290920196919550909350505050565b80151581146115ae57600080fd5b8035613c9181613d25565b600060208284031215613d5057600080fd5b8135610b4c81613d25565b60008060808385031215613d6e57600080fd5b8235915083608084011115613d8257600080fd5b50926020919091019150565b60008060408385031215613da157600080fd5b8235613dac81613c64565b91506020830135613dbc81613d25565b809150509250929050565b60008060408385031215613dda57600080fd5b823591506020830135613dbc81613c64565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051610180810167ffffffffffffffff81118282101715613e3f57613e3f613dec565b60405290565b803563ffffffff81168114613c9157600080fd5b600060c08284031215613e6b57600080fd5b82601f830112613e7a57600080fd5b60405160c0810181811067ffffffffffffffff82111715613e9d57613e9d613dec565b6040528060c0840185811115613eb257600080fd5b845b81811015613ed357613ec581613e45565b835260209283019201613eb4565b509195945050505050565b600080600060608486031215613ef357600080fd5b83359250602084013591506040840135613f0c81613c64565b809150509250925092565b600080600060608486031215613f2c57600080fd5b505081359360208301359350604090920135919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600082821015613f8457613f84613f43565b500390565b65ffffffffffff811681146115ae57600080fd5b8035613c9181613f89565b8035600081900b8114613c9157600080fd5b60006101808284031215613fcd57600080fd5b613fd5613e1b565b613fde83613c86565b8152613fec60208401613c86565b6020820152613ffd60408401613c86565b604082015261400e60608401613d33565b60608201526080830135608082015260a083013560a082015260c083013560c082015261403d60e08401613e45565b60e0820152610100614050818501613f9d565b90820152610120614062848201613f9d565b90820152610140614074848201613e45565b90820152610160614086848201613fa8565b908201529392505050565b6000602082840312156140a357600080fd5b8151610b4c81613c64565b6000602082840312156140c057600080fd5b610b4c82613e45565b6000602082840312156140db57600080fd5b8151610b4c81613d25565b6000602082840312156140f857600080fd5b8151610b4c81613f89565b6000821982111561411657614116613f43565b500190565b600065ffffffffffff8381169083168181101561413a5761413a613f43565b039392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b60006020828403121561418357600080fd5b815160ff81168114610b4c57600080fd5b6000602082840312156141a657600080fd5b5051919050565b600063ffffffff808316818516818304811182151516156141d0576141d0613f43565b02949350505050565b600065ffffffffffff8083168185168083038211156141fa576141fa613f43565b0194935050505056fea26469706673582212204ebe048e16250c60f03f21d2557e0fe1a8ddc2725ee7c865ff636e241b955d9e64736f6c634300080f0033